1 module sbylib.graphics.util.functions; 2 3 import sbylib.math; 4 5 // public import sbylib.math; 6 // public import sbylib.wrapper.freeimage : Image; 7 // 8 // // import sbylib.graphics.canvas : Canvas; 9 // import sbylib.wrapper.gl : Texture; 10 // 11 // import std.traits : isAggregateType, FieldTypeTuple, FieldNameTuple; 12 // 13 mixin template DefineCachedValue(Type, string attribute, string name, string expression, string[] keys) 14 if (keys.length > 0) 15 { 16 17 import std : format, replace; 18 19 static foreach (key; keys) { 20 mixin(format!"%s _%sSavedValue;"(VariableType!(typeof(mixin(key))).stringof, key.replace(".", "_"))); 21 } 22 mixin(format!"%s _%sSavedValue;"(Type.stringof, name)); 23 24 mixin(format!q{ 25 %s %s %s() { 26 bool _shouldUpdate = false; 27 static foreach (key; keys) { 28 if (mixin(format!"_%sSavedValue != %s"(key.replace(".", "_"), key))) { 29 _shouldUpdate = true; 30 mixin(format!"_%sSavedValue = %s;"(key.replace(".", "_"), key)); 31 } 32 } 33 if (_shouldUpdate) 34 _%sSavedValue = %s; 35 return _%sSavedValue; 36 } 37 }(attribute, Type.stringof, name, "%s", "%s", "%s", "%s", name, expression, name)); 38 39 template VariableType(Type) { 40 import std : isSomeFunction, ReturnType; 41 42 static if (isSomeFunction!(Type)) { 43 alias VariableType = ReturnType!(Type); 44 } else { 45 alias VariableType = Type; 46 } 47 } 48 } 49 50 mixin template ImplPos() { 51 import sbylib.math : vec3; 52 // import sbylib.graphics.util : Pixel, pixel, ImplPixelX, ImplPixelY; 53 import sbylib.wrapper.glfw : Window; 54 55 vec3 pos = vec3(0); 56 57 /* 58 59 Pixel[2] pixelPos() { 60 import sbylib.wrapper.gl : GlUtils; 61 62 const viewport = GlUtils().getViewport(); 63 return [ 64 pixel(cast(int)(pos.x * viewport[2]) / 2), 65 pixel(cast(int)(pos.y * viewport[3]) / 2), 66 ]; 67 } 68 69 Pixel[2] pixelPos(Pixel[2] pixel) { 70 import sbylib.wrapper.gl : GlUtils; 71 72 const viewport = GlUtils().getViewport(); 73 74 this.pos.xy = 2 * vec2(pixel) / vec2(viewport[2..$]); 75 76 return pixel; 77 } 78 79 mixin ImplPixelX; 80 mixin ImplPixelY; 81 */ 82 } 83 84 /* 85 mixin template ImplPixelX() { 86 Pixel pixelX() { 87 return this.pixelPos[0]; 88 } 89 90 Pixel pixelX(Pixel pixel) { 91 this.pixelPos = [pixel, this.pixelPos[1]]; 92 return pixel; 93 } 94 } 95 96 mixin template ImplPixelY() { 97 Pixel pixelY() { 98 return this.pixelPos[1]; 99 } 100 101 Pixel pixelY(Pixel pixel) { 102 this.pixelPos = [this.pixelPos[0], pixel]; 103 return pixel; 104 } 105 } 106 */ 107 108 mixin template ImplRot() { 109 import sbylib.math : mat3, Angle; 110 111 mat3 rot = mat3.identity; 112 113 auto rotate(Angle angle) { 114 this.rot *= mat3.axisAngle(vec3(0,0,1), angle); 115 return this; 116 } 117 } 118 119 mixin template ImplScale() { 120 import sbylib.math : vec3; 121 // import sbylib.graphics.util : Pixel, ImplPixelX, ImplPixelY; 122 123 vec3 scale = vec3(1); 124 125 /* 126 Pixel[2] pixelSize(Pixel[2] pixel) { 127 import sbylib.wrapper.gl : GlUtils; 128 129 this.scale.xy = 2 * vec2(pixel) / vec2(GlUtils().getViewport()[2..$]); 130 131 return pixel; 132 } 133 134 Pixel[2] pixelSize() { 135 import sbylib.wrapper.gl : GlUtils; 136 137 const viewport = GlUtils().getViewport(); 138 return [ 139 pixel(cast(int)(this.scale.x * viewport[2]) / 2), 140 pixel(cast(int)(this.scale.y * viewport[3]) / 2), 141 ]; 142 } 143 144 mixin ImplPixelWidth; 145 mixin ImplPixelHeight; 146 */ 147 } 148 149 // mixin template ImplPixelWidth() { 150 // Pixel pixelWidth() { 151 // return this.pixelSize[0]; 152 // } 153 // 154 // Pixel pixelWidth(Pixel pixel) { 155 // this.pixelSize = [pixel, this.pixelSize[1]]; 156 // return pixel; 157 // } 158 // } 159 // 160 // mixin template ImplPixelHeight() { 161 // Pixel pixelHeight() { 162 // return this.pixelSize[1]; 163 // } 164 // 165 // Pixel pixelHeight(Pixel pixel) { 166 // this.pixelSize = [this.pixelSize[0], pixel]; 167 // return pixel; 168 // } 169 // } 170 // 171 mixin template ImplWorldMatrix(string name = "worldMatrix") { 172 import sbylib.graphics.util : DefineCachedValue; 173 import std : hasMember, format; 174 175 private enum hasPos = hasMember!(typeof(this), "pos"); 176 private enum hasRot = hasMember!(typeof(this), "rot"); 177 private enum hasScale = hasMember!(typeof(this), "scale"); 178 private enum expression = format!q{mat4.makeTRS(%s, %s, %s)} 179 (hasPos ? "pos" : "vec3(0)", 180 hasRot ? "rot" : "mat3.identity", 181 hasScale ? "scale" : "vec3(1)"); 182 enum key = 183 (hasPos ? ["pos"] : []) 184 ~ (hasRot ? ["rot"] : []) 185 ~ (hasScale ? ["scale"] : []); 186 187 static if (key.length > 0) { 188 mixin DefineCachedValue!(mat4, "", name, expression, key); 189 } else { 190 mixin(format!q{mat4 %s() { return mat4.identity; }}(name)); 191 } 192 } 193 // 194 // mixin template ImplParentalWorldMatrix(alias parent) { 195 // mixin ImplWorldMatrix!("_worldMatrix") W; 196 // 197 // private enum expression = "parent.worldMatrix * W._worldMatrix"; 198 // private enum key = [parent.stringof] ~ "parent.worldMatrix" ~ W.key; 199 // 200 // mixin DefineCachedValue!(mat4, "@uniform", "worldMatrix", expression, key); 201 // } 202 // 203 mixin template ImplViewMatrix() { 204 import sbylib.graphics.util : DefineCachedValue; 205 import std : hasMember, format; 206 207 private enum hasPos = hasMember!(typeof(this), "pos"); 208 private enum hasRot = hasMember!(typeof(this), "rot"); 209 private enum hasScale = hasMember!(typeof(this), "scale"); 210 private enum expression = format!q{mat4.makeInvertTRS(%s, %s, %s)} 211 (hasPos ? "pos" : "vec3(0)", 212 hasRot ? "rot" : "mat3.identity", 213 hasScale ? "scale" : "vec3(1)"); 214 private enum key = 215 (hasPos ? ["pos"] : []) 216 ~ (hasRot ? ["rot"] : []) 217 ~ (hasScale ? ["scale"] : []); 218 219 static if (key.length > 0) { 220 mixin DefineCachedValue!(mat4, "", "viewMatrix", expression, key); 221 } else { 222 mat4 getViewMatrix() { return mat4.identity; } 223 } 224 } 225 // 226 // /* 227 // void render(Canvas dstCanvas, Texture tex, ivec2 p, ivec2 s) { 228 // import sbylib.graphics.canvas : CanvasBuilder; 229 // import sbylib.wrapper.gl : TextureFilter, BufferBit; 230 // 231 // static Canvas canvas; 232 // if (canvas is null) { 233 // with (CanvasBuilder()) { 234 // color.enable = true; 235 // canvas = build(); 236 // } 237 // } 238 // canvas.color.attach(tex); 239 // 240 // dstCanvas.render(canvas, 241 // 0, 0, tex.width, tex.height, 242 // p.x, p.y, p.x + s.x, p.y + s.y, 243 // TextureFilter.Linear, BufferBit.Color); 244 // } 245 // */ 246 // 247 void lookAt(T)(T entity, vec3 target, vec3 up = vec3(0,1,0)) { 248 entity.rot = mat3.lookAt(normalize(entity.pos - target), up); 249 } 250 // 251 // Texture convert(Image image) { 252 // import sbylib.wrapper.gl : TextureBuilder; 253 // with (TextureBuilder()) { 254 // width = image.width; 255 // height = image.height; 256 // return build(image.dataArray()); 257 // } 258 // } 259 260 mixin template ImplResourceStack() { 261 private void delegate()[] deconstructionStack; 262 263 private void destroyStack() { 264 while (deconstructionStack.empty is false) { 265 deconstructionStack.back()(); 266 deconstructionStack.popBack(); 267 } 268 } 269 270 public T pushResource(T)(T t) { 271 pushReleaseCallback({ t.destroy(); }); 272 return t; 273 } 274 275 public void pushReleaseCallback(void delegate() cb) { 276 deconstructionStack ~= cb; 277 } 278 } 279 280 mixin template Sealable(Type, string name) { 281 import std : replace; 282 283 mixin(q{ 284 private ${type} _${name}; 285 private bool _${name}SealFlag; 286 287 ref ${type} ${name}() { 288 import std : enforce; 289 enforce(_${name}SealFlag is false, "${name} is already sealed."); 290 return _${name}; 291 } 292 293 void seal(alias target : ${name})() { 294 _${name}SealFlag = true; 295 } 296 } 297 .replace("${type}", Type.stringof) 298 .replace("${name}", name)); 299 } 300 301 unittest { 302 import std : assertThrown; 303 304 class A { 305 mixin Sealable!(int, "x"); 306 } 307 308 auto a = new A; 309 a.x = 3; 310 assert(a.x == 3); 311 a.seal!(a.x); 312 assertThrown(a.x = 4); 313 }