예제 #1
0
 public void Add(Sprite sprite)
 {
     EnsureValidToAdd(sprite);
     ValidateBatchSize();
     PrepareSpriteVertices(sprite);
     sprites.Add(sprite);
     sprite.SpriteModified += HandleSpriteModified;
     sprite.BatchIndex = availableSlots.Dequeue();
     UploadSpriteVertices(sprite);
 }
예제 #2
0
        private void PrepareSpriteVertices(Sprite sprite)
        {
            /* Take the sprite's texture region and scale each corner according
            * to the whole texture atlas width/height so that they fall in the
            * range 0-1 (to match the expectations of OpenGL) */
            float scaledWidth = 1.0f / spriteSheet.Texture.Width;
            float scaledHeight = 1.0f / spriteSheet.Texture.Height;
            var region = sprite.TextureRegion;

            /* TextureRegion x,y refer to bottom left position of the quad
            * whereas, OpenGL coordinate system has (0,0) at the upper-left */
            var topLeftCoord = new Vector2(region.X * scaledWidth, (region.Y + region.Height) * scaledHeight);
            var topRightCoord = new Vector2((region.X + region.Width) * scaledWidth, (region.Y + region.Height) * scaledHeight);
            var bottomRightCoord = new Vector2((region.X + region.Width) * scaledWidth, region.Y * scaledHeight);
            var bottomLeftCoord = new Vector2(region.X * scaledWidth, region.Y * scaledHeight);

            // Scale the sprite's colour to fall in the range 0-1
            var sColour = sprite.Colour;
            var scaledColour = new Vector4(sColour.R / 255, sColour.G / 255, sColour.B / 255, sColour.A / 255);

            var topLeftPosition = new Vector2(sprite.X, sprite.Y);
            var topRightPosition = new Vector2(sprite.X + sprite.Width, sprite.Y);
            var bottomRightPosition = new Vector2(sprite.X + sprite.Width, sprite.Y + sprite.Height);
            var bottomLeftPosition = new Vector2(sprite.X, sprite.Y + sprite.Height);

            sprite.Vertices[0] = new VertexPositionColourTexture(topLeftPosition, scaledColour, topLeftCoord);
            sprite.Vertices[1] = new VertexPositionColourTexture(topRightPosition, scaledColour, topRightCoord);
            sprite.Vertices[2] = new VertexPositionColourTexture(bottomRightPosition, scaledColour, bottomRightCoord);
            sprite.Vertices[3] = new VertexPositionColourTexture(bottomLeftPosition, scaledColour, bottomLeftCoord);
        }
예제 #3
0
        public void SpriteHandlers_AreRemovedFromBatch_OnDisposal()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            var sprite2 = new Sprite(spriteSheet.Object.GetRegion(1), 20, 10);
            spriteBatch.Add(sprite, sprite2);

            vbo.ResetCalls();
            spriteBatch.Dispose();
            sprite.Modify((s) => s.Width = 100);
            sprite2.Modify((s) => s.Colour = Colour4.ForestGreen);

            vbo.Verify(
                (v) => v.FillPartial(It.IsAny<IntPtr>(), It.IsAny<IntPtr>(), It.IsAny<VertexPositionColourTexture[]>()),
                Times.Never
            );
        }
예제 #4
0
        public void Remove_ClearsSpriteVerticesFromVbo_OnSpriteRemoval()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            var spriteSize = Sprite.VertexCount * declaration.Stride;

            spriteBatch.Add(sprite);
            var spriteVboSize = new IntPtr(spriteSize);
            var spriteVboOffset = new IntPtr(spriteSize * sprite.BatchIndex);

            // essentially, overwrite the vertex data in the vbo with 4 vertices
            // that have no alpha and are hence transparent.
            var emptyVertex = default(VertexPositionColourTexture);
            var emptyVertices = new VertexPositionColourTexture[]
            {
                emptyVertex,
                emptyVertex,
                emptyVertex,
                emptyVertex
            };

            spriteBatch.Remove(sprite);
            vbo.Verify(v => v.Bind());
            vbo.Verify(v => v.FillPartial(spriteVboOffset, spriteVboSize, emptyVertices));
        }
예제 #5
0
        public void Remove_RemovesGivenSprite_FromExistingSpriteList()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            spriteBatch.Add(sprite);

            spriteBatch.Sprites.ShouldContain(sprite);
            spriteBatch.Remove(sprite);
            spriteBatch.Sprites.ShouldNotContain(sprite);
        }
