Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
TNBExtensions.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.IO;
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.Core.Serialization;
11 using SiliconStudio.Core.Serialization.Serializers;
12 using SiliconStudio.Paradox.Graphics.Data;
13 
14 namespace SiliconStudio.Paradox.Extensions
15 {
16  public static class TNBExtensions
17  {
18  /// <summary>
19  /// Generates the tangents and binormals for this mesh data.
20  /// Tangents and bitangents will be encoded as float4:
21  /// float3 for tangent and an additional float for handedness (1 or -1),
22  /// so that bitangent can be reconstructed.
23  /// More info at http://www.terathon.com/code/tangent.html
24  /// </summary>
25  /// <param name="meshData">The mesh data.</param>
26  public static unsafe void GenerateTangentBinormal(this MeshDrawData meshData)
27  {
28  if (!meshData.IsSimple())
29  throw new ArgumentException("meshData is not simple.");
30 
31  if (meshData.PrimitiveType != PrimitiveType.TriangleList
32  && meshData.PrimitiveType != PrimitiveType.TriangleListWithAdjacency)
33  throw new NotImplementedException();
34 
35  var vertexBufferBinding = meshData.VertexBuffers[0];
36  var indexBufferBinding = meshData.IndexBuffer;
37  var indexData = indexBufferBinding != null ? indexBufferBinding.Buffer.Value.Content : null;
38 
39  var oldVertexStride = vertexBufferBinding.Declaration.VertexStride;
40  var bufferData = vertexBufferBinding.Buffer.Value.Content;
41 
42  // TODO: Usage index in key
43  var offsetMapping = vertexBufferBinding.Declaration
44  .EnumerateWithOffsets()
45  .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset);
46 
47  var positionOffset = offsetMapping["POSITION"];
48  var uvOffset = offsetMapping[VertexElementUsage.TextureCoordinate];
49  var normalOffset = offsetMapping[VertexElementUsage.Normal];
50 
51  // Add tangent to vertex declaration
52  var vertexElements = vertexBufferBinding.Declaration.VertexElements.ToList();
53  if (!offsetMapping.ContainsKey(VertexElementUsage.Tangent))
54  vertexElements.Add(VertexElement.Tangent<Vector4>());
55  vertexBufferBinding.Declaration = new VertexDeclaration(vertexElements.ToArray());
56  var newVertexStride = vertexBufferBinding.Declaration.VertexStride;
57 
58  // Update mapping
59  offsetMapping = vertexBufferBinding.Declaration
60  .EnumerateWithOffsets()
61  .ToDictionary(x => x.VertexElement.SemanticAsText, x => x.Offset);
62 
63  var tangentOffset = offsetMapping[VertexElementUsage.Tangent];
64 
65  var newBufferData = new byte[vertexBufferBinding.Count * newVertexStride];
66 
67  var tangents = new Vector3[vertexBufferBinding.Count];
68  var bitangents = new Vector3[vertexBufferBinding.Count];
69 
70  fixed (byte* indexBufferStart = indexData)
71  fixed (byte* oldBuffer = &bufferData[vertexBufferBinding.Offset])
72  fixed (byte* newBuffer = &newBufferData[0])
73  {
74  var indexBuffer32 = indexBufferBinding != null && indexBufferBinding.Is32Bit ? (int*)indexBufferStart : null;
75  var indexBuffer16 = indexBufferBinding != null && !indexBufferBinding.Is32Bit ? (short*)indexBufferStart : null;
76 
77  var indexCount = indexBufferBinding != null ? indexBufferBinding.Count : vertexBufferBinding.Count;
78 
79  for (int i = 0; i < indexCount; i += 3)
80  {
81  // Get indices
82  int index1 = i + 0;
83  int index2 = i + 1;
84  int index3 = i + 2;
85 
86  if (indexBuffer32 != null)
87  {
88  index1 = indexBuffer32[index1];
89  index2 = indexBuffer32[index2];
90  index3 = indexBuffer32[index3];
91  }
92  else if (indexBuffer16 != null)
93  {
94  index1 = indexBuffer16[index1];
95  index2 = indexBuffer16[index2];
96  index3 = indexBuffer16[index3];
97  }
98 
99  int vertexOffset1 = index1 * oldVertexStride;
100  int vertexOffset2 = index2 * oldVertexStride;
101  int vertexOffset3 = index3 * oldVertexStride;
102 
103  // Get positions
104  var position1 = (Vector3*)&oldBuffer[vertexOffset1 + positionOffset];
105  var position2 = (Vector3*)&oldBuffer[vertexOffset2 + positionOffset];
106  var position3 = (Vector3*)&oldBuffer[vertexOffset3 + positionOffset];
107 
108  // Get texture coordinates
109  var uv1 = (Vector3*)&oldBuffer[vertexOffset1 + uvOffset];
110  var uv2 = (Vector3*)&oldBuffer[vertexOffset2 + uvOffset];
111  var uv3 = (Vector3*)&oldBuffer[vertexOffset3 + uvOffset];
112 
113  // Calculate position and UV vectors from vertex 1 to vertex 2 and 3
114  var edge1 = *position2 - *position1;
115  var edge2 = *position3 - *position1;
116  var uvEdge1 = *uv2 - *uv1;
117  var uvEdge2 = *uv3 - *uv1;
118 
119  var t = Vector3.Normalize(uvEdge2.Y * edge1 - uvEdge1.Y * edge2);
120  var b = Vector3.Normalize(uvEdge1.X * edge2 - uvEdge2.X * edge1);
121 
122  // Contribute to every vertex
123  tangents[index1] += t;
124  tangents[index2] += t;
125  tangents[index3] += t;
126 
127  bitangents[index1] += b;
128  bitangents[index2] += b;
129  bitangents[index3] += b;
130  }
131 
132  var oldVertexOffset = 0;
133  var newVertexOffset = 0;
134  for (int i = 0; i < vertexBufferBinding.Count; ++i)
135  {
136  Utilities.CopyMemory(new IntPtr(&newBuffer[newVertexOffset]), new IntPtr(&oldBuffer[oldVertexOffset]), oldVertexStride);
137 
138  var normal = *(Vector3*)&oldBuffer[oldVertexOffset + normalOffset];
139  var target = ((float*)(&newBuffer[newVertexOffset + tangentOffset]));
140 
141  var tangent = -tangents[i];
142  var bitangent = bitangents[i];
143 
144  // Gram-Schmidt orthogonalize
145  *((Vector3*)target) = Vector3.Normalize(tangent - normal * Vector3.Dot(normal, tangent));
146 
147  // Calculate handedness
148  target[3] = Vector3.Dot(Vector3.Cross(normal, tangent), bitangent) < 0.0f ? -1.0f : 1.0f;
149 
150  oldVertexOffset += oldVertexStride;
151  newVertexOffset += newVertexStride;
152  }
153  }
154 
155  vertexBufferBinding.Offset = 0;
156  vertexBufferBinding.Buffer = new BufferData(BufferFlags.VertexBuffer, newBufferData);
157  }
158  }
159 }
The layout of a vertex buffer with a set of VertexElement.
function b
static VertexElement Tangent(PixelFormat format, int offsetInBytes=AppendAligned)
Declares a VertexElement with the semantic "TANGENT".
static readonly string Tangent
Vertex tangent data.
SiliconStudio.Paradox.Graphics.PrimitiveType PrimitiveType
Data field for SiliconStudio.Paradox.Effects.MeshDraw.PrimitiveType.
Definition: EngineData.cs:170
Represents a three dimensional mathematical vector.
Definition: Vector3.cs:42
TextureCoordinate
The texture coordinate.
Represents a four dimensional mathematical vector.
Definition: Vector4.cs:42
static unsafe void GenerateTangentBinormal(this MeshDrawData meshData)
Generates the tangents and binormals for this mesh data. Tangents and bitangents will be encoded as f...
Data type for SiliconStudio.Paradox.Effects.MeshDraw.
Definition: EngineData.cs:165
PrimitiveType
Defines how vertex data is ordered.
Content of a GPU buffer (vertex buffer, index buffer, etc...).
Definition: BufferData.cs:10
A description of a single element for the input-assembler stage. This structure is related to Direct3...