109 private readonly Random _rng =
new();
110 private readonly SKPaint _paint =
new()
113 BlendMode = SKBlendMode.SrcOver
116 private long _instanceLastTick;
160 public Func<Rectangle, Random, IEnumerable<ImageInstance>>?
Initializer {
get;
set; }
201 public Func<ImageInstance, Rectangle, Random, ImageInstance>?
RecycleInstance {
get;
set; }
236 Rectangle screenBounds,
237 string? nickname =
null)
238 : base(renderSurfaceHost,
242 screenBounds: screenBounds,
257 Rectangle worldBounds,
258 string? nickname =
null)
259 : base(renderSurfaceHost,
261 sceneLayer: sceneLayer,
264 worldBounds: worldBounds,
282 Rectangle screenBounds,
283 Func<Rectangle, Random, IEnumerable<ImageInstance>>? initializer,
284 Func<ImageInstance, Rectangle, bool>? shouldRecycle =
null,
285 Func<ImageInstance, Rectangle, Random, ImageInstance>? recycleInstance =
null,
286 Action<ImageInstance, float>? updateInstance =
null,
287 string? nickname =
null)
288 : this(renderSurfaceHost, view, screenBounds, nickname)
312 Rectangle worldBounds,
313 Func<Rectangle, Random, IEnumerable<ImageInstance>>? initializer,
314 Func<ImageInstance, Rectangle, bool>? shouldRecycle =
null,
315 Func<ImageInstance, Rectangle, Random, ImageInstance>? recycleInstance =
null,
316 Action<ImageInstance, float>? updateInstance =
null,
317 string? nickname =
null)
318 : this(renderSurfaceHost, sceneLayer, worldBounds, nickname)
357 if (_instanceLastTick > 0)
358 dt = HighResTimer.GetDuration(_instanceLastTick, tick);
360 _instanceLastTick = tick;
372 for (
int i = 0; i <
Instances.Count; i++)
375 var oldRefreshBounds = GetRefreshBounds(instance);
377 instance.Bounds =
new RectangleF(
378 instance.Bounds.X + (instance.VelocityX * dt),
379 instance.Bounds.Y + (instance.VelocityY * dt),
380 instance.Bounds.Width,
381 instance.Bounds.Height);
383 instance.Rotation += instance.AngularVelocity * dt;
394 AddRefreshRect(oldRefreshBounds);
395 AddRefreshRect(GetRefreshBounds(recycled));
399 AddRefreshRect(oldRefreshBounds);
400 AddRefreshRect(GetRefreshBounds(instance));
414 var canvas = backbuffer.
Canvas;
420 float sx = srcBounds.Width > 0
421 ? destRectScreen.Width / srcBounds.Width
424 float sy = srcBounds.Height > 0
425 ? destRectScreen.Height / srcBounds.Height
429 canvas.ClipRect(destRectScreen.ToSKRect());
433 _paint.Color = instance.Tint;
435 var dst =
new SKRect(
436 destRectScreen.Left + ((instance.Bounds.Left - srcBounds.Left) * sx),
437 destRectScreen.Top + ((instance.Bounds.Top - srcBounds.Top) * sy),
438 destRectScreen.Left + ((instance.Bounds.Right - srcBounds.Left) * sx),
439 destRectScreen.Top + ((instance.Bounds.Bottom - srcBounds.Top) * sy));
441 if (instance.Rotation != 0f)
443 float cx = (dst.Left + dst.Right) * 0.5f;
444 float cy = (dst.Top + dst.Bottom) * 0.5f;
447 canvas.RotateDegrees(instance.Rotation, cx, cy);
448 canvas.DrawBitmap(instance.Bitmap, dst, _paint);
453 canvas.DrawBitmap(instance.Bitmap, dst, _paint);
464 protected override void Dispose(
bool disposing)
469 base.Dispose(disposing);
476 private void AddRefreshRect(RectangleF rect)
478 const float pad = 4f;
480 var expanded = Rectangle.Ceiling(RectangleF.Inflate(rect, pad, pad));
484 SceneLayer!.RefreshQueue.AddWorldRect(expanded);
489 sceneLayer.RefreshQueue.AddViewScreenRect(
View!, sceneLayer, expanded);
497 private static RectangleF GetRefreshBounds(ImageInstance instance)
499 if (instance.Rotation == 0f && instance.AngularVelocity == 0f)
500 return instance.Bounds;
502 float cx = instance.Bounds.Left + (instance.Bounds.Width * 0.5f);
503 float cy = instance.Bounds.Top + (instance.Bounds.Height * 0.5f);
505 float halfWidth = instance.Bounds.Width * 0.5f;
506 float halfHeight = instance.Bounds.Height * 0.5f;
507 float halfDiagonal = MathF.Sqrt((halfWidth * halfWidth) + (halfHeight * halfHeight));
509 return new RectangleF(
View? View
Gets the view to which this direct drawing is attached, or null if the drawing is in scene-layer mode...
long _lastTick
The last engine tick value used for delta-time calculations in Update.
RenderSurfaceHostBase RenderSurfaceHost
Gets the render surface host that manages this direct drawing's rendering pipeline.
SceneLayer? SceneLayer
Gets the scene layer to which this direct drawing is attached, or null if the drawing is in view mode...
Rectangle WorldBounds
Gets or sets the world-space bounds of this direct drawing in pixels.
DirectDrawingMode Mode
Gets the drawing mode that determines how this direct drawing is positioned and transformed.
Rectangle ScreenBounds
Gets or sets the screen-space bounds of this direct drawing in pixels.
DirectDrawingMovableBase(RenderSurfaceHostBase renderSurfaceHost, DirectDrawingMode mode, SceneLayer? sceneLayer, View? view, Rectangle? screenBounds, Rectangle? worldBounds, string? name=null)
Initializes a new instance of the DirectDrawingMovableBase class with integrated movement capabilitie...
override void Dispose(bool disposing)
Disposes managed resources.
List< ImageInstance > Instances
Gets the collection of image instances managed by this layer.
override void Update(long tick)
Updates all instances, applying built-in motion, optional custom updates, and optional recycling beha...
override void OnDraw(BackbufferBase backbuffer, RectangleF destRectScreen)
Renders all image instances to the backbuffer.
Action< ImageInstance, float >? UpdateInstance
Gets or sets an optional callback invoked after the built-in motion update for each instance.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, Func< Rectangle, Random, IEnumerable< ImageInstance > >? initializer, Func< ImageInstance, Rectangle, bool >? shouldRecycle=null, Func< ImageInstance, Rectangle, Random, ImageInstance >? recycleInstance=null, Action< ImageInstance, float >? updateInstance=null, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class and assigns optional hooks.
void InitializeInstances()
Clears the current instance collection and rebuilds it using Initializer, if one is assigned.
Func< ImageInstance, Rectangle, bool >? ShouldRecycle
Gets or sets an optional callback used to determine whether an instance should be recycled.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, View view, Rectangle screenBounds, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, Func< Rectangle, Random, IEnumerable< ImageInstance > >? initializer, Func< ImageInstance, Rectangle, bool >? shouldRecycle=null, Func< ImageInstance, Rectangle, Random, ImageInstance >? recycleInstance=null, Action< ImageInstance, float >? updateInstance=null, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class for scene-layer rendering and assigns opti...
Func< ImageInstance, Rectangle, Random, ImageInstance >? RecycleInstance
Gets or sets an optional callback used to create a replacement instance when recycling occurs.
ImageInstanceLayer(RenderSurfaceHostBase renderSurfaceHost, SceneLayer sceneLayer, Rectangle worldBounds, string? nickname=null)
Initializes a new instance of the ImageInstanceLayer class for scene-layer rendering.
Func< Rectangle, Random, IEnumerable< ImageInstance > >? Initializer
Gets or sets an optional callback used to populate the initial set of instances.
Represents a base class for managing a graphical backbuffer, and is the in-memory surface where rende...
SKCanvas Canvas
Gets the SkiaSharp canvas used for drawing operations on this backbuffer.
Represents a base class for hosting a render surface, providing functionality for managing rendering ...
Scene Scene
Gets the source Scenes.Scene used for rendering operations.
ReadOnlyCollection< SceneLayer > SceneLayers
Gets a read-only collection of all layers in this scene, in their current order.
DirectDrawingMode
Specifies where direct drawing operations are applied within the rendering pipeline.
@ View
Performs direct drawing at the view level using screen-space coordinates.
@ SceneLayer
Performs direct drawing within a scene layer using world-space coordinates.