| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- /*
- ** Command & Conquer Generals Zero Hour(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h"
- #include "Common/BezierSegment.h"
- #include "Common/BezFwdIterator.h"
- #include <D3DX8Math.h>
- //-------------------------------------------------------------------------------------------------
- BezierSegment::BezierSegment()
- {
- for(int i=0; i < 4; i++)
- m_controlPoints[i].zero();
- }
- //-------------------------------------------------------------------------------------------------
- BezierSegment::BezierSegment(Real x0, Real y0, Real z0,
- Real x1, Real y1, Real z1,
- Real x2, Real y2, Real z2,
- Real x3, Real y3, Real z3)
- {
- m_controlPoints[0].x = x0;
- m_controlPoints[0].y = y0;
- m_controlPoints[0].z = z0;
- m_controlPoints[1].x = x1;
- m_controlPoints[1].y = y1;
- m_controlPoints[1].z = z1;
- m_controlPoints[2].x = x2;
- m_controlPoints[2].y = y2;
- m_controlPoints[2].z = z2;
- m_controlPoints[3].x = x3;
- m_controlPoints[3].y = y3;
- m_controlPoints[3].z = z3;
- }
- BezierSegment::BezierSegment(Real cp[12])
- {
- m_controlPoints[0].x = cp[0];
- m_controlPoints[0].y = cp[1];
- m_controlPoints[0].z = cp[2];
- m_controlPoints[1].x = cp[3];
- m_controlPoints[1].y = cp[4];
- m_controlPoints[1].z = cp[5];
- m_controlPoints[2].x = cp[6];
- m_controlPoints[2].y = cp[7];
- m_controlPoints[2].z = cp[8];
- m_controlPoints[3].x = cp[9];
- m_controlPoints[3].y = cp[10];
- m_controlPoints[3].z = cp[11];
- }
- BezierSegment::BezierSegment(const Coord3D& cp0, const Coord3D& cp1, const Coord3D& cp2, const Coord3D& cp3)
- {
- m_controlPoints[0] = cp0;
- m_controlPoints[1] = cp1;
- m_controlPoints[2] = cp2;
- m_controlPoints[3] = cp3;
- }
- BezierSegment::BezierSegment(Coord3D cp[4])
- {
- m_controlPoints[0] = cp[0];
- m_controlPoints[1] = cp[1];
- m_controlPoints[2] = cp[2];
- m_controlPoints[3] = cp[3];
- }
- //-------------------------------------------------------------------------------------------------
- void BezierSegment::evaluateBezSegmentAtT(Real tValue, Coord3D *outResult) const
- {
- if (!outResult)
- return;
- D3DXVECTOR4 tVec(tValue * tValue * tValue, tValue * tValue, tValue, 1);
- D3DXVECTOR4 xCoords(m_controlPoints[0].x, m_controlPoints[1].x, m_controlPoints[2].x, m_controlPoints[3].x);
- D3DXVECTOR4 yCoords(m_controlPoints[0].y, m_controlPoints[1].y, m_controlPoints[2].y, m_controlPoints[3].y);
- D3DXVECTOR4 zCoords(m_controlPoints[0].z, m_controlPoints[1].z, m_controlPoints[2].z, m_controlPoints[3].z);
- D3DXVECTOR4 tResult;
- D3DXVec4Transform(&tResult, &tVec, &BezierSegment::s_bezBasisMatrix);
-
- outResult->x = D3DXVec4Dot(&xCoords, &tResult);
- outResult->y = D3DXVec4Dot(&yCoords, &tResult);
- outResult->z = D3DXVec4Dot(&zCoords, &tResult);
- }
- //-------------------------------------------------------------------------------------------------
- void BezierSegment::getSegmentPoints(Int numSegments, VecCoord3D *outResult) const
- {
- if (!outResult) {
- return;
- }
-
- outResult->clear();
- outResult->resize(numSegments);
- BezFwdIterator iter(numSegments, this);
- iter.start();
- Int i = 0;
- while (!iter.done()) {
- (*outResult)[i] = iter.getCurrent();
- ++i;
- iter.next();
- }
- }
- //-------------------------------------------------------------------------------------------------
- // This function isn't terribly fast. There are alternatives, and if this is too slow, we can
- // take a look at the other approximations.
- // There is no known close-form solution to this problem.
- Real BezierSegment::getApproximateLength(Real withinTolerance) const
- {
- /*
- How this works:
- We can determine the approximate length of a bezier segment by
- L0 = |(P0,P1)| + |(P1,P2)| + |(P2,P3)|
- L1 = |(P0,P3)|
-
- The length of the segment is approximately 1/2 L0 + 1/2 L1
- P1__P2
- / \
- P0----P3
- The error in this is L1 - L0. If the error is too much, then we subdivide the curve and
- try again.
- */
- Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
- m_controlPoints[1].y - m_controlPoints[0].y,
- m_controlPoints[1].z - m_controlPoints[0].z, };
- Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
- m_controlPoints[2].y - m_controlPoints[1].y,
- m_controlPoints[2].z - m_controlPoints[1].z, };
- Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
- m_controlPoints[3].y - m_controlPoints[2].y,
- m_controlPoints[3].z - m_controlPoints[2].z, };
- Coord3D p0p3 = { m_controlPoints[3].x - m_controlPoints[0].x,
- m_controlPoints[3].y - m_controlPoints[0].y,
- m_controlPoints[3].z - m_controlPoints[0].z, };
- Real length0 = p0p3.length();
- Real length1 = p0p1.length() + p1p2.length() + p2p3.length();
- if ((length1 - length0) > withinTolerance) {
- BezierSegment seg1, seg2;
- splitSegmentAtT(0.5f, seg1, seg2);
- return (seg1.getApproximateLength(withinTolerance) + seg2.getApproximateLength(withinTolerance));
- }
- return (INT_TO_REAL(length0 + length1) / 2.0f);
- }
- //-------------------------------------------------------------------------------------------------
- void BezierSegment::splitSegmentAtT(Real tValue, BezierSegment &outSeg1, BezierSegment &outSeg2) const
- {
- // I think there are faster ways to do this. Could someone clue me in?
- Coord3D p0p1 = { m_controlPoints[1].x - m_controlPoints[0].x,
- m_controlPoints[1].y - m_controlPoints[0].y,
- m_controlPoints[1].z - m_controlPoints[0].z, };
- Coord3D p1p2 = { m_controlPoints[2].x - m_controlPoints[1].x,
- m_controlPoints[2].y - m_controlPoints[1].y,
- m_controlPoints[2].z - m_controlPoints[1].z, };
- Coord3D p2p3 = { m_controlPoints[3].x - m_controlPoints[2].x,
- m_controlPoints[3].y - m_controlPoints[2].y,
- m_controlPoints[3].z - m_controlPoints[2].z, };
- p0p1.scale(tValue);
- p1p2.scale(tValue);
- p2p3.scale(tValue);
- p0p1.add(&m_controlPoints[0]);
- p1p2.add(&m_controlPoints[1]);
- p2p3.add(&m_controlPoints[2]);
- Coord3D triLeft = { p1p2.x - p0p1.x,
- p1p2.y - p0p1.y,
- p1p2.z - p0p1.z, };
- Coord3D triRight = { p2p3.x - p1p2.x,
- p2p3.y - p1p2.y,
- p2p3.z - p1p2.z, };
- triLeft.scale(tValue);
- triRight.scale(tValue);
- triLeft.add(&p0p1);
- triRight.add(&p1p2);
- outSeg1.m_controlPoints[0] = m_controlPoints[0];
- outSeg1.m_controlPoints[1] = p0p1;
- outSeg1.m_controlPoints[2] = triLeft;
- evaluateBezSegmentAtT(tValue, &outSeg1.m_controlPoints[3]);
- outSeg2.m_controlPoints[0] = outSeg1.m_controlPoints[3];
- outSeg2.m_controlPoints[1] = triRight;
- outSeg2.m_controlPoints[2] = p2p3;
- outSeg2.m_controlPoints[3] = m_controlPoints[3];
- }
- //-------------------------------------------------------------------------------------------------
- // The Basis Matrix for a bezier segment
- const D3DXMATRIX BezierSegment::s_bezBasisMatrix(
- -1.0f, 3.0f, -3.0f, 1.0f,
- 3.0f, -6.0f, 3.0f, 0.0f,
- -3.0f, 3.0f, 0.0f, 0.0f,
- 1.0f, 0.0f, 0.0f, 0.0f
- );
|