public void Add(Sprite sprite) { EnsureValidToAdd(sprite); ValidateBatchSize(); PrepareSpriteVertices(sprite); sprites.Add(sprite); sprite.SpriteModified += HandleSpriteModified; sprite.BatchIndex = availableSlots.Dequeue(); UploadSpriteVertices(sprite); }
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); }
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 ); }
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)); }
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); }
public void Add_AddsGivenSprite_ToSpriteList() { var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10); spriteBatch.Add(sprite); spriteBatch.Sprites.ShouldContain(sprite); }
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)); }
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); }
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>(); }
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" ); }
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); }
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) ); }
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" ); }
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" ); } }
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 ); }
private void HandleSpriteModified(Sprite sender, EventArgs args) { PrepareSpriteVertices(sender); UploadSpriteVertices(sender); }
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 ); }
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]); }
public void Remove_ThrowsArgumentException_WhenSpriteIsNotInBatch() { var sprite = new Sprite(spriteSheet.Object.GetRegion(0), 10, 10); Action remove = () => spriteBatch.Remove(sprite); remove.ShouldThrow<ArgumentException>(); }
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); }
public void SetUp() { sprite = new Sprite(textureRegion, 40, 50); }