BezierSegment.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. #include "PreRTS.h"
  24. #include "Common/BezierSegment.h"
  25. #include "Common/BezFwdIterator.h"
  26. #include <D3DX8Math.h>
  27. //-------------------------------------------------------------------------------------------------
  28. BezierSegment::BezierSegment()
  29. {
  30. for(int i=0; i < 4; i++)
  31. m_controlPoints[i].zero();
  32. }
  33. //-------------------------------------------------------------------------------------------------
  34. BezierSegment::BezierSegment(Real x0, Real y0, Real z0,
  35. Real x1, Real y1, Real z1,
  36. Real x2, Real y2, Real z2,
  37. Real x3, Real y3, Real z3)
  38. {
  39. m_controlPoints[0].x = x0;
  40. m_controlPoints[0].y = y0;
  41. m_controlPoints[0].z = z0;
  42. m_controlPoints[1].x = x1;
  43. m_controlPoints[1].y = y1;
  44. m_controlPoints[1].z = z1;
  45. m_controlPoints[2].x = x2;
  46. m_controlPoints[2].y = y2;
  47. m_controlPoints[2].z = z2;
  48. m_controlPoints[3].x = x3;
  49. m_controlPoints[3].y = y3;
  50. m_controlPoints[3].z = z3;
  51. }
  52. BezierSegment::BezierSegment(Real cp[12])
  53. {
  54. m_controlPoints[0].x = cp[0];
  55. m_controlPoints[0].y = cp[1];
  56. m_controlPoints[0].z = cp[2];
  57. m_controlPoints[1].x = cp[3];
  58. m_controlPoints[1].y = cp[4];
  59. m_controlPoints[1].z = cp[5];
  60. m_controlPoints[2].x = cp[6];
  61. m_controlPoints[2].y = cp[7];
  62. m_controlPoints[2].z = cp[8];
  63. m_controlPoints[3].x = cp[9];
  64. m_controlPoints[3].y = cp[10];
  65. m_controlPoints[3].z = cp[11];
  66. }
  67. BezierSegment::BezierSegment(const Coord3D& cp0, const Coord3D& cp1, const Coord3D& cp2, const Coord3D& cp3)
  68. {
  69. m_controlPoints[0] = cp0;
  70. m_controlPoints[1] = cp1;
  71. m_controlPoints[2] = cp2;
  72. m_controlPoints[3] = cp3;
  73. }
  74. BezierSegment::BezierSegment(Coord3D cp[4])
  75. {
  76. m_controlPoints[0] = cp[0];
  77. m_controlPoints[1] = cp[1];
  78. m_controlPoints[2] = cp[2];
  79. m_controlPoints[3] = cp[3];
  80. }
  81. //-------------------------------------------------------------------------------------------------
  82. void BezierSegment::evaluateBezSegmentAtT(Real tValue, Coord3D *outResult) const
  83. {
  84. if (!outResult)
  85. return;
  86. D3DXVECTOR4 tVec(tValue * tValue * tValue, tValue * tValue, tValue, 1);
  87. D3DXVECTOR4 xCoords(m_controlPoints[0].x, m_controlPoints[1].x, m_controlPoints[2].x, m_controlPoints[3].x);
  88. D3DXVECTOR4 yCoords(m_controlPoints[0].y, m_controlPoints[1].y, m_controlPoints[2].y, m_controlPoints[3].y);
  89. D3DXVECTOR4 zCoords(m_controlPoints[0].z, m_controlPoints[1].z, m_controlPoints[2].z, m_controlPoints[3].z);
  90. D3DXVECTOR4 tResult;
  91. D3DXVec4Transform(&tResult, &tVec, &BezierSegment::s_bezBasisMatrix);
  92. outResult->x = D3DXVec4Dot(&xCoords, &tResult);
  93. outResult->y = D3DXVec4Dot(&yCoords, &tResult);
  94. outResult->z = D3DXVec4Dot(&zCoords, &tResult);
  95. }
  96. //-------------------------------------------------------------------------------------------------
  97. void BezierSegment::getSegmentPoints(Int numSegments, VecCoord3D *outResult) const
  98. {
  99. if (!outResult) {
  100. return;
  101. }
  102. outResult->clear();
  103. outResult->resize(numSegments);
  104. BezFwdIterator iter(numSegments, this);
  105. iter.start();
  106. Int i = 0;
  107. while (!iter.done()) {
  108. (*outResult)[i] = iter.getCurrent();
  109. ++i;
  110. iter.next();
  111. }
  112. }
  113. //-------------------------------------------------------------------------------------------------
  114. // This function isn't terribly fast. There are alternatives, and if this is too slow, we can
  115. // take a look at the other approximations.
  116. // There is no known close-form solution to this problem.
  117. Real BezierSegment::getApproximateLength(Real withinTolerance) const
  118. {
  119. /*
  120. How this works:
  121. We can determine the approximate length of a bezier segment by
  122. L0 = |(P0,P1)| + |(P1,P2)| + |(P2,P3)|
  123. L1 = |(P0,P3)|
  124. The length of the segment is approximately 1/2 L0 + 1/2 L1
  125. P1__P2
  126. / \
  127. P0----P3
  128. The error in this is L1 - L0. If the error is too much, then we subdivide the curve and
  129. try again.
  130. */
  131. Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
  132. m_controlPoints[1].y - m_controlPoints[0].y,
  133. m_controlPoints[1].z - m_controlPoints[0].z, };
  134. Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
  135. m_controlPoints[2].y - m_controlPoints[1].y,
  136. m_controlPoints[2].z - m_controlPoints[1].z, };
  137. Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
  138. m_controlPoints[3].y - m_controlPoints[2].y,
  139. m_controlPoints[3].z - m_controlPoints[2].z, };
  140. Coord3D p0p3 = { m_controlPoints[3].x - m_controlPoints[0].x,
  141. m_controlPoints[3].y - m_controlPoints[0].y,
  142. m_controlPoints[3].z - m_controlPoints[0].z, };
  143. Real length0 = p0p3.length();
  144. Real length1 = p0p1.length() + p1p2.length() + p2p3.length();
  145. if ((length1 - length0) > withinTolerance) {
  146. BezierSegment seg1, seg2;
  147. splitSegmentAtT(0.5f, seg1, seg2);
  148. return (seg1.getApproximateLength(withinTolerance) + seg2.getApproximateLength(withinTolerance));
  149. }
  150. return (INT_TO_REAL(length0 + length1) / 2.0f);
  151. }
  152. //-------------------------------------------------------------------------------------------------
  153. void BezierSegment::splitSegmentAtT(Real tValue, BezierSegment &outSeg1, BezierSegment &outSeg2) const
  154. {
  155. // I think there are faster ways to do this. Could someone clue me in?
  156. Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
  157. m_controlPoints[1].y - m_controlPoints[0].y,
  158. m_controlPoints[1].z - m_controlPoints[0].z, };
  159. Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
  160. m_controlPoints[2].y - m_controlPoints[1].y,
  161. m_controlPoints[2].z - m_controlPoints[1].z, };
  162. Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
  163. m_controlPoints[3].y - m_controlPoints[2].y,
  164. m_controlPoints[3].z - m_controlPoints[2].z, };
  165. p0p1.scale(tValue);
  166. p1p2.scale(tValue);
  167. p2p3.scale(tValue);
  168. p0p1.add(&m_controlPoints[0]);
  169. p1p2.add(&m_controlPoints[1]);
  170. p2p3.add(&m_controlPoints[2]);
  171. Coord3D triLeft = { p1p2.x - p0p1.x,
  172. p1p2.y - p0p1.y,
  173. p1p2.z - p0p1.z, };
  174. Coord3D triRight = { p2p3.x - p1p2.x,
  175. p2p3.y - p1p2.y,
  176. p2p3.z - p1p2.z, };
  177. triLeft.scale(tValue);
  178. triRight.scale(tValue);
  179. triLeft.add(&p0p1);
  180. triRight.add(&p1p2);
  181. outSeg1.m_controlPoints[0] = m_controlPoints[0];
  182. outSeg1.m_controlPoints[1] = p0p1;
  183. outSeg1.m_controlPoints[2] = triLeft;
  184. evaluateBezSegmentAtT(tValue, &outSeg1.m_controlPoints[3]);
  185. outSeg2.m_controlPoints[0] = outSeg1.m_controlPoints[3];
  186. outSeg2.m_controlPoints[1] = triRight;
  187. outSeg2.m_controlPoints[2] = p2p3;
  188. outSeg2.m_controlPoints[3] = m_controlPoints[3];
  189. }
  190. //-------------------------------------------------------------------------------------------------
  191. // The Basis Matrix for a bezier segment
  192. const D3DXMATRIX BezierSegment::s_bezBasisMatrix(
  193. -1.0f, 3.0f, -3.0f, 1.0f,
  194. 3.0f, -6.0f, 3.0f, 0.0f,
  195. -3.0f, 3.0f, 0.0f, 0.0f,
  196. 1.0f, 0.0f, 0.0f, 0.0f
  197. );