public void RenderScene_ShouldIgnoreOrderInLayer_WhenEntitiesAreInDifferentSortingLayers()
        {
            // Arrange
            const string otherSortingLayer = "Other";

            SetupSortingLayers(RenderingConfiguration.DefaultSortingLayerName, otherSortingLayer);

            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            renderingSceneBuilder.AddCamera();
            var entity1 = renderingSceneBuilder.AddSprite(orderInLayer: 0, sortingLayerName: otherSortingLayer);
            var entity2 = renderingSceneBuilder.AddSprite(orderInLayer: 1);
            var scene   = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            Received.InOrder(() =>
            {
                _renderer2D.RenderSprite(entity2.GetSprite(), entity2.Get2DTransformationMatrix());
                _renderer2D.RenderSprite(entity1.GetSprite(), entity1.Get2DTransformationMatrix());
            });
        }
        public void RenderScene_ShouldApplyViewRectangleOfCameraWithOverscanMatchedByWidth_WhenCameraAndScreenAspectRatioDiffers()
        {
            // Arrange
            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            var cameraTransform = new Transform2DComponent
            {
                Translation = new Vector2(10, -10),
                Rotation    = 0,
                Scale       = Vector2.One
            };
            var cameraEntity = renderingSceneBuilder.AddCamera(cameraTransform);
            var camera       = cameraEntity.GetComponent <CameraComponent>();

            camera.AspectRatioBehavior = AspectRatioBehavior.Overscan;

            // Camera view rectangle 2xScreenWidth and 4xScreenHeight
            // Camera view rectangle is 1:1 ratio while screen is 2:1 ratio
            camera.ViewRectangle = new Vector2(ScreenWidth * 2, ScreenHeight * 4);

            var entity = renderingSceneBuilder.AddSprite(Transform2DComponent.CreateDefault());
            var scene  = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            _renderer2D.Received(1).RenderSprite(entity.GetSprite(),
                                                 // Sprite transform is half the scale and translation due to camera view rectangle being scaled by width to match
                                                 new Matrix3x3(m11: 0.5, m12: 0, m13: -5, m21: 0, m22: 0.5, m23: 5, m31: 0, m32: 0, m33: 1));
        }
        public void RenderScene_ShouldApplyViewRectangleOfCamera_WhenSceneContainsEntityAndCamera()
        {
            // Arrange
            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            var cameraTransform = new Transform2DComponent
            {
                Translation = new Vector2(10, -10),
                Rotation    = 0,
                Scale       = Vector2.One
            };
            var cameraEntity = renderingSceneBuilder.AddCamera(cameraTransform);
            var camera       = cameraEntity.GetComponent <CameraComponent>();

            // Camera view rectangle is twice the screen resolution
            camera.ViewRectangle = new Vector2(ScreenWidth * 2, ScreenHeight * 2);

            var entity = renderingSceneBuilder.AddSprite(Transform2DComponent.CreateDefault());
            var scene  = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            _renderer2D.Received(1).RenderSprite(entity.GetSprite(),
                                                 // Sprite transform is half the scale and translation due to camera view rectangle
                                                 new Matrix3x3(m11: 0.5, m12: 0, m13: -5, m21: 0, m22: 0.5, m23: 5, m31: 0, m32: 0, m33: 1));
        }
        public void RenderScene_ShouldNotRenderSprite_WhenSceneContainsEntityWithSpriteRendererAndTransformButDoesNotContainCamera()
        {
            // Arrange
            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            renderingSceneBuilder.AddSprite();
            var scene = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            _renderer2D.DidNotReceive().RenderSprite(Arg.Any <Sprite>(), Arg.Any <Matrix3x3>());
        }
        public void RenderScene_ShouldApplyViewRectangleOfCameraWithUnderscanMatchedByHeight_WhenCameraAndScreenAspectRatioDiffers()
        {
            // Arrange
            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            var cameraTransform = new Transform2DComponent
            {
                Translation = new Vector2(10, -10),
                Rotation    = 0,
                Scale       = Vector2.One
            };
            var cameraEntity = renderingSceneBuilder.AddCamera(cameraTransform);
            var camera       = cameraEntity.GetComponent <CameraComponent>();

            camera.AspectRatioBehavior = AspectRatioBehavior.Underscan;

            // Camera view rectangle 1xScreenWidth and 2xScreenHeight
            // Camera view rectangle is 1:1 ratio while screen is 2:1 ratio
            camera.ViewRectangle = new Vector2(ScreenWidth, ScreenHeight * 2);

            var entity = renderingSceneBuilder.AddSprite(Transform2DComponent.CreateDefault());
            var scene  = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            Received.InOrder(() =>
            {
                _renderer2D.Clear(Color.FromArgb(255, 255, 255, 255));
                _renderer2D.Clear(Color.FromArgb(255, 0, 0, 0));
                _renderer2D.SetClippingRectangle(new AxisAlignedRectangle(ScreenHeight, ScreenHeight));
                _renderer2D.Clear(Color.FromArgb(255, 255, 255, 255));
                _renderer2D.Received(1).RenderSprite(entity.GetSprite(),
                                                     // Sprite transform is half the scale and translation due to camera view rectangle being scaled by height to match
                                                     new Matrix3x3(m11: 0.5, m12: 0, m13: -5, m21: 0, m22: 0.5, m23: 5, m31: 0, m32: 0, m33: 1));
                _renderer2D.ClearClipping();
            });
        }
        public void RenderScene_ShouldPerformCameraTransformationOnEntity_WhenSceneContainsEntityAndCamera()
        {
            // Arrange
            var renderingSystem       = GetRenderingSystem();
            var renderingSceneBuilder = new RenderingSceneBuilder();

            var cameraTransform = new Transform2DComponent
            {
                Translation = new Vector2(10, -10),
                Rotation    = 0,
                Scale       = Vector2.One
            };

            renderingSceneBuilder.AddCamera(cameraTransform);
            var entity = renderingSceneBuilder.AddSprite(Transform2DComponent.CreateDefault());
            var scene  = renderingSceneBuilder.Build();

            // Act
            renderingSystem.RenderScene(scene);

            // Assert
            _renderer2D.Received(1).RenderSprite(entity.GetSprite(), Matrix3x3.CreateTranslation(new Vector2(-10, 10)));
        }