예제 #6
0
 public void Add_AddsGivenSprite_ToSpriteList()
 {
     var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
     spriteBatch.Add(sprite);
     spriteBatch.Sprites.ShouldContain(sprite);
 }
예제 #7
0
        public void Add_UploadsGivenSpriteVertices_OnAdd()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            var spriteSize = Sprite.VertexCount * declaration.Stride;

            spriteBatch.Add(sprite);
            var spriteVboSize = new IntPtr(spriteSize);
            var spriteVboOffset = new IntPtr(spriteSize * sprite.BatchIndex);

            vbo.Verify(v => v.Bind());
            vbo.Verify(v => v.FillPartial(spriteVboOffset, spriteVboSize, sprite.Vertices));
        }
예제 #8
0
 private void UploadSpriteVertices(Sprite sprite)
 {
     var size = new IntPtr(vbo.VertexDeclaration.Stride * Sprite.VertexCount);
     var offset = new IntPtr(
         vbo.VertexDeclaration.Stride * Sprite.VertexCount * sprite.BatchIndex
     );
     vbo.Bind();
     vbo.FillPartial(offset, size, sprite.Vertices);
 }
예제 #9
0
        public void Add_ThrowsBatchSizeExceededException_OnAddingTooManySprites()
        {
            const int batchSize = 100;
            for (int i = 0; i < batchSize; i++)
            {
                var sprite = new Sprite(spriteSheet.Object.GetRegion(0), i, i);
                spriteBatch.Add(sprite);
            }
            var illegalSprite = new Sprite(spriteSheet.Object.GetRegion(0), 101, 101);
            Action add = () => spriteBatch.Add(illegalSprite);

            add.ShouldThrow<SpriteBatchSizeExceededException>();
        }
예제 #10
0
        public void Add_ThrowsArgumentException_WhenGivenSpriteIsNotUsingTextureRegionFromBatch()
        {
            var sprite = new Sprite(new TextureRegion(1, 1, 10, 10), 20, 20);
            Action add = () => spriteBatch.Add(sprite);

            add.ShouldThrow<ArgumentException>().Message.ShouldContain(
                "sprite must use a texture region from the batch's SpriteSheet"
            );
        }
예제 #11
0
 public void Add_AssignsBatchIndex_ToSprite()
 {
     var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
     var sprite2 = new Sprite(spriteSheet.Object.GetRegion(1), 20, 10);
     spriteBatch.Add(sprite, sprite2);
     sprite.BatchIndex.ShouldBe(0);
     sprite2.BatchIndex.ShouldBe(1);
 }
예제 #12
0
        public void Add_PreparesSpriteVertices_OnSuccessfulAdd()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            spriteBatch.Add(sprite);

            /* The sprite batch should take the sprite's texture region
            *  and scale each corner according to the whole texture atlas width/height
            * so that they fall in the range 0-1 (to match the expectations of OpenGL) */

            float scaledWidth = 1.0f / spriteSheet.Object.Texture.Width;
            float scaledHeight = 1.0f / spriteSheet.Object.Texture.Height;
            var region = sprite.TextureRegion;

            // texture packer x,y refer to bottom left position of the texture region
            // whereas, OpenGL coordinate system has (0,0) at the upper-left
            var topLeftCoord = new Vector2(region.X * scaledWidth, (region.Y + region.Height) * scaledHeight);
            var topRightCoord = new Vector2((region.X + region.Width) * scaledWidth, (region.Y + region.Height) * scaledHeight);
            var bottomRightCoord = new Vector2((region.X + region.Width) * scaledWidth, region.Y * scaledHeight);
            var bottomLeftCoord = new Vector2(region.X * scaledWidth, region.Y * scaledHeight);

            // we should also scale the sprite's colour to fall in the range 0-1
            var sColour = sprite.Colour;
            var scaledColour = new Vector4(sColour.R / 255, sColour.G / 255, sColour.B / 255, sColour.A / 255);

            // expected vertex positions
            var topLeftPosition = new Vector2(sprite.X, sprite.Y);
            var topRightPosition = new Vector2(sprite.X + sprite.Width, sprite.Y);
            var bottomRightPosition = new Vector2(sprite.X + sprite.Width, sprite.Y + sprite.Height);
            var bottomLeftPosition = new Vector2(sprite.X, sprite.Y + sprite.Height);

            var topLeftVertex = new VertexPositionColourTexture(topLeftPosition, scaledColour, topLeftCoord);
            var topRightvertex = new VertexPositionColourTexture(topRightPosition, scaledColour, topRightCoord);
            var bottomRightVertex = new VertexPositionColourTexture(bottomRightPosition, scaledColour, bottomRightCoord);
            var bottomLeftVertex = new VertexPositionColourTexture(bottomLeftPosition, scaledColour, bottomLeftCoord);

            sprite.Vertices.ShouldSatisfyAllConditions(
                () => sprite.Vertices[0].ShouldBe(topLeftVertex),
                () => sprite.Vertices[1].ShouldBe(topRightvertex),
                () => sprite.Vertices[2].ShouldBe(bottomRightVertex),
                () => sprite.Vertices[3].ShouldBe(bottomLeftVertex)
            );
        }
