Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
MergeExtensions.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 using System.Linq;
6 
7 using SiliconStudio.Paradox.Effects.Data;
8 using SiliconStudio.Paradox.Graphics;
9 using SiliconStudio.Core;
10 using SiliconStudio.Paradox.Graphics.Data;
11 
12 namespace SiliconStudio.Paradox.Extensions
13 {
14  public static class MergeExtensions
15  {
16  /// <summary>
17  /// Transform a vertex buffer positions, normals, tangents and bitangents using the given matrix.
18  /// </summary>
19  /// <param name="meshDrawDatas">The mesh draw datas.</param>
20  /// <param name="can32BitIndex">A flag stating if 32 bit index buffers.</param>
21  public unsafe static MeshDrawData MergeDrawData(IList<MeshDrawData> meshDrawDatas, bool can32BitIndex)
22  {
23  if (meshDrawDatas.Count == 0)
24  throw new ArgumentException("Need at least 1 MeshDrawData.", "meshDrawDatas");
25 
26  if (meshDrawDatas.Count == 1)
27  return meshDrawDatas[0];
28 
29  // Check that vertex buffer declarations are matching
30  var firstMeshDrawData = meshDrawDatas[0];
31  if (!firstMeshDrawData.IsSimple())
32  throw new InvalidOperationException("Can only merge simple MeshDrawData.");
33 
34  var firstVertexBuffer = firstMeshDrawData.VertexBuffers[0];
35  var hasIndexBuffer = IsIndexed(meshDrawDatas);
36  int totalVertexCount = 0;
37  int totalIndexCount = 0;
38 
39  //TODO: extend to non-simple vertex declarations, fill with default values it missing declarations etc. ?
40 
41  for (int i = 0; i < meshDrawDatas.Count; i++)
42  {
43  var meshDrawData = meshDrawDatas[i];
44 
45  // This should not happen anymore
46  if (i != 0)
47  {
48  if (!meshDrawData.IsSimple())
49  throw new InvalidOperationException("Can only merge simple MeshDrawData.");
50 
51  if (meshDrawData.VertexBuffers.Length != firstMeshDrawData.VertexBuffers.Length)
52  throw new InvalidOperationException("Non-matching vertex buffer declarations.");
53 
54  if (!meshDrawData.VertexBuffers[0].Declaration.Equals(firstMeshDrawData.VertexBuffers[0].Declaration))
55  throw new InvalidOperationException("Non-matching vertex buffer declarations.");
56  }
57 
58  if (meshDrawData.PrimitiveType != PrimitiveType.TriangleList)
59  throw new InvalidOperationException("Can only merge TriangleList.");
60 
61  // Update vertex/index counts
62  totalVertexCount += meshDrawData.VertexBuffers[0].Count;
63  if (hasIndexBuffer)
64  {
65  if (meshDrawData.IndexBuffer != null)
66  totalIndexCount += meshDrawData.IndexBuffer.Count;
67  else
68  totalIndexCount += meshDrawData.VertexBuffers[0].Count;
69  }
70  }
71 
72  // Allocate vertex buffer
73  var result = new MeshDrawData { PrimitiveType = PrimitiveType.TriangleList };
74  var destBufferData = new byte[firstVertexBuffer.Declaration.VertexStride * totalVertexCount];
75  result.VertexBuffers = new VertexBufferBindingData[] {
77  new BufferData(BufferFlags.VertexBuffer, destBufferData),
78  firstVertexBuffer.Declaration,
79  totalVertexCount,
80  firstVertexBuffer.Stride)};
81 
82  // Copy vertex buffers
83  fixed (byte* destBufferDataStart = &destBufferData[0])
84  {
85  var destBufferDataCurrent = destBufferDataStart;
86  foreach (MeshDrawData meshDrawData in meshDrawDatas)
87  {
88  var sourceBuffer = meshDrawData.VertexBuffers[0].Buffer.Value;
89  fixed (byte* sourceBufferDataStart = &sourceBuffer.Content[0])
90  {
91  Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart, sourceBuffer.Content.Length);
92  destBufferDataCurrent += sourceBuffer.Content.Length;
93  }
94  }
95  }
96 
97  if (hasIndexBuffer)
98  {
99  var use32BitIndex = can32BitIndex && totalVertexCount > ushort.MaxValue; // 65535 = 0xFFFF is kept for primitive restart in strip
100 
101  // Allocate index buffer
102  destBufferData = new byte[(use32BitIndex ? sizeof(uint) : sizeof(ushort)) * totalIndexCount];
103  result.IndexBuffer = new IndexBufferBindingData(
104  new BufferData(BufferFlags.IndexBuffer, destBufferData),
105  use32BitIndex,
106  totalIndexCount);
107 
108  // Copy index buffers
109  fixed (byte* destBufferDataStart = &destBufferData[0])
110  {
111  var destBufferDataCurrent = destBufferDataStart;
112  var offset = 0;
113  foreach (MeshDrawData meshDrawData in meshDrawDatas)
114  {
115  var indexBuffer = meshDrawData.IndexBuffer;
116  byte[] sourceBufferContent = null;
117  var is32Bit = false;
118 
119  if (indexBuffer != null)
120  {
121  sourceBufferContent = indexBuffer.Buffer.Value.Content;
122  is32Bit = indexBuffer.Is32Bit;
123  }
124 
125  if (offset != 0 || (use32BitIndex != meshDrawData.IndexBuffer.Is32Bit))
126  {
127  if (use32BitIndex)
128  sourceBufferContent = CreateIntIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit);
129  else
130  sourceBufferContent = CreateShortIndexBuffer(offset, meshDrawData.IndexBuffer.Count, sourceBufferContent, is32Bit);
131  }
132 
133  fixed (byte* sourceBufferDataStart = &sourceBufferContent[0])
134  {
135  Utilities.CopyMemory((IntPtr)destBufferDataCurrent, (IntPtr)sourceBufferDataStart,
136  sourceBufferContent.Length);
137  destBufferDataCurrent += sourceBufferContent.Length;
138  }
139 
140  offset += meshDrawData.VertexBuffers[0].Count;
141  }
142  }
143 
144  result.DrawCount = totalIndexCount;
145  }
146  else
147  {
148  result.DrawCount = totalVertexCount;
149  }
150 
151  return result;
152  }
153 
154  /// <summary>
155  /// Group meshes that can be merged because they have the same vertex declaration.
156  /// </summary>
157  /// <param name="meshDrawDatas">The list of meshes.</param>
158  /// <returns>A list of grouped meshes.</returns>
159  public static List<List<MeshDrawData>> CreateDeclarationMergeGroup(IList<MeshDrawData> meshDrawDatas)
160  {
161  var mergingLists = new List<List<MeshDrawData>>();
162 
163  foreach (var meshDrawData in meshDrawDatas)
164  {
165  if (!meshDrawData.IsSimple() || meshDrawData.PrimitiveType != PrimitiveType.TriangleList) // only one (interlaced) vertex buffer is allowed
166  {
167  mergingLists.Add(new List<MeshDrawData> { meshDrawData });
168  continue;
169  }
170 
171  bool createNewGroup = true;
172  var currentDecl = meshDrawData.VertexBuffers[0].Declaration;
173  // look for matching descriptions
174  foreach (var group in mergingLists)
175  {
176  if (currentDecl.Equals(group[0].VertexBuffers[0].Declaration))
177  {
178  group.Add(meshDrawData);
179  createNewGroup = false;
180  break;
181  }
182  }
183 
184  if (createNewGroup)
185  mergingLists.Add(new List<MeshDrawData> { meshDrawData });
186  }
187 
188  return mergingLists;
189  }
190 
191  /// <summary>
192  /// Create group of MeshDrawData that will be merged.
193  /// </summary>
194  /// <param name="meshDrawDatas">List of MehsDrawData to merge.</param>
195  /// <param name="can32BitIndex">A flag stating if 32 bit index buffers are allowed.</param>
196  /// <returns>A List of groups to merge internally.</returns>
197  public static List<List<MeshDrawData>> CreateOptimizedMergeGroups(IList<MeshDrawData> meshDrawDatas, bool can32BitIndex)
198  {
199  if (meshDrawDatas.Count == 0)
200  return new List<List<MeshDrawData>>();
201 
202  if (meshDrawDatas.Count == 1)
203  return new List<List<MeshDrawData>> { meshDrawDatas.ToList() };
204 
205  // list of (vertices count -> list of meshes)
206  // vertices count is the higher possible value for an index
207  var mergingLists = new List<KeyValuePair<int, List<MeshDrawData>>>();
208 
209  var maxVerticesCount = can32BitIndex ? uint.MaxValue : ushort.MaxValue;
210 
211  foreach (var drawData in meshDrawDatas)
212  {
213  mergingLists = mergingLists.OrderBy(x => x.Key).Reverse().ToList();
214 
215  // TODO : be careful of null vertex buffers too ?
216  var vertexCount = drawData.VertexBuffers[0].Count;
217  var insertIndex = -1;
218  for (var j = 0; j < mergingLists.Count; ++j)
219  {
220  if (mergingLists[j].Key + vertexCount <= maxVerticesCount)
221  {
222  insertIndex = j;
223  break;
224  }
225  }
226 
227  if (insertIndex < 0) // new entry
228  {
229  var newList = new List<MeshDrawData>() { drawData };
230  mergingLists.Add(new KeyValuePair<int, List<MeshDrawData>>(vertexCount, newList));
231  }
232  else // append
233  {
234  var kvp = mergingLists[insertIndex];
235  kvp.Value.Add(drawData);
236  mergingLists[insertIndex] = new KeyValuePair<int, List<MeshDrawData>>(vertexCount + kvp.Key, kvp.Value);
237  }
238 
239  }
240 
241  return mergingLists.Select(x => x.Value).ToList();
242  }
243 
244  /// <summary>
245  /// Create an short typed index buffer.
246  /// </summary>
247  /// <param name="offset">The offset to apply to the indices.</param>
248  /// <param name="count">The number of indices.</param>
249  /// <param name="baseIndices">A possible base index buffer</param>
250  /// <param name="is32Bit">Stating if baseIndices is filled with 32 bits int</param>
251  /// <returns>A new index buffer.</returns>
252  public static byte[] CreateShortIndexBuffer(int offset, int count, byte[] baseIndices = null, bool is32Bit = true)
253  {
254  var indices = new ushort[count];
255  if (baseIndices == null)
256  {
257  for (var i = 0; i < count; ++i)
258  {
259  indices[i] = (ushort)(i + offset);
260  }
261  }
262  else
263  {
264  for (var i = 0; i < count; ++i)
265  {
266  if (is32Bit)
267  indices[i] = (ushort)(BitConverter.ToInt32(baseIndices, 4 * i) + offset);
268  else
269  indices[i] = (ushort)(BitConverter.ToInt16(baseIndices, 2 * i) + offset);
270  }
271  }
272 
273  var sizeOf = Utilities.SizeOf(indices);
274  var buffer = new byte[sizeOf];
275  Utilities.Write(buffer, indices, 0, indices.Length);
276  return buffer;
277  }
278 
279  /// <summary>
280  /// Create an int typed index buffer.
281  /// </summary>
282  /// <param name="offset">The offset to apply to the indices.</param>
283  /// <param name="count">The number of indices.</param>
284  /// <param name="baseIndices">A possible base index buffer</param>
285  /// <param name="is32Bits">Stating if baseIndices is filled with 32 bits int</param>
286  /// <returns>A new index buffer.</returns>
287  public static byte[] CreateIntIndexBuffer(int offset, int count, byte[] baseIndices = null, bool is32Bits = true)
288  {
289  var indices = new uint[count];
290  if (baseIndices == null)
291  {
292  for (var i = 0; i < count; ++i)
293  {
294  indices[i] = (uint)(i + offset);
295  }
296  }
297  else
298  {
299  for (var i = 0; i < count; ++i)
300  {
301  if (is32Bits)
302  indices[i] = (uint)(BitConverter.ToInt32(baseIndices, 4*i) + offset);
303  else
304  indices[i] = (uint)(BitConverter.ToInt16(baseIndices, 2 * i) + offset);
305  }
306  }
307 
308  var sizeOf = Utilities.SizeOf(indices);
309  var buffer = new byte[sizeOf];
310  Utilities.Write(buffer, indices, 0, indices.Length);
311  return buffer;
312  }
313 
314  /// <summary>
315  /// Check if a index buffer will be needed for this merge group.
316  /// </summary>
317  /// <param name="meshDrawDatas">The list of MeshDrawdata to merge.</param>
318  /// <returns>True if an index is needed, false otherwise.</returns>
319  public static bool IsIndexed(IList<MeshDrawData> meshDrawDatas)
320  {
321  return meshDrawDatas.Any(drawdata => drawdata.IndexBuffer != null);
322  }
323 
324  /// <summary>
325  /// Group the meshes.
326  /// </summary>
327  /// <param name="meshDrawDatas">The list of meshes to group.</param>
328  /// <param name="can32BitIndex">A flag stating if 32 bit index buffers are allowed</param>
329  /// <returns>The list of merged meshes.</returns>
330  public static List<MeshDrawData> GroupDrawData(this IList<MeshDrawData> meshDrawDatas, bool can32BitIndex)
331  {
332  var declGroups = CreateDeclarationMergeGroup(meshDrawDatas);
333  var groups = declGroups.SelectMany(x => CreateOptimizedMergeGroups(x, can32BitIndex)).ToList();
334  return groups.Select(x => MergeDrawData(x, can32BitIndex)).ToList();
335  }
336  }
337 }
static unsafe MeshDrawData MergeDrawData(IList< MeshDrawData > meshDrawDatas, bool can32BitIndex)
Transform a vertex buffer positions, normals, tangents and bitangents using the given matrix...
static List< List< MeshDrawData > > CreateOptimizedMergeGroups(IList< MeshDrawData > meshDrawDatas, bool can32BitIndex)
Create group of MeshDrawData that will be merged.
SiliconStudio.Paradox.Graphics.Data.IndexBufferBindingData IndexBuffer
Data field for SiliconStudio.Paradox.Effects.MeshDraw.IndexBuffer.
Definition: EngineData.cs:190
static byte[] CreateShortIndexBuffer(int offset, int count, byte[] baseIndices=null, bool is32Bit=true)
Create an short typed index buffer.
_In_ size_t count
Definition: DirectXTexP.h:174
System.Int32 Count
Data field for SiliconStudio.Paradox.Graphics.IndexBufferBinding.Count.
Data type for SiliconStudio.Paradox.Graphics.IndexBufferBinding.
Data type for SiliconStudio.Paradox.Graphics.VertexBufferBinding.
static byte[] CreateIntIndexBuffer(int offset, int count, byte[] baseIndices=null, bool is32Bits=true)
Create an int typed index buffer.
Data type for SiliconStudio.Paradox.Effects.MeshDraw.
Definition: EngineData.cs:165
PrimitiveType
Defines how vertex data is ordered.
System.Boolean Is32Bit
Data field for SiliconStudio.Paradox.Graphics.IndexBufferBinding.Is32Bit.
static bool IsIndexed(IList< MeshDrawData > meshDrawDatas)
Check if a index buffer will be needed for this merge group.
static List< List< MeshDrawData > > CreateDeclarationMergeGroup(IList< MeshDrawData > meshDrawDatas)
Group meshes that can be merged because they have the same vertex declaration.
Content of a GPU buffer (vertex buffer, index buffer, etc...).
Definition: BufferData.cs:10
static List< MeshDrawData > GroupDrawData(this IList< MeshDrawData > meshDrawDatas, bool can32BitIndex)
Group the meshes.