spine-runtimes/spine-xna/src/SpriteBatcher.cs
2013-04-11 10:52:00 +02:00

255 lines
10 KiB
C#

/*******************************************************************************
* Copyright (c) 2013, Esoteric Software
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace Spine {
// #region License
// /*
// Microsoft Public License (Ms-PL)
// MonoGame - Copyright © 2009 The MonoGame Team
//
// All rights reserved.
//
// This license governs use of the accompanying software. If you use the software, you accept this license. If you do not
// accept the license, do not use the software.
//
// 1. Definitions
// The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under
// U.S. copyright law.
//
// A "contribution" is the original software, or any additions or changes to the software.
// A "contributor" is any person that distributes its contribution under this license.
// "Licensed patents" are a contributor's patent claims that read directly on its contribution.
//
// 2. Grant of Rights
// (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
// (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3,
// each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
//
// 3. Conditions and Limitations
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software,
// your patent license from such contributor to the software ends automatically.
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution
// notices that are present in the software.
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including
// a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object
// code form, you may only do so under a license that complies with this license.
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees
// or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent
// permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular
// purpose and non-infringement.
// */
// #endregion License
//
/// <summary>
/// This class handles the queueing of batch items into the GPU by creating the triangle tesselations
/// that are used to draw the sprite textures. This class supports int.MaxValue number of sprites to be
/// batched and will process them into short.MaxValue groups (strided by 6 for the number of vertices
/// sent to the GPU).
/// </summary>
public class SpriteBatcher {
/// <summary>
/// Initialization size for the batch item list and queue.
/// </summary>
private const int InitialBatchSize = 256;
/// <summary>
/// The maximum number of batch items that can be processed per iteration
/// </summary>
private const int MaxBatchSize = short.MaxValue / 6; // 6 = 4 vertices unique and 2 shared, per quad
/// <summary>
/// Initialization size for the vertex array, in batch units.
/// </summary>
private const int InitialVertexArraySize = 256;
/// <summary>
/// The list of batch items to process.
/// </summary>
private readonly List<SpriteBatchItem> _batchItemList;
/// <summary>
/// The available SpriteBatchItem queue so that we reuse these objects when we can.
/// </summary>
private readonly Queue<SpriteBatchItem> _freeBatchItemQueue;
/// <summary>
/// Vertex index array. The values in this array never change.
/// </summary>
private short[] _index;
private VertexPositionColorTexture[] _vertexArray;
public SpriteBatcher () {
_batchItemList = new List<SpriteBatchItem>(InitialBatchSize);
_freeBatchItemQueue = new Queue<SpriteBatchItem>(InitialBatchSize);
EnsureArrayCapacity(InitialBatchSize);
}
/// <summary>
/// Create an instance of SpriteBatchItem if there is none available in the free item queue. Otherwise,
/// a previously allocated SpriteBatchItem is reused.
/// </summary>
/// <returns></returns>
public SpriteBatchItem CreateBatchItem () {
SpriteBatchItem item;
if (_freeBatchItemQueue.Count > 0)
item = _freeBatchItemQueue.Dequeue();
else
item = new SpriteBatchItem();
_batchItemList.Add(item);
return item;
}
/// <summary>
/// Resize and recreate the missing indices for the index and vertex position color buffers.
/// </summary>
/// <param name="numBatchItems"></param>
private void EnsureArrayCapacity (int numBatchItems) {
int neededCapacity = 6 * numBatchItems;
if (_index != null && neededCapacity <= _index.Length) {
// Short circuit out of here because we have enough capacity.
return;
}
short[] newIndex = new short[6 * numBatchItems];
int start = 0;
if (_index != null) {
_index.CopyTo(newIndex, 0);
start = _index.Length / 6;
}
for (var i = start; i < numBatchItems; i++) {
/*
* TL TR
* 0----1 0,1,2,3 = index offsets for vertex indices
* | /| TL,TR,BL,BR are vertex references in SpriteBatchItem.
* | / |
* | / |
* |/ |
* 2----3
* BL BR
*/
// Triangle 1
newIndex[i * 6 + 0] = (short)(i * 4);
newIndex[i * 6 + 1] = (short)(i * 4 + 1);
newIndex[i * 6 + 2] = (short)(i * 4 + 2);
// Triangle 2
newIndex[i * 6 + 3] = (short)(i * 4 + 1);
newIndex[i * 6 + 4] = (short)(i * 4 + 3);
newIndex[i * 6 + 5] = (short)(i * 4 + 2);
}
_index = newIndex;
_vertexArray = new VertexPositionColorTexture[4 * numBatchItems];
}
public void Draw (GraphicsDevice device) {
// nothing to do
if (_batchItemList.Count == 0)
return;
// Determine how many iterations through the drawing code we need to make
int batchIndex = 0;
int batchCount = _batchItemList.Count;
// Iterate through the batches, doing short.MaxValue sets of vertices only.
while (batchCount > 0) {
// setup the vertexArray array
var startIndex = 0;
var index = 0;
Texture2D tex = null;
int numBatchesToProcess = batchCount;
if (numBatchesToProcess > MaxBatchSize) {
numBatchesToProcess = MaxBatchSize;
}
EnsureArrayCapacity(numBatchesToProcess);
// Draw the batches
for (int i = 0; i < numBatchesToProcess; i++, batchIndex++) {
SpriteBatchItem item = _batchItemList[batchIndex];
// if the texture changed, we need to flush and bind the new texture
var shouldFlush = !ReferenceEquals(item.Texture, tex);
if (shouldFlush) {
FlushVertexArray(device, startIndex, index);
tex = item.Texture;
startIndex = index = 0;
device.Textures[0] = tex;
}
// store the SpriteBatchItem data in our vertexArray
_vertexArray[index++] = item.vertexTL;
_vertexArray[index++] = item.vertexTR;
_vertexArray[index++] = item.vertexBL;
_vertexArray[index++] = item.vertexBR;
// Release the texture and return the item to the queue.
item.Texture = null;
_freeBatchItemQueue.Enqueue(item);
}
// flush the remaining vertexArray data
FlushVertexArray(device, startIndex, index);
// Update our batch count to continue the process of culling down large batches
batchCount -= numBatchesToProcess;
}
_batchItemList.Clear();
}
/// <summary>
/// Sends the triangle list to the graphics device. Here is where the actual drawing starts.
/// </summary>
/// <param name="start">Start index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
/// <param name="end">End index of vertices to draw. Not used except to compute the count of vertices to draw.</param>
private void FlushVertexArray (GraphicsDevice device, int start, int end) {
if (start == end)
return;
var vertexCount = end - start;
device.DrawUserIndexedPrimitives(
PrimitiveType.TriangleList,
_vertexArray,
0,
vertexCount,
_index,
0,
(vertexCount / 4) * 2,
VertexPositionColorTexture.VertexDeclaration);
}
}
public class SpriteBatchItem {
public Texture2D Texture;
public VertexPositionColorTexture vertexTL;
public VertexPositionColorTexture vertexTR;
public VertexPositionColorTexture vertexBL;
public VertexPositionColorTexture vertexBR;
}
}