예제 #13
0
        public void Add_ThrowsDuplicateSpriteException_WhenAddingTheSameSpriteAgain()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            spriteBatch.Add(sprite);
            Action addAgain = () => spriteBatch.Add(sprite);

            addAgain.ShouldThrow<DuplicateSpriteException>().Message.ShouldContain(
                "The given sprite has already been added to this sprite batch"
            );
        }
예제 #14
0
 private void EnsureValidToAdd(Sprite sprite)
 {
     sprite.ThrowIfNull(nameof(sprite));
     if (!spriteSheet.TextureRegions.Contains(sprite.TextureRegion))
     {
         throw new ArgumentException(
             "The given sprite must use a texture region from the batch's SpriteSheet"
         );
     }
     if (sprites.Contains(sprite))
     {
         throw new DuplicateSpriteException(
             "The given sprite has already been added to this sprite batch"
         );
     }
 }
예제 #15
0
        public void SpriteVertices_AreReuploaded_WhenSpriteHasBeenModified()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            spriteBatch.Add(sprite);

            vbo.ResetCalls();
            sprite.Modify((s) => {
                s.X = 80;
                s.Y = 160;
            });
            vbo.Verify(
                v => v.FillPartial(It.IsAny<IntPtr>(), It.IsAny<IntPtr>(), sprite.Vertices),
                Times.Once
            );
        }
예제 #16
0
 private void HandleSpriteModified(Sprite sender, EventArgs args)
 {
     PrepareSpriteVertices(sender);
     UploadSpriteVertices(sender);
 }
예제 #17
0
        public void SpriteVertices_AreNotReuploaded_OnceSpriteHasBeenRemovedFromBatch()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            spriteBatch.Add(sprite);

            vbo.ResetCalls();
            spriteBatch.Remove(sprite);
            sprite.Modify((s) => s.X = 80);

            vbo.Verify(
                v => v.FillPartial(It.IsAny<IntPtr>(), It.IsAny<IntPtr>(), sprite.Vertices),
                Times.Never
            );
        }
예제 #18
0
 private void ClearSpriteData(Sprite sprite)
 {
     var size = new IntPtr(vbo.VertexDeclaration.Stride * Sprite.VertexCount);
     var offset = new IntPtr(
         vbo.VertexDeclaration.Stride * Sprite.VertexCount * sprite.BatchIndex
     );
     vbo.Bind();
     vbo.FillPartial(offset, size, new VertexPositionColourTexture[4]);
 }
예제 #19
0
        public void Remove_ThrowsArgumentException_WhenSpriteIsNotInBatch()
        {
            var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10);
            Action remove = () => spriteBatch.Remove(sprite);

            remove.ShouldThrow<ArgumentException>();
        }
예제 #20
0
 public void Remove(Sprite sprite)
 {
     if (!sprites.Contains(sprite))
     {
         throw new ArgumentException(
             nameof(sprite) + " was not found in this sprite batch"
         );
     }
     sprites.Remove(sprite);
     sprite.SpriteModified -= HandleSpriteModified;
     // may need a dictionary of weak references of sprites to batchIndexes
     // in the case a sprite becomes null after being added to the batch
     availableSlots.Enqueue(sprite.BatchIndex);
     ClearSpriteData(sprite);
 }
예제 #21
0
 public void SetUp()
 {
     sprite = new Sprite(textureRegion, 40, 50);
 }