├── .gitignore ├── LICENSE ├── README.md ├── dll ├── SDL2.dll ├── SDL2_image.dll ├── SDL2_mixer.dll ├── SDL2_ttf.dll ├── libFLAC-8.dll ├── libfreetype-6.dll ├── libogg-0.dll ├── libpng16-16.dll ├── libtiff-5.dll ├── libvorbis-0.dll ├── libvorbisfile-3.dll ├── libwebp-4.dll ├── smpeg2.dll └── zlib1.dll ├── doc.bat ├── dub.json ├── example ├── .gitignore ├── dub.sdl ├── res │ ├── audio │ │ ├── song.mp3 │ │ └── whoosh.wav │ ├── font │ │ ├── Roboto.ttf │ │ ├── roboto.bmfc │ │ ├── roboto.fnt │ │ └── roboto_0.png │ ├── shuriken-icon.png │ └── tex │ │ ├── shuriken-color.png │ │ ├── shuriken-color.svg │ │ ├── shuriken-normal.png │ │ ├── shuriken.png │ │ └── shuriken.svg ├── source │ └── app.d └── views │ ├── default.frag │ ├── default.vert │ ├── font.vert │ └── normal.frag └── source └── d2d ├── audio ├── music.d └── sound.d ├── core ├── bytestream.d ├── color3.d ├── fpslimiter.d ├── idisposable.d ├── iverifiable.d ├── quadtree.d ├── transformable.d └── utils.d ├── font ├── bmfont.d ├── bmtext.d ├── ifont.d ├── itext.d ├── ttffont.d └── ttftext.d ├── package.d ├── rendering ├── bitmap.d ├── color.d ├── idrawable.d ├── irendertarget.d ├── mesh.d ├── rectangleshape.d ├── shader.d ├── shaderprogram.d ├── shape.d ├── spritebatch.d ├── spritesheet.d └── texture.d ├── toolkit ├── game.d └── input │ ├── keyboard.d │ └── mouse.d └── window ├── window.d ├── windowevent.d └── windowflags.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.a 5 | *.o 6 | *.obj 7 | bin/ 8 | lib/ 9 | *-test-* 10 | 11 | docs/ 12 | dub.selections.json 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2015-2019 Jan Jurzitza 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # D2DGame 2 | 2D Game Engine for Ludum Dare written in D. Its similar to SFML and wraps SDL for rendering etc. 3 | 4 | ## Documentation 5 | The documentation can be found at [https://d2d.webfreak.org/](https://d2d.webfreak.org/) 6 | 7 | ## Optional Versions 8 | 9 | If you wish to enable several features such as Fonts, Textures, Audio, etc. you may want to enable these version identifiers in your dub recipe: 10 | 11 | * `BindSDL_Image` - Enables BMFont, BMText, Bitmap.load 12 | * `BindSDL_Mixer` - Enables Music, Sound 13 | * `BindSDL_TTF` - Enables TTFFont, TTFText 14 | 15 | # Note 16 | If your on Ubuntu, run this command to install dependencies packages 17 | 18 | ```sh 19 | sudo apt install libsdl2-dev libsdl-ttf2.0-dev libsdl2-mixer-dev libsdl2-image-dev 20 | ``` 21 | -------------------------------------------------------------------------------- /dll/SDL2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/SDL2.dll -------------------------------------------------------------------------------- /dll/SDL2_image.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/SDL2_image.dll -------------------------------------------------------------------------------- /dll/SDL2_mixer.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/SDL2_mixer.dll -------------------------------------------------------------------------------- /dll/SDL2_ttf.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/SDL2_ttf.dll -------------------------------------------------------------------------------- /dll/libFLAC-8.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libFLAC-8.dll -------------------------------------------------------------------------------- /dll/libfreetype-6.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libfreetype-6.dll -------------------------------------------------------------------------------- /dll/libogg-0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libogg-0.dll -------------------------------------------------------------------------------- /dll/libpng16-16.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libpng16-16.dll -------------------------------------------------------------------------------- /dll/libtiff-5.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libtiff-5.dll -------------------------------------------------------------------------------- /dll/libvorbis-0.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libvorbis-0.dll -------------------------------------------------------------------------------- /dll/libvorbisfile-3.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libvorbisfile-3.dll -------------------------------------------------------------------------------- /dll/libwebp-4.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/libwebp-4.dll -------------------------------------------------------------------------------- /dll/smpeg2.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/smpeg2.dll -------------------------------------------------------------------------------- /dll/zlib1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/dll/zlib1.dll -------------------------------------------------------------------------------- /doc.bat: -------------------------------------------------------------------------------- 1 | cd /d %~dp0 2 | dub --config=lib --build=docs 3 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d2dgame", 3 | "description": "D 2D Game-Engine", 4 | "copyright": "Copyright © 2015-2021, WebFreak001", 5 | "authors": ["WebFreak001"], 6 | "license": "MIT", 7 | "dependencies": 8 | { 9 | "bindbc-sdl": "~>0.6.1", 10 | "bindbc-opengl": "~>0.7.0", 11 | "gl3n": "~>1.3.1", 12 | "bmfont": "~>0.1.0", 13 | "crunch": "~>0.1.0" 14 | }, 15 | "versions": [ 16 | "GL_32", "GL_ARB_texture_filter_anisotropic", "ARB_texture_filter_anisotropic", "GL_ARB_tessellation_shader", 17 | "SDL_202" 18 | ], 19 | "copyFiles-windows": [ 20 | "dll/libFLAC-8.dll", 21 | "dll/libfreetype-6.dll", 22 | "dll/libogg-0.dll", 23 | "dll/libpng16-16.dll", 24 | "dll/libtiff-5.dll", 25 | "dll/libvorbis-0.dll", 26 | "dll/libvorbisfile-3.dll", 27 | "dll/libwebp-4.dll", 28 | "dll/SDL2.dll", 29 | "dll/SDL2_image.dll", 30 | "dll/SDL2_mixer.dll", 31 | "dll/SDL2_ttf.dll", 32 | "dll/smpeg2.dll", 33 | "dll/zlib1.dll" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | /example 2 | -------------------------------------------------------------------------------- /example/dub.sdl: -------------------------------------------------------------------------------- 1 | name "example" 2 | 3 | dependency "d2dgame" path=".." 4 | 5 | versions "BindSDL_Image" "BindSDL_Mixer" 6 | 7 | -------------------------------------------------------------------------------- /example/res/audio/song.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/audio/song.mp3 -------------------------------------------------------------------------------- /example/res/audio/whoosh.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/audio/whoosh.wav -------------------------------------------------------------------------------- /example/res/font/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/font/Roboto.ttf -------------------------------------------------------------------------------- /example/res/font/roboto.bmfc: -------------------------------------------------------------------------------- 1 | # AngelCode Bitmap Font Generator configuration file 2 | fileVersion=1 3 | 4 | # font settings 5 | fontName=Roboto 6 | fontFile= 7 | charSet=0 8 | fontSize=32 9 | aa=1 10 | scaleH=100 11 | useSmoothing=1 12 | isBold=0 13 | isItalic=0 14 | useUnicode=1 15 | disableBoxChars=1 16 | outputInvalidCharGlyph=0 17 | dontIncludeKerningPairs=0 18 | useHinting=1 19 | renderFromOutline=0 20 | useClearType=1 21 | 22 | # character alignment 23 | paddingDown=1 24 | paddingUp=1 25 | paddingRight=1 26 | paddingLeft=1 27 | spacingHoriz=1 28 | spacingVert=1 29 | useFixedHeight=0 30 | forceZero=0 31 | 32 | # output file 33 | outWidth=768 34 | outHeight=512 35 | outBitDepth=8 36 | fontDescFormat=2 37 | fourChnlPacked=0 38 | textureFormat=png 39 | textureCompression=0 40 | alphaChnl=0 41 | redChnl=4 42 | greenChnl=4 43 | blueChnl=4 44 | invA=0 45 | invR=0 46 | invG=0 47 | invB=0 48 | 49 | # outline 50 | outlineThickness=0 51 | 52 | # selected chars 53 | chars=32-126,160-383,399,402,416-417,431-432,496,506-511,536-539,567,601,700,710-711,713,728-733,755,768 54 | chars=769,771,777,783,803,900-906,908,910-929,931-974,977-978,982,1024-1158,1160-1299,7680-7681,7742-7743 55 | chars=7808-7813,7840-7929,8013,8192-8203,8208-8209,8211-8213,8215-8222,8224-8226,8229-8231,8240,8242-8243 56 | chars=8249-8250,8252,8260,8308,8319,8355-8356,8358-8364,8369,8377-8378,8380-8381,8453,8467,8470,8482,8486 57 | chars=8494,8539-8542,8706,8710,8719,8721-8722,8730,8734,8747,8776,8800,8804-8805,9674,60929-60930,63171 58 | chars=64257-64260,65279,65532-65533 59 | 60 | # imported icon images 61 | -------------------------------------------------------------------------------- /example/res/font/roboto.fnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/font/roboto.fnt -------------------------------------------------------------------------------- /example/res/font/roboto_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/font/roboto_0.png -------------------------------------------------------------------------------- /example/res/shuriken-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/shuriken-icon.png -------------------------------------------------------------------------------- /example/res/tex/shuriken-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/tex/shuriken-color.png -------------------------------------------------------------------------------- /example/res/tex/shuriken-color.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 26 | 30 | 34 | 35 | 37 | 41 | 45 | 46 | 57 | 68 | 79 | 90 | 101 | 102 | 120 | 129 | 130 | 132 | 133 | 135 | image/svg+xml 136 | 138 | 139 | 140 | 141 | 142 | 147 | 153 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /example/res/tex/shuriken-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/tex/shuriken-normal.png -------------------------------------------------------------------------------- /example/res/tex/shuriken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebFreak001/D2DGame/040ba8d80c28a5f36213b495dc342ed09c53f142/example/res/tex/shuriken.png -------------------------------------------------------------------------------- /example/res/tex/shuriken.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 26 | 30 | 34 | 35 | 37 | 41 | 45 | 46 | 57 | 68 | 79 | 90 | 101 | 102 | 120 | 129 | 130 | 132 | 133 | 135 | image/svg+xml 136 | 138 | 139 | 140 | 141 | 142 | 147 | 153 | 163 | 169 | 175 | 181 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /example/source/app.d: -------------------------------------------------------------------------------- 1 | import d2d; 2 | 3 | import std.algorithm; 4 | import std.conv : to; 5 | 6 | Texture shurikenTex; 7 | 8 | class Shuriken : RectangleShape 9 | { 10 | vec2 offs; 11 | float rotaSpeed = 0; 12 | 13 | public this(int x, int y, int xa, int ya, float rotation) 14 | { 15 | super(); 16 | texture = shurikenTex; 17 | offs = vec2(xa * 0.5f, ya * 0.5f); 18 | if (offs.length < 1) 19 | offs = offs.normalized(); 20 | if (offs.length > 10) 21 | offs = offs.normalized() * 10; 22 | origin = vec2(64, 64); 23 | this.rotation = rotation; 24 | rotaSpeed = 0.1f * max(0.1f, offs.length * 0.05f); 25 | position = vec2(x, y); 26 | size = vec2(128, 128); 27 | create(); 28 | } 29 | 30 | void update(float delta) 31 | { 32 | move(offs * delta * 100); 33 | rotate(rotaSpeed * delta * 100); 34 | } 35 | 36 | override void draw(IRenderTarget target, ShaderProgram shader = null) 37 | { 38 | matrixStack.push(); 39 | matrixStack.top = matrixStack.top * transform; 40 | if (texture !is null) 41 | texture.bind(0); 42 | shader.bind(); 43 | shader.set("invTransWorld", matrixStack.top.inverse().transposed()); 44 | target.draw(_mesh, shader); 45 | matrixStack.pop(); 46 | } 47 | 48 | bool isIn(int width, int height) 49 | { 50 | return position.x > -80 && position.y > -80 && position.x < width + 80 && position.y < height + 80; 51 | } 52 | } 53 | 54 | class Game1 : Game 55 | { 56 | private: 57 | Shuriken[] shuriken; 58 | Shuriken mouse; 59 | int lastX, lastY; 60 | int currentX, currentY; 61 | bool clicked = false; 62 | int thrown = 0; 63 | ShaderProgram shader, textShader; 64 | Texture normal; 65 | Sound whosh; 66 | Music music; 67 | IFont font; 68 | IText text; 69 | 70 | public: 71 | override void start() 72 | { 73 | windowWidth = 1280; 74 | windowHeight = 720; 75 | windowTitle = "Shuriken Simulator EXTREME SUPER ULTRA DELUXE EDITION OMEGA 2.WHOA"; 76 | maxFPS = 0; 77 | flags |= WindowFlags.Resizable; 78 | } 79 | 80 | override void load() 81 | { 82 | music = new Music("res/audio/song.mp3"); 83 | music.play(0); 84 | 85 | whosh = new Sound("res/audio/whoosh.wav"); 86 | 87 | shurikenTex = new Texture("res/tex/shuriken-color.png", TextureFilterMode.LinearMipmapLinear, TextureFilterMode.Linear, TextureClampMode.ClampToEdge, TextureClampMode.ClampToEdge); 88 | mouse = new Shuriken(0, 0, 0, 0, 0); 89 | 90 | shader = new ShaderProgram(); 91 | shader.attach(Shader.create(ShaderType.Vertex, import ("default.vert"))); 92 | shader.attach(Shader.create(ShaderType.Fragment, import ("normal.frag"))); 93 | shader.link(); 94 | shader.registerUniform("projection"); 95 | shader.registerUniform("transform"); 96 | shader.registerUniform("tex"); 97 | shader.registerUniform("tex2"); 98 | shader.registerUniform("invTransWorld"); 99 | 100 | shader.set("tex", 0); 101 | shader.set("tex2", 1); 102 | 103 | textShader = new ShaderProgram(); 104 | textShader.attach(Shader.create(ShaderType.Vertex, import ("font.vert"))); 105 | textShader.attach(Shader.create(ShaderType.Fragment, import ("default.frag"))); 106 | textShader.link(); 107 | textShader.registerUniform("projection"); 108 | textShader.registerUniform("transform"); 109 | textShader.registerUniform("tex"); 110 | textShader.registerUniform("texRect"); 111 | textShader.registerUniform("sizeRect"); 112 | 113 | textShader.set("tex", 0); 114 | 115 | normal = new Texture("res/tex/shuriken-normal.png", TextureFilterMode.LinearMipmapLinear, TextureFilterMode.Linear, TextureClampMode.ClampToEdge, TextureClampMode.ClampToEdge); 116 | 117 | font = new BMFont("res/font/"); 118 | font.load("res/font/roboto.fnt", 0); 119 | 120 | text = font.renderMultiline("Shuriken Simulator EXTREME SUPER ULTRA DELUXE EDITION OMEGA 2.WHOA\nThrown: " ~ thrown.to!string); 121 | } 122 | 123 | override void update(float delta) 124 | { 125 | int width = window.width; 126 | int height = window.height; 127 | foreach_reverse(i, shurik; shuriken) 128 | { 129 | shurik.update(delta); 130 | if (!shurik.isIn(width, height)) 131 | { 132 | shurik.dispose(); 133 | shuriken = shuriken.remove(i); 134 | } 135 | } 136 | } 137 | 138 | override void onEvent(Event event) 139 | { 140 | switch (event.type) 141 | { 142 | case Event.Type.MouseButtonPressed: 143 | clicked = true; 144 | break; 145 | case Event.Type.MouseMoved: 146 | lastX = currentX; 147 | lastY = currentY; 148 | currentX = event.x; 149 | currentY = event.y; 150 | break; 151 | case Event.Type.MouseButtonReleased: 152 | clicked = false; 153 | shuriken ~= new Shuriken(event.x, event.y, event.x - lastX, event.y - lastY, mouse.rotation); 154 | whosh.play(); 155 | thrown++; 156 | text.text = "Shuriken Simulator EXTREME SUPER ULTRA DELUXE EDITION OMEGA 2.WHOA\nThrown: " ~ thrown.to!string; 157 | break; 158 | case Event.Type.Resized: 159 | window.resize(event.width, event.height); 160 | break; 161 | default: 162 | break; 163 | } 164 | } 165 | 166 | override void draw() 167 | { 168 | window.clear(Color3.SkyBlue); 169 | 170 | foreach_reverse(shurik; shuriken) 171 | { 172 | normal.bind(1); 173 | window.draw(shurik, shader); 174 | } 175 | 176 | if (clicked) 177 | { 178 | mouse.position = vec2(currentX, currentY); 179 | normal.bind(1); 180 | window.draw(mouse, shader); 181 | } 182 | 183 | window.draw(text, textShader); 184 | } 185 | } 186 | 187 | void main() 188 | { 189 | new Game1().run(); 190 | } 191 | -------------------------------------------------------------------------------- /example/views/default.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | uniform sampler2D tex; 4 | 5 | in vec2 texCoord; 6 | 7 | layout(location = 0) out vec4 out_frag_color; 8 | 9 | void main() 10 | { 11 | out_frag_color = texture(tex, texCoord); 12 | } 13 | -------------------------------------------------------------------------------- /example/views/default.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | layout(location = 0) in vec3 in_position; 3 | layout(location = 1) in vec2 in_tex; 4 | 5 | uniform mat4 transform; 6 | uniform mat4 projection; 7 | 8 | out vec2 texCoord; 9 | 10 | void main() 11 | { 12 | gl_Position = projection * transform * vec4(in_position, 1); 13 | 14 | texCoord = in_tex; 15 | } 16 | -------------------------------------------------------------------------------- /example/views/font.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | layout(location = 0) in vec3 in_position; 3 | layout(location = 1) in vec2 in_tex; 4 | 5 | uniform mat4 transform; 6 | uniform mat4 projection; 7 | uniform vec4 texRect; 8 | uniform vec4 sizeRect; 9 | 10 | out vec2 texCoord; 11 | 12 | void main() 13 | { 14 | gl_Position = projection * transform * vec4(in_position.xy * sizeRect.zw + sizeRect.xy, 0, 1); 15 | 16 | texCoord = texRect.xy + in_position.xy * texRect.zw; 17 | } 18 | -------------------------------------------------------------------------------- /example/views/normal.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | uniform sampler2D tex; 4 | uniform sampler2D tex2; 5 | 6 | uniform mat4 invTransWorld; 7 | 8 | in vec2 texCoord; 9 | 10 | layout(location = 0) out vec4 out_frag_color; 11 | 12 | void main() 13 | { 14 | out_frag_color = vec4( 15 | texture(tex, texCoord).rgb 16 | * dot( 17 | normalize(texture(tex2, texCoord).xyz * 2 - 1) * mat3(invTransWorld), 18 | normalize(vec3(-0.5, 0.5, 1)) 19 | ), 20 | texture(tex, texCoord).a 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /source/d2d/audio/music.d: -------------------------------------------------------------------------------- 1 | module d2d.audio.music; 2 | 3 | version(BindSDL_Mixer): 4 | 5 | import d2d; 6 | 7 | /// 1.0 / MIX_MAX_VOLUME 8 | enum INV_MIX_MAX_VOLUME = 1.0f / MIX_MAX_VOLUME; 9 | 10 | /// 11 | enum FadingStatus : ubyte 12 | { 13 | /// 14 | NoFading = MIX_NO_FADING, 15 | /// 16 | FadingOut = MIX_FADING_OUT, 17 | /// 18 | FadingIn = MIX_FADING_IN, 19 | } 20 | 21 | /// 22 | enum MusicType : ubyte 23 | { 24 | /// 25 | CommandBased = MUS_CMD, 26 | /// 27 | Wav = MUS_WAV, 28 | /// 29 | Mod = MUS_MOD, 30 | /// 31 | ModPlug = MUS_MODPLUG, 32 | /// 33 | Mid = MUS_MID, 34 | /// 35 | Ogg = MUS_OGG, 36 | /// 37 | Mp3 = MUS_MP3, 38 | /// 39 | Mp3Mad = MUS_MP3_MAD, 40 | /// 41 | None = MUS_NONE, 42 | } 43 | 44 | /// Thin wrap around Mix_Music including loading [FLAC, MikMod, Ogg Vorbis, MP3, Wav] using SDL_Mixer. 45 | class Music : IVerifiable, IDisposable 46 | { 47 | private Mix_Music* _handle; 48 | 49 | /// Handle to the `Mix_Music*`. 50 | public @property Mix_Music* handle() 51 | { 52 | return _handle; 53 | } 54 | 55 | /// Checks if the handle is not null. 56 | public @property bool valid() 57 | { 58 | return _handle !is null; 59 | } 60 | 61 | private this(Mix_Music * handle) 62 | { 63 | _handle = handle; 64 | } 65 | 66 | /// Loads a music file 67 | public this(string path) 68 | { 69 | _handle = Mix_LoadMUS(path.toStringz()); 70 | } 71 | 72 | public ~this() 73 | { 74 | dispose(); 75 | } 76 | 77 | /// Creates music from a `Mix_Music*`. 78 | public static Music fromMusic(Mix_Music* handle) 79 | { 80 | return new Music(handle); 81 | } 82 | 83 | public static bool load() 84 | { 85 | return Mix_OpenAudio(44_100, MIX_DEFAULT_FORMAT, 2, 4096) == 0; 86 | } 87 | 88 | /// Returns the latest sound related error 89 | public static @property string error() 90 | { 91 | return cast(string) Mix_GetError().fromStringz(); 92 | } 93 | 94 | /// Deallocates the memory and invalidates `this`. 95 | public void dispose() 96 | { 97 | if (valid) 98 | { 99 | stop(); 100 | Mix_FreeMusic(_handle); 101 | _handle = null; 102 | } 103 | } 104 | 105 | /// Plays this music, will stop other musics. -1 loops will play forever. 106 | /// Returns: true on success, or false on errors 107 | public bool play(int loops = 1) 108 | { 109 | return Mix_PlayMusic(_handle, loops) == 0; 110 | } 111 | 112 | /// Starts this music by fading in over `ms` milliseconds, will stop other musics. -1 loops will play forever. 113 | /// Params: 114 | /// ms = Milliseconds for the fade-in effect to complete. 115 | /// loops = -1 loops will play forever. 0 plays the music zero times. 116 | /// Returns: true on success, or false on errors 117 | public bool fadeIn(int ms, int loops = 1) 118 | { 119 | return Mix_FadeInMusic(_handle, loops, ms) == 0; 120 | } 121 | 122 | /// Starts this music by fading in over `ms` milliseconds, will stop other musics. 123 | /// Params: 124 | /// ms = Milliseconds for the fade-in effect to complete. 125 | /// loops = -1 loops will play forever. 0 plays the music zero times. 126 | /// position = Set the position of the currently playing music. The position takes different meanings for different music sources. It only works on the music sources listed below. 127 | /// MOD 128 | /// The double is cast to Uint16 and used for a pattern number in the module. Passing zero is similar to rewinding the song. 129 | /// OGG/MP3 130 | /// Jumps to position seconds from the beginning of the song. 131 | /// Returns: true on success, or false on errors 132 | public bool fadeIn(int ms, int loops, double position) 133 | { 134 | return Mix_FadeInMusicPos(_handle, loops, ms, position) == 0; 135 | } 136 | 137 | /// 138 | public @property MusicType type() 139 | { 140 | return cast(MusicType) Mix_GetMusicType(_handle); 141 | } 142 | 143 | /// Modifies the position where currently playing. 144 | /// Params: 145 | /// value = Set the position of the currently playing music. The position takes different meanings for different music sources. It only works on the music sources listed below. Will automatically rewind before setting. 146 | /// MOD 147 | /// The double is cast to Uint16 and used for a pattern number in the module. Passing zero is similar to rewinding the song. 148 | /// OGG/MP3 149 | /// Jumps to position seconds from the beginning of the song. 150 | /// Returns: true on success, or false if the codec doesn't support this function 151 | public static @property bool position(double value) 152 | { 153 | Mix_RewindMusic(); 154 | return Mix_SetMusicPosition(value) == 0; 155 | } 156 | 157 | /// Rewinds music to the start. This is safe to use on halted, paused, and already playing music. It is not useful to rewind the music immediately after starting playback, because it starts at the beginning by default. 158 | public static void rewind() 159 | { 160 | Mix_RewindMusic(); 161 | } 162 | 163 | /// Halt playback of music. This interrupts music fader effects. 164 | public static void stop() 165 | { 166 | Mix_RewindMusic(); 167 | } 168 | 169 | /// Pause the music playback. You may halt paused music. 170 | /// Note: Music can only be paused if it is actively playing. 171 | public static void pause() 172 | { 173 | Mix_PauseMusic(); 174 | } 175 | 176 | /// Unpause the music. This is safe to use on halted, paused, and already playing music. 177 | public static void resume() 178 | { 179 | Mix_ResumeMusic(); 180 | } 181 | 182 | /// Returns the current volume as float in range 0.0 to 1.0 183 | public static @property float volume() 184 | { 185 | return Mix_VolumeMusic(-1) * INV_MIX_MAX_VOLUME; 186 | } 187 | 188 | /// Sets the current volume as float in range 0.0 to 1.0 189 | public static @property void volume(float value) 190 | { 191 | Mix_VolumeMusic(cast(int) (value * MIX_MAX_VOLUME)); 192 | } 193 | 194 | /// 195 | public static @property bool isPlaying() 196 | { 197 | return Mix_PlayingMusic() == 1; 198 | } 199 | 200 | /// 201 | public static @property bool isPaused() 202 | { 203 | return Mix_PausedMusic() == 1; 204 | } 205 | 206 | /// 207 | public static @property FadingStatus fadingStatus() 208 | { 209 | return cast(FadingStatus) Mix_FadingMusic(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /source/d2d/audio/sound.d: -------------------------------------------------------------------------------- 1 | module d2d.audio.sound; 2 | 3 | version(BindSDL_Mixer): 4 | 5 | import d2d; 6 | 7 | /// Thin wrap around Mix_Chunk including loading [FLAC, MikMod, Ogg Vorbis, MP3, Wav] using SDL_Mixer. 8 | class Sound : IVerifiable, IDisposable 9 | { 10 | private Mix_Chunk* _handle; 11 | 12 | /// Handle to the `Mix_Chunk*`. 13 | public @property Mix_Chunk* handle() 14 | { 15 | return _handle; 16 | } 17 | 18 | /// Checks if the handle is not null. 19 | public @property bool valid() 20 | { 21 | return _handle !is null; 22 | } 23 | 24 | private this(Mix_Chunk * handle) 25 | { 26 | _handle = handle; 27 | } 28 | 29 | /// Loads a sound file 30 | public this(string path) 31 | { 32 | // Confusing name, this can load any format 33 | _handle = Mix_LoadWAV(path.toStringz()); 34 | } 35 | 36 | public ~this() 37 | { 38 | dispose(); 39 | } 40 | 41 | /// Creates sound from a `Mix_Chunk*`. 42 | public static Sound fromChunk(Mix_Chunk* chunk) 43 | { 44 | return new Sound(chunk); 45 | } 46 | 47 | /// Returns the latest sound related error 48 | public static @property string error() 49 | { 50 | return cast(string) Mix_GetError().fromStringz(); 51 | } 52 | 53 | /// Deallocates the memory and invalidates `this`. 54 | public void dispose() 55 | { 56 | if (valid) 57 | { 58 | stop(); 59 | Mix_FreeChunk(_handle); 60 | _handle = null; 61 | } 62 | } 63 | 64 | /// Plays this sound in the selected channel. 65 | /// Params: 66 | /// loops = -1 loops will play forever. 0 plays once. 67 | /// channel = -1 loops will choose the first free unreserved channel. 68 | /// maxTicks = Maximum milliseconds to play this sound. If not enough loops or the sample chunk is not long enough, then the sample may stop before this timeout occurs. -1 means play forever. 69 | /// Returns: the channel the sample is played on. On any errors, -1 is returned. 70 | public int play(int loops = 0, int channel = -1, int maxTicks = -1) 71 | { 72 | return Mix_PlayChannelTimed(channel, _handle, loops, maxTicks); 73 | } 74 | 75 | /// Plays this sound in the selected channel with a fade in effect. 76 | /// Params: 77 | /// ms = Milliseconds for the fade-in effect to complete. 78 | /// loops = -1 loops will play forever. 0 plays once. 79 | /// channel = -1 loops will choose the first free unreserved channel. 80 | /// maxTicks = Maximum milliseconds to play this sound. If not enough loops or the sample chunk is not long enough, then the sample may stop before this timeout occurs. -1 means play forever. 81 | /// Returns: the channel the sample is played on. On any errors, -1 is returned. 82 | public int fadeIn(int ms, int loops = 1, int channel = -1, int maxTicks = -1) 83 | { 84 | return Mix_FadeInChannelTimed(channel, _handle, loops, ms, maxTicks); 85 | } 86 | 87 | /// Halt playback of sound. This interrupts sound fader effects. 88 | /// Params: 89 | /// channel = Channel to stop playing, or -1 for all channels. 90 | public static void stop(int channel = -1) 91 | { 92 | Mix_HaltChannel(channel); 93 | } 94 | 95 | /// Pause the sound playback. You may halt paused sounds. 96 | /// Params: 97 | /// channel = Channel to stop playing, or -1 for all channels. 98 | /// Note: Sound can only be paused if it is actively playing. 99 | public static void pause(int channel = -1) 100 | { 101 | Mix_Pause(channel); 102 | } 103 | 104 | /// Unpause the sound. This is safe to use on halted, paused, and already playing sounds. 105 | /// Params: 106 | /// channel = Channel to stop playing, or -1 for all channels. 107 | public static void resume(int channel = -1) 108 | { 109 | Mix_Resume(channel); 110 | } 111 | 112 | /// Returns the current volume as float in range 0.0 to 1.0 113 | /// Params: 114 | /// channel = -1 for all channels. 115 | public static float getVolume(int channel = -1) 116 | { 117 | return Mix_Volume(channel, -1) * INV_MIX_MAX_VOLUME; 118 | } 119 | 120 | /// Sets the current volume as float in range 0.0 to 1.0 121 | /// Params: 122 | /// value = volume between 0.0 and 1.0. 123 | /// channel = -1 for all channels. 124 | public static void setVolume(float value, int channel = -1) 125 | { 126 | Mix_Volume(channel, cast(int) (value * MIX_MAX_VOLUME)); 127 | } 128 | 129 | /// Tells you how many channels are playing if set to -1 130 | /// Params: 131 | /// channel = -1 for all channels. 132 | public static @property int isPlaying(int channel = -1) 133 | { 134 | return Mix_Playing(channel); 135 | } 136 | 137 | /// Tells you how many channels are paused if set to -1 138 | /// Params: 139 | /// channel = -1 for all channels. 140 | public static @property int isPaused(int channel = -1) 141 | { 142 | return Mix_Paused(channel); 143 | } 144 | 145 | /// 146 | /// Params: 147 | /// channel = -1 is invalid. 148 | public static @property FadingStatus fadingStatus(int channel) 149 | { 150 | assert(channel != -1); 151 | return cast(FadingStatus) Mix_FadingChannel(channel); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /source/d2d/core/bytestream.d: -------------------------------------------------------------------------------- 1 | module d2d.core.bytestream; 2 | 3 | public import std.system : Endian; 4 | public import std.bitmanip; 5 | import std.traits; 6 | 7 | /// Struct for wrapping a ubyte[] with read functions and endianness. 8 | struct ByteStreamImpl (Endian endianness) 9 | { 10 | /// Contains the data. 11 | ubyte[] stream; 12 | alias stream this; 13 | 14 | /// Sets the stream to data. 15 | this(ubyte[] data) 16 | { 17 | stream = data; 18 | } 19 | 20 | /// Sets the stream to data. 21 | void opAssign(ubyte[] data) 22 | { 23 | stream = data; 24 | } 25 | 26 | /// Advances the stream by `amount` bytes. 27 | void skip(size_t amount) 28 | { 29 | stream = stream[amount .. $]; 30 | } 31 | 32 | /// Returns the first `T.sizeof` bytes from the stream and advances the stream. 33 | T read(T = ubyte)() 34 | { 35 | return stream.read!(T, endianness)(); 36 | } 37 | 38 | /// Returns a `T[]` from the stream and advances the stream. 39 | T[] read(T = ubyte)(size_t length) 40 | { 41 | T[] data = new T[length]; 42 | for (size_t i = 0; i < length; i++) 43 | data[i] = read!(T)(); 44 | return data; 45 | } 46 | 47 | /// Reads until value is `value` and returns everything read excluding `value`. Advances after found `value`. Returns `null` if `value` was not found. 48 | T readTo(T)(ubyte value) 49 | { 50 | // Check for ForeachType!T.sizeof == 1 maybe? 51 | static assert(isArray!T, "T must be an Array!"); 52 | for (size_t i = 0; i < stream.length; i++) 53 | { 54 | if (stream[i] == value) 55 | { 56 | scope (exit) stream = stream[i + 1 .. $]; 57 | return cast(T) stream[0 .. i]; 58 | } 59 | } 60 | return cast(T) null; 61 | } 62 | 63 | /// Reads until value is `value` and returns everything read including `value`. Advances after found `value`. Returns `null` if `value` was not found. 64 | T readToIncluding(T)(ubyte value) 65 | { 66 | // Check for ForeachType!T.sizeof == 1 maybe? 67 | static assert(isArray!T, "T must be an Array!"); 68 | for (size_t i = 0; i < stream.length; i++) 69 | { 70 | if (stream[i] == value) 71 | { 72 | scope (exit) stream = stream[i + 1 .. $]; 73 | return cast(T) stream[0 .. i + 1]; 74 | } 75 | } 76 | return cast(T) null; 77 | } 78 | 79 | /// Returns `T.sizeof` bytes from the stream at position `index` without advancing the stream. 80 | T peek(T = ubyte)(size_t index = 0) 81 | { 82 | return stream.peek!(T, endianness)(index); 83 | } 84 | 85 | /// Returns a `T[]` from the stream. 86 | T[] peek(T = ubyte)(size_t index, size_t length) 87 | { 88 | T[] data = new T[length]; 89 | for (size_t i = 0; i < length; i++) 90 | data[i] = peek!(T)(index + i * T.sizeof); 91 | return data; 92 | } 93 | 94 | /// Writes `data` to the stream at position `index`. 95 | void write(T)(T data, size_t index) 96 | { 97 | static if (isArray!(T)) 98 | { 99 | for (size_t i = 0; i < data.length; i++) 100 | write(data[i], index + i * typeof(data[0]).sizeof); 101 | } 102 | else 103 | { 104 | stream.write!(T, endianness)(data, index); 105 | } 106 | } 107 | 108 | /// Appends `data` to the stream. 109 | void append(T)(T data) 110 | { 111 | static if (isArray!(T)) 112 | { 113 | for (size_t i = 0; i < data.length; i++) 114 | append(data[i]); 115 | } 116 | else 117 | { 118 | stream.length += T.sizeof; 119 | write(data, stream.length - T.sizeof); 120 | } 121 | } 122 | } 123 | 124 | alias ByteStream = ByteStreamImpl!(Endian.bigEndian); 125 | alias BigByteStream = ByteStream; 126 | alias LittleByteStream = ByteStreamImpl!(Endian.littleEndian); 127 | 128 | /// 129 | unittest 130 | { 131 | ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 132 | 133 | assert(stream.peek!uint () == 0x01_05_16_09); 134 | 135 | stream.append!(ushort[])([0x4A_2B, 0x59_12]); 136 | assert(stream.read!ulong () == 0x01_05_16_09_2C_FF_08_4AUL); 137 | assert(stream == [0x2B, 0x59, 0x12]); 138 | 139 | stream.write!ubyte (0x44, 1); 140 | assert(stream == [0x2B, 0x44, 0x12]); 141 | 142 | stream.write!(ubyte[])([0x01, 0x02, 0x03], 0); 143 | assert(stream == [0x01, 0x02, 0x03]); 144 | } 145 | 146 | /// 147 | unittest 148 | { 149 | ByteStream stream = (cast(ubyte[]) "abc\0") ~cast(ubyte[])[0x44]; 150 | assert(stream.readTo!string(cast(ubyte) '\0') == "abc"); 151 | assert(stream == [0x44]); 152 | } 153 | 154 | /// 155 | unittest 156 | { 157 | ByteStream stream = (cast(ubyte[]) "abc\0") ~cast(ubyte[])[0x44]; 158 | assert(stream.readToIncluding!string(cast(ubyte) '\0') == "abc\0"); 159 | assert(stream == [0x44]); 160 | } 161 | 162 | unittest 163 | { 164 | ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 165 | 166 | assert(stream.peek!uint () == 0x01_05_16_09); 167 | assert(stream.peek!ushort () == 0x01_05); 168 | assert(stream.peek!ubyte () == 0x01); 169 | 170 | assert(stream.peek!uint (2) == 0x16_09_2C_FF); 171 | assert(stream.peek!ushort (2) == 0x16_09); 172 | assert(stream.peek!ubyte (2) == 0x16); 173 | 174 | assert(stream.peek!ushort (1, 2) == [0x05_16, 0x09_2C]); 175 | 176 | stream ~= 0x4A; 177 | 178 | assert(stream.peek!uint (0, 2) == [0x01_05_16_09, 0x2C_FF_08_4A]); 179 | } 180 | 181 | unittest 182 | { 183 | ByteStream stream = cast(ubyte[])[0x01, 0x05]; 184 | 185 | stream.append!ushort (0x4A_2B); 186 | assert(stream == [0x01, 0x05, 0x4A, 0x2B]); 187 | 188 | stream.append!(ubyte[])(cast(ubyte[])[0x16, 0x09, 0x2C, 0xFF, 0x08]); 189 | assert(stream == [0x01, 0x05, 0x4A, 0x2B, 0x16, 0x09, 0x2C, 0xFF, 0x08]); 190 | } 191 | 192 | unittest 193 | { 194 | ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 195 | 196 | stream.write!ubyte (0x06, 1); 197 | assert(stream == [0x01, 0x06, 0x16, 0x09, 0x2C, 0xFF, 0x08]); 198 | 199 | stream.write!(ushort[])(cast(ushort[])[0x07_4A, 0x2B_EA], 1); 200 | assert(stream == [0x01, 0x07, 0x4A, 0x2B, 0xEA, 0xFF, 0x08]); 201 | } 202 | 203 | unittest 204 | { 205 | ByteStream stream = cast(ubyte[])[0x01, 0x05, 0x16, 0x09, 0x2C, 0xFF, 0x08]; 206 | 207 | assert(stream.read() == 0x01); 208 | assert(stream.read!ushort () == 0x05_16); 209 | assert(stream.read!uint () == 0x09_2C_FF_08); 210 | assert(stream.length == 0); 211 | } 212 | -------------------------------------------------------------------------------- /source/d2d/core/color3.d: -------------------------------------------------------------------------------- 1 | module d2d.core.color3; 2 | 3 | import d2d; 4 | 5 | /// Class containing aliases for each different HTML Color converted to floats. 6 | class Color3 7 | { 8 | /// HTML Color: AliceBlue 9 | alias AliceBlue = AliasSeq!(0.9411764705882353, 0.9725490196078431, 1); 10 | /// HTML Color: AntiqueWhite 11 | alias AntiqueWhite = AliasSeq!(0.9803921568627451, 0.9215686274509803, 0.8431372549019608); 12 | /// HTML Color: Aqua 13 | alias Aqua = AliasSeq!(0, 1, 1); 14 | /// HTML Color: Aquamarine 15 | alias Aquamarine = AliasSeq!(0.4980392156862745, 1, 0.8313725490196079); 16 | /// HTML Color: Azure 17 | alias Azure = AliasSeq!(0.9411764705882353, 1, 1); 18 | /// HTML Color: Beige 19 | alias Beige = AliasSeq!(0.9607843137254902, 0.9607843137254902, 0.8627450980392157); 20 | /// HTML Color: Bisque 21 | alias Bisque = AliasSeq!(1, 0.8941176470588236, 0.7686274509803922); 22 | /// HTML Color: Black 23 | alias Black = AliasSeq!(0, 0, 0); 24 | /// HTML Color: BlanchedAlmond 25 | alias BlanchedAlmond = AliasSeq!(1, 0.9215686274509803, 0.803921568627451); 26 | /// HTML Color: Blue 27 | alias Blue = AliasSeq!(0, 0, 1); 28 | /// HTML Color: BlueViolet 29 | alias BlueViolet = AliasSeq!(0.5411764705882353, 0.16862745098039217, 0.8862745098039215); 30 | /// HTML Color: Brown 31 | alias Brown = AliasSeq!(0.6470588235294118, 0.16470588235294117, 0.16470588235294117); 32 | /// HTML Color: BurlyWood 33 | alias BurlyWood = AliasSeq!(0.8705882352941177, 0.7215686274509804, 0.5294117647058824); 34 | /// HTML Color: CadetBlue 35 | alias CadetBlue = AliasSeq!(0.37254901960784315, 0.6196078431372549, 0.6274509803921569); 36 | /// HTML Color: Chartreuse 37 | alias Chartreuse = AliasSeq!(0.4980392156862745, 1, 0); 38 | /// HTML Color: Chocolate 39 | alias Chocolate = AliasSeq!(0.8235294117647058, 0.4117647058823529, 0.11764705882352941); 40 | /// HTML Color: Coral 41 | alias Coral = AliasSeq!(1, 0.4980392156862745, 0.3137254901960784); 42 | /// HTML Color: CornflowerBlue 43 | alias CornflowerBlue = AliasSeq!(0.39215686274509803, 0.5843137254901961, 0.9294117647058824); 44 | /// HTML Color: Cornsilk 45 | alias Cornsilk = AliasSeq!(1, 0.9725490196078431, 0.8627450980392157); 46 | /// HTML Color: Crimson 47 | alias Crimson = AliasSeq!(0.8627450980392157, 0.0784313725490196, 0.23529411764705882); 48 | /// HTML Color: Cyan 49 | alias Cyan = AliasSeq!(0, 1, 1); 50 | /// HTML Color: DarkBlue 51 | alias DarkBlue = AliasSeq!(0, 0, 0.5450980392156862); 52 | /// HTML Color: DarkCyan 53 | alias DarkCyan = AliasSeq!(0, 0.5450980392156862, 0.5450980392156862); 54 | /// HTML Color: DarkGoldenRod 55 | alias DarkGoldenRod = AliasSeq!(0.7215686274509804, 0.5254901960784314, 0.043137254901960784); 56 | /// HTML Color: DarkGray 57 | alias DarkGray = AliasSeq!(0.6627450980392157, 0.6627450980392157, 0.6627450980392157); 58 | /// HTML Color: DarkGreen 59 | alias DarkGreen = AliasSeq!(0, 0.39215686274509803, 0); 60 | /// HTML Color: DarkKhaki 61 | alias DarkKhaki = AliasSeq!(0.7411764705882353, 0.7176470588235294, 0.4196078431372549); 62 | /// HTML Color: DarkMagenta 63 | alias DarkMagenta = AliasSeq!(0.5450980392156862, 0, 0.5450980392156862); 64 | /// HTML Color: DarkOliveGreen 65 | alias DarkOliveGreen = AliasSeq!(0.3333333333333333, 0.4196078431372549, 0.1843137254901961); 66 | /// HTML Color: DarkOrange 67 | alias DarkOrange = AliasSeq!(1, 0.5490196078431373, 0); 68 | /// HTML Color: DarkOrchid 69 | alias DarkOrchid = AliasSeq!(0.6, 0.19607843137254902, 0.8); 70 | /// HTML Color: DarkRed 71 | alias DarkRed = AliasSeq!(0.5450980392156862, 0, 0); 72 | /// HTML Color: DarkSalmon 73 | alias DarkSalmon = AliasSeq!(0.9137254901960784, 0.5882352941176471, 0.47843137254901963); 74 | /// HTML Color: DarkSeaGreen 75 | alias DarkSeaGreen = AliasSeq!(0.5607843137254902, 0.7372549019607844, 0.5607843137254902); 76 | /// HTML Color: DarkSlateBlue 77 | alias DarkSlateBlue = AliasSeq!(0.2823529411764706, 0.23921568627450981, 0.5450980392156862); 78 | /// HTML Color: DarkSlateGray 79 | alias DarkSlateGray = AliasSeq!(0.1843137254901961, 0.30980392156862746, 0.30980392156862746); 80 | /// HTML Color: DarkTurquoise 81 | alias DarkTurquoise = AliasSeq!(0, 0.807843137254902, 0.8196078431372549); 82 | /// HTML Color: DarkViolet 83 | alias DarkViolet = AliasSeq!(0.5803921568627451, 0, 0.8274509803921568); 84 | /// HTML Color: DeepPink 85 | alias DeepPink = AliasSeq!(1, 0.0784313725490196, 0.5764705882352941); 86 | /// HTML Color: DeepSkyBlue 87 | alias DeepSkyBlue = AliasSeq!(0, 0.7490196078431373, 1); 88 | /// HTML Color: DimGray 89 | alias DimGray = AliasSeq!(0.4117647058823529, 0.4117647058823529, 0.4117647058823529); 90 | /// HTML Color: DodgerBlue 91 | alias DodgerBlue = AliasSeq!(0.11764705882352941, 0.5647058823529412, 1); 92 | /// HTML Color: FireBrick 93 | alias FireBrick = AliasSeq!(0.6980392156862745, 0.13333333333333333, 0.13333333333333333); 94 | /// HTML Color: FloralWhite 95 | alias FloralWhite = AliasSeq!(1, 0.9803921568627451, 0.9411764705882353); 96 | /// HTML Color: ForestGreen 97 | alias ForestGreen = AliasSeq!(0.13333333333333333, 0.5450980392156862, 0.13333333333333333); 98 | /// HTML Color: Fuchsia 99 | alias Fuchsia = AliasSeq!(1, 0, 1); 100 | /// HTML Color: Gainsboro 101 | alias Gainsboro = AliasSeq!(0.8627450980392157, 0.8627450980392157, 0.8627450980392157); 102 | /// HTML Color: GhostWhite 103 | alias GhostWhite = AliasSeq!(0.9725490196078431, 0.9725490196078431, 1); 104 | /// HTML Color: Gold 105 | alias Gold = AliasSeq!(1, 0.8431372549019608, 0); 106 | /// HTML Color: GoldenRod 107 | alias GoldenRod = AliasSeq!(0.8549019607843137, 0.6470588235294118, 0.12549019607843137); 108 | /// HTML Color: Gray 109 | alias Gray = AliasSeq!(0.5019607843137255, 0.5019607843137255, 0.5019607843137255); 110 | /// HTML Color: Green 111 | alias Green = AliasSeq!(0, 0.5019607843137255, 0); 112 | /// HTML Color: GreenYellow 113 | alias GreenYellow = AliasSeq!(0.6784313725490196, 1, 0.1843137254901961); 114 | /// HTML Color: HoneyDew 115 | alias HoneyDew = AliasSeq!(0.9411764705882353, 1, 0.9411764705882353); 116 | /// HTML Color: HotPink 117 | alias HotPink = AliasSeq!(1, 0.4117647058823529, 0.7058823529411765); 118 | /// HTML Color: IndianRed 119 | alias IndianRed = AliasSeq!(0.803921568627451, 0.3607843137254902, 0.3607843137254902); 120 | /// HTML Color: Indigo 121 | alias Indigo = AliasSeq!(0.29411764705882354, 0, 0.5098039215686274); 122 | /// HTML Color: Ivory 123 | alias Ivory = AliasSeq!(1, 1, 0.9411764705882353); 124 | /// HTML Color: Khaki 125 | alias Khaki = AliasSeq!(0.9411764705882353, 0.9019607843137255, 0.5490196078431373); 126 | /// HTML Color: Lavender 127 | alias Lavender = AliasSeq!(0.9019607843137255, 0.9019607843137255, 0.9803921568627451); 128 | /// HTML Color: LavenderBlush 129 | alias LavenderBlush = AliasSeq!(1, 0.9411764705882353, 0.9607843137254902); 130 | /// HTML Color: LawnGreen 131 | alias LawnGreen = AliasSeq!(0.48627450980392156, 0.9882352941176471, 0); 132 | /// HTML Color: LemonChiffon 133 | alias LemonChiffon = AliasSeq!(1, 0.9803921568627451, 0.803921568627451); 134 | /// HTML Color: LightBlue 135 | alias LightBlue = AliasSeq!(0.6784313725490196, 0.8470588235294118, 0.9019607843137255); 136 | /// HTML Color: LightCoral 137 | alias LightCoral = AliasSeq!(0.9411764705882353, 0.5019607843137255, 0.5019607843137255); 138 | /// HTML Color: LightCyan 139 | alias LightCyan = AliasSeq!(0.8784313725490196, 1, 1); 140 | /// HTML Color: LightGoldenRodYellow 141 | alias LightGoldenRodYellow = AliasSeq!(0.9803921568627451, 142 | 0.9803921568627451, 0.8235294117647058); 143 | /// HTML Color: LightGray 144 | alias LightGray = AliasSeq!(0.8274509803921568, 0.8274509803921568, 0.8274509803921568); 145 | /// HTML Color: LightGreen 146 | alias LightGreen = AliasSeq!(0.5647058823529412, 0.9333333333333333, 0.5647058823529412); 147 | /// HTML Color: LightPink 148 | alias LightPink = AliasSeq!(1, 0.7137254901960784, 0.7568627450980392); 149 | /// HTML Color: LightSalmon 150 | alias LightSalmon = AliasSeq!(1, 0.6274509803921569, 0.47843137254901963); 151 | /// HTML Color: LightSeaGreen 152 | alias LightSeaGreen = AliasSeq!(0.12549019607843137, 0.6980392156862745, 0.6666666666666666); 153 | /// HTML Color: LightSkyBlue 154 | alias LightSkyBlue = AliasSeq!(0.5294117647058824, 0.807843137254902, 0.9803921568627451); 155 | /// HTML Color: LightSlateGray 156 | alias LightSlateGray = AliasSeq!(0.4666666666666667, 0.5333333333333333, 0.6); 157 | /// HTML Color: LightSteelBlue 158 | alias LightSteelBlue = AliasSeq!(0.6901960784313725, 0.7686274509803922, 0.8705882352941177); 159 | /// HTML Color: LightYellow 160 | alias LightYellow = AliasSeq!(1, 1, 0.8784313725490196); 161 | /// HTML Color: Lime 162 | alias Lime = AliasSeq!(0, 1, 0); 163 | /// HTML Color: LimeGreen 164 | alias LimeGreen = AliasSeq!(0.19607843137254902, 0.803921568627451, 0.19607843137254902); 165 | /// HTML Color: Linen 166 | alias Linen = AliasSeq!(0.9803921568627451, 0.9411764705882353, 0.9019607843137255); 167 | /// HTML Color: Magenta 168 | alias Magenta = AliasSeq!(1, 0, 1); 169 | /// HTML Color: Maroon 170 | alias Maroon = AliasSeq!(0.5019607843137255, 0, 0); 171 | /// HTML Color: MediumAquaMarine 172 | alias MediumAquaMarine = AliasSeq!(0.4, 0.803921568627451, 0.6666666666666666); 173 | /// HTML Color: MediumBlue 174 | alias MediumBlue = AliasSeq!(0, 0, 0.803921568627451); 175 | /// HTML Color: MediumOrchid 176 | alias MediumOrchid = AliasSeq!(0.7294117647058823, 0.3333333333333333, 0.8274509803921568); 177 | /// HTML Color: MediumPurple 178 | alias MediumPurple = AliasSeq!(0.5764705882352941, 0.4392156862745098, 0.8588235294117647); 179 | /// HTML Color: MediumSeaGreen 180 | alias MediumSeaGreen = AliasSeq!(0.23529411764705882, 0.7019607843137254, 0.44313725490196076); 181 | /// HTML Color: MediumSlateBlue 182 | alias MediumSlateBlue = AliasSeq!(0.4823529411764706, 0.40784313725490196, 0.9333333333333333); 183 | /// HTML Color: MediumSpringGreen 184 | alias MediumSpringGreen = AliasSeq!(0, 0.9803921568627451, 0.6039215686274509); 185 | /// HTML Color: MediumTurquoise 186 | alias MediumTurquoise = AliasSeq!(0.2823529411764706, 0.8196078431372549, 0.8); 187 | /// HTML Color: MediumVioletRed 188 | alias MediumVioletRed = AliasSeq!(0.7803921568627451, 0.08235294117647059, 0.5215686274509804); 189 | /// HTML Color: MidnightBlue 190 | alias MidnightBlue = AliasSeq!(0.09803921568627451, 0.09803921568627451, 0.4392156862745098); 191 | /// HTML Color: MintCream 192 | alias MintCream = AliasSeq!(0.9607843137254902, 1, 0.9803921568627451); 193 | /// HTML Color: MistyRose 194 | alias MistyRose = AliasSeq!(1, 0.8941176470588236, 0.8823529411764706); 195 | /// HTML Color: Moccasin 196 | alias Moccasin = AliasSeq!(1, 0.8941176470588236, 0.7098039215686275); 197 | /// HTML Color: NavajoWhite 198 | alias NavajoWhite = AliasSeq!(1, 0.8705882352941177, 0.6784313725490196); 199 | /// HTML Color: Navy 200 | alias Navy = AliasSeq!(0, 0, 0.5019607843137255); 201 | /// HTML Color: OldLace 202 | alias OldLace = AliasSeq!(0.9921568627450981, 0.9607843137254902, 0.9019607843137255); 203 | /// HTML Color: Olive 204 | alias Olive = AliasSeq!(0.5019607843137255, 0.5019607843137255, 0); 205 | /// HTML Color: OliveDrab 206 | alias OliveDrab = AliasSeq!(0.4196078431372549, 0.5568627450980392, 0.13725490196078433); 207 | /// HTML Color: Orange 208 | alias Orange = AliasSeq!(1, 0.6470588235294118, 0); 209 | /// HTML Color: OrangeRed 210 | alias OrangeRed = AliasSeq!(1, 0.27058823529411763, 0); 211 | /// HTML Color: Orchid 212 | alias Orchid = AliasSeq!(0.8549019607843137, 0.4392156862745098, 0.8392156862745098); 213 | /// HTML Color: PaleGoldenRod 214 | alias PaleGoldenRod = AliasSeq!(0.9333333333333333, 0.9098039215686274, 0.6666666666666666); 215 | /// HTML Color: PaleGreen 216 | alias PaleGreen = AliasSeq!(0.596078431372549, 0.984313725490196, 0.596078431372549); 217 | /// HTML Color: PaleTurquoise 218 | alias PaleTurquoise = AliasSeq!(0.6862745098039216, 0.9333333333333333, 0.9333333333333333); 219 | /// HTML Color: PaleVioletRed 220 | alias PaleVioletRed = AliasSeq!(0.8588235294117647, 0.4392156862745098, 0.5764705882352941); 221 | /// HTML Color: PapayaWhip 222 | alias PapayaWhip = AliasSeq!(1, 0.9372549019607843, 0.8352941176470589); 223 | /// HTML Color: PeachPuff 224 | alias PeachPuff = AliasSeq!(1, 0.8549019607843137, 0.7254901960784313); 225 | /// HTML Color: Peru 226 | alias Peru = AliasSeq!(0.803921568627451, 0.5215686274509804, 0.24705882352941178); 227 | /// HTML Color: Pink 228 | alias Pink = AliasSeq!(1, 0.7529411764705882, 0.796078431372549); 229 | /// HTML Color: Plum 230 | alias Plum = AliasSeq!(0.8666666666666667, 0.6274509803921569, 0.8666666666666667); 231 | /// HTML Color: PowderBlue 232 | alias PowderBlue = AliasSeq!(0.6901960784313725, 0.8784313725490196, 0.9019607843137255); 233 | /// HTML Color: Purple 234 | alias Purple = AliasSeq!(0.5019607843137255, 0, 0.5019607843137255); 235 | /// HTML Color: RebeccaPurple 236 | alias RebeccaPurple = AliasSeq!(0.4, 0.2, 0.6); 237 | /// HTML Color: Red 238 | alias Red = AliasSeq!(1, 0, 0); 239 | /// HTML Color: RosyBrown 240 | alias RosyBrown = AliasSeq!(0.7372549019607844, 0.5607843137254902, 0.5607843137254902); 241 | /// HTML Color: RoyalBlue 242 | alias RoyalBlue = AliasSeq!(0.2549019607843137, 0.4117647058823529, 0.8823529411764706); 243 | /// HTML Color: SaddleBrown 244 | alias SaddleBrown = AliasSeq!(0.5450980392156862, 0.27058823529411763, 0.07450980392156863); 245 | /// HTML Color: Salmon 246 | alias Salmon = AliasSeq!(0.9803921568627451, 0.5019607843137255, 0.4470588235294118); 247 | /// HTML Color: SandyBrown 248 | alias SandyBrown = AliasSeq!(0.9568627450980393, 0.6431372549019608, 0.3764705882352941); 249 | /// HTML Color: SeaGreen 250 | alias SeaGreen = AliasSeq!(0.1803921568627451, 0.5450980392156862, 0.3411764705882353); 251 | /// HTML Color: SeaShell 252 | alias SeaShell = AliasSeq!(1, 0.9607843137254902, 0.9333333333333333); 253 | /// HTML Color: Sienna 254 | alias Sienna = AliasSeq!(0.6274509803921569, 0.3215686274509804, 0.17647058823529413); 255 | /// HTML Color: Silver 256 | alias Silver = AliasSeq!(0.7529411764705882, 0.7529411764705882, 0.7529411764705882); 257 | /// HTML Color: SkyBlue 258 | alias SkyBlue = AliasSeq!(0.5294117647058824, 0.807843137254902, 0.9215686274509803); 259 | /// HTML Color: SlateBlue 260 | alias SlateBlue = AliasSeq!(0.41568627450980394, 0.35294117647058826, 0.803921568627451); 261 | /// HTML Color: SlateGray 262 | alias SlateGray = AliasSeq!(0.4392156862745098, 0.5019607843137255, 0.5647058823529412); 263 | /// HTML Color: Snow 264 | alias Snow = AliasSeq!(1, 0.9803921568627451, 0.9803921568627451); 265 | /// HTML Color: SpringGreen 266 | alias SpringGreen = AliasSeq!(0, 1, 0.4980392156862745); 267 | /// HTML Color: SteelBlue 268 | alias SteelBlue = AliasSeq!(0.27450980392156865, 0.5098039215686274, 0.7058823529411765); 269 | /// HTML Color: Tan 270 | alias Tan = AliasSeq!(0.8235294117647058, 0.7058823529411765, 0.5490196078431373); 271 | /// HTML Color: Teal 272 | alias Teal = AliasSeq!(0, 0.5019607843137255, 0.5019607843137255); 273 | /// HTML Color: Thistle 274 | alias Thistle = AliasSeq!(0.8470588235294118, 0.7490196078431373, 0.8470588235294118); 275 | /// HTML Color: Tomato 276 | alias Tomato = AliasSeq!(1, 0.38823529411764707, 0.2784313725490196); 277 | /// HTML Color: Turquoise 278 | alias Turquoise = AliasSeq!(0.25098039215686274, 0.8784313725490196, 0.8156862745098039); 279 | /// HTML Color: Violet 280 | alias Violet = AliasSeq!(0.9333333333333333, 0.5098039215686274, 0.9333333333333333); 281 | /// HTML Color: Wheat 282 | alias Wheat = AliasSeq!(0.9607843137254902, 0.8705882352941177, 0.7019607843137254); 283 | /// HTML Color: White 284 | alias White = AliasSeq!(1, 1, 1); 285 | /// HTML Color: WhiteSmoke 286 | alias WhiteSmoke = AliasSeq!(0.9607843137254902, 0.9607843137254902, 0.9607843137254902); 287 | /// HTML Color: Yellow 288 | alias Yellow = AliasSeq!(1, 1, 0); 289 | /// HTML Color: YellowGreen 290 | alias YellowGreen = AliasSeq!(0.6039215686274509, 0.803921568627451, 0.19607843137254902); 291 | } 292 | 293 | /// Color3 can be passed instead of 3 floats. 294 | unittest 295 | { 296 | import std.format : format; 297 | 298 | string formatColor(float r, float g, float b) 299 | { 300 | return format("{%f, %f, %f}", r, g, b); 301 | } 302 | 303 | assert(formatColor(Color3.White) == formatColor(1, 1, 1)); 304 | 305 | import std.stdio : writeln; 306 | 307 | void example(float r, float g, float b, float a) 308 | { 309 | writeln(r, ": ", formatColor(g, b, a)); 310 | } 311 | 312 | example(0.4f, Color3.Tomato); 313 | example(Color3.Yellow, 1.2f); 314 | } 315 | -------------------------------------------------------------------------------- /source/d2d/core/fpslimiter.d: -------------------------------------------------------------------------------- 1 | module d2d.core.fpslimiter; 2 | 3 | import core.thread; 4 | import std.datetime; 5 | 6 | /** 7 | * Class for limiting your FPS. 8 | * Examples: 9 | * --- 10 | * FPSLimiter limiter = new FPSLimiter(25); 11 | * while(window.open) 12 | * { 13 | * window.clear(); 14 | * // Draw and Update stuff 15 | * window.display(); 16 | * limiter.wait(); 17 | * } 18 | * --- 19 | */ 20 | class FPSLimiter 21 | { 22 | protected: 23 | int _fps = 0; 24 | int _skiphns = 0; 25 | ulong _next = 0; 26 | long _sleep = 0; 27 | 28 | public: 29 | /// Creates a new FPS Limiter instance with specified max FPS. 30 | this(int maxFPS) 31 | { 32 | _fps = maxFPS; 33 | _skiphns = 10_000_000 / _fps; 34 | _next = Clock.currStdTime(); 35 | } 36 | 37 | /// Calculates how long to wait and then waits that amount of time to ensure the target FPS. 38 | void wait() 39 | { 40 | _next += _skiphns; 41 | _sleep = _next - Clock.currStdTime(); 42 | if (_sleep > 0) 43 | { 44 | Thread.sleep(dur!("hnsecs")(_sleep)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /source/d2d/core/idisposable.d: -------------------------------------------------------------------------------- 1 | module d2d.core.idisposable; 2 | 3 | /** 4 | * Interface containing a delete function. Often combined with IVerifiable. 5 | * Examples: 6 | * --- 7 | * class A : IDisposable 8 | * { 9 | * ~this() 10 | * { 11 | * dispose(); 12 | * } 13 | * 14 | * public void dispose() 15 | * { 16 | * // Delete Native Stuff 17 | * } 18 | * } 19 | * --- 20 | */ 21 | interface IDisposable 22 | { 23 | /// Function for deallocating memory. Should be called in destructor. 24 | public void dispose(); 25 | } 26 | -------------------------------------------------------------------------------- /source/d2d/core/iverifiable.d: -------------------------------------------------------------------------------- 1 | module d2d.core.iverifiable; 2 | 3 | /// Interface containing a isValid function. Often combined with IDisposable. 4 | interface IVerifiable 5 | { 6 | /// Function for checking if this is valid. 7 | public @property bool valid(); 8 | } 9 | -------------------------------------------------------------------------------- /source/d2d/core/quadtree.d: -------------------------------------------------------------------------------- 1 | module d2d.core.quadtree; 2 | 3 | import std.algorithm; 4 | import std.conv; 5 | import std.functional; 6 | import std.range; 7 | import std.string; 8 | 9 | import gl3n.linalg; 10 | 11 | import d2d.core.utils; 12 | 13 | struct QuadTree(T, size_t maxObjects, size_t maxDepth, string mapper = "a") 14 | { 15 | @disable this(this); 16 | 17 | rect bounds; 18 | 19 | static if (maxDepth > 0) 20 | { 21 | alias SubTree = QuadTree!(T, maxObjects, maxDepth - 1, mapper); 22 | /// 0 | 1 23 | /// ---+--- 24 | /// 2 | 3 25 | SubTree*[4] nodes; 26 | } 27 | 28 | T[] children; 29 | 30 | static rect map(T item) 31 | { 32 | return unaryFun!mapper(item); 33 | } 34 | 35 | void clear() 36 | { 37 | static if (maxDepth > 0) 38 | if (nodes[0]!is null) 39 | foreach (ref node; nodes) 40 | { 41 | node.clear(); 42 | node = null; 43 | } 44 | 45 | children.length = 0; 46 | } 47 | 48 | static if (maxDepth > 0) 49 | { 50 | private void split() 51 | { 52 | const center = bounds.center; 53 | 54 | nodes[0] = new SubTree(rect.dim(0, 0, center)); 55 | nodes[1] = new SubTree(rect.dim(center.x, 0, center)); 56 | nodes[2] = new SubTree(rect.dim(0, center.y, center)); 57 | nodes[3] = new SubTree(rect.dim(center, center)); 58 | } 59 | 60 | bool isSplit() @property const @safe 61 | { 62 | return nodes[0]!is null; 63 | } 64 | 65 | /// Determines in which quadrant a rectangle would fall or -1 if it spans multiple. 66 | static if (!is(T : rect)) 67 | int getQuadrant(T item) 68 | { 69 | return getQuadrant(map(item)); 70 | } 71 | 72 | /// ditto 73 | int getQuadrant(rect r) 74 | { 75 | int index = -1; 76 | const center = bounds.center; 77 | 78 | const aboveMidpoint = r.y < center.y && r.y + r.height < center.y; 79 | const belowMidpoint = r.y > center.y && r.y + r.height > center.y; 80 | const leftOfMidpoint = r.x < center.x && r.x + r.width < center.x; 81 | const rightOfMidpoint = r.x > center.x && r.x + r.width > center.x; 82 | 83 | if (aboveMidpoint) 84 | { 85 | if (leftOfMidpoint) 86 | index = 0; 87 | else if (rightOfMidpoint) 88 | index = 1; 89 | } 90 | else if (belowMidpoint) 91 | { 92 | if (leftOfMidpoint) 93 | index = 2; 94 | else if (rightOfMidpoint) 95 | index = 3; 96 | } 97 | 98 | return index; 99 | } 100 | } 101 | 102 | void insert(T item) 103 | { 104 | static if (maxDepth > 0) 105 | { 106 | if (nodes[0]!is null) 107 | { 108 | auto index = getQuadrant(item); 109 | if (index >= 0) 110 | { 111 | nodes[index].insert(item); 112 | return; 113 | } 114 | } 115 | } 116 | 117 | children.assumeSafeAppend() ~= item; 118 | 119 | static if (maxDepth > 0) 120 | { 121 | if (children.length > maxObjects) 122 | { 123 | if (nodes[0] is null) 124 | split(); 125 | 126 | size_t i; 127 | while (i < children.length) 128 | { 129 | auto index = getQuadrant(children[i]); 130 | if (index == -1) 131 | i++; 132 | else 133 | { 134 | auto other = children[i]; 135 | children[i] = children[$ - 1]; 136 | children.length--; 137 | nodes[index].insert(other); 138 | } 139 | } 140 | } 141 | } 142 | } 143 | 144 | bool remove(T item) 145 | { 146 | if (item is T.init) 147 | return false; 148 | 149 | static if (maxDepth > 0) 150 | { 151 | if (nodes[0]!is null) 152 | { 153 | auto index = getQuadrant(item); 154 | if (index >= 0) 155 | { 156 | auto ret = nodes[index].remove(item); 157 | if (ret) 158 | return true; 159 | } 160 | } 161 | } 162 | 163 | foreach (i, child; children) 164 | if (child is item) 165 | { 166 | children[i] = children[$ - 1]; 167 | children.length--; 168 | return true; 169 | } 170 | 171 | return false; 172 | } 173 | 174 | T[] retrieve(rect r, bool exact = true) 175 | { 176 | static if (maxDepth > 0) 177 | { 178 | auto ret = appender!(T[]); 179 | if (exact) 180 | ret.put(children.filter!(a => map(a).intersects(r))); 181 | else 182 | ret.put(children); 183 | 184 | if (nodes[0]!is null) 185 | { 186 | auto index = getQuadrant(r); 187 | if (index != -1) 188 | ret.put(nodes[index].retrieve(r, exact)); 189 | else 190 | foreach (ref node; nodes) 191 | ret.put(node.retrieve(r, exact)); 192 | } 193 | return ret.data; 194 | } 195 | else 196 | { 197 | if (exact) 198 | return children.filter!(a => map(a).intersects(r)).array; 199 | else 200 | return children; 201 | } 202 | } 203 | 204 | string toString() const @safe 205 | { 206 | auto ret = appender!string; 207 | ret.put("quad!"); 208 | ret.put(maxDepth.to!string); 209 | 210 | ret.put("(self: "); 211 | ret.put(children.length.to!string); 212 | static if (maxDepth > 0) 213 | if (isSplit) 214 | { 215 | ret.put(",\n"); 216 | ret.put(("NW: " ~ nodes[0].toString()).indent); 217 | ret.put(",\n"); 218 | ret.put(("NE: " ~ nodes[1].toString()).indent); 219 | ret.put(",\n"); 220 | ret.put(("SE: " ~ nodes[2].toString()).indent); 221 | ret.put(",\n"); 222 | ret.put(("SW: " ~ nodes[3].toString()).indent); 223 | } 224 | ret.put(")"); 225 | 226 | return ret.data; 227 | } 228 | } 229 | 230 | private string indent(string s, string indentation = "\t") @safe 231 | { 232 | return s.lineSplitter!(KeepTerminator.yes) 233 | .map!(a => a.length ? indentation ~ a : a) 234 | .join; 235 | } 236 | 237 | unittest 238 | { 239 | QuadTree!(rect, 2, 5) quadtree; 240 | quadtree.bounds = rect.dim(800, 600); 241 | 242 | assert(quadtree.retrieve(rect.dim(800, 600)).count == 0); 243 | 244 | quadtree.insert(rect.dim(40, 40, 100, 100)); 245 | 246 | assert(quadtree.retrieve(rect.dim(800, 600), false).count == 1); 247 | assert(quadtree.retrieve(rect.dim(139, 40, 100, 100), false).count == 1); 248 | assert(quadtree.retrieve(rect.dim(140, 40, 100, 100), false).count == 1); 249 | assert(quadtree.retrieve(rect.dim(141, 40, 100, 100), false).count == 1); 250 | assert(quadtree.retrieve(rect.dim(40, 139, 100, 100), false).count == 1); 251 | assert(quadtree.retrieve(rect.dim(40, 140, 100, 100), false).count == 1); 252 | assert(quadtree.retrieve(rect.dim(40, 141, 100, 100), false).count == 1); 253 | 254 | assert(quadtree.retrieve(rect.dim(800, 600)).count == 1); 255 | assert(quadtree.retrieve(rect.dim(139, 40, 100, 100)).count == 1); 256 | assert(quadtree.retrieve(rect.dim(140, 40, 100, 100)).count == 1); 257 | assert(quadtree.retrieve(rect.dim(141, 40, 100, 100)).count == 0); 258 | assert(quadtree.retrieve(rect.dim(40, 139, 100, 100)).count == 1); 259 | assert(quadtree.retrieve(rect.dim(40, 140, 100, 100)).count == 1); 260 | assert(quadtree.retrieve(rect.dim(40, 141, 100, 100)).count == 0); 261 | 262 | assert(!quadtree.isSplit); 263 | 264 | quadtree.insert(rect.dim(30, 410, 20, 20)); 265 | assert(!quadtree.isSplit); 266 | assert(quadtree.retrieve(rect.dim(800, 600), false).count == 2); 267 | assert(quadtree.retrieve(rect.dim(0, 0, 1, 1), false).count == 2); 268 | assert(quadtree.retrieve(rect.dim(400, 0, 1, 1), false).count == 2); 269 | 270 | quadtree.insert(rect.dim(410, 10, 20, 20)); 271 | assert(quadtree.isSplit); 272 | assert(quadtree.retrieve(rect.dim(800, 600), false).count == 3); 273 | assert(quadtree.retrieve(rect.dim(1, 1, 1, 1), false).count == 1); 274 | assert(quadtree.retrieve(rect.dim(401, 1, 1, 1), false).count == 1); 275 | assert(quadtree.retrieve(rect.dim(1, 401, 1, 1), false).count == 1); 276 | assert(quadtree.retrieve(rect.dim(401, 401, 1, 1), false).count == 0); 277 | 278 | quadtree.insert(rect.dim(390, 390, 20, 20)); 279 | assert(quadtree.retrieve(rect.dim(800, 600), false).count == 4); 280 | assert(quadtree.retrieve(rect.dim(1, 1, 1, 1), false).count == 2); 281 | assert(quadtree.retrieve(rect.dim(401, 1, 1, 1), false).count == 2); 282 | assert(quadtree.retrieve(rect.dim(1, 401, 1, 1), false).count == 2); 283 | assert(quadtree.retrieve(rect.dim(401, 401, 1, 1), false).count == 1); 284 | } 285 | -------------------------------------------------------------------------------- /source/d2d/core/transformable.d: -------------------------------------------------------------------------------- 1 | module d2d.core.transformable; 2 | 3 | import d2d; 4 | 5 | /// Base class for Drawables containing code for rotation, scaling and translation around an origin. 6 | class Transformable 7 | { 8 | private: 9 | mat4 _transform = mat4.identity; 10 | vec2 _origin = vec2(0); 11 | vec2 _position = vec2(0); 12 | vec2 _scale = vec2(1); 13 | float _rotation = 0; 14 | bool _needsChange = true; 15 | public: 16 | @nogc: 17 | /// Rotates this around origin with the specified amount relatively. 18 | void rotate(float amount) 19 | { 20 | _rotation += amount; 21 | _needsChange = true; 22 | } 23 | 24 | /// Scales this around origin with the specified amount relatively. 25 | void scale(vec2 amount) 26 | { 27 | _scale += amount; 28 | _needsChange = true; 29 | } 30 | 31 | /// Moves this with the specified amount relatively. 32 | void move(vec2 amount) 33 | { 34 | _position += amount; 35 | _needsChange = true; 36 | } 37 | 38 | @property: 39 | /// Calculates the transformation matrix when needed and returns it. 40 | mat4 transform() 41 | { 42 | if (_needsChange) 43 | { 44 | _transform = mat4.scaling(_scale.x, _scale.y, 1) * mat4.translation(_position.x, _position.y, 0) 45 | * mat4.zrotation(_rotation) * mat4.translation(-_origin.x, -_origin.y, 0); 46 | 47 | assert(_transform.isFinite); 48 | } 49 | 50 | return _transform; 51 | } 52 | 53 | /// Sets the position of this transform. 54 | void position(vec2 position) 55 | { 56 | _position = position; 57 | _needsChange = true; 58 | } 59 | 60 | /// Gets the position of this transform. 61 | vec2 position() 62 | { 63 | return _position; 64 | } 65 | 66 | /// Sets the origin position of this transform. 67 | void origin(vec2 origin) 68 | { 69 | _origin = origin; 70 | _needsChange = true; 71 | } 72 | 73 | /// Gets the origin position of this transform. 74 | vec2 origin() 75 | { 76 | return _origin; 77 | } 78 | 79 | /// Sets the scaling of this transform. 80 | void scaling(vec2 scale) 81 | { 82 | _scale = scale; 83 | _needsChange = true; 84 | } 85 | 86 | /// Gets the scaling of this transform. 87 | vec2 scaling() 88 | { 89 | return _scale; 90 | } 91 | 92 | /// Sets the rotation of this transform. 93 | void rotation(float rotation) 94 | { 95 | _rotation = rotation; 96 | _needsChange = true; 97 | } 98 | 99 | /// Gets the rotation of this transform. 100 | float rotation() 101 | { 102 | return _rotation; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /source/d2d/core/utils.d: -------------------------------------------------------------------------------- 1 | module d2d.core.utils; 2 | 3 | import gl3n.linalg; 4 | 5 | struct Rectangle(T) 6 | { 7 | alias vec2t = Vector!(T, 2); 8 | 9 | T x = 0, y = 0, width = 0, height = 0; 10 | 11 | vec2t center() const @property nothrow @nogc pure @safe 12 | { 13 | return vec2t(x + width / 2, y + height / 2); 14 | } 15 | 16 | vec2t size() const @property nothrow @nogc pure @safe 17 | { 18 | return vec2t(width, height); 19 | } 20 | 21 | vec2t size(vec2t newSize) @property nothrow @nogc pure @safe 22 | { 23 | width = newSize.x; 24 | height = newSize.y; 25 | return newSize; 26 | } 27 | 28 | Rectangle!T normalized() const @property nothrow @nogc pure @safe 29 | { 30 | Rectangle!T copy = this; 31 | if (copy.width < 0) 32 | { 33 | copy.x += copy.width; 34 | copy.width = -copy.width; 35 | } 36 | if (copy.height < 0) 37 | { 38 | copy.y += copy.height; 39 | copy.height = -copy.height; 40 | } 41 | return copy; 42 | } 43 | 44 | Rectangle!T scale(vec2t size) const @property nothrow @nogc pure @safe 45 | { 46 | return scale(size.x, size.y); 47 | } 48 | 49 | Rectangle!T scale(T x, T y) const @property nothrow @nogc pure @safe 50 | { 51 | return Rectangle!T(this.x * x, this.y * y, width * x, height * y); 52 | } 53 | 54 | static Rectangle!T dim(T width, T height) nothrow @nogc pure @safe 55 | { 56 | return Rectangle!T(0, 0, width, height); 57 | } 58 | 59 | static Rectangle!T dim(vec2t size) nothrow @nogc pure @safe 60 | { 61 | return Rectangle!T(0, 0, size.x, size.y); 62 | } 63 | 64 | static Rectangle!T dim(T x, T y, T width, T height) nothrow @nogc pure @safe 65 | { 66 | return Rectangle!T(x, y, width, height); 67 | } 68 | 69 | static Rectangle!T dim(vec2t pos, T width, T height) nothrow @nogc pure @safe 70 | { 71 | return Rectangle!T(pos.x, pos.y, width, height); 72 | } 73 | 74 | static Rectangle!T dim(T x, T y, vec2t size) nothrow @nogc pure @safe 75 | { 76 | return Rectangle!T(x, y, size.x, size.y); 77 | } 78 | 79 | static Rectangle!T dim(vec2t pos, vec2t size) nothrow @nogc pure @safe 80 | { 81 | return Rectangle!T(pos.x, pos.y, size.x, size.y); 82 | } 83 | 84 | static Rectangle!T abs(T x1, T y1, T x2, T y2) nothrow @nogc pure @safe 85 | { 86 | return Rectangle!T(x1, y1, x2 - x1, y2 - y1); 87 | } 88 | 89 | static Rectangle!T abs(vec2t pos1, T x2, T y2) nothrow @nogc pure @safe 90 | { 91 | return Rectangle!T(pos1.x, pos1.y, x2 - pos1.x, y2 - pos1.y); 92 | } 93 | 94 | static Rectangle!T abs(T x1, T y1, vec2t pos2) nothrow @nogc pure @safe 95 | { 96 | return Rectangle!T(x1, y1, pos2.x - x1, pos2.y - y1); 97 | } 98 | 99 | static Rectangle!T abs(vec2t pos1, vec2t pos2) nothrow @nogc pure @safe 100 | { 101 | return Rectangle!T(pos1.x, pos1.y, pos2.x - pos1.x, pos2.y - pos1.y); 102 | } 103 | 104 | vec2t pos00() @property const nothrow @nogc pure @safe 105 | { 106 | return vec2t(x, y); 107 | } 108 | 109 | vec2t pos10() @property const nothrow @nogc pure @safe 110 | { 111 | return vec2t(x + width, y); 112 | } 113 | 114 | vec2t pos11() @property const nothrow @nogc pure @safe 115 | { 116 | return vec2t(x + width, y + height); 117 | } 118 | 119 | vec2t pos01() @property const nothrow @nogc pure @safe 120 | { 121 | return vec2t(x, y + height); 122 | } 123 | 124 | Rectangle!T translate(T x, T y) nothrow @nogc pure @safe 125 | { 126 | return Rectangle!T(this.x + x, this.y + y, width, height); 127 | } 128 | 129 | Rectangle!T translate(vec2t position) nothrow @nogc pure @safe 130 | { 131 | return Rectangle!T(x + position.x, y + position.y, width, height); 132 | } 133 | 134 | bool intersects(const rect other) nothrow @nogc pure @safe 135 | { 136 | return !(x + width < other.x || other.x + other.width < x || y + height < other.y 137 | || other.y + other.height < y); 138 | } 139 | } 140 | 141 | alias rect = Rectangle!float; 142 | alias rectd = Rectangle!double; 143 | alias recti = Rectangle!int; 144 | -------------------------------------------------------------------------------- /source/d2d/font/bmfont.d: -------------------------------------------------------------------------------- 1 | module d2d.font.bmfont; 2 | version (BindSDL_Image) : import d2d; 3 | 4 | import std.algorithm : max; 5 | import std.utf : byDchar; 6 | import std.path : buildPath; 7 | 8 | import BM = bmfont; 9 | import fs = std.file; 10 | 11 | /// Implementation for AngelCode BMFont. 12 | class BMFont : IFont 13 | { 14 | private: 15 | BM.Font _handle = void; 16 | Texture[] _textures; 17 | string _textureFolder; 18 | 19 | public: 20 | ~this() 21 | { 22 | } 23 | 24 | /// Creates a new BMFont instance with a textureFolder used as base path for texture loading 25 | this(string textureFolder) 26 | { 27 | _textureFolder = textureFolder; 28 | } 29 | 30 | /// Handle to underlying `bmfont.Font` handle. 31 | @property ref BM.Font handle() 32 | { 33 | return _handle; 34 | } 35 | 36 | /// All textures in the BMFont file 37 | @property ref Texture[] textures() 38 | { 39 | return _textures; 40 | } 41 | 42 | /// Loads the font from a binary or text bmfont file. 43 | override void load(string file, int sizeInPt) 44 | { 45 | _handle = BM.parseFnt(cast(ubyte[]) fs.read(file)); 46 | foreach (page; _handle.pages) 47 | textures ~= new Texture(buildPath(_textureFolder, page)); 48 | } 49 | 50 | /// Returns true if font has been loaded. 51 | override @property bool valid() 52 | { 53 | return _handle.type != 0; 54 | } 55 | 56 | override void dispose() 57 | { 58 | } 59 | 60 | /// Renders a string to an IText. 61 | override IText render(string text, float scale = 1.0f) 62 | { 63 | BMText ret = new BMText(this); 64 | ret.text = text; 65 | ret.scale = scale; 66 | return ret; 67 | } 68 | 69 | /// Renders a multiline string to an IText. 70 | override IText renderMultiline(string text, float scale = 1.0f) 71 | { 72 | BMText ret = new BMText(this); 73 | ret.text = text; 74 | ret.scale = scale; 75 | ret.multiline = true; 76 | return ret; 77 | } 78 | 79 | /// Returns the line height of this font. 80 | @property float lineHeight() 81 | { 82 | return cast(float) _handle.common.lineHeight; 83 | } 84 | 85 | /// Returns the dimensions of a string with this font. 86 | override vec2 measureText(string text, float scale = 1.0f) 87 | { 88 | if (!text.length) 89 | return vec2(0, 0); 90 | int w, h; 91 | dchar last; 92 | foreach (c; text.byDchar) 93 | { 94 | auto bmChar = _handle.getChar(c); 95 | w += bmChar.xadvance; 96 | h = max(h, bmChar.yoffset + bmChar.height); 97 | if (last != dchar.init) 98 | w += _handle.getKerning(last, c); 99 | last = c; 100 | } 101 | auto lastBmChar = _handle.getChar(last); 102 | w = w - lastBmChar.xadvance + lastBmChar.xoffset + lastBmChar.width; 103 | return vec2(w * scale, h * scale); 104 | } 105 | 106 | /// Returns the dimensions of a multiline string with this font. 107 | override vec2 measureTextMultiline(string text, float scale = 1.0f) 108 | { 109 | string[] lines = text.split('\n'); 110 | int w; 111 | foreach (string line; lines) 112 | { 113 | vec2 size = measureText(line); 114 | w = max(w, cast(int) size.x); 115 | } 116 | return vec2(w * scale, 117 | ((lines.length - 1) * _handle.common.lineHeight + measureText(lines[$ - 1]).y) * scale); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /source/d2d/font/bmtext.d: -------------------------------------------------------------------------------- 1 | module d2d.font.bmtext; 2 | version (BindSDL_Image) : import d2d; 3 | 4 | import std.utf : byDchar; 5 | 6 | /// Implementation containing text drawable functions using an AngelCode BMFont. Nice and fast for unscaled text, reduced memory usage, render time scales with text length, no re-render times, requires some additional shader uniforms. 7 | class BMText : Transformable, IText 8 | { 9 | private: 10 | float _scale = 1.0f; 11 | string _text = ""; 12 | BMFont _font; 13 | bool _multiline = false; 14 | RectangleShape unitRect; 15 | float iWidth, iHeight; 16 | 17 | public: 18 | /// Creates an empty bmfont text 19 | this(BMFont font) 20 | { 21 | _font = font; 22 | iWidth = 1.0f / _font.handle.common.scaleW; 23 | iHeight = 1.0f / _font.handle.common.scaleH; 24 | unitRect = RectangleShape.create(vec2(0, 0), vec2(1, 1)); 25 | } 26 | 27 | /// Gets the scale in percent. 28 | override @property float scale() 29 | { 30 | return _scale; 31 | } 32 | 33 | /// Sets the scale in percent. 34 | override @property void scale(float value) 35 | { 36 | _scale = value; 37 | } 38 | 39 | /// Gets the text. 40 | override @property string text() 41 | { 42 | return _text; 43 | } 44 | 45 | /// Modifies the text. 46 | override @property void text(string value) 47 | { 48 | _text = value; 49 | } 50 | 51 | /// Returns if this text should be rendered multiline. 52 | @property bool multiline() 53 | { 54 | return _multiline; 55 | } 56 | 57 | /// Sets if this text should be rendered multiline. 58 | @property void multiline(bool value) 59 | { 60 | _multiline = value; 61 | } 62 | 63 | /// Dynamically draws the text onto a target. Requires the shader to have `texRect` vec4(uvX, uvY, uvW, uvH) and `sizeRect` vec4(posX, posY, width, height) uniforms. 64 | override void draw(IRenderTarget target, ShaderProgram shader = null) 65 | { 66 | matrixStack.push(); 67 | matrixStack.top *= mat4.scaling(_scale, _scale, 1); 68 | matrixStack.top *= transform; 69 | int x, y; 70 | dchar last; 71 | for (int i = 0; i < _font.textures.length; i++) 72 | _font.textures[i].bind(i); 73 | if (shader) 74 | shader.bind(); 75 | foreach (dc; _text.byDchar) 76 | { 77 | auto bmChar = _font.handle.getChar(dc); 78 | if (bmChar.id == dchar.init) 79 | { 80 | if (_multiline && dc == '\n') 81 | { 82 | x = 0; 83 | y += _font.handle.common.lineHeight; 84 | } 85 | continue; 86 | } 87 | 88 | shader.set("tex", cast(int) bmChar.page); 89 | shader.set("texRect", vec4(bmChar.x * iWidth, bmChar.y * iHeight, 90 | bmChar.width * iWidth, bmChar.height * iHeight)); 91 | shader.set("sizeRect", vec4(x + bmChar.xoffset, y + bmChar.yoffset, 92 | bmChar.width, bmChar.height)); 93 | unitRect.draw(target, shader); 94 | if (_multiline && dc == '\n') 95 | { 96 | x = 0; 97 | y += _font.handle.common.lineHeight; 98 | continue; 99 | } 100 | if (last != dchar.init) 101 | x += _font.handle.getKerning(last, dc); 102 | x += bmChar.xadvance; 103 | last = dc; 104 | } 105 | matrixStack.pop(); 106 | } 107 | 108 | override @property bool valid() 109 | { 110 | return true; 111 | } 112 | 113 | override void dispose() 114 | { 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /source/d2d/font/ifont.d: -------------------------------------------------------------------------------- 1 | module d2d.font.ifont; 2 | 3 | import d2d; 4 | 5 | /// Interface for font loaders. 6 | interface IFont : IDisposable, IVerifiable 7 | { 8 | /// Loads the font from a file. Size may not do anything with bitmap fonts, use IText.scale instead! 9 | void load(string file, int sizeInPt); 10 | 11 | /// Renders a string to an IText. 12 | IText render(string text, float scale = 1.0f); 13 | 14 | /// Renders a multiline string to an IText. 15 | IText renderMultiline(string text, float scale = 1.0f); 16 | 17 | /// Returns the dimensions of a string with this font. 18 | vec2 measureText(string text, float scale = 1.0f); 19 | 20 | /// Returns the dimensions of a multiline string with this font. 21 | vec2 measureTextMultiline(string text, float scale = 1.0f); 22 | } 23 | -------------------------------------------------------------------------------- /source/d2d/font/itext.d: -------------------------------------------------------------------------------- 1 | module d2d.font.itext; 2 | 3 | import d2d; 4 | 5 | /// Interface containing text drawable functions. 6 | interface IText : IDrawable, IDisposable, IVerifiable 7 | { 8 | /// Gets the scale in percent. 9 | @property float scale(); 10 | /// Sets the scale in percent. 11 | @property void scale(float value); 12 | 13 | /// Gets the text. 14 | @property string text(); 15 | /// Modifies the text. 16 | @property void text(string value); 17 | } 18 | -------------------------------------------------------------------------------- /source/d2d/font/ttffont.d: -------------------------------------------------------------------------------- 1 | module d2d.font.ttffont; 2 | 3 | version(BindSDL_TTF): 4 | 5 | import d2d; 6 | 7 | import std.algorithm : max; 8 | 9 | /// Implementation for SDL_ttf. 10 | class TTFFont : IFont 11 | { 12 | private: 13 | TTF_Font* _handle; 14 | 15 | public: 16 | ~this() 17 | { 18 | dispose(); 19 | } 20 | 21 | /// Handle to underlying `TTF_Font*` handle. 22 | @property TTF_Font* handle() 23 | { 24 | return _handle; 25 | } 26 | 27 | /// Loads the font from a file. 28 | override void load(string file, int sizeInPt) 29 | { 30 | _handle = TTF_OpenFont(file.toStringz(), sizeInPt); 31 | if (!valid) 32 | throw new Exception(cast(string) TTF_GetError().fromStringz()); 33 | } 34 | 35 | /// Returns if `_handle` is not `null`. 36 | override @property bool valid() 37 | { 38 | return _handle !is null; 39 | } 40 | 41 | /// Deallocates memory and invalidates this. 42 | override void dispose() 43 | { 44 | if (valid) 45 | { 46 | TTF_CloseFont(_handle); 47 | _handle = null; 48 | } 49 | } 50 | 51 | /// Renders a string to an IText. 52 | override IText render(string text, float scale = 1.0f) 53 | { 54 | TTFText ret = new TTFText(this); 55 | ret.text = text; 56 | ret.scale = scale; 57 | return ret; 58 | } 59 | 60 | /// Renders a multiline string to an IText. 61 | override IText renderMultiline(string text, float scale = 1.0f) 62 | { 63 | TTFText ret = new TTFText(this); 64 | ret.text = text; 65 | ret.scale = scale; 66 | ret.multiline = true; 67 | return ret; 68 | } 69 | 70 | /// Returns the line height of this font. 71 | @property float lineHeight() 72 | { 73 | return TTF_FontHeight(_handle); 74 | } 75 | 76 | /// Returns the dimensions of a string with this font. 77 | override vec2 measureText(string text, float scale = 1.0f) 78 | { 79 | int w, h; 80 | TTF_SizeUTF8(_handle, text.toStringz(), &w, &h); 81 | return vec2(w * scale, h * scale); 82 | } 83 | 84 | /// Returns the dimensions of a multiline string with this font. 85 | override vec2 measureTextMultiline(string text, float scale = 1.0f) 86 | { 87 | string[] lines = text.split('\n'); 88 | int w, h; 89 | foreach (string line; lines) 90 | { 91 | int lw, lh; 92 | TTF_SizeText(_handle, line.toStringz(), &lw, &lh); 93 | w = max(w, lw); 94 | h += lh; 95 | } 96 | return vec2(w * scale, h * scale); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /source/d2d/font/ttftext.d: -------------------------------------------------------------------------------- 1 | module d2d.font.ttftext; 2 | 3 | version(BindSDL_TTF): 4 | 5 | import d2d; 6 | 7 | import std.algorithm : max; 8 | 9 | /// Implementation containing text drawable functions using a TTF font. Nice for static texts, slow for dynamic texts. 10 | class TTFText : RectangleShape, IText 11 | { 12 | private: 13 | float _scale = 1.0f; 14 | string _text = ""; 15 | TTFFont _font; 16 | bool _multiline = false; 17 | 18 | public: 19 | /// Creates an empty ttf text 20 | this(TTFFont font) 21 | { 22 | _font = font; 23 | texture = new Texture(); 24 | texture.create(0, 0, []); 25 | } 26 | 27 | /// Gets the scale in percent. 28 | override @property float scale() 29 | { 30 | return _scale; 31 | } 32 | 33 | /// Sets the scale in percent. 34 | override @property void scale(float value) 35 | { 36 | _scale = value; 37 | } 38 | 39 | /// Gets the text. 40 | override @property string text() 41 | { 42 | return _text; 43 | } 44 | 45 | /// Modifies the text. 46 | override @property void text(string value) 47 | { 48 | if (_text != value) 49 | { 50 | _text = value; 51 | redraw(); 52 | } 53 | } 54 | 55 | /// Returns if this text should be rendered multiline. 56 | @property bool multiline() 57 | { 58 | return _multiline; 59 | } 60 | 61 | /// Sets if this text should be rendered multiline. 62 | @property void multiline(bool value) 63 | { 64 | if (_multiline != value) 65 | { 66 | _multiline = value; 67 | redraw(); 68 | } 69 | } 70 | 71 | /// Should get called automatically when something changed. 72 | void redraw() 73 | { 74 | Bitmap bmp; 75 | if (_multiline) 76 | { 77 | string[] lines = text.split('\n'); 78 | int maxWidth = 0; 79 | foreach (string line; lines) 80 | { 81 | int w, h; 82 | TTF_SizeText(_font.handle, line.toStringz(), &w, &h); 83 | maxWidth = max(w, maxWidth); 84 | } 85 | bmp = Bitmap.fromSurface(TTF_RenderUTF8_Blended_Wrapped(_font.handle, text.toStringz(), Color.White.sdl_color, maxWidth)); 86 | } 87 | else 88 | { 89 | bmp = Bitmap.fromSurface(TTF_RenderUTF8_Blended(_font.handle, text.toStringz(), Color.White.sdl_color)); 90 | } 91 | if (!bmp.valid) 92 | throw new Exception(cast(string) TTF_GetError().fromStringz()); 93 | texture.recreateFromBitmap(bmp, "Blended Text: \"" ~ text ~ "\""); 94 | size = vec2(texture.width, texture.height); 95 | create(); 96 | } 97 | 98 | override void draw(IRenderTarget target, ShaderProgram shader = null) 99 | { 100 | matrixStack.push(); 101 | matrixStack.top = matrixStack.top * mat4.scaling(_scale, _scale, 1); 102 | super.draw(target, shader); 103 | matrixStack.pop(); 104 | } 105 | 106 | override @property bool valid() 107 | { 108 | return super.valid; 109 | } 110 | 111 | override void dispose() 112 | { 113 | super.dispose(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /source/d2d/package.d: -------------------------------------------------------------------------------- 1 | /** Module for including all D2DGame components. 2 | * Examples: 3 | * --- 4 | * import D2D; 5 | * 6 | * void main() 7 | * { 8 | * Window window = new Window(); 9 | * 10 | * Event event; // Or WindowEvent 11 | * while(window.open) 12 | * { 13 | * while (window.pollEvent(event)) 14 | * { 15 | * if(event.type == Event.Type.Quit) 16 | * window.close(); 17 | * } 18 | * window.clear(); 19 | * 20 | * // Update & Render here 21 | * 22 | * window.display(); 23 | * } 24 | * } 25 | * --- 26 | */ 27 | module d2d; 28 | 29 | public 30 | { 31 | import bindbc.sdl; 32 | import bindbc.sdl.image; 33 | import bindbc.sdl.mixer; 34 | import bindbc.sdl.ttf; 35 | 36 | import bindbc.opengl; 37 | 38 | import soloader = bindbc.loader.sharedlib; 39 | 40 | import gl3n.aabb; 41 | import gl3n.ext.matrixstack; 42 | import gl3n.frustum; 43 | import gl3n.interpolate; 44 | import gl3n.linalg; 45 | import gl3n.plane; 46 | import gl3n.util; 47 | 48 | import std.math; 49 | 50 | import d2d.core.bytestream; 51 | import d2d.core.color3; 52 | import d2d.core.fpslimiter; 53 | import d2d.core.idisposable; 54 | import d2d.core.iverifiable; 55 | import d2d.core.quadtree; 56 | import d2d.core.transformable; 57 | import d2d.core.utils; 58 | 59 | import d2d.window.window; 60 | import d2d.window.windowevent; 61 | import d2d.window.windowflags; 62 | 63 | import d2d.rendering.bitmap; 64 | import d2d.rendering.color; 65 | import d2d.rendering.idrawable; 66 | import d2d.rendering.irendertarget; 67 | import d2d.rendering.mesh; 68 | import d2d.rendering.rectangleshape; 69 | import d2d.rendering.shader; 70 | import d2d.rendering.shaderprogram; 71 | import d2d.rendering.shape; 72 | import d2d.rendering.spritebatch; 73 | import d2d.rendering.spritesheet; 74 | import d2d.rendering.texture; 75 | 76 | import d2d.audio.music; 77 | import d2d.audio.sound; 78 | 79 | import d2d.font.bmfont; 80 | import d2d.font.bmtext; 81 | import d2d.font.ifont; 82 | import d2d.font.itext; 83 | import d2d.font.ttffont; 84 | import d2d.font.ttftext; 85 | 86 | import d2d.toolkit.game; 87 | import d2d.toolkit.input.keyboard; 88 | import d2d.toolkit.input.mouse; 89 | 90 | import std.string; 91 | import std.meta : AliasSeq; 92 | } 93 | 94 | /// 2D rotation on a mat4. 95 | pure mat4 rotate2d(mat4 mat, float alpha) 96 | { 97 | mat = mat.rotatez(alpha); 98 | return mat; 99 | } 100 | 101 | /// 2D scale on a mat4. 102 | pure mat4 scale2d(mat4 mat, float x, float y) 103 | { 104 | mat = mat.scale(x, y, 1); 105 | return mat; 106 | } 107 | 108 | /// 2D translation on a mat4. 109 | pure mat4 translate2d(mat4 mat, float x, float y) 110 | { 111 | mat = mat.translate(x, y, 0); 112 | return mat; 113 | } 114 | 115 | /// Matrix stack for modelview (like glPopMatrix, glPushMatrix). 116 | MatrixStack!mat4 matrixStack = gl3n.ext.matrixstack.matrixStack!mat4(); 117 | /// Matrix stack for projection. 118 | MatrixStack!mat4 projectionStack = gl3n.ext.matrixstack.matrixStack!mat4(); 119 | 120 | /// Initializes matrix stacks 121 | static this() 122 | { 123 | matrixStack.set(mat4.identity); 124 | projectionStack.set(mat4.orthographic(0, 1, 1, 0, -1, 1)); 125 | } 126 | -------------------------------------------------------------------------------- /source/d2d/rendering/bitmap.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.bitmap; 2 | 3 | import d2d; 4 | 5 | /// Thin wrap around SDL_Surface including loading [png, webp, tiff, bmp] using SDL_Image. 6 | class Bitmap : IVerifiable, IDisposable 7 | { 8 | private SDL_Surface* _handle; 9 | 10 | /// Handle to the `SDL_Surface*`. 11 | public @property SDL_Surface* surface() 12 | { 13 | return _handle; 14 | } 15 | 16 | /// Checks if the handle is not null. 17 | public @property bool valid() 18 | { 19 | return _handle !is null; 20 | } 21 | 22 | /// Width of this bitmap. Returns 0 if invalid. 23 | public @property int width() 24 | { 25 | if (!valid) 26 | return 0; 27 | return _handle.w; 28 | } 29 | 30 | /// Height of this bitmap. Returns 0 if invalid. 31 | public @property int height() 32 | { 33 | if (!valid) 34 | return 0; 35 | return _handle.h; 36 | } 37 | 38 | private this(SDL_Surface * surface) 39 | { 40 | _handle = surface; 41 | } 42 | 43 | /// Creates a new width x height bitmap with a specified bit depth. 44 | public this(int width, int height, int depth = 24, int redChannel = 0, int greenChannel = 0, int blueChannel = 0, int alphaChannel = 0) 45 | { 46 | _handle = SDL_CreateRGBSurface(0, width, height, depth, redChannel, greenChannel, blueChannel, alphaChannel); 47 | } 48 | 49 | public ~this() 50 | { 51 | dispose(); 52 | } 53 | 54 | /// Creates a new width x height bitmap with a specified bit depth containing pixel data. 55 | public this(void[] pixels, int width, int height, int depth = 24, int redChannel = 0, int greenChannel = 0, int blueChannel = 0, int alphaChannel = 0) 56 | { 57 | _handle = SDL_CreateRGBSurfaceFrom(pixels.ptr, width, height, depth, width * (depth >> 3), redChannel, greenChannel, blueChannel, alphaChannel); 58 | } 59 | 60 | /// Creates a bitmap from a `SDL_Surface*`. 61 | public static Bitmap fromSurface(SDL_Surface* surface) 62 | { 63 | return new Bitmap(surface); 64 | } 65 | 66 | /// Loads a png/webp/tiff/bmp from a file on the filesystem. 67 | version (BindSDL_Image) public static Bitmap load(string file) 68 | { 69 | Bitmap bmp = new Bitmap(IMG_Load(file.toStringz())); 70 | 71 | if (!bmp.valid) 72 | throw new Exception(cast(string) IMG_GetError().fromStringz()); 73 | 74 | return bmp; 75 | } 76 | 77 | /// Deallocates the memory and invalidates `this`. 78 | public void dispose() 79 | { 80 | if (valid) 81 | { 82 | SDL_FreeSurface(_handle); 83 | _handle = null; 84 | } 85 | } 86 | 87 | /// Saves the bitmap to a .bmp file. 88 | public void save(string file) 89 | { 90 | SDL_SaveBMP(_handle, file.toStringz()); 91 | } 92 | 93 | /// Copies the bitmap and creates a new bitmap in the given pixelformat. 94 | public Bitmap convert(const SDL_PixelFormat* format) 95 | { 96 | return new Bitmap(SDL_ConvertSurface(_handle, format, 0)); 97 | } 98 | 99 | /// Gets the rgb hex from the color based on the bitmap format. 100 | public uint mapRGB(Color color) 101 | { 102 | return SDL_MapRGB(_handle.format, color.R, color.G, color.B); 103 | } 104 | 105 | /// Gets the rgb hex from the color based on the bitmap format. 106 | public uint mapRGBA(ubyte r, ubyte g, ubyte b, ubyte a) 107 | { 108 | return SDL_MapRGBA(_handle.format, r, g, b, a); 109 | } 110 | 111 | /// Fills the entire bitmap with one color. 112 | public void fill(Color color) 113 | { 114 | SDL_FillRect(_handle, null, mapRGB(color)); 115 | } 116 | 117 | /// Fills a rectangle with one color. 118 | public void fill(int x, int y, int width, int height, Color color) 119 | { 120 | SDL_FillRect(_handle, new SDL_Rect(x, y, width, height), mapRGB(color)); 121 | } 122 | 123 | /// Gets the RGB color at position x, y. (0, 0) is top left. 124 | public Color getPixel(int x, int y) 125 | { 126 | ubyte r, g, b; 127 | SDL_GetRGB((cast(uint*) _handle.pixels)[x + y * width], _handle.format, &r, &g, &b); 128 | return Color(r, g, b); 129 | } 130 | 131 | /// Gets the RGBA color at position x, y as R, G, B, A ubyte array. (0, 0) is top left. 132 | public ubyte[] getPixelRGBA(int x, int y) 133 | { 134 | ubyte r, g, b, a; 135 | SDL_GetRGBA((cast(uint*) _handle.pixels)[x + y * width], _handle.format, &r, &g, &b, &a); 136 | return [r, g, b, a]; 137 | } 138 | 139 | /// Sets the RGB color at position x, y. (0, 0) is top left. 140 | public void setPixel(int x, int y, Color color) 141 | { 142 | (cast(uint*) _handle.pixels)[x + y * width] = mapRGB(color); 143 | } 144 | 145 | /// Sets the RGBA color at position x, y. (0, 0) is top left. 146 | public void setPixelRGBA(int x, int y, ubyte r, ubyte g, ubyte b, ubyte a) 147 | { 148 | (cast(uint*) _handle.pixels)[x + y * width] = mapRGBA(r, g, b, a); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /source/d2d/rendering/color.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.color; 2 | 3 | import d2d; 4 | 5 | /// Color structure for Bitmaps and converting HSL -> RGB and Hex -> float[3]. 6 | /// Can be converted to a `SDL_Color`. 7 | /// Only contains RGB Colors. 8 | struct Color 9 | { 10 | /// HTML color AliceBlue 11 | public static const Color AliceBlue = Color.fromRGB(0xF0F8FF); 12 | /// HTML color AntiqueWhite 13 | public static const Color AntiqueWhite = Color.fromRGB(0xFAEBD7); 14 | /// HTML color Aqua 15 | public static const Color Aqua = Color.fromRGB(0x00FFFF); 16 | /// HTML color Aquamarine 17 | public static const Color Aquamarine = Color.fromRGB(0x7FFFD4); 18 | /// HTML color Azure 19 | public static const Color Azure = Color.fromRGB(0xF0FFFF); 20 | /// HTML color Beige 21 | public static const Color Beige = Color.fromRGB(0xF5F5DC); 22 | /// HTML color Bisque 23 | public static const Color Bisque = Color.fromRGB(0xFFE4C4); 24 | /// HTML color Black 25 | public static const Color Black = Color.fromRGB(0x000000); 26 | /// HTML color BlanchedAlmond 27 | public static const Color BlanchedAlmond = Color.fromRGB(0xFFEBCD); 28 | /// HTML color Blue 29 | public static const Color Blue = Color.fromRGB(0x0000FF); 30 | /// HTML color BlueViolet 31 | public static const Color BlueViolet = Color.fromRGB(0x8A2BE2); 32 | /// HTML color Brown 33 | public static const Color Brown = Color.fromRGB(0xA52A2A); 34 | /// HTML color BurlyWood 35 | public static const Color BurlyWood = Color.fromRGB(0xDEB887); 36 | /// HTML color CadetBlue 37 | public static const Color CadetBlue = Color.fromRGB(0x5F9EA0); 38 | /// HTML color Chartreuse 39 | public static const Color Chartreuse = Color.fromRGB(0x7FFF00); 40 | /// HTML color Chocolate 41 | public static const Color Chocolate = Color.fromRGB(0xD2691E); 42 | /// HTML color Coral 43 | public static const Color Coral = Color.fromRGB(0xFF7F50); 44 | /// HTML color CornflowerBlue 45 | public static const Color CornflowerBlue = Color.fromRGB(0x6495ED); 46 | /// HTML color Cornsilk 47 | public static const Color Cornsilk = Color.fromRGB(0xFFF8DC); 48 | /// HTML color Crimson 49 | public static const Color Crimson = Color.fromRGB(0xDC143C); 50 | /// HTML color Cyan 51 | public static const Color Cyan = Color.fromRGB(0x00FFFF); 52 | /// HTML color DarkBlue 53 | public static const Color DarkBlue = Color.fromRGB(0x00008B); 54 | /// HTML color DarkCyan 55 | public static const Color DarkCyan = Color.fromRGB(0x008B8B); 56 | /// HTML color DarkGoldenRod 57 | public static const Color DarkGoldenRod = Color.fromRGB(0xB8860B); 58 | /// HTML color DarkGray 59 | public static const Color DarkGray = Color.fromRGB(0xA9A9A9); 60 | /// HTML color DarkGreen 61 | public static const Color DarkGreen = Color.fromRGB(0x006400); 62 | /// HTML color DarkKhaki 63 | public static const Color DarkKhaki = Color.fromRGB(0xBDB76B); 64 | /// HTML color DarkMagenta 65 | public static const Color DarkMagenta = Color.fromRGB(0x8B008B); 66 | /// HTML color DarkOliveGreen 67 | public static const Color DarkOliveGreen = Color.fromRGB(0x556B2F); 68 | /// HTML color DarkOrange 69 | public static const Color DarkOrange = Color.fromRGB(0xFF8C00); 70 | /// HTML color DarkOrchid 71 | public static const Color DarkOrchid = Color.fromRGB(0x9932CC); 72 | /// HTML color DarkRed 73 | public static const Color DarkRed = Color.fromRGB(0x8B0000); 74 | /// HTML color DarkSalmon 75 | public static const Color DarkSalmon = Color.fromRGB(0xE9967A); 76 | /// HTML color DarkSeaGreen 77 | public static const Color DarkSeaGreen = Color.fromRGB(0x8FBC8F); 78 | /// HTML color DarkSlateBlue 79 | public static const Color DarkSlateBlue = Color.fromRGB(0x483D8B); 80 | /// HTML color DarkSlateGray 81 | public static const Color DarkSlateGray = Color.fromRGB(0x2F4F4F); 82 | /// HTML color DarkTurquoise 83 | public static const Color DarkTurquoise = Color.fromRGB(0x00CED1); 84 | /// HTML color DarkViolet 85 | public static const Color DarkViolet = Color.fromRGB(0x9400D3); 86 | /// HTML color DeepPink 87 | public static const Color DeepPink = Color.fromRGB(0xFF1493); 88 | /// HTML color DeepSkyBlue 89 | public static const Color DeepSkyBlue = Color.fromRGB(0x00BFFF); 90 | /// HTML color DimGray 91 | public static const Color DimGray = Color.fromRGB(0x696969); 92 | /// HTML color DodgerBlue 93 | public static const Color DodgerBlue = Color.fromRGB(0x1E90FF); 94 | /// HTML color FireBrick 95 | public static const Color FireBrick = Color.fromRGB(0xB22222); 96 | /// HTML color FloralWhite 97 | public static const Color FloralWhite = Color.fromRGB(0xFFFAF0); 98 | /// HTML color ForestGreen 99 | public static const Color ForestGreen = Color.fromRGB(0x228B22); 100 | /// HTML color Fuchsia 101 | public static const Color Fuchsia = Color.fromRGB(0xFF00FF); 102 | /// HTML color Gainsboro 103 | public static const Color Gainsboro = Color.fromRGB(0xDCDCDC); 104 | /// HTML color GhostWhite 105 | public static const Color GhostWhite = Color.fromRGB(0xF8F8FF); 106 | /// HTML color Gold 107 | public static const Color Gold = Color.fromRGB(0xFFD700); 108 | /// HTML color GoldenRod 109 | public static const Color GoldenRod = Color.fromRGB(0xDAA520); 110 | /// HTML color Gray 111 | public static const Color Gray = Color.fromRGB(0x808080); 112 | /// HTML color Green 113 | public static const Color Green = Color.fromRGB(0x008000); 114 | /// HTML color GreenYellow 115 | public static const Color GreenYellow = Color.fromRGB(0xADFF2F); 116 | /// HTML color HoneyDew 117 | public static const Color HoneyDew = Color.fromRGB(0xF0FFF0); 118 | /// HTML color HotPink 119 | public static const Color HotPink = Color.fromRGB(0xFF69B4); 120 | /// HTML color IndianRed 121 | public static const Color IndianRed = Color.fromRGB(0xCD5C5C); 122 | /// HTML color Indigo 123 | public static const Color Indigo = Color.fromRGB(0x4B0082); 124 | /// HTML color Ivory 125 | public static const Color Ivory = Color.fromRGB(0xFFFFF0); 126 | /// HTML color Khaki 127 | public static const Color Khaki = Color.fromRGB(0xF0E68C); 128 | /// HTML color Lavender 129 | public static const Color Lavender = Color.fromRGB(0xE6E6FA); 130 | /// HTML color LavenderBlush 131 | public static const Color LavenderBlush = Color.fromRGB(0xFFF0F5); 132 | /// HTML color LawnGreen 133 | public static const Color LawnGreen = Color.fromRGB(0x7CFC00); 134 | /// HTML color LemonChiffon 135 | public static const Color LemonChiffon = Color.fromRGB(0xFFFACD); 136 | /// HTML color LightBlue 137 | public static const Color LightBlue = Color.fromRGB(0xADD8E6); 138 | /// HTML color LightCoral 139 | public static const Color LightCoral = Color.fromRGB(0xF08080); 140 | /// HTML color LightCyan 141 | public static const Color LightCyan = Color.fromRGB(0xE0FFFF); 142 | /// HTML color LightGoldenRodYellow 143 | public static const Color LightGoldenRodYellow = Color.fromRGB(0xFAFAD2); 144 | /// HTML color LightGray 145 | public static const Color LightGray = Color.fromRGB(0xD3D3D3); 146 | /// HTML color LightGreen 147 | public static const Color LightGreen = Color.fromRGB(0x90EE90); 148 | /// HTML color LightPink 149 | public static const Color LightPink = Color.fromRGB(0xFFB6C1); 150 | /// HTML color LightSalmon 151 | public static const Color LightSalmon = Color.fromRGB(0xFFA07A); 152 | /// HTML color LightSeaGreen 153 | public static const Color LightSeaGreen = Color.fromRGB(0x20B2AA); 154 | /// HTML color LightSkyBlue 155 | public static const Color LightSkyBlue = Color.fromRGB(0x87CEFA); 156 | /// HTML color LightSlateGray 157 | public static const Color LightSlateGray = Color.fromRGB(0x778899); 158 | /// HTML color LightSteelBlue 159 | public static const Color LightSteelBlue = Color.fromRGB(0xB0C4DE); 160 | /// HTML color LightYellow 161 | public static const Color LightYellow = Color.fromRGB(0xFFFFE0); 162 | /// HTML color Lime 163 | public static const Color Lime = Color.fromRGB(0x00FF00); 164 | /// HTML color LimeGreen 165 | public static const Color LimeGreen = Color.fromRGB(0x32CD32); 166 | /// HTML color Linen 167 | public static const Color Linen = Color.fromRGB(0xFAF0E6); 168 | /// HTML color Magenta 169 | public static const Color Magenta = Color.fromRGB(0xFF00FF); 170 | /// HTML color Maroon 171 | public static const Color Maroon = Color.fromRGB(0x800000); 172 | /// HTML color MediumAquaMarine 173 | public static const Color MediumAquaMarine = Color.fromRGB(0x66CDAA); 174 | /// HTML color MediumBlue 175 | public static const Color MediumBlue = Color.fromRGB(0x0000CD); 176 | /// HTML color MediumOrchid 177 | public static const Color MediumOrchid = Color.fromRGB(0xBA55D3); 178 | /// HTML color MediumPurple 179 | public static const Color MediumPurple = Color.fromRGB(0x9370DB); 180 | /// HTML color MediumSeaGreen 181 | public static const Color MediumSeaGreen = Color.fromRGB(0x3CB371); 182 | /// HTML color MediumSlateBlue 183 | public static const Color MediumSlateBlue = Color.fromRGB(0x7B68EE); 184 | /// HTML color MediumSpringGreen 185 | public static const Color MediumSpringGreen = Color.fromRGB(0x00FA9A); 186 | /// HTML color MediumTurquoise 187 | public static const Color MediumTurquoise = Color.fromRGB(0x48D1CC); 188 | /// HTML color MediumVioletRed 189 | public static const Color MediumVioletRed = Color.fromRGB(0xC71585); 190 | /// HTML color MidnightBlue 191 | public static const Color MidnightBlue = Color.fromRGB(0x191970); 192 | /// HTML color MintCream 193 | public static const Color MintCream = Color.fromRGB(0xF5FFFA); 194 | /// HTML color MistyRose 195 | public static const Color MistyRose = Color.fromRGB(0xFFE4E1); 196 | /// HTML color Moccasin 197 | public static const Color Moccasin = Color.fromRGB(0xFFE4B5); 198 | /// HTML color NavajoWhite 199 | public static const Color NavajoWhite = Color.fromRGB(0xFFDEAD); 200 | /// HTML color Navy 201 | public static const Color Navy = Color.fromRGB(0x000080); 202 | /// HTML color OldLace 203 | public static const Color OldLace = Color.fromRGB(0xFDF5E6); 204 | /// HTML color Olive 205 | public static const Color Olive = Color.fromRGB(0x808000); 206 | /// HTML color OliveDrab 207 | public static const Color OliveDrab = Color.fromRGB(0x6B8E23); 208 | /// HTML color Orange 209 | public static const Color Orange = Color.fromRGB(0xFFA500); 210 | /// HTML color OrangeRed 211 | public static const Color OrangeRed = Color.fromRGB(0xFF4500); 212 | /// HTML color Orchid 213 | public static const Color Orchid = Color.fromRGB(0xDA70D6); 214 | /// HTML color PaleGoldenRod 215 | public static const Color PaleGoldenRod = Color.fromRGB(0xEEE8AA); 216 | /// HTML color PaleGreen 217 | public static const Color PaleGreen = Color.fromRGB(0x98FB98); 218 | /// HTML color PaleTurquoise 219 | public static const Color PaleTurquoise = Color.fromRGB(0xAFEEEE); 220 | /// HTML color PaleVioletRed 221 | public static const Color PaleVioletRed = Color.fromRGB(0xDB7093); 222 | /// HTML color PapayaWhip 223 | public static const Color PapayaWhip = Color.fromRGB(0xFFEFD5); 224 | /// HTML color PeachPuff 225 | public static const Color PeachPuff = Color.fromRGB(0xFFDAB9); 226 | /// HTML color Peru 227 | public static const Color Peru = Color.fromRGB(0xCD853F); 228 | /// HTML color Pink 229 | public static const Color Pink = Color.fromRGB(0xFFC0CB); 230 | /// HTML color Plum 231 | public static const Color Plum = Color.fromRGB(0xDDA0DD); 232 | /// HTML color PowderBlue 233 | public static const Color PowderBlue = Color.fromRGB(0xB0E0E6); 234 | /// HTML color Purple 235 | public static const Color Purple = Color.fromRGB(0x800080); 236 | /// HTML color RebeccaPurple 237 | public static const Color RebeccaPurple = Color.fromRGB(0x663399); 238 | /// HTML color Red 239 | public static const Color Red = Color.fromRGB(0xFF0000); 240 | /// HTML color RosyBrown 241 | public static const Color RosyBrown = Color.fromRGB(0xBC8F8F); 242 | /// HTML color RoyalBlue 243 | public static const Color RoyalBlue = Color.fromRGB(0x4169E1); 244 | /// HTML color SaddleBrown 245 | public static const Color SaddleBrown = Color.fromRGB(0x8B4513); 246 | /// HTML color Salmon 247 | public static const Color Salmon = Color.fromRGB(0xFA8072); 248 | /// HTML color SandyBrown 249 | public static const Color SandyBrown = Color.fromRGB(0xF4A460); 250 | /// HTML color SeaGreen 251 | public static const Color SeaGreen = Color.fromRGB(0x2E8B57); 252 | /// HTML color SeaShell 253 | public static const Color SeaShell = Color.fromRGB(0xFFF5EE); 254 | /// HTML color Sienna 255 | public static const Color Sienna = Color.fromRGB(0xA0522D); 256 | /// HTML color Silver 257 | public static const Color Silver = Color.fromRGB(0xC0C0C0); 258 | /// HTML color SkyBlue 259 | public static const Color SkyBlue = Color.fromRGB(0x87CEEB); 260 | /// HTML color SlateBlue 261 | public static const Color SlateBlue = Color.fromRGB(0x6A5ACD); 262 | /// HTML color SlateGray 263 | public static const Color SlateGray = Color.fromRGB(0x708090); 264 | /// HTML color Snow 265 | public static const Color Snow = Color.fromRGB(0xFFFAFA); 266 | /// HTML color SpringGreen 267 | public static const Color SpringGreen = Color.fromRGB(0x00FF7F); 268 | /// HTML color SteelBlue 269 | public static const Color SteelBlue = Color.fromRGB(0x4682B4); 270 | /// HTML color Tan 271 | public static const Color Tan = Color.fromRGB(0xD2B48C); 272 | /// HTML color Teal 273 | public static const Color Teal = Color.fromRGB(0x008080); 274 | /// HTML color Thistle 275 | public static const Color Thistle = Color.fromRGB(0xD8BFD8); 276 | /// HTML color Tomato 277 | public static const Color Tomato = Color.fromRGB(0xFF6347); 278 | /// HTML color Turquoise 279 | public static const Color Turquoise = Color.fromRGB(0x40E0D0); 280 | /// HTML color Violet 281 | public static const Color Violet = Color.fromRGB(0xEE82EE); 282 | /// HTML color Wheat 283 | public static const Color Wheat = Color.fromRGB(0xF5DEB3); 284 | /// HTML color White 285 | public static const Color White = Color.fromRGB(0xFFFFFF); 286 | /// HTML color WhiteSmoke 287 | public static const Color WhiteSmoke = Color.fromRGB(0xF5F5F5); 288 | /// HTML color Yellow 289 | public static const Color Yellow = Color.fromRGB(0xFFFF00); 290 | /// HTML color YellowGreen 291 | public static const Color YellowGreen = Color.fromRGB(0x9ACD32); 292 | 293 | /// Creates a new 24 bit color using 3x 8 bit components. 294 | /// Params: 295 | /// r = Red value in range 0 - 255 296 | /// g = Green value in range 0 - 255 297 | /// b = Blue value in range 0 - 255 298 | public this(ubyte r, ubyte g, ubyte b) 299 | { 300 | _r = r; 301 | _g = g; 302 | _b = b; 303 | } 304 | 305 | /// Creates a new 24 bit color. 306 | /// Params: 307 | /// hex = integer in RGB byte order 308 | public static Color fromRGB(const int hex) 309 | { 310 | return Color((hex >> 16) & 0xFF, (hex >> 8) & 0xFF, hex & 0xFF); 311 | } 312 | 313 | /// Creates a new 24 bit color. 314 | /// Params: 315 | /// hex = integer in BGR byte order 316 | public static Color fromBGR(const int hex) 317 | { 318 | return Color(hex & 0xFF, (hex >> 8) & 0xFF, (hex >> 16) & 0xFF); 319 | } 320 | 321 | /// Converts from HSL to RGB 322 | /// Params: 323 | /// hue = Hue in range 0 - 1 324 | /// saturation = Saturation in range 0 - 1 325 | /// lightness = Lightness in range 0 - 1 326 | public void fromHSL(double hue, double saturation, double lightness) 327 | { 328 | immutable double v = lightness <= 0.5 ? (lightness * (1 + saturation)) : (1 + saturation - lightness * saturation); 329 | double r, g, b; 330 | r = g = b = 1; 331 | 332 | if (v > 0) 333 | { 334 | double m; 335 | double sv; 336 | int sextant; 337 | double fract, vsf, mid1, mid2; 338 | 339 | m = lightness + lightness - v; 340 | sv = (v - m) / v; 341 | hue = (hue % 1.0) * 6.0; 342 | sextant = cast(int) hue; 343 | fract = hue - sextant; 344 | vsf = v * sv * fract; 345 | mid1 = m + vsf; 346 | mid2 = v - vsf; 347 | switch (sextant) 348 | { 349 | case 0: 350 | r = v; 351 | g = mid1; 352 | b = m; 353 | break; 354 | case 1: 355 | r = mid2; 356 | g = v; 357 | b = m; 358 | break; 359 | case 2: 360 | r = m; 361 | g = v; 362 | b = mid1; 363 | break; 364 | case 3: 365 | r = m; 366 | g = mid2; 367 | b = v; 368 | break; 369 | case 4: 370 | r = mid1; 371 | g = m; 372 | b = v; 373 | break; 374 | case 5: 375 | r = v; 376 | g = m; 377 | b = mid2; 378 | break; 379 | default: 380 | break; 381 | } 382 | } 383 | _r = cast(ubyte) (r * 255); 384 | _g = cast(ubyte) (g * 255); 385 | _b = cast(ubyte) (b * 255); 386 | } 387 | 388 | public override bool opEquals()(auto ref const Color color) const 389 | { 390 | return _r == color._r && _g == color._g && _b == color._b; 391 | } 392 | 393 | /// Creates an SDL_Color from `this`. 394 | public @property SDL_Color sdl_color() const 395 | { 396 | return SDL_Color(_r, _g, _b, 255); 397 | } 398 | 399 | /// Red value in range 0 - 255 as a ubyte. 400 | public @property ref ubyte R() 401 | { 402 | return _r; 403 | } 404 | 405 | /// Green value in range 0 - 255 as a ubyte. 406 | public @property ref ubyte G() 407 | { 408 | return _g; 409 | } 410 | 411 | /// Blue value in range 0 - 255 as a ubyte. 412 | public @property ref ubyte B() 413 | { 414 | return _b; 415 | } 416 | 417 | /// Red value in range 0 - 1 as a float. 418 | public @property float fR() const 419 | { 420 | return _r * 0.00392156862f; 421 | } 422 | 423 | /// Green value in range 0 - 1 as a float. 424 | public @property float fG() const 425 | { 426 | return _g * 0.00392156862f; 427 | } 428 | 429 | /// Blue value in range 0 - 1 as a float. 430 | public @property float fB() const 431 | { 432 | return _b * 0.00392156862f; 433 | } 434 | 435 | /// RGB in range 0 - 1 as vec3 436 | public @property vec3 f() const 437 | { 438 | return vec3(fR, fG, fB); 439 | } 440 | 441 | /// Color as RGB hex. 442 | public @property int RGB() const 443 | { 444 | return _r << 16 | _g << 8 | _b; 445 | } 446 | 447 | /// Color as BGR hex. 448 | public @property int BGR() const 449 | { 450 | return _r | _g << 8 | _b << 16; 451 | } 452 | 453 | private ubyte _r, _g, _b; 454 | } 455 | -------------------------------------------------------------------------------- /source/d2d/rendering/idrawable.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.idrawable; 2 | 3 | import d2d; 4 | 5 | /// Basic interface for everything that is able to draw. 6 | interface IDrawable 7 | { 8 | /// Will get called by `IRenderTarget.draw(IDrawable);` 9 | void draw(IRenderTarget target, ShaderProgram shader = null); 10 | } 11 | -------------------------------------------------------------------------------- /source/d2d/rendering/irendertarget.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.irendertarget; 2 | 3 | import d2d; 4 | 5 | /// Interface for containers being able to draw elements. 6 | interface IRenderTarget 7 | { 8 | /// Set active container. 9 | void bind(); 10 | /// Resize the container texture to the new width and height. 11 | void resize(int width, int height); 12 | /// Create a container texture in the given resolution. 13 | void create(int width, int height); 14 | 15 | /// Clears the container texture by calling `bind()` -> `glClearColor(r, g, b, 1)` -> `glClear(GL_COLOR_BUFFER_BIT)`. 16 | final void clear(float r, float g, float b) 17 | { 18 | bind(); 19 | glClearColor(r, g, b, 1); 20 | glClear(GL_COLOR_BUFFER_BIT); 21 | } 22 | 23 | /// Draws drawable using optional shader onto this. This will call `drawable.draw(this, shader);` 24 | /// If shader is `null`, `ShaderProgram.default` is gonna be used. 25 | final void draw(IDrawable drawable, ShaderProgram shader = null) 26 | { 27 | bind(); 28 | drawable.draw(this, shader); 29 | } 30 | 31 | /// Draws raw geometry to the container texture using an optional shader. 32 | /// If shader is null, ShaderProgram.default is gonna be used. 33 | final void draw(Mesh mesh, ShaderProgram shader = null) 34 | { 35 | draw(mesh.renderable, shader); 36 | } 37 | 38 | /// Draws raw geometry to the container texture using an optional shader. 39 | /// If shader is null, ShaderProgram.default is gonna be used. 40 | final void draw(RenderableMesh mesh, ShaderProgram shader = null) 41 | { 42 | bind(); 43 | if (shader is null) 44 | shader = ShaderProgram.defaultShader; 45 | 46 | shader.bind(); 47 | shader.set("transform", matrixStack.top); 48 | shader.set("projection", projectionStack.top); 49 | 50 | glBindVertexArray(mesh.bufferID); 51 | 52 | if (mesh.indexed) 53 | glDrawElements(mesh.primitiveType, mesh.count, mesh.indexType, 54 | cast(void*)(mesh.start * mesh.indexStride)); 55 | else 56 | glDrawArrays(mesh.primitiveType, mesh.start, mesh.count); 57 | } 58 | 59 | /// Returns the result of the container texture. 60 | @property Texture texture(); 61 | } 62 | -------------------------------------------------------------------------------- /source/d2d/rendering/mesh.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.mesh; 2 | 3 | import d2d; 4 | 5 | /// Class containing mesh IDs and length for OpenGL drawing. 6 | class RenderableMesh 7 | { 8 | /// OpenGL id of the buffer. 9 | public uint bufferID; 10 | /// Length of the index/data buffer. 11 | public uint count; 12 | /// Offset index in the index/data buffer. 13 | public uint start; 14 | /// OpenGL primitive type. 15 | public GLenum primitiveType = GL_TRIANGLES; 16 | /// Is this an indexed mesh. 17 | public bool indexed = true; 18 | /// Type of indices. 19 | public GLenum indexType = GL_UNSIGNED_INT; 20 | 21 | /// OpenGL array of the vbo buffers. 22 | deprecated("no longer used") public uint[] vbos; 23 | 24 | deprecated alias indexLength = count; 25 | 26 | uint indexStride() const @property 27 | { 28 | if (!indexed) 29 | return 0; 30 | 31 | switch (indexType) 32 | { 33 | case GL_UNSIGNED_BYTE: 34 | return 1; 35 | case GL_UNSIGNED_SHORT: 36 | return 2; 37 | case GL_UNSIGNED_INT: 38 | return 4; 39 | default: 40 | return 0; 41 | } 42 | } 43 | 44 | /// Constructor for creating a new RenderableMesh with existing data. 45 | deprecated public this(uint bufferID, uint[] vbos, uint indexLength) 46 | { 47 | this.bufferID = bufferID; 48 | this.vbos = vbos; 49 | this.count = indexLength; 50 | indexed = true; 51 | } 52 | 53 | /// Constructor for creating a new RenderableMesh with existing data. 54 | public this(uint bufferID, uint count, bool indexed) 55 | { 56 | this.bufferID = bufferID; 57 | this.count = count; 58 | this.indexed = indexed; 59 | } 60 | } 61 | 62 | /// Class for raw geometry. 63 | class Mesh : IDisposable, IVerifiable 64 | { 65 | ~this() 66 | { 67 | dispose(); 68 | } 69 | 70 | /// Array of the vertices. 71 | public @property vec3[] vertices() 72 | { 73 | return _vertices; 74 | } 75 | 76 | /// Array of the indices. 77 | public @property uint[] indices() 78 | { 79 | return _indices; 80 | } 81 | 82 | /// Array of the texture coordinates. 83 | public @property vec2[] texCoords() 84 | { 85 | return _texCoords; 86 | } 87 | 88 | /// Checks if this Mesh can be drawn. 89 | public @property bool valid() 90 | { 91 | return renderable !is null; 92 | } 93 | 94 | /// Adds one vertex. 95 | public void addVertex(vec3 vertex) 96 | { 97 | _vertices ~= vertex; 98 | } 99 | 100 | /// Adds many vertices. 101 | public void addVertices(const vec3[] vertices) 102 | { 103 | _vertices ~= vertices; 104 | } 105 | 106 | /// Adds one index. 107 | public void addIndex(uint index) 108 | { 109 | _indices ~= index; 110 | } 111 | 112 | /// Adds many indices. 113 | public void addIndices(const uint[] indices) 114 | { 115 | _indices ~= indices; 116 | } 117 | 118 | /// Adds one texture coordinate. 119 | public void addTexCoord(vec2 texCoord) 120 | { 121 | _texCoords ~= texCoord; 122 | } 123 | 124 | /// Adds many texture coordinates. 125 | public void addTexCoords(const vec2[] texCoords) 126 | { 127 | _texCoords ~= texCoords; 128 | } 129 | 130 | /// Deletes the mesh from memory and cleans up. 131 | public void dispose() 132 | { 133 | if (valid) 134 | { 135 | glDeleteVertexArrays(1, &vao); 136 | glDeleteBuffers(vbo.length, &vbo[0]); 137 | glDeleteVertexArrays(1, &renderable.bufferID); 138 | renderable = null; 139 | } 140 | } 141 | 142 | /// Generates the RenderableMesh from the previously defined vertices and makes `this` valid. 143 | public void create() 144 | { 145 | glGenVertexArrays(1, &vao); 146 | glBindVertexArray(vao); 147 | 148 | glGenBuffers(vbo.length, &vbo[0]); 149 | 150 | glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 151 | glBufferData(GL_ARRAY_BUFFER, vec3.sizeof * vertices.length, vertices.ptr, GL_STATIC_DRAW); 152 | glVertexAttribPointer(0u, 3, GL_FLOAT, cast(ubyte) 0, 0, null); 153 | glEnableVertexAttribArray(0); 154 | 155 | glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 156 | glBufferData(GL_ARRAY_BUFFER, vec2.sizeof * texCoords.length, texCoords.ptr, GL_STATIC_DRAW); 157 | glVertexAttribPointer(1u, 2, GL_FLOAT, cast(ubyte) 0, 0, null); 158 | glEnableVertexAttribArray(1); 159 | 160 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vbo[2]); 161 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, uint.sizeof * indices.length, 162 | indices.ptr, GL_STATIC_DRAW); 163 | 164 | glBindVertexArray(0); 165 | 166 | renderable = new RenderableMesh(vao, cast(uint) indices.length, true); 167 | } 168 | 169 | /// Renderable mesh when create got called. Before its `null`. 170 | public RenderableMesh renderable = null; 171 | 172 | private uint vao; 173 | private uint[3] vbo; 174 | 175 | private vec3[] _vertices; 176 | private vec3[] _normals; 177 | private vec2[] _texCoords; 178 | private uint[] _indices; 179 | } 180 | -------------------------------------------------------------------------------- /source/d2d/rendering/rectangleshape.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.rectangleshape; 2 | 3 | import d2d; 4 | 5 | /** 6 | * A resizable rectangle containing a texture. 7 | * Examples: 8 | * --- 9 | * auto rect = new RectangleShape(); 10 | * rect.size = vec2(100, 50); // 100x50 px 11 | * rect.create(); 12 | * window.draw(rect); 13 | * 14 | * // OR 15 | * 16 | * auto rect = RectangleShape.create(vec2(0, 0), vec2(100, 50)); // At 0,0 with size 100x50 px 17 | * window.draw(rect); 18 | * --- 19 | */ 20 | class RectangleShape : Shape, IDisposable, IVerifiable 21 | { 22 | protected: 23 | Mesh _mesh; // TODO: Only 1 Mesh 24 | vec4 _texCoords = vec4(0, 0, 1, 1); 25 | vec2 _size = vec2(1, 1); 26 | 27 | public: 28 | /// 29 | this() 30 | { 31 | _mesh = new Mesh(); 32 | create(); 33 | } 34 | 35 | ~this() 36 | { 37 | _mesh = null; 38 | dispose(); 39 | } 40 | 41 | /// Returns if the mesh is valid. 42 | override @property bool valid() 43 | { 44 | return _mesh.valid; 45 | } 46 | 47 | /// Property for the size of the rectangle. 48 | @property ref vec2 size() 49 | { 50 | return _size; 51 | } 52 | 53 | /// Property for begin xy and end xy using a vec4 for texture coordinates. 54 | @property ref vec4 texCoords() 55 | { 56 | return _texCoords; 57 | } 58 | 59 | override void dispose() 60 | { 61 | if (_mesh && _mesh.valid) 62 | { 63 | _mesh.dispose(); 64 | _mesh = null; 65 | } 66 | } 67 | 68 | /// Creates a new mesh after disposing the old mesh. 69 | void create() 70 | { 71 | _mesh.dispose(); 72 | _mesh = new Mesh(); 73 | _mesh.addVertices([vec3(0, 0, 0), vec3(_size.x, 0, 0), vec3(_size.x, _size.y, 0), vec3(0, _size.y, 0)]); 74 | _mesh.addTexCoords([vec2(_texCoords.x, _texCoords.y), vec2(_texCoords.z, _texCoords.y), vec2(_texCoords.z, _texCoords.w), vec2(_texCoords.x, _texCoords.w)]); 75 | _mesh.addIndices([0, 1, 2, 0, 2, 3]); 76 | _mesh.create(); 77 | } 78 | 79 | /// Sets the current transformation matrix and draws this onto the target. 80 | override void draw(IRenderTarget target, ShaderProgram shader = null) 81 | { 82 | assert(_mesh.valid, "Mesh not valid!"); 83 | matrixStack.push(); 84 | matrixStack.top = matrixStack.top * transform; 85 | if (texture !is null) 86 | texture.bind(0); 87 | target.draw(_mesh, shader); 88 | matrixStack.pop(); 89 | } 90 | 91 | /// 92 | static RectangleShape create(vec2 position, vec2 size) 93 | { 94 | auto shape = new RectangleShape(); 95 | shape.position = position; 96 | shape.size = size; 97 | shape.create(); 98 | return shape; 99 | } 100 | 101 | /// 102 | static RectangleShape create(Texture texture, vec2 position, vec2 size) 103 | { 104 | auto shape = new RectangleShape(); 105 | shape.texture = texture; 106 | shape.position = position; 107 | shape.size = size; 108 | shape.create(); 109 | return shape; 110 | } 111 | 112 | /// 113 | static RectangleShape create(Texture texture, vec2 position, vec2 size, vec4 texCoords) 114 | { 115 | auto shape = new RectangleShape(); 116 | shape.texture = texture; 117 | shape.position = position; 118 | shape.size = size; 119 | shape.texCoords = texCoords; 120 | shape.create(); 121 | return shape; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /source/d2d/rendering/shader.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.shader; 2 | 3 | import d2d; 4 | 5 | import std.conv : to; 6 | 7 | /// All valid types of shaders for the `Shader` class. 8 | enum ShaderType : ubyte 9 | { 10 | /// Vertex Shader 11 | Vertex, 12 | /// Tessellation Control Shader 13 | TessControl, 14 | /// Tessellation Evaluation Shader 15 | TessEvaluation, 16 | /// Geometry Shader 17 | Geometry, 18 | /// Fragment/Pixel Shader 19 | Fragment 20 | } 21 | 22 | /// Class containing a single shader for combining in a ShaderProgram. 23 | class Shader : IVerifiable 24 | { 25 | /// Loads the shader content into memory. 26 | public bool load(ShaderType type, string content) 27 | { 28 | this.content = content; 29 | switch (type) 30 | { 31 | case ShaderType.Vertex: 32 | _id = glCreateShader(GL_VERTEX_SHADER); 33 | break; 34 | case ShaderType.TessControl: 35 | if (!hasARBTesselationShader) 36 | throw new Exception( 37 | "Tried to create a TESS_CONTROL_SHADER without GL_ARB_tessellation_shader extension being available"); 38 | 39 | _id = glCreateShader(GL_TESS_CONTROL_SHADER); 40 | break; 41 | case ShaderType.TessEvaluation: 42 | if (!hasARBTesselationShader) 43 | throw new Exception( 44 | "Tried to create a TESS_EVALUATION_SHADER without GL_ARB_tessellation_shader extension being available"); 45 | 46 | _id = glCreateShader(GL_TESS_EVALUATION_SHADER); 47 | break; 48 | case ShaderType.Geometry: 49 | _id = glCreateShader(GL_GEOMETRY_SHADER); 50 | break; 51 | case ShaderType.Fragment: 52 | _id = glCreateShader(GL_FRAGMENT_SHADER); 53 | break; 54 | default: 55 | throw new Exception("ShaderType " ~ type.to!string ~ " is not defined!"); 56 | } 57 | 58 | const int len = cast(const(int)) content.length; 59 | 60 | glShaderSource(_id, 1, [content.ptr].ptr, &len); 61 | return true; 62 | } 63 | 64 | /// Creates a shader, loads the content and compiles it in one function. 65 | static Shader create(ShaderType type, string content) 66 | { 67 | Shader shader = new Shader(); 68 | shader.load(type, content); 69 | shader.compile(); 70 | return shader; 71 | } 72 | 73 | /// Compiles the shader and throws an Exception if an error occured. 74 | /// Will automatically be called when attaching the shader to a ShaderProgram instance. 75 | public bool compile() 76 | { 77 | if (compiled) 78 | return true; 79 | glCompileShader(_id); 80 | int success = 0; 81 | glGetShaderiv(_id, GL_COMPILE_STATUS, &success); 82 | 83 | if (success == 0) 84 | { 85 | int logSize = 0; 86 | glGetShaderiv(_id, GL_INFO_LOG_LENGTH, &logSize); 87 | 88 | char* log = new char[logSize].ptr; 89 | glGetShaderInfoLog(_id, logSize, &logSize, &log[0]); 90 | 91 | throw new Exception(cast(string) log[0 .. logSize]); 92 | } 93 | compiled = true; 94 | return true; 95 | } 96 | 97 | /// The OpenGL id of this shader. 98 | public @property uint id() 99 | { 100 | return _id; 101 | } 102 | 103 | /// Checks if this shader is valid. 104 | public @property bool valid() 105 | { 106 | return _id > 0; 107 | } 108 | 109 | private uint _id = 0; 110 | private string content; 111 | private bool compiled = false; 112 | } 113 | -------------------------------------------------------------------------------- /source/d2d/rendering/shaderprogram.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.shaderprogram; 2 | 3 | import d2d; 4 | 5 | static import std.file; 6 | 7 | /// Class for combining shaders to a bindable ShaderProgram. 8 | class ShaderProgram : IVerifiable 9 | { 10 | /// 11 | public this() 12 | { 13 | program = glCreateProgram(); 14 | } 15 | 16 | /// Will directly load the content from vertex and fragment and will create and return a ShaderProgram. 17 | public static ShaderProgram fromVertexFragmentFiles(string vertex, string fragment) 18 | { 19 | Shader v = new Shader(); 20 | v.load(ShaderType.Vertex, std.file.readText(vertex)); 21 | 22 | Shader f = new Shader(); 23 | f.load(ShaderType.Fragment, std.file.readText(fragment)); 24 | 25 | ShaderProgram program = new ShaderProgram; 26 | program.attach(v); 27 | program.attach(f); 28 | program.link(); 29 | return program; 30 | } 31 | 32 | /// Attaches a new shader to the program. 33 | /// Will call `shader.compile()` if necessary. 34 | public void attach(Shader shader) 35 | { 36 | shader.compile(); 37 | glAttachShader(program, shader.id); 38 | } 39 | 40 | /// Creates the program and binds it. 41 | public void link() 42 | { 43 | glLinkProgram(program); 44 | bind(); 45 | } 46 | 47 | /// Binds `this` for usage. 48 | public void bind() 49 | { 50 | glUseProgram(program); 51 | } 52 | 53 | /// Regsiters a uniform variable in the shader for later setting. 54 | public int registerUniform(string uniform) 55 | { 56 | if ((uniform in _properties) !is null) 57 | return _properties[uniform]; 58 | _properties[uniform] = glGetUniformLocation(program, uniform.toStringz()); 59 | return _properties[uniform]; 60 | } 61 | 62 | /// 63 | public void set(string uniform, int value) 64 | { 65 | debug if ((uniform in _properties) is null) 66 | { 67 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 68 | } 69 | glUniform1i(_properties[uniform], value); 70 | } 71 | 72 | /// 73 | public void set(string uniform, float value) 74 | { 75 | debug if ((uniform in _properties) is null) 76 | { 77 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 78 | } 79 | glUniform1f(_properties[uniform], value); 80 | } 81 | 82 | /// 83 | public void set(string uniform, vec2 value) 84 | { 85 | debug if ((uniform in _properties) is null) 86 | { 87 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 88 | } 89 | glUniform2fv(_properties[uniform], 1, value.value_ptr); 90 | } 91 | 92 | /// 93 | public void set(string uniform, vec3 value) 94 | { 95 | debug if ((uniform in _properties) is null) 96 | { 97 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 98 | } 99 | glUniform3fv(_properties[uniform], 1, value.value_ptr); 100 | } 101 | 102 | /// 103 | public void set(string uniform, vec4 value) 104 | { 105 | debug if ((uniform in _properties) is null) 106 | { 107 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 108 | } 109 | glUniform4fv(_properties[uniform], 1, value.value_ptr); 110 | } 111 | 112 | /// 113 | public void set(string uniform, mat2 value) 114 | { 115 | debug if ((uniform in _properties) is null) 116 | { 117 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 118 | } 119 | glUniformMatrix2fv(_properties[uniform], 1, 1, value.value_ptr); 120 | } 121 | 122 | /// 123 | public void set(string uniform, mat3 value) 124 | { 125 | debug if ((uniform in _properties) is null) 126 | { 127 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 128 | } 129 | glUniformMatrix3fv(_properties[uniform], 1, 1, value.value_ptr); 130 | } 131 | 132 | /// 133 | public void set(string uniform, mat4 value) 134 | { 135 | debug if ((uniform in _properties) is null) 136 | { 137 | throw new Exception("Uniform '" ~ uniform ~ "' is not defined!"); 138 | } 139 | glUniformMatrix4fv(_properties[uniform], 1, 1, value.value_ptr); 140 | } 141 | 142 | /// 143 | public void opIndexAssign(T)(T value, string uniform) 144 | { 145 | set(uniform, value); 146 | } 147 | 148 | /// 149 | public uint id() 150 | { 151 | return program; 152 | } 153 | 154 | /// 155 | public @property bool valid() 156 | { 157 | return program > 0; 158 | } 159 | 160 | private uint program; 161 | private int[string] _properties; 162 | 163 | /// Regular texture shader. 164 | public static ShaderProgram defaultShader; 165 | 166 | static void load() 167 | { 168 | defaultShader = new ShaderProgram(); 169 | Shader vertex = new Shader(); 170 | vertex.load(ShaderType.Vertex, "#version 330 171 | layout(location = 0) in vec3 in_position; 172 | layout(location = 1) in vec2 in_tex; 173 | 174 | uniform mat4 transform; 175 | uniform mat4 projection; 176 | 177 | out vec2 texCoord; 178 | 179 | void main() 180 | { 181 | gl_Position = projection * transform * vec4(in_position, 1); 182 | 183 | texCoord = in_tex; 184 | } 185 | "); 186 | Shader fragment = new Shader(); 187 | fragment.load(ShaderType.Fragment, "#version 330 188 | uniform sampler2D tex; 189 | 190 | in vec2 texCoord; 191 | 192 | layout(location = 0) out vec4 out_frag_color; 193 | 194 | void main() 195 | { 196 | out_frag_color = texture(tex, texCoord); 197 | } 198 | "); 199 | defaultShader.attach(vertex); 200 | defaultShader.attach(fragment); 201 | defaultShader.link(); 202 | defaultShader.bind(); 203 | defaultShader.registerUniform("tex"); 204 | defaultShader.registerUniform("transform"); 205 | defaultShader.registerUniform("projection"); 206 | defaultShader.set("tex", 0); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /source/d2d/rendering/shape.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.shape; 2 | 3 | import d2d; 4 | 5 | /// Base class for Shapes. 6 | class Shape : Transformable, IDrawable 7 | { 8 | /// Abstract draw function called from `IRenderTarget`. 9 | abstract void draw(IRenderTarget target, ShaderProgram shader = null); 10 | 11 | /// Property holding the assigned texture. 12 | public @property ref Texture texture() 13 | { 14 | return _texture; 15 | } 16 | 17 | private Texture _texture; 18 | } 19 | -------------------------------------------------------------------------------- /source/d2d/rendering/spritebatch.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.spritebatch; 2 | 3 | import crunch; 4 | 5 | import d2d; 6 | 7 | enum DrawOrigin 8 | { 9 | topLeft = 0, 10 | topCenter = 1, 11 | topRight = 2, 12 | middleLeft = 4, 13 | middleCenter = 5, 14 | middleRight = 6, 15 | bottomLeft = 8, 16 | bottomCenter = 9, 17 | bottomRight = 10 18 | } 19 | 20 | class SpriteBatchImpl(uint maxVertexCount) : IDisposable, IVerifiable, IDrawable 21 | { 22 | struct Vertex 23 | { 24 | vec2 position; 25 | vec2 texCoord; 26 | vec4 color; 27 | } 28 | 29 | Vertex[maxVertexCount] vertices; 30 | RenderableMesh renderable; 31 | 32 | private Texture texture; 33 | private uint index; 34 | private uint vao; 35 | private uint vbo; 36 | private bool running; 37 | 38 | /// Regular texture shader. 39 | public static ShaderProgram spriteShader; 40 | 41 | static void load() 42 | { 43 | spriteShader = new ShaderProgram(); 44 | Shader vertex = new Shader(); 45 | vertex.load(ShaderType.Vertex, "#version 330 46 | layout(location = 0) in vec3 in_position; 47 | layout(location = 1) in vec2 in_tex; 48 | layout(location = 2) in vec4 in_color; 49 | 50 | uniform mat4 transform; 51 | uniform mat4 projection; 52 | 53 | out vec2 texCoord; 54 | out vec4 color; 55 | 56 | void main() 57 | { 58 | gl_Position = projection * transform * vec4(in_position, 1); 59 | 60 | texCoord = in_tex; 61 | color = in_color; 62 | } 63 | "); 64 | Shader fragment = new Shader(); 65 | fragment.load(ShaderType.Fragment, "#version 330 66 | uniform sampler2D tex; 67 | 68 | in vec2 texCoord; 69 | in vec4 color; 70 | 71 | layout(location = 0) out vec4 out_frag_color; 72 | 73 | void main() 74 | { 75 | out_frag_color = texture(tex, texCoord) * color; 76 | } 77 | "); 78 | spriteShader.attach(vertex); 79 | spriteShader.attach(fragment); 80 | spriteShader.link(); 81 | spriteShader.bind(); 82 | spriteShader.registerUniform("tex"); 83 | spriteShader.registerUniform("transform"); 84 | spriteShader.registerUniform("projection"); 85 | spriteShader.set("tex", 0); 86 | } 87 | 88 | this() 89 | { 90 | glGenVertexArrays(1, &vao); 91 | glBindVertexArray(vao); 92 | 93 | glGenBuffers(1, &vbo); 94 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 95 | glBufferData(GL_ARRAY_BUFFER, maxVertexCount * Vertex.sizeof, &vertices[0], GL_DYNAMIC_DRAW); 96 | glEnableVertexAttribArray(0); 97 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, Vertex.sizeof, 98 | cast(void*)(Vertex.position.offsetof)); 99 | glEnableVertexAttribArray(1); 100 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, Vertex.sizeof, 101 | cast(void*)(Vertex.texCoord.offsetof)); 102 | glEnableVertexAttribArray(2); 103 | glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, Vertex.sizeof, 104 | cast(void*)(Vertex.color.offsetof)); 105 | 106 | glBindVertexArray(0); 107 | 108 | renderable = new RenderableMesh(vao, 0, false); 109 | 110 | if (spriteShader is null) 111 | load(); 112 | } 113 | 114 | void dispose() 115 | { 116 | if (valid) 117 | { 118 | renderable = null; 119 | } 120 | } 121 | 122 | @property bool valid() 123 | { 124 | return renderable !is null; 125 | } 126 | 127 | void draw(IRenderTarget target, ShaderProgram shader = spriteShader) 128 | { 129 | texture.bind(); 130 | target.draw(renderable, shader); 131 | } 132 | 133 | void begin(Texture texture) 134 | { 135 | if (running) 136 | throw new Exception("Tried to begin when begin was already called"); 137 | 138 | index = 0; 139 | running = true; 140 | this.texture = texture; 141 | } 142 | 143 | void end() 144 | { 145 | if (!running) 146 | throw new Exception("Tried to end when begin was not called"); 147 | 148 | running = false; 149 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 150 | glBufferSubData(GL_ARRAY_BUFFER, 0, index * Vertex.sizeof, &vertices[0]); 151 | renderable.count = index; 152 | } 153 | 154 | void drawSprite(Crunch.Image image, vec2 position, vec4 color = vec4(1)) 155 | { 156 | drawSprite(image, position, vec2(1, 1), color); 157 | } 158 | 159 | void drawSprite(Crunch.Image image, vec2 position, float rotation, 160 | DrawOrigin origin, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 161 | { 162 | drawSprite(image, position, vec2(1, 1), rotation, origin, originOffset, color); 163 | } 164 | 165 | void drawSprite(Crunch.Image image, vec2 position, float scale, vec4 color = vec4(1)) 166 | { 167 | drawSprite(image, position, vec2(scale, scale), color); 168 | } 169 | 170 | void drawSprite(Crunch.Image image, vec2 position, float scale, float rotation, 171 | DrawOrigin origin, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 172 | { 173 | drawSprite(image, position, vec2(scale, scale), rotation, origin, originOffset, color); 174 | } 175 | 176 | void drawSprite(Crunch.Image sprite, vec2 position, vec2 scale, vec4 color = vec4(1)) 177 | { 178 | vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height); 179 | vec2 size = vec2(sprite.width, sprite.height); 180 | vec2 drawSize = vec2(size.x * scale.x, size.y * scale.y); 181 | 182 | drawRectangle(position, drawSize, rect.dim(sprite.x, sprite.y, size).scale(uvScale), color); 183 | } 184 | 185 | void drawSprite(Crunch.Image sprite, vec2 position, vec2 scale, float rotation, 186 | DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 187 | { 188 | vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height); 189 | vec2 size = vec2(sprite.width, sprite.height); 190 | vec2 drawSize = vec2(size.x * scale.x, size.y * scale.y); 191 | 192 | drawRectangle(position, drawSize, rect.dim(sprite.x, sprite.y, size) 193 | .scale(uvScale), rotation, origin, originOffset, color); 194 | } 195 | 196 | void drawSprite(Crunch.Image sprite, mat3 transformation, vec4 color = vec4(1)) 197 | { 198 | vec2 uvScale = vec2(1.0f / texture.width, 1.0f / texture.height); 199 | vec2 size = vec2(sprite.width, sprite.height); 200 | 201 | drawRectangle(rect.dim(size), rect.dim(sprite.x, sprite.y, size) 202 | .scale(uvScale), transformation, color); 203 | } 204 | 205 | void drawRectangle(vec2 position, vec2 size, vec4 color = vec4(1)) 206 | { 207 | drawRectangle(rect.dim(position, size), color); 208 | } 209 | 210 | void drawRectangle(vec2 position, vec2 size, float rotation, 211 | DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 212 | { 213 | drawRectangle(rect.dim(position, size), rotation, origin, originOffset, color); 214 | } 215 | 216 | void drawRectangle(rect rectangle, vec4 color = vec4(1)) 217 | { 218 | drawRectangle(rectangle, rect(0, 0, 1, 1), color); 219 | } 220 | 221 | void drawRectangle(rect rectangle, float rotation, 222 | DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 223 | { 224 | drawRectangle(rectangle, rect(0, 0, 1, 1), rotation, origin, originOffset, color); 225 | } 226 | 227 | void drawRectangle(vec2 position, vec2 size, rect uv, vec4 color = vec4(1)) 228 | { 229 | drawRectangle(rect.dim(position, size), uv, color); 230 | } 231 | 232 | void drawRectangle(vec2 position, vec2 size, rect uv, float rotation, 233 | DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 234 | { 235 | drawRectangle(rect.dim(position, size), uv, rotation, origin, originOffset, color); 236 | } 237 | 238 | void drawRectangle(rect rectangle, rect uv, vec4 color = vec4(1)) 239 | { 240 | if (!running) 241 | throw new Exception("Tried to draw when begin was not called"); 242 | 243 | vertices[index++] = Vertex(rectangle.pos00, uv.pos00, color); 244 | vertices[index++] = Vertex(rectangle.pos10, uv.pos10, color); 245 | vertices[index++] = Vertex(rectangle.pos01, uv.pos01, color); 246 | 247 | vertices[index++] = Vertex(rectangle.pos01, uv.pos01, color); 248 | vertices[index++] = Vertex(rectangle.pos10, uv.pos10, color); 249 | vertices[index++] = Vertex(rectangle.pos11, uv.pos11, color); 250 | } 251 | 252 | void drawRectangle(rect rectangle, rect uv, mat3 transformation, vec4 color = vec4(1)) 253 | { 254 | if (!running) 255 | throw new Exception("Tried to draw when begin was not called"); 256 | 257 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos00, 1)).xy, uv.pos00, color); 258 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos10, 1)).xy, uv.pos10, color); 259 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos01, 1)).xy, uv.pos01, color); 260 | 261 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos01, 1)).xy, uv.pos01, color); 262 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos10, 1)).xy, uv.pos10, color); 263 | vertices[index++] = Vertex((transformation * vec3(rectangle.pos11, 1)).xy, uv.pos11, color); 264 | } 265 | 266 | void drawRectangle(rect rectangle, rect uv, float rotation, 267 | DrawOrigin origin = DrawOrigin.topLeft, vec2 originOffset = vec2(0), vec4 color = vec4(1)) 268 | { 269 | if (!running) 270 | throw new Exception("Tried to draw when begin was not called"); 271 | 272 | vec2 offset = originOffset; 273 | 274 | auto horizontal = cast(int) origin & 0b11; 275 | auto vertical = cast(int) origin & 0b1100; 276 | 277 | offset.x -= rectangle.x; 278 | offset.y -= rectangle.y; 279 | 280 | vec2 drawOffset = vec2(0); 281 | 282 | if (horizontal == 1) 283 | { 284 | drawOffset.x = -rectangle.width * 0.5; 285 | offset.x -= rectangle.width * 0.5; 286 | } 287 | else if (horizontal == 2) 288 | { 289 | drawOffset.x = -rectangle.width; 290 | offset.x -= rectangle.width; 291 | } 292 | 293 | if (vertical == 4) 294 | { 295 | drawOffset.y = -rectangle.height * 0.5; 296 | offset.y -= rectangle.height * 0.5; 297 | } 298 | else if (vertical == 8) 299 | { 300 | drawOffset.y = -rectangle.height; 301 | offset.y -= rectangle.height; 302 | } 303 | 304 | float s = -sin(rotation); 305 | float c = cos(rotation); 306 | 307 | mat2 m = mat2(c, -s, s, c); 308 | 309 | vertices[index++] = Vertex((rectangle.pos00 + offset) * m - offset + drawOffset, 310 | uv.pos00, color); 311 | vertices[index++] = Vertex((rectangle.pos10 + offset) * m - offset + drawOffset, 312 | uv.pos10, color); 313 | vertices[index++] = Vertex((rectangle.pos01 + offset) * m - offset + drawOffset, 314 | uv.pos01, color); 315 | 316 | vertices[index++] = Vertex((rectangle.pos01 + offset) * m - offset + drawOffset, 317 | uv.pos01, color); 318 | vertices[index++] = Vertex((rectangle.pos10 + offset) * m - offset + drawOffset, 319 | uv.pos10, color); 320 | vertices[index++] = Vertex((rectangle.pos11 + offset) * m - offset + drawOffset, 321 | uv.pos11, color); 322 | } 323 | 324 | void drawTriangle(Vertex[3] tri) 325 | { 326 | vertices[index++] = tri[0]; 327 | vertices[index++] = tri[1]; 328 | vertices[index++] = tri[2]; 329 | } 330 | } 331 | 332 | alias SpriteBatch = SpriteBatchImpl!(65536); 333 | -------------------------------------------------------------------------------- /source/d2d/rendering/spritesheet.d: -------------------------------------------------------------------------------- 1 | module d2d.rendering.spritesheet; 2 | 3 | import std.algorithm; 4 | import std.conv; 5 | import std.file : read; 6 | import std.path; 7 | 8 | import d2d.rendering.texture; 9 | 10 | import crunch; 11 | 12 | struct Spritesheet 13 | { 14 | Texture[] textures; 15 | Crunch sprites; 16 | 17 | version (BindSDL_Image) void load(string path) 18 | { 19 | load(cast(ubyte[]) read(path), dirName(path)); 20 | } 21 | 22 | version (BindSDL_Image) void load(ubyte[] data, string cwd) 23 | { 24 | sprites = crunchFromCompact(data, false); 25 | textures.length = sprites.textures.length; 26 | 27 | foreach (i, tex; sprites.textures) 28 | textures[i] = new Texture(buildPath(cwd, tex.name ~ ".png"), TextureFilterMode.Nearest, 29 | TextureFilterMode.Nearest, TextureClampMode.ClampToEdge, TextureClampMode.ClampToEdge); 30 | } 31 | 32 | auto buildLookup(Names...)(int texture) 33 | { 34 | static immutable string[] names = buildNameList!Names; 35 | union Data 36 | { 37 | mixin(buildLookupStructCode(names)); 38 | Crunch.Image[names.length] images; 39 | } 40 | 41 | Data ret; 42 | int index = 0; 43 | int texIndex = 0; 44 | while (index < names.length && texIndex < sprites.textures[texture].images.length) 45 | { 46 | if (sprites.textures[texture].images[texIndex].name == names[index]) 47 | { 48 | ret.images[index] = sprites.textures[texture].images[texIndex]; 49 | index++; 50 | } 51 | texIndex++; 52 | } 53 | if (index != names.length) 54 | throw new Exception( 55 | "Not all images loaded! Got " ~ index.to!string ~ " out of " ~ names.length.to!string); 56 | return ret.lookup; 57 | } 58 | } 59 | 60 | private string[] buildNameList(Names...)() 61 | { 62 | string[] ret = [Names]; 63 | ret.sort!"a 0; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /source/d2d/toolkit/game.d: -------------------------------------------------------------------------------- 1 | module d2d.toolkit.game; 2 | 3 | import d2d; 4 | 5 | import std.datetime.stopwatch; 6 | 7 | /// Class for easily creating a window 8 | abstract class Game 9 | { 10 | private: 11 | int _width = 800, _height = 480; 12 | Bitmap _icon = null; 13 | Window _window = null; 14 | string _title = "Game"; 15 | int _fps = 60; 16 | StopWatch _stopwatch; 17 | WindowFlags _flags = WindowFlags.Default; 18 | ShaderProgram _postShader = null; 19 | 20 | protected: 21 | /// Window start width, will not update afterwards 22 | @property ref int windowWidth() 23 | { 24 | return _width; 25 | } 26 | 27 | /// Window start height, will not update afterwards 28 | @property ref int windowHeight() 29 | { 30 | return _height; 31 | } 32 | 33 | /// Window icon, will not update afterwards 34 | @property ref Bitmap windowIcon() 35 | { 36 | return _icon; 37 | } 38 | 39 | /// Window title, will not update afterwards 40 | @property ref string windowTitle() 41 | { 42 | return _title; 43 | } 44 | 45 | /// Max FPS, will not update afterwards 46 | @property ref int maxFPS() 47 | { 48 | return _fps; 49 | } 50 | 51 | /// Window start flags, will not update afterwards 52 | @property ref WindowFlags flags() 53 | { 54 | return _flags; 55 | } 56 | 57 | /// Handle to the window. 58 | @property Window window() 59 | { 60 | return _window; 61 | } 62 | 63 | /// Optional post processing shader. 64 | @property ref ShaderProgram postShader() 65 | { 66 | return _postShader; 67 | } 68 | 69 | /// Start function will get called before window is created. Variables can be changed here. 70 | abstract void start(); 71 | 72 | /// Load function thats meant for loading content. 73 | abstract void load(); 74 | 75 | /// Will get called every frame before `draw`. 76 | abstract void update(float delta); 77 | 78 | /// Draw code goes here, `window.display` is not needed. 79 | abstract void draw(); 80 | 81 | /// Happens when some window event gets called. 82 | void onEvent(Event event) 83 | { 84 | Keyboard.instance.handle(event); 85 | Mouse.instance.handle(event); 86 | } 87 | 88 | public: 89 | /// Starts the window and calls all functions 90 | void run() 91 | { 92 | start(); 93 | 94 | _window = new Window(_width, _height, _title, _flags); 95 | 96 | if (_icon is null) 97 | { 98 | //dfmt off 99 | _icon = new Bitmap( 100 | [ 101 | 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 102 | 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00ffffff, 103 | 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff9c9c9c, 0xff000000, 0xff000000, 0xff000000, 0xff383838, 0xffcccccc, 0xffffffff, 104 | 0xffffffff, 0xff000000, 0xff303030, 0xff303030, 0xff303030, 0xff1c1c1c, 0xff000000, 0xff9c9c9c, 0xffcccccc, 0xff000000, 0xff292929, 0xff303030, 0xff292929, 0xff000000, 0xff6d6d6d, 0xffffffff, 105 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff707070, 0xff131313, 0xff000000, 0xffcccccc, 0xff303030, 0xffb7b7b7, 0xffcccccc, 0xffb7b7b7, 0xff131313, 0xff000000, 0xffffffff, 106 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff555555, 0xff000000, 0xff9c9c9c, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff1e1e1e, 0xff000000, 0xffffffff, 107 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffa7a7a7, 0xff000000, 0xff6d6d6d, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffb0b0b0, 0xff000000, 0xff555555, 0xffffffff, 108 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffb0b0b0, 0xff000000, 0xff727272, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff333333, 0xff0d0d0d, 0xffa7a7a7, 0xffffffff, 109 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff8b8b8b, 0xff000000, 0xff909090, 0xffcccccc, 0xffcccccc, 0xff545454, 0xff000000, 0xff5b5b5b, 0xffcccccc, 0xffffffff, 110 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff444444, 0xff060606, 0xffb7b7b7, 0xffcccccc, 0xff545454, 0xff000000, 0xff454545, 0xffcccccc, 0xffcccccc, 0xffffffff, 111 | 0xffffffff, 0xff000000, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xff545454, 0xff000000, 0xff343434, 0xffcccccc, 0xff545454, 0xff000000, 0xff303030, 0xffcccccc, 0xffcccccc, 0xffcccccc, 0xffffffff, 112 | 0xffffffff, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff303030, 0xffa7a7a7, 0xffb0b0b0, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xff000000, 0xffffffff, 113 | 0xffffffff, 0xff303030, 0xff303030, 0xff303030, 0xff303030, 0xff454545, 0xffcccccc, 0xffcccccc, 0xffb7b7b7, 0xff303030, 0xff303030, 0xff303030, 0xff303030, 0xff303030, 0xff303030, 0xffffffff, 114 | 0x00ffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x00ffffff, 115 | 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 116 | 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 0x00ffffff, 117 | ], 16, 16, 32, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); 118 | //dfmt on 119 | } 120 | 121 | _window.setIcon(_icon); 122 | 123 | FPSLimiter limiter; 124 | if (_fps > 0) 125 | limiter = new FPSLimiter(_fps); 126 | 127 | load(); 128 | 129 | Event event; 130 | Duration delta; 131 | while (_window.open) 132 | { 133 | _stopwatch.start(); 134 | while (_window.pollEvent(event)) 135 | { 136 | if (event.type == Event.Type.Quit) 137 | _window.close(); 138 | else 139 | onEvent(event); 140 | } 141 | 142 | update(delta.total!"hnsecs" / 10_000_000.0f); 143 | 144 | draw(); 145 | 146 | if (_postShader !is null) 147 | _window.display(_postShader); 148 | else 149 | _window.display(); 150 | 151 | if (limiter !is null) 152 | limiter.wait(); 153 | 154 | _stopwatch.stop(); 155 | delta = _stopwatch.peek(); 156 | _stopwatch.reset(); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /source/d2d/toolkit/input/keyboard.d: -------------------------------------------------------------------------------- 1 | module d2d.toolkit.input.keyboard; 2 | 3 | import d2d.window.windowevent; 4 | 5 | struct Keyboard 6 | { 7 | @disable this(this); 8 | 9 | bool[256] keys; 10 | bool[uint] extendedKeys; 11 | 12 | static Keyboard instance; 13 | 14 | Keyboard dup() 15 | { 16 | return Keyboard(keys, extendedKeys.dup); 17 | } 18 | 19 | bool isPressed(int key) 20 | { 21 | if (key < keys.length) 22 | return keys[key]; 23 | else if (auto v = key in extendedKeys) 24 | return *v; 25 | else 26 | return false; 27 | } 28 | 29 | void keyDown(int key) 30 | { 31 | if (key < keys.length) 32 | keys[key] = true; 33 | else 34 | extendedKeys[key] = true; 35 | } 36 | 37 | void keyUp(int key) 38 | { 39 | if (key < keys.length) 40 | keys[key] = false; 41 | else 42 | extendedKeys[key] = false; 43 | } 44 | 45 | void handle(Event event) 46 | { 47 | switch (event.type) 48 | { 49 | case Event.Type.KeyPressed: 50 | keyDown(event.key); 51 | break; 52 | case Event.Type.KeyReleased: 53 | keyUp(event.key); 54 | break; 55 | default: 56 | break; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /source/d2d/toolkit/input/mouse.d: -------------------------------------------------------------------------------- 1 | module d2d.toolkit.input.mouse; 2 | 3 | import d2d.window.windowevent; 4 | 5 | struct Mouse 6 | { 7 | @disable this(this); 8 | 9 | int x, y; 10 | int dx, dy; 11 | int wheelX, wheelY; 12 | int wheelDX, wheelDY; 13 | 14 | bool[32] buttons; 15 | 16 | static Mouse instance; 17 | 18 | Mouse dup() 19 | { 20 | return Mouse(x, y, dx, dy, wheelX, wheelY, wheelDX, wheelDY, buttons); 21 | } 22 | 23 | void move(int x, int y, int dx, int dy) 24 | { 25 | this.x = x; 26 | this.y = y; 27 | this.dx = dx; 28 | this.dy = dy; 29 | } 30 | 31 | void moveTo(int x, int y) 32 | { 33 | move(x, y, x - this.x, y - this.y); 34 | } 35 | 36 | void scroll(int dx, int dy) 37 | { 38 | wheelX += dx; 39 | wheelY += dy; 40 | wheelDX = dx; 41 | wheelDY = dy; 42 | } 43 | 44 | void click(int button, bool down) 45 | { 46 | buttons[button] = down; 47 | } 48 | 49 | void handle(Event event) 50 | { 51 | switch (event.type) 52 | { 53 | case Event.Type.MouseWheelMoved: 54 | scroll(event.x, event.y); 55 | break; 56 | case Event.Type.MouseMoved: 57 | move(event.x, event.y, event.xrel, event.yrel); 58 | break; 59 | case Event.Type.MouseButtonPressed: 60 | if (event.x != x || event.y != y) 61 | moveTo(event.x, event.y); 62 | click(event.mousebutton, true); 63 | break; 64 | case Event.Type.MouseButtonReleased: 65 | if (event.x != x || event.y != y) 66 | moveTo(event.x, event.y); 67 | click(event.mousebutton, false); 68 | break; 69 | default: 70 | break; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /source/d2d/window/window.d: -------------------------------------------------------------------------------- 1 | module d2d.window.window; 2 | 3 | import d2d; 4 | 5 | import std.stdio : stderr; 6 | 7 | deprecated("Use BindSDL_Mixer, BindSDL_TTF, BindSDL_Image") enum DynLibs 8 | { 9 | none 10 | } 11 | 12 | /// Single-Window class wrapping SDL_Window. 13 | class Window : IVerifiable, IDisposable, IRenderTarget 14 | { 15 | private: 16 | SDL_Window* _handle; 17 | int _id; 18 | uint _fbo, _drb; 19 | Texture _texture; 20 | RectangleShape _displayPlane; 21 | bool _direct = false; 22 | mat4 _postMatrix; 23 | 24 | public: 25 | /// Static variable to a SDL GL Context. 26 | static SDL_GLContext glContext = null; 27 | 28 | /// Creates a new centered window with specified title and flags on a 800x480 resolution. 29 | this()(string title = "D2DGame", SDL_WindowFlags flags = WindowFlags.Default) 30 | { 31 | this(800, 480, title, flags); 32 | } 33 | 34 | deprecated this()(string title, SDL_WindowFlags flags, DynLibs dynamicLibs) 35 | { 36 | static assert(false, 37 | "Use BindSDL_Mixer, BindSDL_TTF, BindSDL_Image versions instead of DynLibs constructor"); 38 | } 39 | 40 | /// Creates a new centered window with specified dimensions, title and flags. 41 | this(int width, int height, string title = "D2DGame", SDL_WindowFlags flags = WindowFlags.Default) 42 | { 43 | this(SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, title, flags); 44 | } 45 | 46 | /// Creates a new window with specified parameters. 47 | this(int x, int y, int width, int height, string title, 48 | SDL_WindowFlags flags = WindowFlags.Default) 49 | { 50 | SDLSupport sdl = loadSDL(); 51 | loadErrorCheck("SDL"); 52 | if (sdl == SDLSupport.noLibrary) 53 | { 54 | stderr.writeln("SDL failed to load (no library found)"); 55 | throw new Exception("SDL not found"); 56 | } 57 | else if (sdl == SDLSupport.badLibrary) 58 | { 59 | stderr.writeln("SDL failed to load some symbols"); 60 | throw new Exception("SDL version invalid"); 61 | } 62 | else if (sdl != sdlSupport) 63 | { 64 | stderr.writeln("SDL version failed to match"); 65 | throw new Exception("SDL version invalid"); 66 | } 67 | 68 | version (BindSDL_Image) 69 | { 70 | auto img = loadSDLImage(); 71 | loadErrorCheck("SDL_Image"); 72 | if (img != sdlImageSupport) 73 | { 74 | stderr.writeln("Failed loading SDL_Image"); 75 | throw new Exception("Failed to load SDL_Image"); 76 | } 77 | } 78 | 79 | version (BindSDL_Mixer) 80 | { 81 | auto mixer = loadSDLMixer(); 82 | loadErrorCheck("SDL_Mixer"); 83 | if (mixer != sdlMixerSupport) 84 | { 85 | stderr.writeln("Failed loading SDL_Mixer"); 86 | throw new Exception("Failed to load SDL_Mixer"); 87 | } 88 | } 89 | 90 | version (BindSDL_TTF) 91 | { 92 | auto ttf = loadSDLTTF(); 93 | loadErrorCheck("SDL_TTF"); 94 | if (ttf != sdlTTFSupport) 95 | { 96 | stderr.writeln("Failed loading SDL_TTF"); 97 | throw new Exception("Failed to load SDL_TTF"); 98 | } 99 | } 100 | 101 | SDL_Init(SDL_INIT_EVERYTHING); 102 | 103 | version (BindSDL_TTF) 104 | if (TTF_Init() == -1) 105 | { 106 | throw new Exception("Error Initializing SDL_TTF: " ~ TTF_GetError().fromStringz.idup); 107 | } 108 | 109 | _handle = SDL_CreateWindow(title.toStringz(), x, y, width, height, flags | SDL_WINDOW_OPENGL); 110 | if (!valid) 111 | throw new Exception("Couldn't create window!"); 112 | _id = SDL_GetWindowID(_handle); 113 | 114 | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); 115 | SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); 116 | 117 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 118 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 119 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); 120 | 121 | glContext = SDL_GL_CreateContext(_handle); 122 | 123 | SDL_GL_SetSwapInterval(0); 124 | 125 | auto gl = loadOpenGL(); 126 | loadErrorCheck("OpenGL"); 127 | if (gl == GLSupport.noLibrary) 128 | { 129 | stderr.writeln("OpenGL failed to load (no library found)"); 130 | throw new Exception("OpenGL not found"); 131 | } 132 | else if (gl == GLSupport.badLibrary) 133 | { 134 | stderr.writeln("OpenGL failed to load some symbols"); 135 | throw new Exception("OpenGL could not be initialized"); 136 | } 137 | else if (gl != glSupport) 138 | throw new Exception("Needs at least OpenGL 3.2 to run"); 139 | 140 | if (SDL_GL_MakeCurrent(_handle, glContext) < 0) 141 | throw new Exception(cast(string) fromStringz(SDL_GetError())); 142 | 143 | Texture.supportsAnisotropy = hasARBTextureFilterAnisotropic; 144 | 145 | glEnable(GL_BLEND); 146 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 147 | 148 | ShaderProgram.load(); 149 | Texture.load(); 150 | version (BindSDL_Mixer) 151 | if (!Music.load()) 152 | throw new Exception(Music.error); 153 | 154 | create(width, height); 155 | 156 | _displayPlane = new RectangleShape(); 157 | _displayPlane.size = vec2(1, 1); 158 | _displayPlane.create(); 159 | _displayPlane.texture = _texture; 160 | 161 | _postMatrix = mat4.orthographic(0, 1, 0, 1, -1, 1); 162 | 163 | foreach (info; soloader.errors) 164 | stderr.writeln(info.error, info.message); 165 | } 166 | 167 | ~this() 168 | { 169 | if (valid) 170 | dispose(); 171 | } 172 | 173 | /// Polls a event from the stack and returns `true` if one was found. 174 | bool pollEvent(ref WindowEvent event) 175 | { 176 | SDL_Event evt; 177 | if (SDL_PollEvent(&evt)) 178 | { 179 | event = WindowEvent(); 180 | event.fromSDL(evt); 181 | return true; 182 | } 183 | return false; 184 | } 185 | 186 | /// Pushes `WindowEvent.Quit` to the event stack. 187 | void quit() 188 | { 189 | SDL_Event sdlevent; 190 | sdlevent.type = SDL_QUIT; 191 | SDL_PushEvent(&sdlevent); 192 | } 193 | 194 | void bind() 195 | { 196 | if (!_direct) 197 | { 198 | glBindFramebuffer(GL_FRAMEBUFFER, _fbo); 199 | glViewport(0, 0, _texture.width, _texture.height); 200 | } 201 | } 202 | 203 | void resize(int width, int height) 204 | { 205 | glDeleteFramebuffers(1, &_fbo); 206 | _texture.dispose(); 207 | create(width, height); 208 | _displayPlane.texture = _texture; 209 | projectionStack.set(mat4.orthographic(0, width, height, 0, -1, 1)); 210 | } 211 | 212 | void create(int width, int height) 213 | { 214 | glGenFramebuffers(1, &_fbo); 215 | glBindFramebuffer(GL_FRAMEBUFFER, _fbo); 216 | 217 | _texture = new Texture(); 218 | _texture.minFilter = TextureFilterMode.Nearest; 219 | _texture.magFilter = TextureFilterMode.Nearest; 220 | _texture.create(width, height, GL_RGB, null); 221 | 222 | glGenRenderbuffers(1, &_drb); 223 | glBindRenderbuffer(GL_RENDERBUFFER, _drb); 224 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); 225 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _drb); 226 | 227 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, _texture.id, 0); 228 | 229 | glDrawBuffers(1, [GL_COLOR_ATTACHMENT0].ptr); 230 | projectionStack.set(mat4.orthographic(0, width, height, 0, -1, 1)); 231 | if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 232 | throw new Exception("Invalid Framebuffer"); 233 | } 234 | 235 | /// Texture containing rendered content. 236 | @property Texture texture() 237 | { 238 | return _texture; 239 | } 240 | 241 | /// Displays rendered content to the window. 242 | void display(ShaderProgram post = null) 243 | { 244 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 245 | int x, y; 246 | SDL_GetWindowSize(_handle, &x, &y); 247 | glViewport(0, 0, x, y); 248 | glClearColor(Color3.BlueViolet, 1); 249 | glClear(GL_COLOR_BUFFER_BIT); 250 | _direct = true; 251 | projectionStack.push(); 252 | projectionStack.set(_postMatrix); 253 | matrixStack.push(); 254 | matrixStack.set(mat4.identity); 255 | draw(_displayPlane, post); 256 | matrixStack.pop(); 257 | projectionStack.pop(); 258 | _direct = false; 259 | SDL_GL_SwapWindow(_handle); 260 | } 261 | 262 | /// Dynamically sets the title of the window. 263 | @property void title(string title) 264 | { 265 | SDL_SetWindowTitle(_handle, title.toStringz()); 266 | } 267 | 268 | /// Dynamically gets the title of the window. 269 | @property string title() 270 | { 271 | string title = SDL_GetWindowTitle(_handle).fromStringz().dup; 272 | return title; 273 | } 274 | 275 | /// Dynamically sets the width of the window. 276 | @property void width(int width) 277 | { 278 | SDL_SetWindowSize(_handle, width, height); 279 | resize(width, height); 280 | } 281 | 282 | /// Dynamically gets the width of the window. 283 | @property int width() 284 | { 285 | int x, y; 286 | SDL_GetWindowSize(_handle, &x, &y); 287 | return x; 288 | } 289 | 290 | /// Dynamically sets the height of the window. 291 | @property void height(int height) 292 | { 293 | SDL_SetWindowSize(_handle, width, height); 294 | resize(width, height); 295 | } 296 | 297 | /// Dynamically gets the height of the window. 298 | @property int height() 299 | { 300 | int x, y; 301 | SDL_GetWindowSize(_handle, &x, &y); 302 | return y; 303 | } 304 | 305 | /// Dynamically sets the maximum width of the window. 306 | @property void maxWidth(int maxWidth) 307 | { 308 | SDL_SetWindowMaximumSize(_handle, maxWidth, maxHeight); 309 | } 310 | 311 | /// Dynamically gets the maximum width of the window. 312 | @property int maxWidth() 313 | { 314 | int x, y; 315 | SDL_GetWindowMaximumSize(_handle, &x, &y); 316 | return x; 317 | } 318 | 319 | /// Dynamically sets the maximum height of the window. 320 | @property void maxHeight(int maxHeight) 321 | { 322 | SDL_SetWindowMaximumSize(_handle, maxWidth, maxHeight); 323 | } 324 | 325 | /// Dynamically gets the maximum height of the window. 326 | @property int maxHeight() 327 | { 328 | int x, y; 329 | SDL_GetWindowMaximumSize(_handle, &x, &y); 330 | return y; 331 | } 332 | 333 | /// Dynamically sets the minimum width of the window. 334 | @property void minWidth(int minWidth) 335 | { 336 | SDL_SetWindowMinimumSize(_handle, minWidth, minHeight); 337 | } 338 | 339 | /// Dynamically gets the minimum width of the window. 340 | @property int minWidth() 341 | { 342 | int x, y; 343 | SDL_GetWindowMinimumSize(_handle, &x, &y); 344 | return x; 345 | } 346 | 347 | /// Dynamically sets the minimum height of the window. 348 | @property void minHeight(int minHeight) 349 | { 350 | SDL_SetWindowMinimumSize(_handle, minWidth, minHeight); 351 | } 352 | 353 | /// Dynamically gets the minimum height of the window. 354 | @property int minHeight() 355 | { 356 | int x, y; 357 | SDL_GetWindowMinimumSize(_handle, &x, &y); 358 | return y; 359 | } 360 | 361 | /// Dynamically sets the x position of the window. 362 | @property void x(int x) 363 | { 364 | SDL_SetWindowPosition(_handle, x, y); 365 | } 366 | 367 | /// Dynamically gets the x position of the window. 368 | @property int x() 369 | { 370 | int x, y; 371 | SDL_GetWindowPosition(_handle, &x, &y); 372 | return x; 373 | } 374 | 375 | /// Dynamically sets the y position of the window. 376 | @property void y(int y) 377 | { 378 | SDL_SetWindowPosition(_handle, x, y); 379 | } 380 | 381 | /// Dynamically gets the y position of the window. 382 | @property int y() 383 | { 384 | int x, y; 385 | SDL_GetWindowPosition(_handle, &x, &y); 386 | return y; 387 | } 388 | 389 | /// Shows the window if hidden. 390 | void show() 391 | { 392 | SDL_ShowWindow(_handle); 393 | } 394 | 395 | /// Hides the window. 396 | void hide() 397 | { 398 | SDL_HideWindow(_handle); 399 | } 400 | 401 | /// Minimizes the window. 402 | void minimize() 403 | { 404 | SDL_MinimizeWindow(_handle); 405 | } 406 | 407 | /// Maximizes the window. 408 | void maximize() 409 | { 410 | SDL_MaximizeWindow(_handle); 411 | } 412 | 413 | /// Restores the window state from minimized or maximized. 414 | void restore() 415 | { 416 | SDL_RestoreWindow(_handle); 417 | } 418 | 419 | /// Raises the window to top and focuses it for input. 420 | void focus() 421 | { 422 | SDL_RaiseWindow(_handle); 423 | } 424 | 425 | /// Sets the icon to a Btimap. 426 | void setIcon(Bitmap icon) 427 | { 428 | SDL_SetWindowIcon(_handle, icon.surface); 429 | } 430 | 431 | /// Closes the window and invalidates it. 432 | /// See_Also: Window.close 433 | void dispose() 434 | { 435 | glDeleteFramebuffers(1, &_fbo); 436 | _texture.dispose(); 437 | if (valid) 438 | { 439 | SDL_DestroyWindow(_handle); 440 | _handle = null; 441 | } 442 | } 443 | 444 | /// Closes the window and invalidates it. 445 | /// See_Also: Window.dispose 446 | void close() 447 | { 448 | dispose(); 449 | } 450 | 451 | /// Returns if the is still open. 452 | /// See_Also: Window.valid 453 | @property bool open() 454 | { 455 | return valid; 456 | } 457 | 458 | /// Returns if the window is still open. 459 | /// See_Also: Window.open 460 | @property bool valid() 461 | { 462 | return _handle !is null; 463 | } 464 | } 465 | 466 | /// 467 | unittest 468 | { 469 | Window window = new Window(800, 600, "Unittest"); 470 | 471 | assert(window.valid); 472 | 473 | assert(window.title == "Unittest"); 474 | window.title = "Window Title"; 475 | assert(window.title == "Window Title"); 476 | // Automatic conversion from c-strings to D-strings 477 | 478 | assert(window.width == 800); 479 | 480 | window.close(); 481 | assert(!window.valid); 482 | } 483 | 484 | void loadErrorCheck(string what) 485 | { 486 | if (soloader.errorCount > 0) 487 | { 488 | stderr.writeln("Errors loading ", what, ":"); 489 | foreach (info; soloader.errors) 490 | stderr.writeln(info.error, info.message); 491 | soloader.resetErrors(); 492 | } 493 | } 494 | -------------------------------------------------------------------------------- /source/d2d/window/windowevent.d: -------------------------------------------------------------------------------- 1 | module d2d.window.windowevent; 2 | 3 | import d2d; 4 | 5 | import std.math : abs; 6 | 7 | /// Event structure on Window Events. 8 | struct WindowEvent 9 | { 10 | /// Types that can occur when WindowEvents are fired. 11 | enum Type 12 | { 13 | Undefined, 14 | /// Occurs when X button is clicked. 15 | Close, 16 | /// Occurs when window got resized. 17 | Resized, 18 | /// Occurs when window got moved. 19 | Moved, 20 | /// Occurs when window lost focus. 21 | LostFocus, 22 | /// Occurs when window gained focus again. 23 | GainedFocus, 24 | /// Occurs when `window.show` got called. 25 | Shown, 26 | /// Occurs when `window.hide` got called. 27 | Hidden, 28 | /// Occurs when the window minimizes. 29 | Minimized, 30 | /// Occurs when the window maximizes. 31 | Maximized, 32 | /// Occurs when the window restores from minimized/maximized state. 33 | Restored, 34 | /// Occurs when the user typed some text on the keyboard for text fields. 35 | TextEntered, 36 | /// Occurs when the user presses a key on the keyboard. Will fire repeatedly when held down. 37 | KeyPressed, 38 | /// Occurs when the user releases a key on the keyboard. 39 | KeyReleased, 40 | /// Occurs when the user scrolled. 41 | MouseWheelMoved, 42 | /// Occurs when the user pressed a mouse button. 43 | MouseButtonPressed, 44 | /// Occurs when the user released a mouse button. 45 | MouseButtonReleased, 46 | /// Occurs when the user moved the mouse. 47 | MouseMoved, 48 | /// Occurs when the mouse hovers over the window. 49 | MouseEntered, 50 | /// Occurs when the mouse no longer hovers over the window 51 | MouseLeft, 52 | /// Occurs when a button on a controller got pressed. 53 | ControllerButtonPressed, 54 | /// Occurs when a button on a controller got released. 55 | ControllerButtonReleased, 56 | /// Occurs when an axis on a controller got moved. 57 | ControllerAxis, 58 | /// Occurs when a controller connected. 59 | ControllerConnected, 60 | /// Occurs when a controller disconnected. 61 | ControllerDisconnected, 62 | /// Occurs when window is closing. 63 | Quit 64 | } 65 | 66 | /// Type of this event. 67 | Type type; 68 | 69 | union 70 | { 71 | /// x position in events. 72 | int x; 73 | /// width in events. 74 | int width; 75 | } 76 | 77 | union 78 | { 79 | /// y position in events. 80 | int y; 81 | /// height in events. 82 | int height; 83 | } 84 | /// Relative mouse coordinates in Mouse events. 85 | int xrel, yrel; 86 | union 87 | { 88 | /// Mouse button in Mouse events. 89 | int mousebutton; 90 | /// Key in Controller and Keyboard events. 91 | int key; 92 | /// Axis id in ControllerAxis event. 93 | int axis; 94 | } 95 | /// Controller id in any Controller event. 96 | int controllerID; 97 | union 98 | { 99 | /// Axis value in ControllerAxis event. 100 | short axisValue; 101 | /// Non-zero if this is a key repeat. 102 | ubyte repeat; 103 | } 104 | /// Text contained in TextEntered event. 105 | string text; 106 | 107 | /// Function for converting a SDL event to a WindowEvent 108 | void fromSDL(const ref SDL_Event event) 109 | { 110 | switch (event.type) 111 | { 112 | case SDL_WINDOWEVENT: 113 | switch (event.window.event) 114 | { 115 | case SDL_WINDOWEVENT_SHOWN: 116 | type = Type.Shown; 117 | break; 118 | case SDL_WINDOWEVENT_HIDDEN: 119 | type = Type.Hidden; 120 | break; 121 | case SDL_WINDOWEVENT_MOVED: 122 | type = Type.Moved; 123 | x = event.window.data1; 124 | y = event.window.data2; 125 | break; 126 | case SDL_WINDOWEVENT_RESIZED: 127 | type = Type.Resized; 128 | x = event.window.data1; 129 | y = event.window.data2; 130 | break; 131 | case SDL_WINDOWEVENT_MINIMIZED: 132 | type = Type.Minimized; 133 | break; 134 | case SDL_WINDOWEVENT_MAXIMIZED: 135 | type = Type.Maximized; 136 | break; 137 | case SDL_WINDOWEVENT_RESTORED: 138 | type = Type.Restored; 139 | break; 140 | case SDL_WINDOWEVENT_ENTER: 141 | type = Type.MouseEntered; 142 | break; 143 | case SDL_WINDOWEVENT_LEAVE: 144 | type = Type.MouseLeft; 145 | break; 146 | case SDL_WINDOWEVENT_FOCUS_GAINED: 147 | type = Type.GainedFocus; 148 | break; 149 | case SDL_WINDOWEVENT_FOCUS_LOST: 150 | type = Type.LostFocus; 151 | break; 152 | case SDL_WINDOWEVENT_CLOSE: 153 | type = Type.Close; 154 | break; 155 | default: 156 | type = Type.Undefined; 157 | break; 158 | } 159 | break; 160 | case SDL_KEYDOWN: 161 | type = Type.KeyPressed; 162 | key = event.key.keysym.sym; 163 | repeat = event.key.repeat; 164 | break; 165 | case SDL_KEYUP: 166 | type = Type.KeyReleased; 167 | key = event.key.keysym.sym; 168 | repeat = event.key.repeat; 169 | break; 170 | case SDL_MOUSEWHEEL: 171 | type = Type.MouseWheelMoved; 172 | x = event.wheel.x; 173 | y = event.wheel.y; 174 | break; 175 | case SDL_MOUSEMOTION: 176 | type = Type.MouseMoved; 177 | x = event.motion.x; 178 | y = event.motion.y; 179 | xrel = event.motion.xrel; 180 | yrel = event.motion.yrel; 181 | break; 182 | case SDL_MOUSEBUTTONDOWN: 183 | type = Type.MouseButtonPressed; 184 | x = event.button.x; 185 | y = event.button.y; 186 | mousebutton = event.button.button; 187 | break; 188 | case SDL_MOUSEBUTTONUP: 189 | type = Type.MouseButtonReleased; 190 | x = event.button.x; 191 | y = event.button.y; 192 | mousebutton = event.button.button; 193 | break; 194 | case SDL_CONTROLLERDEVICEADDED: 195 | type = Type.ControllerConnected; 196 | controllerID = event.cdevice.which; 197 | SDL_GameControllerOpen(controllerID); 198 | break; 199 | case SDL_CONTROLLERDEVICEREMOVED: 200 | type = Type.ControllerDisconnected; 201 | controllerID = event.cdevice.which; 202 | break; 203 | case SDL_CONTROLLERBUTTONDOWN: 204 | type = Type.ControllerButtonPressed; 205 | controllerID = event.cbutton.which; 206 | key = event.cbutton.button; 207 | break; 208 | case SDL_CONTROLLERBUTTONUP: 209 | type = Type.ControllerButtonReleased; 210 | controllerID = event.cbutton.which; 211 | key = event.cbutton.button; 212 | break; 213 | case SDL_CONTROLLERAXISMOTION: 214 | type = Type.ControllerAxis; 215 | short value = event.caxis.value; 216 | if (value < -32_767) 217 | value = -32_767; 218 | if ((event.caxis.axis == 0 || event.caxis.axis == 1) && abs(value) < 7849) 219 | { 220 | value = 0; 221 | } 222 | if ((event.caxis.axis == 2 || event.caxis.axis == 3) && abs(value) < 8689) 223 | { 224 | value = 0; 225 | } 226 | if ((event.caxis.axis == 4 || event.caxis.axis == 5) && abs(value) < 30) 227 | { 228 | value = 0; 229 | } 230 | axis = event.caxis.axis; 231 | axisValue = value; 232 | break; 233 | case SDL_TEXTINPUT: 234 | type = Type.TextEntered; 235 | text = cast(string) fromStringz(event.text.text.ptr); 236 | break; 237 | case SDL_QUIT: 238 | type = Type.Quit; 239 | break; 240 | default: 241 | type = Type.Undefined; 242 | break; 243 | } 244 | } 245 | } 246 | 247 | /// 248 | unittest 249 | { 250 | SDL_Event source; 251 | WindowEvent event; 252 | source.type = SDL_KEYDOWN; 253 | source.key.keysym.sym = cast(SDL_Keycode) 42; 254 | event.fromSDL(source); 255 | 256 | assert(event.type == WindowEvent.Type.KeyPressed); 257 | assert(event.key == 42); 258 | } 259 | 260 | /// 261 | alias Event = WindowEvent; 262 | -------------------------------------------------------------------------------- /source/d2d/window/windowflags.d: -------------------------------------------------------------------------------- 1 | module d2d.window.windowflags; 2 | 3 | import d2d; 4 | 5 | /// Window creation flags. 6 | enum WindowFlags : SDL_WindowFlags 7 | { 8 | /// Fullscreen window with custom resolution. 9 | Fullscreen = SDL_WINDOW_FULLSCREEN, 10 | /// Fullscreen window without automatic resolution. 11 | FullscreenAuto = SDL_WINDOW_FULLSCREEN_DESKTOP, 12 | /// Directly show the window without calling `show();` 13 | Shown = SDL_WINDOW_SHOWN, 14 | /// Window is hidden by default and needs to be shown by `show();` 15 | Hidden = SDL_WINDOW_HIDDEN, 16 | /// Window has no border or title bar. 17 | Borderless = SDL_WINDOW_BORDERLESS, 18 | /// Window is resizable. 19 | Resizable = SDL_WINDOW_RESIZABLE, 20 | /// Window is initially started in minimized mode. 21 | Minimized = SDL_WINDOW_MINIMIZED, 22 | /// Window is initially started in maximized mode. 23 | Maximized = SDL_WINDOW_MAXIMIZED, 24 | /// Window directly gains input and mouse focus on startup. 25 | Focused = SDL_WINDOW_INPUT_FOCUS | SDL_WINDOW_MOUSE_FOCUS, 26 | /// Window allows high DPI monitors. 27 | HighDPI = SDL_WINDOW_ALLOW_HIGHDPI, 28 | /// Combination of `Shown | Focused` 29 | Default = Shown | Focused, 30 | } 31 | 32 | unittest 33 | { 34 | assert((WindowFlags.Default | WindowFlags.HighDPI) == (WindowFlags.Shown | WindowFlags.Focused | WindowFlags.HighDPI)); 35 | } 36 | --------------------------------------------------------------------------------