this repo has no description
at develop 724 lines 22 kB view raw
1// Copyright (c) 2021 ezequias2d <ezequiasmoises@gmail.com> and the Peridot contributors 2// This code is licensed under MIT license (see LICENSE for details) 3 4using System.Drawing; 5using System.Numerics; 6 7namespace Peridot; 8 9/// <summary> 10/// A class for drawing sprites in one or more optimized batches. 11/// </summary> 12/// <typeparam name="TImage">The image type to renderer.</typeparam> 13public abstract class SpriteBatch<TImage> : ISpriteBatch<TImage> where TImage : notnull 14{ 15 /// <summary> 16 /// The batcher with all entities to renderer. 17 /// </summary> 18 protected readonly Batcher<TImage> m_batcher; 19 20 protected readonly Image m_whiteImage; 21 22 private bool m_beginCalled; 23 24 /// <summary> 25 /// Creates a new <see cref="SpriteBatch{TImage}"/>. 26 /// </summary> 27 public SpriteBatch(Image whiteImage) 28 { 29 m_batcher = new(); 30 m_whiteImage = whiteImage; 31 m_beginCalled = false; 32 IsDisposed = false; 33 ResetScissor(); 34 } 35 36 /// <summary> 37 /// Deconstructor of <see cref="SpriteBatch{TImage}"/>. 38 /// </summary> 39 ~SpriteBatch() 40 { 41 CoreDispose(false); 42 } 43 44 /// <summary> 45 /// The view matrix to use to renderer. 46 /// </summary> 47 public Matrix4x4 ViewMatrix { get; set; } 48 49 /// <inheritdoc/> 50 public bool IsDisposed { get; protected set; } 51 52 /// <inheritdoc/> 53 public RectangleF Scissor { get; set; } 54 55 /// <summary> 56 /// Begins the sprite branch. 57 /// </summary> 58 /// <exception cref="InvalidOperationException">Thrown if <see cref="Begin"/> is called next time without previous <see cref="End"/>.</exception> 59 public void Begin() 60 { 61 if (m_beginCalled) 62 throw new InvalidOperationException("Begin cannot be called again until End has been successfully called."); 63 64 ViewMatrix = Matrix4x4.Identity; 65 m_beginCalled = true; 66 m_batcher.Clear(); 67 } 68 69 /// <summary> 70 /// Flushes all batched text and sprites to the screen. 71 /// </summary> 72 /// <exception cref="InvalidOperationException">This command should be called after <see cref="Begin"/> and drawing commands.</exception> 73 public void End() 74 { 75 if (!m_beginCalled) 76 throw new InvalidOperationException("Begin must be called before calling End."); 77 78 m_beginCalled = false; 79 } 80 81 /// <inheritdoc/> 82 public void Draw(Image image, RectangleF destinationRectangle, RectangleF sourceRectangle, Color color, float rotation, Vector2 origin, SpriteOptions options, float layerDepth) => 83 Draw(GetHandle(image), destinationRectangle, sourceRectangle, color, rotation, origin, options, layerDepth); 84 85 /// <inheritdoc/> 86 public void Draw(Image image, Vector2 position, RectangleF sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteOptions options, float layerDepth) => 87 Draw(GetHandle(image), position, sourceRectangle, color, rotation, origin, scale, options, layerDepth); 88 89 /// <inheritdoc/> 90 public void Draw(TImage image, RectangleF destinationRectangle, RectangleF sourceRectangle, Color color, float rotation, Vector2 origin, SpriteOptions options, float layerDepth) 91 { 92 CheckValid(image); 93 ref var item = ref m_batcher.Add(image); 94 var size = GetSize(image); 95 var vsize = new Vector2(size.Width, size.Height); 96 97 item = new(vsize, destinationRectangle, sourceRectangle, color, rotation, origin, layerDepth, Transform(Scissor, ViewMatrix), options); 98 } 99 100 /// <inheritdoc/> 101 public void Draw(TImage image, Vector2 position, RectangleF sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteOptions options, float layerDepth) 102 { 103 CheckValid(image); 104 ref var item = ref m_batcher.Add(image); 105 var size = GetSize(image); 106 var vsize = new Vector2(size.Width, size.Height); 107 108 item = new(vsize, position, sourceRectangle, color, rotation, origin, scale, layerDepth, Transform(Scissor, ViewMatrix), options); 109 } 110 111 /// <inheritdoc/> 112 public void Dispose() 113 { 114 CoreDispose(true); 115 GC.SuppressFinalize(this); 116 } 117 118 private void CoreDispose(bool disposing) 119 { 120 if (IsDisposed) 121 return; 122 IsDisposed = true; 123 124 Dispose(disposing); 125 } 126 127 /// <summary> 128 /// Disposes resources. 129 /// </summary> 130 /// <param name="disposing">If called by <see cref="Dispose()"/></param> 131 protected abstract void Dispose(bool disposing); 132 133 private void CheckValid(TImage image) 134 { 135 if (image == null) 136 throw new ArgumentNullException(nameof(image)); 137 if (!m_beginCalled) 138 throw new InvalidOperationException("Draw was called, but Begin has not yet been called. Begin must be called successfully before you can call Draw."); 139 } 140 141 /// <inheritdoc/> 142 public void ResetScissor() 143 { 144 const float v = 1 << 23; 145 const float s = -(1 << 22); 146 Scissor = new RectangleF(s, s, v, v); 147 } 148 149 /// <inheritdoc/> 150 public void IntersectScissor(RectangleF clip) 151 { 152 var scissor = Scissor; 153 scissor.Intersect(clip); 154 Scissor = scissor; 155 } 156 157 private static RectangleF Transform(RectangleF rect, Matrix4x4 matrix) 158 { 159 var pos = Vector4.Transform(new Vector4(rect.X, rect.Y, 0, 1), matrix); 160 var size = Vector4.Transform(new Vector4(rect.X + rect.Width, rect.Y + rect.Height, 0, 1), matrix); 161 return new(pos.X, pos.Y, size.X - pos.X, size.Y - pos.Y); 162 } 163 164 #region ISpriteBatch 165 /// <inheritdoc/> 166 public void Draw(Image image, 167 RectangleF destinationRectangle, 168 RectangleF sourceRectangle, 169 Color color, 170 float rotation, 171 Vector2 origin, 172 float layerDepth) 173 { 174 Draw(image, destinationRectangle, sourceRectangle, color, rotation, origin, SpriteOptions.None, layerDepth); 175 } 176 177 /// <inheritdoc/> 178 public void Draw(Image image, 179 Vector2 position, 180 RectangleF sourceRectangle, 181 Color color, 182 float rotation, 183 Vector2 origin, 184 Vector2 scale, 185 float layerDepth) 186 { 187 Draw(image, position, sourceRectangle, color, rotation, origin, scale, SpriteOptions.None, layerDepth); 188 } 189 190 /// <inheritdoc/> 191 public void Draw(Image image, 192 RectangleF destinationRectangle, 193 RectangleF? sourceRectangle, 194 Color color, 195 float rotation, 196 Vector2 origin, 197 SpriteOptions options, 198 float layerDepth) 199 { 200 var srcRect = sourceRectangle ?? new(0, 0, image.Width, image.Height); 201 Draw(image, destinationRectangle, srcRect, color, rotation, origin, options, layerDepth); 202 } 203 204 /// <inheritdoc/> 205 public void Draw(Image image, 206 RectangleF destinationRectangle, 207 RectangleF? sourceRectangle, 208 Color color, 209 float rotation, 210 Vector2 origin, 211 float layerDepth) 212 { 213 Draw(image, destinationRectangle, sourceRectangle, color, rotation, origin, SpriteOptions.None, layerDepth); 214 } 215 216 /// <inheritdoc/> 217 public void Draw(Image image, 218 Vector2 position, 219 RectangleF? sourceRectangle, 220 Color color, 221 float rotation, 222 Vector2 origin, 223 Vector2 scale, 224 SpriteOptions options, 225 float layerDepth) 226 { 227 Draw(image: image, 228 position: position, 229 sourceRectangle: sourceRectangle ?? new() 230 { 231 X = 0, 232 Y = 0, 233 Width = image.Width, 234 Height = image.Height, 235 }, 236 color: color, 237 rotation: rotation, 238 origin: origin, 239 scale: scale, 240 options: options, 241 layerDepth: layerDepth); 242 } 243 244 /// <inheritdoc/> 245 public void Draw(Image image, 246 Vector2 position, 247 RectangleF? sourceRectangle, 248 Color color, 249 float rotation, 250 Vector2 origin, 251 Vector2 scale, 252 float layerDepth) 253 { 254 Draw(image: image, 255 position: position, 256 sourceRectangle: sourceRectangle, 257 color: color, 258 rotation: rotation, 259 origin: origin, 260 scale: scale, 261 options: SpriteOptions.None, 262 layerDepth: layerDepth); 263 } 264 265 /// <inheritdoc/> 266 public void Draw(Image image, 267 Vector2 position, 268 RectangleF? sourceRectangle, 269 Color color, 270 float rotation, 271 Vector2 origin, 272 float scale, 273 SpriteOptions options, 274 float layerDepth) 275 { 276 Draw(image: image, 277 position: position, 278 sourceRectangle: sourceRectangle, 279 color: color, 280 rotation: rotation, 281 origin: origin, 282 scale: new Vector2(scale), 283 options: options, 284 layerDepth: layerDepth); 285 } 286 287 /// <inheritdoc/> 288 public void Draw(Image image, 289 Vector2 position, 290 RectangleF? sourceRectangle, 291 Color color, 292 float rotation, 293 Vector2 origin, 294 float scale, 295 float layerDepth) 296 { 297 Draw(image: image, 298 position: position, 299 sourceRectangle: sourceRectangle, 300 color: color, 301 rotation: rotation, 302 origin: origin, 303 scale: scale, 304 options: SpriteOptions.None, 305 layerDepth: layerDepth); 306 } 307 308 /// <inheritdoc/> 309 public void Draw(Image image, 310 Vector2 position, 311 RectangleF? sourceRectangle, 312 Color color, 313 SpriteOptions options, 314 float layerDepth) 315 { 316 Draw(image: image, 317 position: position, 318 sourceRectangle: sourceRectangle, 319 color: color, 320 rotation: 0f, 321 origin: default, 322 scale: 1f, 323 options: options, 324 layerDepth: layerDepth); 325 } 326 327 /// <inheritdoc/> 328 public void Draw(Image image, 329 Vector2 position, 330 RectangleF? sourceRectangle, 331 Color color, 332 float layerDepth) 333 { 334 Draw(image: image, 335 position: position, 336 sourceRectangle: sourceRectangle, 337 color: color, 338 options: SpriteOptions.None, 339 layerDepth: layerDepth); 340 } 341 342 /// <inheritdoc/> 343 public void Draw(Image image, 344 RectangleF destinationRectangle, 345 RectangleF? sourceRectangle, 346 Color color, 347 SpriteOptions options, 348 float layerDepth) 349 { 350 Draw(image: image, 351 destinationRectangle: destinationRectangle, 352 sourceRectangle: sourceRectangle, 353 color: color, 354 rotation: 0, 355 origin: default, 356 options: options, 357 layerDepth: layerDepth); 358 } 359 360 /// <inheritdoc/> 361 public void Draw(Image image, 362 RectangleF destinationRectangle, 363 RectangleF? sourceRectangle, 364 Color color, 365 float layerDepth) 366 { 367 Draw(image: image, 368 destinationRectangle: destinationRectangle, 369 sourceRectangle: sourceRectangle, 370 color: color, 371 options: SpriteOptions.None, 372 layerDepth: layerDepth); 373 } 374 375 /// <inheritdoc/> 376 public void Draw(Image image, 377 Vector2 position, 378 Color color, 379 SpriteOptions options, 380 float layerDepth) 381 { 382 Draw(image: image, 383 position: position, 384 sourceRectangle: new Rectangle(default, image.Size), 385 color: color, 386 rotation: 0, 387 origin: default, 388 scale: Vector2.One, 389 options: options, 390 layerDepth: layerDepth); 391 } 392 393 /// <inheritdoc/> 394 public void Draw(Image image, 395 Vector2 position, 396 Color color, 397 float layerDepth) 398 { 399 Draw(image: image, 400 position: position, 401 color: color, 402 options: SpriteOptions.None, 403 layerDepth: layerDepth); 404 } 405 406 /// <inheritdoc/> 407 public void Draw(Image image, 408 RectangleF destinationRectangle, 409 Color color, 410 SpriteOptions options, 411 float layerDepth) 412 { 413 Draw(image: image, 414 destinationRectangle: destinationRectangle, 415 sourceRectangle: new Rectangle(default, image.Size), 416 color: color, 417 rotation: 0, 418 origin: default, 419 options: options, 420 layerDepth: layerDepth); 421 } 422 423 /// <inheritdoc/> 424 public void Draw(Image image, 425 RectangleF destinationRectangle, 426 Color color, 427 float layerDepth) 428 { 429 Draw(image: image, 430 destinationRectangle: destinationRectangle, 431 color: color, 432 options: SpriteOptions.None, 433 layerDepth: layerDepth); 434 } 435 #endregion 436 437 #region ISpriteBatch<TImage> 438 439 /// <inheritdoc/> 440 public void Draw(TImage image, 441 RectangleF destinationRectangle, 442 RectangleF sourceRectangle, 443 Color color, 444 float rotation, 445 Vector2 origin, 446 float layerDepth) 447 { 448 Draw(image: image, 449 destinationRectangle: destinationRectangle, 450 sourceRectangle: sourceRectangle, 451 color: color, 452 rotation: rotation, 453 origin: origin, 454 options: SpriteOptions.None, 455 layerDepth: layerDepth); 456 } 457 458 /// <inheritdoc/> 459 public void Draw(TImage image, 460 Vector2 position, 461 RectangleF sourceRectangle, 462 Color color, 463 float rotation, 464 Vector2 origin, 465 Vector2 scale, 466 float layerDepth) 467 { 468 Draw(image: image, 469 position: position, 470 sourceRectangle: sourceRectangle, 471 color: color, 472 rotation: rotation, 473 origin: origin, 474 scale: scale, 475 options: SpriteOptions.None, 476 layerDepth: layerDepth); 477 } 478 479 /// <inheritdoc/> 480 public void Draw(TImage image, 481 RectangleF destinationRectangle, 482 RectangleF? sourceRectangle, 483 Color color, 484 float rotation, 485 Vector2 origin, 486 SpriteOptions options, 487 float layerDepth) 488 { 489 var size = GetSize(image); 490 Draw(image: image, 491 destinationRectangle: destinationRectangle, 492 sourceRectangle: sourceRectangle ?? new() 493 { 494 X = 0, 495 Y = 0, 496 Width = size.Width, 497 Height = size.Height, 498 }, 499 color: color, 500 rotation: rotation, 501 origin: origin, 502 options: options, 503 layerDepth: layerDepth); 504 } 505 506 /// <inheritdoc/> 507 public void Draw(TImage image, 508 RectangleF destinationRectangle, 509 RectangleF? sourceRectangle, 510 Color color, 511 float rotation, 512 Vector2 origin, 513 float layerDepth) 514 { 515 Draw(image, destinationRectangle, sourceRectangle, color, rotation, origin, SpriteOptions.None, layerDepth); 516 } 517 518 /// <inheritdoc/> 519 public void Draw(TImage image, 520 Vector2 position, 521 RectangleF? sourceRectangle, 522 Color color, 523 float rotation, 524 Vector2 origin, 525 Vector2 scale, 526 SpriteOptions options, 527 float layerDepth) 528 { 529 var size = GetSize(image); 530 Draw(image: image, 531 position: position, 532 sourceRectangle: sourceRectangle ?? new() 533 { 534 X = 0, 535 Y = 0, 536 Width = size.Width, 537 Height = size.Height, 538 }, 539 color: color, 540 rotation: rotation, 541 origin: origin, 542 scale: scale, 543 options: options, 544 layerDepth: layerDepth); 545 } 546 547 /// <inheritdoc/> 548 public void Draw(TImage image, 549 Vector2 position, 550 RectangleF? sourceRectangle, 551 Color color, 552 float rotation, 553 Vector2 origin, 554 Vector2 scale, 555 float layerDepth) 556 { 557 Draw(image: image, 558 position: position, 559 sourceRectangle: sourceRectangle, 560 color: color, 561 rotation: rotation, 562 origin: origin, 563 scale: scale, 564 options: SpriteOptions.None, 565 layerDepth: layerDepth); 566 } 567 568 /// <inheritdoc/> 569 public void Draw(TImage image, 570 Vector2 position, 571 RectangleF? sourceRectangle, 572 Color color, 573 float rotation, 574 Vector2 origin, 575 float scale, 576 SpriteOptions options, 577 float layerDepth) 578 { 579 Draw(image, position, sourceRectangle, color, rotation, origin, new Vector2(scale), options, layerDepth); 580 } 581 582 /// <inheritdoc/> 583 public void Draw(TImage image, 584 Vector2 position, 585 RectangleF? sourceRectangle, 586 Color color, 587 float rotation, 588 Vector2 origin, 589 float scale, 590 float layerDepth) 591 { 592 Draw(image, position, sourceRectangle, color, rotation, origin, scale, SpriteOptions.None, layerDepth); 593 } 594 595 /// <inheritdoc/> 596 public void Draw(TImage image, 597 Vector2 position, 598 RectangleF? sourceRectangle, 599 Color color, 600 SpriteOptions options, 601 float layerDepth) 602 { 603 Draw(image, position, sourceRectangle, color, 0f, default, 0f, options, layerDepth); 604 } 605 606 /// <inheritdoc/> 607 public void Draw(TImage image, 608 Vector2 position, 609 RectangleF? sourceRectangle, 610 Color color, 611 float layerDepth) 612 { 613 Draw(image, position, sourceRectangle, color, SpriteOptions.None, layerDepth); 614 } 615 616 /// <inheritdoc/> 617 public void Draw(TImage image, 618 RectangleF destinationRectangle, 619 RectangleF? sourceRectangle, 620 Color color, 621 SpriteOptions options, 622 float layerDepth) 623 { 624 Draw(image, destinationRectangle, sourceRectangle, color, 0, default, options, layerDepth); 625 } 626 627 /// <inheritdoc/> 628 public void Draw(TImage image, 629 RectangleF destinationRectangle, 630 RectangleF? sourceRectangle, 631 Color color, 632 float layerDepth) 633 { 634 Draw(image, destinationRectangle, sourceRectangle, color, SpriteOptions.None, layerDepth); 635 } 636 637 /// <inheritdoc/> 638 public void Draw(TImage image, 639 Vector2 position, 640 Color color, 641 SpriteOptions options, 642 float layerDepth) 643 { 644 Draw(image, position, new Rectangle(default, GetSize(image)), color, 0, default, default, options, layerDepth); 645 } 646 647 /// <inheritdoc/> 648 public void Draw(TImage image, 649 Vector2 position, 650 Color color, 651 float layerDepth) 652 { 653 Draw(image, position, new Rectangle(default, GetSize(image)), color, 0, default, default, SpriteOptions.None, layerDepth); 654 } 655 656 /// <inheritdoc/> 657 public void Draw(TImage image, 658 RectangleF destinationRectangle, 659 Color color, 660 SpriteOptions options, 661 float layerDepth) 662 { 663 Draw(image, destinationRectangle, new Rectangle(default, GetSize(image)), color, 0, default, options, layerDepth); 664 } 665 666 /// <inheritdoc/> 667 public void Draw(TImage image, 668 RectangleF destinationRectangle, 669 Color color, 670 float layerDepth) 671 { 672 Draw(image, destinationRectangle, color, SpriteOptions.None, layerDepth); 673 } 674 675 /// <inheritdoc/> 676 public void DrawRect(RectangleF rectangle, Color color, float rotation, Vector2 origin, float layerDepth) 677 { 678 Draw(m_whiteImage, rectangle, new RectangleF(0, 0, 1, 1), color, rotation, origin, layerDepth); 679 } 680 681 /// <inheritdoc/> 682 public void DrawRect(RectangleF rectangle, Color color, float layerDepth) 683 { 684 Draw(m_whiteImage, rectangle, new RectangleF(0, 0, 1, 1), color, layerDepth); 685 } 686 687 /// <inheritdoc/> 688 public void DrawDot(Vector2 position, float size, Color color, float layerDepth) 689 { 690 Draw(m_whiteImage, new RectangleF(position.X, position.Y, size, size), new RectangleF(0, 0, 1, 1), color, layerDepth); 691 } 692 693 /// <inheritdoc/> 694 public void DrawSegment(Vector2 a, Vector2 b, Color color, float thickness, float layerDepth) 695 { 696 var diff = b - a; 697 var length = MathF.Sqrt(Vector2.Dot(diff, diff)); 698 var angle = MathF.Atan2(diff.Y, diff.X); 699 DrawSegment(a, length, color, angle, thickness, layerDepth); 700 } 701 702 /// <inheritdoc/> 703 public void DrawSegment(Vector2 start, float length, Color color, float angle, float thickness, float layerDepth) 704 { 705 var rect = new RectangleF(start.X, start.Y, length, thickness); 706 DrawRect(rect, color, angle, new Vector2(0, 0.5f), layerDepth); 707 } 708 709 #endregion 710 711 /// <summary> 712 /// Gets size of a image. 713 /// </summary> 714 /// <param name="image">Image.</param> 715 /// <returns>Image Size.</returns> 716 protected abstract Size GetSize(TImage image); 717 718 /// <summary> 719 /// Gets TImage. 720 /// </summary> 721 /// <param name="image"></param> 722 /// <returns></returns> 723 protected abstract TImage GetHandle(Image image); 724}