A simple .NET Framework to make 2D games quick and easy.
at main 17 kB view raw
1using System.Drawing; 2using System.Numerics; 3using Fjord.Input; 4using Fjord.Scenes; 5using static SDL2.SDL; 6using static SDL2.SDL_gfx; 7using static SDL2.SDL_ttf; 8 9namespace Fjord.Graphics; 10 11public enum Flip 12{ 13 None, 14 Horizontal, 15 Vertical, 16 Both 17} 18 19public enum Center 20{ 21 TopLeft, 22 TopMiddle, 23 TopRight, 24 MiddleLeft, 25 Middle, 26 MiddleRight, 27 BottomLeft, 28 BottomMiddle, 29 BottomRight 30} 31 32public static class Draw 33{ 34 internal static string? CurrentSceneID = null; 35 36 internal static List<IDrawInstruction> drawBuffer = new(); 37 38 internal static Dictionary<string, IntPtr> textureCache = new(); 39 40 public static void Box(Vector4 rect, Vector4 color, int depth=0) { 41 new Rectangle(rect) 42 { 43 color = color, 44 depth = depth, 45 fill = true 46 }.Render(); 47 } 48 49 public static void Rectangle(Vector4 rect, Vector4 color, int depth=0) { 50 new Rectangle(rect) { 51 color = color, 52 depth = depth, 53 fill = false 54 }.Render(); 55 } 56 57 public static void RoundedBox(Vector4 rect, Vector4 color, float radius, int depth=0) { 58 new Rectangle(rect) 59 { 60 color = color, 61 depth = depth, 62 fill = true, 63 borderRadius = radius 64 }.Render(); 65 } 66 67 public static void RoundedRectangle(Vector4 rect, Vector4 color, float radius, int depth=0) { 68 new Rectangle(rect) { 69 color = color, 70 depth = depth, 71 fill = false, 72 borderRadius = radius 73 }.Render(); 74 } 75 76 public static void FillCircle(Vector2 position, float radius, Vector4 color, int depth=0) { 77 new Circle(position, radius) { 78 color = color, 79 depth = depth, 80 fill = true 81 }.Render(); 82 } 83 84 public static void Circle(Vector2 position, float radius, Vector4 color, int depth=0) { 85 new Circle(position, radius) { 86 color = color, 87 depth = depth, 88 fill = false 89 }.Render(); 90 } 91 92 public static void TextureExt(Vector2 position, IntPtr texture, float angle, int depth=0) 93 { 94 new Texture(texture) 95 { 96 position = position, 97 depth = depth, 98 angle = angle 99 }.Render(); 100 } 101 102 public static void Texture(Vector2 position, IntPtr texture, int depth=0) 103 { 104 new Texture(texture) 105 { 106 position = position, 107 depth = depth, 108 angle = 0 109 }.Render(); 110 } 111 112 public static void Geometry(List<SDL_Vertex> verts, int depth=0) 113 { 114 new Geometry() 115 { 116 verticies = verts, 117 depth = depth 118 }.Render(); 119 } 120 121 public static void Text(Vector2 position, string font, string text, int size, Vector4 color, int depth = 0) 122 { 123 new Text(font, text) 124 { 125 position = position, 126 size = size, 127 color = color, 128 depth = depth 129 }.Render(); 130 } 131 132 public static void Line(Vector2 point1, Vector2 point2, Vector4 color, int depth = 0) 133 { 134 new Line(point1, point2) 135 { 136 color = color, 137 depth = depth 138 }.Render(); 139 } 140 141 internal static void RectangleDirect(Rectangle rect) { 142 SDL_Color col = new SDL_Color() { 143 r = (byte)rect.color.X, 144 g = (byte)rect.color.Y, 145 b = (byte)rect.color.Z, 146 a = (byte)rect.color.W 147 }; 148 SDL_Rect SDLRect = new () { 149 x = (int)rect.rect.X, 150 y = (int)rect.rect.Y, 151 w = (int)rect.rect.Z, 152 h = (int)rect.rect.W 153 }; 154 155 Helpers.SDL_SetRenderDrawColor(Game.SDLRenderer, col); 156 if(rect.borderRadius != null) 157 { 158 if(rect.fill) { 159 if(rect.borderRadius > Math.Min(rect.rect.Z, rect.rect.W) / 2) { 160 rect.borderRadius = Math.Min(rect.rect.Z, rect.rect.W) / 2; 161 } 162 163 SDL_Rect rect1 = new () { 164 x = (int)rect.rect.X, 165 y = (int)(rect.rect.Y + rect.borderRadius), 166 w = (int)rect.rect.Z, 167 h = (int)(rect.rect.W - rect.borderRadius * 2) 168 }; 169 SDL_RenderFillRect(Game.SDLRenderer, ref rect1); 170 171 SDL_Rect rect2 = new () { 172 x = (int)(rect.rect.X + rect.borderRadius), 173 y = (int)rect.rect.Y, 174 w = (int)(rect.rect.Z - rect.borderRadius * 2), 175 h = (int)rect.rect.W 176 }; 177 SDL_RenderFillRect(Game.SDLRenderer, ref rect2); 178 179 //Top Left 180 CircleDirect( 181 new Circle(new(rect.rect.X + (float)rect.borderRadius, rect.rect.Y + (float)rect.borderRadius), (float)rect.borderRadius) 182 .Fill(true) 183 .Color(rect.color) 184 ); 185 186 //Top Right 187 CircleDirect( 188 new Circle(new(rect.rect.X + rect.rect.Z - (float)rect.borderRadius - 1, rect.rect.Y + (float)rect.borderRadius), (float)rect.borderRadius) 189 .Fill(true) 190 .Color(rect.color) 191 ); 192 193 //Bottom Left 194 CircleDirect( 195 new Circle(new(rect.rect.X + (float)rect.borderRadius + 1, rect.rect.Y + rect.rect.W - (float)rect.borderRadius), (float)rect.borderRadius) 196 .Fill(true) 197 .Color(rect.color) 198 ); 199 200 // Bottom Right 201 CircleDirect( 202 new Circle(new(rect.rect.X + rect.rect.Z - (float)rect.borderRadius - 1, rect.rect.Y + rect.rect.W - (float)rect.borderRadius - 1), (float)rect.borderRadius) 203 .Fill(true) 204 .Color(rect.color) 205 ); 206 } else 207 roundedRectangleRGBA(Game.SDLRenderer, (short)rect.rect.X, (short)rect.rect.Y, (short)(rect.rect.X + rect.rect.Z), (short)(rect.rect.Y + rect.rect.W), (short)rect.borderRadius, col.r, col.g, col.b, col.a); 208 } else { 209 if(rect.fill) 210 SDL_RenderFillRect(Game.SDLRenderer, ref SDLRect); 211 else 212 SDL_RenderDrawRect(Game.SDLRenderer, ref SDLRect); 213 } 214 } 215 216 internal static void CircleDirect(Circle circle) { 217 if(circle.fill) { 218 Helpers.SDL_SetRenderDrawColor(Game.SDLRenderer, Helpers.V4ToColor(circle.color)); 219 int x = (int)circle.radius; 220 int y = 0; 221 int err = 0; 222 223 int x0 = (int)circle.position.X; 224 int y0 = (int)circle.position.Y; 225 226 while (x >= y) 227 { 228 SDL_RenderDrawLine(Game.SDLRenderer, x0 + x, y0 + y, x0 - x, y0 + y); 229 SDL_RenderDrawLine(Game.SDLRenderer, x0 - y, y0 + x, x0 - y, y0 - x); 230 231 SDL_RenderDrawLine(Game.SDLRenderer, x0 + x, y0 - y, x0 - x, y0 - y); 232 SDL_RenderDrawLine(Game.SDLRenderer, x0 + y, y0 - x, x0 + y, y0 + x); 233 234 if (err <= 0) 235 { 236 y += 1; 237 err += 2*y + 1; 238 } 239 240 if (err > 0) 241 { 242 x -= 1; 243 err -= 2*x + 1; 244 } 245 } 246 } else 247 if(aacircleRGBA(Game.SDLRenderer, (short)circle.position.X, (short)circle.position.Y, (short)circle.radius, (byte)circle.color.X, (byte)circle.color.Y, (byte)circle.color.Z, (byte)circle.color.W) == -1) { 248 Debug.Log(LogLevel.Error, "Failed to draw aacircleRGBA"); 249 } 250 SDL_SetRenderDrawColor(Game.SDLRenderer, 0, 0, 0, 255); 251 } 252 253 internal static void TextureDirect(Texture texture) 254 { 255 SDL_Rect rect = new() 256 { 257 x = (int)texture.position.X, 258 y = (int)texture.position.Y, 259 w = (int)(texture.textureSize.X * texture.sizeMultiplier.X), 260 h = (int)(texture.textureSize.Y * texture.sizeMultiplier.Y) 261 }; 262 263 SDL_RendererFlip tmpFlip = texture.flip == Flip.Horizontal ? SDL_RendererFlip.SDL_FLIP_HORIZONTAL : texture.flip == Flip.Vertical ? SDL_RendererFlip.SDL_FLIP_VERTICAL : texture.flip == Flip.Both ? SDL_RendererFlip.SDL_FLIP_HORIZONTAL | SDL_RendererFlip.SDL_FLIP_VERTICAL : SDL_RendererFlip.SDL_FLIP_NONE; 264 265 Vector2 center = new(); 266 Vector2 textureSize = texture.textureSize; 267 268 if(!texture.isCustomCenter) 269 { 270 switch(texture.center) 271 { 272 case Graphics.Center.TopLeft: { 273 center = new(0, 0); 274 } break; 275 case Graphics.Center.TopMiddle: { 276 center = new(rect.w / 2, 0); 277 } break; 278 case Graphics.Center.TopRight: { 279 center = new(rect.h, 0); 280 } break; 281 282 case Graphics.Center.MiddleLeft: { 283 center = new(0, rect.h / 2); 284 } break; 285 case Graphics.Center.Middle: { 286 center = new(rect.w / 2, rect.h / 2); 287 } break; 288 case Graphics.Center.MiddleRight: { 289 center = new(rect.w, rect.h / 2); 290 } break; 291 292 case Graphics.Center.BottomLeft: { 293 center = new(0, rect.h); 294 } break; 295 case Graphics.Center.BottomMiddle: { 296 center = new(rect.w / 2, rect.h); 297 } break; 298 case Graphics.Center.BottomRight: { 299 center = new(rect.w, rect.h); 300 } break; 301 } 302 } else 303 { 304 center = new(texture.customCenter.X, texture.customCenter.Y); 305 } 306 307 SDL_Point sdlcenter = new() 308 { 309 x = (int)center.X, 310 y = (int)center.Y 311 }; 312 rect.x -= sdlcenter.x; 313 rect.y -= sdlcenter.y; 314 315 SDL_SetTextureAlphaMod(texture.SDLTexture, (byte)texture.alpha); 316 317 if(texture.srcTextureOffset is not null) 318 { 319 SDL_Rect srcRect = new() 320 { 321 x = (int)texture.srcTextureOffset.Value.X, 322 y = (int)texture.srcTextureOffset.Value.Y, 323 w = (int)texture.srcTextureOffset.Value.Z, 324 h = (int)texture.srcTextureOffset.Value.W 325 }; 326 SDL_RenderCopyEx(Game.SDLRenderer, texture.SDLTexture, ref srcRect, ref rect, texture.angle, ref sdlcenter, tmpFlip); 327 } else { 328 SDL_RenderCopyEx(Game.SDLRenderer, texture.SDLTexture, IntPtr.Zero, ref rect, texture.angle, ref sdlcenter, tmpFlip); 329 } 330 331 SDL_SetTextureAlphaMod(texture.SDLTexture, 255); 332 333 if(texture.destroy) 334 { 335 SDL_DestroyTexture(texture.SDLTexture); 336 SDL_FreeSurface(texture.SDLSurface); 337 texture = null; 338 } 339 } 340 341 internal static void GeometryDirect(Geometry geometry) 342 { 343 SDL_RenderGeometry(Game.SDLRenderer, IntPtr.Zero, geometry.verticies.ToArray(), geometry.verticies.Count, IntPtr.Zero, 0); 344 } 345 346 internal static void TextDirect(Text text) 347 { 348 if(!Font.Fonts.ContainsKey(text.font + text.size.ToString())) 349 { 350 Font.Fonts.Add(text.font + text.size.ToString(), TTF_OpenFont(text.font, text.size)); 351 } 352 353 SDL_Color col = Helpers.V4ToColor(text.color); 354 string CacheKey = text.font + text.value + text.size + col.r + col.g + col.b + col.a; 355 if (!Font.FontCache.ContainsKey(CacheKey)) 356 { 357 IntPtr surface = TTF_RenderText_Blended(Font.Fonts[text.font + text.size.ToString()], text.value, col); 358 IntPtr texture = SDL_CreateTextureFromSurface(Game.SDLRenderer, surface); 359 Font.FontCache.Add(CacheKey, texture); 360 SDL_FreeSurface(surface); 361 } 362 363 SDL_QueryTexture(Font.FontCache[CacheKey], out uint format, out int access, out int textureW, out int textureH); 364 365 SDL_Rect rect = new() 366 { 367 x = (int)text.position.X, 368 y = (int)text.position.Y, 369 w = (int)(textureW), 370 h = (int)(textureH) 371 372 }; 373 374 SDL_RenderCopy(Game.SDLRenderer, Font.FontCache[CacheKey], IntPtr.Zero, ref rect); 375 } 376 377 internal static void LineDirect(Line line) 378 { 379 SDL_SetRenderDrawColor(Game.SDLRenderer, (byte)line.color.X, (byte)line.color.Y, (byte)line.color.Z, (byte)line.color.W); 380 SDL_RenderDrawLine(Game.SDLRenderer, (int)line.point1.X, (int)line.point1.Y, (int)line.point2.X, (int)line.point2.Y); 381 } 382 383 internal static void DrawDrawBuffer(List<IDrawInstruction> drawBufferLocal, string? sceneId) { 384 385 drawBufferLocal.Sort((a, b) => a.depth.CompareTo(b.depth)); 386 foreach(IDrawInstruction drawInsObj in drawBufferLocal) { 387 if(drawInsObj is Rectangle rect) { 388 if(sceneId != null) 389 { 390 rect.rect.X -= SceneHandler.Get(sceneId).Camera.Offset.X; 391 rect.rect.Y -= SceneHandler.Get(sceneId).Camera.Offset.Y; 392 } 393 Draw.RectangleDirect(rect); 394 } else if(drawInsObj is Circle circle) { 395 if(circle.hoverAnimation != null) 396 { 397 if(circle.hoverAnimation.xDriver != null) 398 circle.position.X *= (circle.hoverAnimation.xDriver(circle.hoverAnimation.progress) + 1); 399 if(circle.hoverAnimation.yDriver != null) 400 circle.position.Y *= (circle.hoverAnimation.yDriver(circle.hoverAnimation.progress) + 1); 401 if(circle.hoverAnimation.radiusDriver != null) 402 circle.radius *= (circle.hoverAnimation.radiusDriver(circle.hoverAnimation.progress) + 1); 403 if(circle.hoverAnimation.colorDriver != null) 404 circle.color = Helpers.Lerp(circle.color, circle.hoverAnimation.colorGoal, circle.hoverAnimation.progress); 405 // drawInsClone.color *= (drawIns.hoverAnimation.colorDriver(drawIns.hoverAnimation.progress) + 1); 406 if(sceneId == null ? Helpers.PointDistance(GlobalMouse.Position, circle.position) < circle.radius : Helpers.PointDistance(SceneHandler.Scenes[sceneId].Mouse.Position, circle.position) < circle.radius) { 407 if(circle.hoverAnimation.progress < 1) 408 circle.hoverAnimation.progress += circle.hoverAnimation.speed * (float)Game.DeltaTime; 409 } else if(circle.hoverAnimation.progress > 0) { 410 circle.hoverAnimation.progress -= circle.hoverAnimation.speed * (float)Game.DeltaTime; 411 if(circle.hoverAnimation.progress < 0) 412 circle.hoverAnimation.progress = 0; 413 } 414 415 } 416 if(sceneId != null) 417 circle.position -= SceneHandler.Get(sceneId).Camera.Offset; 418 Draw.CircleDirect(circle); 419 } else if(drawInsObj is Texture texture) 420 { 421 if(sceneId != null) 422 texture.position -= SceneHandler.Get(sceneId).Camera.Offset; 423 Draw.TextureDirect(texture); 424 } else if(drawInsObj is Geometry geo) 425 { 426 if(sceneId != null) 427 { 428 for(var i = 0; i < geo.verticies.Count; i++) 429 { 430 geo.verticies[i] = new SDL_Vertex() { 431 position = new SDL_FPoint() { 432 x = geo.verticies[i].position.x - SceneHandler.Get(sceneId).Camera.Offset.X, 433 y = geo.verticies[i].position.y - SceneHandler.Get(sceneId).Camera.Offset.Y, 434 }, 435 color = geo.verticies[i].color, 436 tex_coord = geo.verticies[i].tex_coord 437 }; 438 } 439 } 440 441 Draw.GeometryDirect(geo); 442 } else if(drawInsObj is Text text) 443 { 444 if(sceneId != null) 445 text.position -= SceneHandler.Get(sceneId).Camera.Offset; 446 Draw.TextDirect(text); 447 } else if(drawInsObj is Line line) 448 { 449 if(sceneId != null) 450 { 451 line.point1 -= SceneHandler.Get(sceneId).Camera.Offset; 452 line.point2 -= SceneHandler.Get(sceneId).Camera.Offset; 453 } 454 455 Draw.LineDirect(line); 456 } 457 } 458 } 459}