Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
IndexExtensions.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 using SiliconStudio.Paradox.Effects.Data;
7 using SiliconStudio.Paradox.Graphics;
8 using SiliconStudio.Core;
9 using SiliconStudio.Core.Mathematics;
10 using SiliconStudio.Paradox.Graphics.Data;
11 
12 namespace SiliconStudio.Paradox.Extensions
13 {
14  public static class IndexExtensions
15  {
16  /// <summary>
17  /// Generates an index buffer for this mesh data.
18  /// </summary>
19  /// <param name="meshData">The mesh data.</param>
20  public unsafe static void GenerateIndexBuffer(this MeshDrawData meshData)
21  {
22  // For now, require a MeshData with only one vertex buffer and no index buffer
23  if (meshData.VertexBuffers.Length != 1 || meshData.IndexBuffer != null)
24  throw new NotImplementedException();
25 
26  var oldVertexBuffer = meshData.VertexBuffers[0];
27  var vertexStride = oldVertexBuffer.Declaration.VertexStride;
28  var indexMapping = GenerateIndexMapping(oldVertexBuffer);
29  var vertices = indexMapping.Vertices;
30 
31  // Generate vertex buffer
32  var vertexBufferData = new byte[oldVertexBuffer.Declaration.VertexStride * indexMapping.Vertices.Length];
33  fixed (byte* oldVertexBufferDataStart = &oldVertexBuffer.Buffer.Value.Content[oldVertexBuffer.Offset])
34  fixed (byte* vertexBufferDataStart = &vertexBufferData[0])
35  {
36  var vertexBufferDataCurrent = vertexBufferDataStart;
37  for (int i = 0; i < vertices.Length; ++i)
38  {
39  Utilities.CopyMemory((IntPtr)vertexBufferDataCurrent, new IntPtr(&oldVertexBufferDataStart[vertexStride * vertices[i]]), vertexStride);
40  vertexBufferDataCurrent += vertexStride;
41  }
42  meshData.VertexBuffers[0] = new VertexBufferBindingData(new BufferData(BufferFlags.VertexBuffer, vertexBufferData), oldVertexBuffer.Declaration, indexMapping.Vertices.Length);
43  }
44 
45  // Generate index buffer
46  var indexBufferData = new byte[indexMapping.Indices.Length * Utilities.SizeOf<int>()];
47  fixed (int* indexDataStart = &indexMapping.Indices[0])
48  fixed (byte* indexBufferDataStart = &indexBufferData[0])
49  {
50  Utilities.CopyMemory((IntPtr)indexBufferDataStart, (IntPtr)indexDataStart, indexBufferData.Length);
51  meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), true, indexMapping.Indices.Length);
52  }
53  }
54 
55  /// <summary>
56  /// Compacts the index buffer from 32 bits to 16 bits per index, if possible.
57  /// </summary>
58  /// <param name="meshData">The mesh data.</param>
59  /// <returns>Returns true if index buffer was actually compacted.</returns>
60  /// <exception cref="System.NotImplementedException"></exception>
61  public static unsafe bool CompactIndexBuffer(this MeshDrawData meshData)
62  {
63  // Already processed?
64  if (meshData.IndexBuffer == null || !meshData.IndexBuffer.Is32Bit)
65  return false;
66 
67  // For now, require a MeshData with only one vertex buffer and no index buffer
68  if (meshData.VertexBuffers.Length != 1)
69  throw new NotImplementedException();
70 
71  var vertexBufferBinding = meshData.VertexBuffers[0];
72  var vertexCount = vertexBufferBinding.Count;
73 
74  // Can't compact?
75  // Note that 65536 could be valid, but 0xFFFF is kept for primitive restart in strips.
76  if (vertexCount >= 65536 || !meshData.IndexBuffer.Is32Bit)
77  return false;
78 
79  // Create new index buffer
80  var indexCount = meshData.IndexBuffer.Count;
81  var indexBufferData = new byte[indexCount * Utilities.SizeOf<ushort>()];
82  fixed (byte* oldIndexBufferDataStart = &meshData.IndexBuffer.Buffer.Value.Content[0])
83  fixed (byte* indexBufferDataStart = &indexBufferData[0])
84  {
85  var oldIndexBufferDataPtr = (int*)oldIndexBufferDataStart;
86  var indexBufferDataPtr = (ushort*)indexBufferDataStart;
87 
88  for (int i = 0; i < indexCount; ++i)
89  {
90  // This also works to convert 0xFFFFFFFF into 0xFFFF (primitive restart in strips).
91  *indexBufferDataPtr++ = (ushort)*oldIndexBufferDataPtr++;
92  }
93 
94  meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), false, indexCount);
95  }
96 
97  return true;
98  }
99 
100  /// <summary>
101  /// Generates the index buffer with dominant edge and vertex informations.
102  /// Each triangle gets its indices expanded to 12 control points, with 0 to 2 being original triangle,
103  /// 3 to 8 being dominant edges and 9 to 11 being dominant vertices.
104  /// </summary>
105  /// <param name="meshData">The mesh data.</param>
106  public static unsafe void GenerateIndexBufferAEN(this MeshDrawData meshData)
107  {
108  // For now, require a MeshData with only one vertex buffer and one index buffer
109  if (meshData.VertexBuffers.Length != 1 || meshData.IndexBuffer == null)
110  throw new NotImplementedException();
111 
112  // More info at http://developer.download.nvidia.com/whitepapers/2010/PN-AEN-Triangles-Whitepaper.pdf
113  // This implementation might need some performance improvements
114  var indexBuffer = meshData.IndexBuffer;
115 
116  var triangleCount = indexBuffer.Count / 3;
117  var newIndices = new int[triangleCount * 12];
118 
119  var positionMapping = GenerateIndexMapping(meshData.VertexBuffers[0], "POSITION");
120  var dominantEdges = new Dictionary<EdgeKeyAEN, EdgeAEN>();
121  var dominantVertices = new Dictionary<int, int>();
122 
123  fixed (byte* indexBufferStart = &indexBuffer.Buffer.Value.Content[indexBuffer.Offset])
124  {
125  var oldIndices = (int*)indexBufferStart;
126  var triangleIndices = stackalloc int[3];
127  var positionIndices = stackalloc int[3];
128 
129  // Step 2: prepare initial data
130  for (int i = 0; i < triangleCount; ++i)
131  {
132  triangleIndices[0] = oldIndices[i * 3 + 0];
133  triangleIndices[1] = oldIndices[i * 3 + 1];
134  triangleIndices[2] = oldIndices[i * 3 + 2];
135 
136  positionIndices[0] = positionMapping.Indices[triangleIndices[0]];
137  positionIndices[1] = positionMapping.Indices[triangleIndices[1]];
138  positionIndices[2] = positionMapping.Indices[triangleIndices[2]];
139 
140  newIndices[i * 12 + 0] = triangleIndices[0];
141  newIndices[i * 12 + 1] = triangleIndices[1];
142  newIndices[i * 12 + 2] = triangleIndices[2];
143  newIndices[i * 12 + 3] = triangleIndices[0];
144  newIndices[i * 12 + 4] = triangleIndices[1];
145  newIndices[i * 12 + 5] = triangleIndices[1];
146  newIndices[i * 12 + 6] = triangleIndices[2];
147  newIndices[i * 12 + 7] = triangleIndices[2];
148  newIndices[i * 12 + 8] = triangleIndices[0];
149  newIndices[i * 12 + 9] = triangleIndices[0];
150  newIndices[i * 12 + 10] = triangleIndices[1];
151  newIndices[i * 12 + 11] = triangleIndices[2];
152 
153  // Step 2b/2c: Build dominant vertex/edge list
154  for (int j = 0; j < 3; ++j)
155  {
156  dominantVertices[positionIndices[j]] = triangleIndices[j];
157 
158  var edge = new EdgeAEN(
159  triangleIndices[((j + 0) % 3)],
160  triangleIndices[((j + 1) % 3)],
161  positionIndices[((j + 0) % 3)],
162  positionIndices[((j + 1) % 3)]);
163 
164  dominantEdges[new EdgeKeyAEN(edge)] = edge;
165 
166  edge = edge.Reverse();
167  dominantEdges[new EdgeKeyAEN(edge)] = edge;
168  }
169  }
170 
171  // Step3: Find dominant vertex/edge
172  for (int i = 0; i < triangleCount; ++i)
173  {
174  triangleIndices[0] = oldIndices[i * 3 + 0];
175  triangleIndices[1] = oldIndices[i * 3 + 1];
176  triangleIndices[2] = oldIndices[i * 3 + 2];
177 
178  positionIndices[0] = positionMapping.Indices[triangleIndices[0]];
179  positionIndices[1] = positionMapping.Indices[triangleIndices[1]];
180  positionIndices[2] = positionMapping.Indices[triangleIndices[2]];
181 
182  for (int j = 0; j < 3; ++j)
183  {
184  // Dominant edge
185  int vertexKey;
186  if (dominantVertices.TryGetValue(positionIndices[j], out vertexKey))
187  {
188  newIndices[i * 12 + 9 + j] = vertexKey;
189  }
190 
191  // Dominant vertex
192  EdgeAEN edge;
193  var edgeKey = new EdgeKeyAEN(positionIndices[((j + 0) % 3)], positionIndices[((j + 1) % 3)]);
194  if (dominantEdges.TryGetValue(edgeKey, out edge))
195  {
196  newIndices[i * 12 + 3 + j * 2 + 0] = edge.Index0;
197  newIndices[i * 12 + 3 + j * 2 + 1] = edge.Index1;
198  }
199  }
200  }
201  }
202 
203  // Generate index buffer
204  var indexBufferData = new byte[triangleCount * 12 * Utilities.SizeOf<int>()];
205  fixed (int* indexDataStart = &newIndices[0])
206  fixed (byte* indexBufferDataStart = &indexBufferData[0])
207  {
208  Utilities.CopyMemory((IntPtr)indexBufferDataStart, (IntPtr)indexDataStart, indexBufferData.Length);
209  meshData.IndexBuffer = new IndexBufferBindingData(new BufferData(BufferFlags.IndexBuffer, indexBufferData), true, triangleCount * 12);
210  }
211 
212  meshData.DrawCount = triangleCount * 12;
213  meshData.PrimitiveType = PrimitiveType.PatchList.ControlPointCount(12);
214  }
215 
216  private struct EdgeKeyAEN
217  {
218  public readonly int PositionIndex0;
219  public readonly int PositionIndex1;
220 
221  public EdgeKeyAEN(int positionIndex0, int positionIndex1)
222  {
223  PositionIndex0 = positionIndex0;
224  PositionIndex1 = positionIndex1;
225  }
226 
227  public EdgeKeyAEN(EdgeAEN edge)
228  : this(edge.PositionIndex0, edge.PositionIndex1)
229  {
230  }
231 
232  public override int GetHashCode()
233  {
234  unchecked
235  {
236  return (PositionIndex0 * 397) ^ PositionIndex1;
237  }
238  }
239  }
240 
241  private struct EdgeAEN
242  {
243  public readonly int Index0;
244  public readonly int Index1;
245  public readonly int PositionIndex0;
246  public readonly int PositionIndex1;
247 
248  public EdgeAEN(int index0, int index1, int positionIndex0, int positionIndex1)
249  {
250  Index0 = index0;
251  Index1 = index1;
252  PositionIndex0 = positionIndex0;
253  PositionIndex1 = positionIndex1;
254  }
255 
256  public EdgeAEN Reverse()
257  {
258  return new EdgeAEN(Index1, Index0, PositionIndex1, PositionIndex0);
259  }
260  }
261 
262  /// <summary>
263  /// Generates an index mapping with the specified vertex elements.
264  /// If no vertex elements are specified, use the whole vertex.
265  /// </summary>
266  /// <param name="vertexBufferBinding">The vertex buffer binding.</param>
267  /// <param name="usages">The vertex element usages to consider.</param>
268  /// <returns></returns>
269  public static unsafe IndexMappingResult GenerateIndexMapping(this VertexBufferBindingData vertexBufferBinding, params string[] usages)
270  {
271  var bufferData = vertexBufferBinding.Buffer.Value.Content;
272  var vertexStride = vertexBufferBinding.Declaration.VertexStride;
273  var vertexCount = vertexBufferBinding.Count;
274  var activeBytes = stackalloc byte[vertexStride];
275 
276  var vertexMapping = new List<int>();
277  var indexMapping = new int[vertexCount];
278 
279  var mapping = new Dictionary<VertexKey, int>(new VertexKeyEqualityComparer(activeBytes, vertexStride));
280 
281  // Create a "mask" of part of the vertices that will be used
282  // TODO: Use bit packing?
283  for (int i = 0; i < vertexBufferBinding.Declaration.VertexStride; ++i)
284  {
285  activeBytes[i] = (byte)(usages.Length == 0 ? 1 : 0);
286  }
287 
288  foreach (var vertexElement in vertexBufferBinding.Declaration.EnumerateWithOffsets())
289  {
290  if (usages.Contains(vertexElement.VertexElement.SemanticAsText))
291  {
292  for (int i = 0; i < vertexElement.Size; ++i)
293  {
294  activeBytes[vertexElement.Offset + i] = 1;
295  }
296  }
297  }
298 
299  // Generate index buffer
300  fixed (byte* bufferPointerStart = &bufferData[vertexBufferBinding.Offset])
301  {
302  var bufferPointer = bufferPointerStart;
303 
304  for (int i = 0; i < vertexCount; ++i)
305  {
306  // Create VertexKey (will generate hash)
307  var vertexKey = new VertexKey(bufferPointer, activeBytes, vertexStride);
308 
309  // Get or create new index
310  int currentIndex;
311  if (!mapping.TryGetValue(vertexKey, out currentIndex))
312  {
313  currentIndex = vertexMapping.Count;
314  mapping.Add(vertexKey, currentIndex);
315  vertexMapping.Add(i);
316  }
317 
318  // Assign index in result buffer
319  indexMapping[i] = currentIndex;
320 
321  bufferPointer += vertexStride;
322  }
323  }
324 
325  return new IndexMappingResult { Vertices = vertexMapping.ToArray(), Indices = indexMapping };
326  }
327 
328  public struct IndexMappingResult
329  {
330  public int[] Vertices;
331  public int[] Indices;
332  }
333 
334  internal unsafe struct VertexKey
335  {
336  public byte* Vertex;
337  public int Hash;
338 
339  public VertexKey(byte* vertex, byte* activeBytes, int vertexStride)
340  {
341  Vertex = vertex;
342  unchecked
343  {
344  Hash = 0;
345  for (int i = 0; i < vertexStride; ++i)
346  {
347  if (*activeBytes++ != 0)
348  Hash = (Hash * 31) ^ *vertex;
349  vertex++;
350  }
351  }
352  }
353  }
354 
355  internal unsafe class VertexKeyEqualityComparer : EqualityComparer<VertexKey>
356  {
357  private byte* activeBytes;
358  private int vertexStride;
359 
360  public VertexKeyEqualityComparer(byte* activeBytes, int vertexStride)
361  {
362  this.activeBytes = activeBytes;
363  this.vertexStride = vertexStride;
364  }
365 
366  public override bool Equals(VertexKey x, VertexKey y)
367  {
368  var vertex1 = x.Vertex;
369  var vertex2 = y.Vertex;
370 
371  var currentActiveBytes = activeBytes;
372 
373  for (int i = 0; i < vertexStride; ++i)
374  {
375  if (*currentActiveBytes++ != 0)
376  {
377  if (*vertex1 != *vertex2)
378  return false;
379  }
380 
381  vertex1++;
382  vertex2++;
383  }
384 
385  return true;
386  }
387 
388  public override int GetHashCode(VertexKey obj)
389  {
390  return obj.Hash;
391  }
392  }
393  }
394 }
SiliconStudio.Paradox.Graphics.Data.VertexBufferBindingData[] VertexBuffers
Data field for SiliconStudio.Paradox.Effects.MeshDraw.VertexBuffers.
Definition: EngineData.cs:185
static unsafe void GenerateIndexBufferAEN(this MeshDrawData meshData)
Generates the index buffer with dominant edge and vertex informations. Each triangle gets its indices...
_In_ size_t _In_ DXGI_FORMAT _In_ size_t _In_ float size_t y
Definition: DirectXTexP.h:191
SiliconStudio.Paradox.Graphics.Data.IndexBufferBindingData IndexBuffer
Data field for SiliconStudio.Paradox.Effects.MeshDraw.IndexBuffer.
Definition: EngineData.cs:190
static unsafe bool CompactIndexBuffer(this MeshDrawData meshData)
Compacts the index buffer from 32 bits to 16 bits per index, if possible.
static unsafe void GenerateIndexBuffer(this MeshDrawData meshData)
Generates an index buffer for this mesh data.
Data type for SiliconStudio.Paradox.Graphics.IndexBufferBinding.
SiliconStudio.Core.Serialization.ContentReference< SiliconStudio.Paradox.Graphics.Data.BufferData > Buffer
Data field for SiliconStudio.Paradox.Graphics.IndexBufferBinding.Buffer.
Data type for SiliconStudio.Paradox.Graphics.VertexBufferBinding.
static unsafe IndexMappingResult GenerateIndexMapping(this VertexBufferBindingData vertexBufferBinding, params string[] usages)
Generates an index mapping with the specified vertex elements. If no vertex elements are specified...
Data type for SiliconStudio.Paradox.Effects.MeshDraw.
Definition: EngineData.cs:165
System.Boolean Is32Bit
Data field for SiliconStudio.Paradox.Graphics.IndexBufferBinding.Is32Bit.
Content of a GPU buffer (vertex buffer, index buffer, etc...).
Definition: BufferData.cs:10