PageRenderTime 80ms CodeModel.GetById 43ms RepoModel.GetById 0ms app.codeStats 0ms

/SilverlightViewport/SilverlightViewport/Primitives3D/BezierPrimitive.cs

#
C# | 200 lines | 110 code | 32 blank | 58 comment | 9 complexity | 465bb3ff7e173e1178bd419e7ebd826d MD5 | raw file
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // BezierPrimitive.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using System.Diagnostics;
  12. using Microsoft.Xna.Framework;
  13. #endregion
  14. namespace Primitives3D
  15. {
  16. /// <summary>
  17. /// Base class for primitives that are made out of cubic bezier patches
  18. /// (a type of curved surface). This is used by the TeapotPrimitive.
  19. /// </summary>
  20. public abstract class BezierPrimitive : GeometricPrimitive
  21. {
  22. /// <summary>
  23. /// Creates indices for a patch that is tessellated at the specified level.
  24. /// </summary>
  25. protected void CreatePatchIndices(int tessellation, bool isMirrored)
  26. {
  27. int stride = tessellation + 1;
  28. for (int i = 0; i < tessellation; i++)
  29. {
  30. for (int j = 0; j < tessellation; j++)
  31. {
  32. // Make a list of six index values (two triangles).
  33. int[] indices =
  34. {
  35. i * stride + j,
  36. (i + 1) * stride + j,
  37. (i + 1) * stride + j + 1,
  38. i * stride + j,
  39. (i + 1) * stride + j + 1,
  40. i * stride + j + 1,
  41. };
  42. // If this patch is mirrored, reverse the
  43. // indices to keep the correct winding order.
  44. if (isMirrored)
  45. {
  46. Array.Reverse(indices);
  47. }
  48. // Create the indices.
  49. foreach (int index in indices)
  50. {
  51. AddIndex(CurrentVertex + index);
  52. }
  53. }
  54. }
  55. }
  56. /// <summary>
  57. /// Creates vertices for a patch that is tessellated at the specified level.
  58. /// </summary>
  59. protected void CreatePatchVertices(Vector3[] patch, int tessellation, bool isMirrored)
  60. {
  61. Debug.Assert(patch.Length == 16);
  62. for (int i = 0; i <= tessellation; i++)
  63. {
  64. float ti = (float)i / tessellation;
  65. for (int j = 0; j <= tessellation; j++)
  66. {
  67. float tj = (float)j / tessellation;
  68. // Perform four horizontal bezier interpolations
  69. // between the control points of this patch.
  70. Vector3 p1 = Bezier(patch[0], patch[1], patch[2], patch[3], ti);
  71. Vector3 p2 = Bezier(patch[4], patch[5], patch[6], patch[7], ti);
  72. Vector3 p3 = Bezier(patch[8], patch[9], patch[10], patch[11], ti);
  73. Vector3 p4 = Bezier(patch[12], patch[13], patch[14], patch[15], ti);
  74. // Perform a vertical interpolation between the results of the
  75. // previous horizontal interpolations, to compute the position.
  76. Vector3 position = Bezier(p1, p2, p3, p4, tj);
  77. // Perform another four bezier interpolations between the control
  78. // points, but this time vertically rather than horizontally.
  79. Vector3 q1 = Bezier(patch[0], patch[4], patch[8], patch[12], tj);
  80. Vector3 q2 = Bezier(patch[1], patch[5], patch[9], patch[13], tj);
  81. Vector3 q3 = Bezier(patch[2], patch[6], patch[10], patch[14], tj);
  82. Vector3 q4 = Bezier(patch[3], patch[7], patch[11], patch[15], tj);
  83. // Compute vertical and horizontal tangent vectors.
  84. Vector3 tangentA = BezierTangent(p1, p2, p3, p4, tj);
  85. Vector3 tangentB = BezierTangent(q1, q2, q3, q4, ti);
  86. // Cross the two tangent vectors to compute the normal.
  87. Vector3 normal = Vector3.Cross(tangentA, tangentB);
  88. if (normal.Length() > 0.0001f)
  89. {
  90. normal.Normalize();
  91. // If this patch is mirrored, we must invert the normal.
  92. if (isMirrored)
  93. normal = -normal;
  94. }
  95. else
  96. {
  97. // In a tidy and well constructed bezier patch, the preceding
  98. // normal computation will always work. But the classic teapot
  99. // model is not tidy or well constructed! At the top and bottom
  100. // of the teapot, it contains degenerate geometry where a patch
  101. // has several control points in the same place, which causes
  102. // the tangent computation to fail and produce a zero normal.
  103. // We 'fix' these cases by just hard-coding a normal that points
  104. // either straight up or straight down, depending on whether we
  105. // are on the top or bottom of the teapot. This is not a robust
  106. // solution for all possible degenerate bezier patches, but hey,
  107. // it's good enough to make the teapot work correctly!
  108. if (position.Y > 0)
  109. normal = Vector3.Up;
  110. else
  111. normal = Vector3.Down;
  112. }
  113. // Create the vertex.
  114. AddVertex(position, normal);
  115. }
  116. }
  117. }
  118. /// <summary>
  119. /// Performs a cubic bezier interpolation between four scalar control
  120. /// points, returning the value at the specified time (t ranges 0 to 1).
  121. /// </summary>
  122. static float Bezier(float p1, float p2, float p3, float p4, float t)
  123. {
  124. return p1 * (1 - t) * (1 - t) * (1 - t) +
  125. p2 * 3 * t * (1 - t) * (1 - t) +
  126. p3 * 3 * t * t * (1 - t) +
  127. p4 * t * t * t;
  128. }
  129. /// <summary>
  130. /// Performs a cubic bezier interpolation between four Vector3 control
  131. /// points, returning the value at the specified time (t ranges 0 to 1).
  132. /// </summary>
  133. static Vector3 Bezier(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 p4, float t)
  134. {
  135. Vector3 result = new Vector3();
  136. result.X = Bezier(p1.X, p2.X, p3.X, p4.X, t);
  137. result.Y = Bezier(p1.Y, p2.Y, p3.Y, p4.Y, t);
  138. result.Z = Bezier(p1.Z, p2.Z, p3.Z, p4.Z, t);
  139. return result;
  140. }
  141. /// <summary>
  142. /// Computes the tangent of a cubic bezier curve at the specified time,
  143. /// when given four scalar control points.
  144. /// </summary>
  145. static float BezierTangent(float p1, float p2, float p3, float p4, float t)
  146. {
  147. return p1 * (-1 + 2 * t - t * t) +
  148. p2 * (1 - 4 * t + 3 * t * t) +
  149. p3 * (2 * t - 3 * t * t) +
  150. p4 * (t * t);
  151. }
  152. /// <summary>
  153. /// Computes the tangent of a cubic bezier curve at the specified time,
  154. /// when given four Vector3 control points. This is used for calculating
  155. /// normals (by crossing the horizontal and vertical tangent vectors).
  156. /// </summary>
  157. static Vector3 BezierTangent(Vector3 p1, Vector3 p2,
  158. Vector3 p3, Vector3 p4, float t)
  159. {
  160. Vector3 result = new Vector3();
  161. result.X = BezierTangent(p1.X, p2.X, p3.X, p4.X, t);
  162. result.Y = BezierTangent(p1.Y, p2.Y, p3.Y, p4.Y, t);
  163. result.Z = BezierTangent(p1.Z, p2.Z, p3.Z, p4.Z, t);
  164. result.Normalize();
  165. return result;
  166. }
  167. }
  168. }