/src/ogre/OgreMain/src/OgreRotationSpline.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | #include "OgreRotationalSpline.h" |
30 | | |
31 | | |
32 | | |
33 | | namespace Ogre { |
34 | | |
35 | | //--------------------------------------------------------------------- |
36 | | RotationalSpline::RotationalSpline() |
37 | 0 | : mAutoCalc(true) |
38 | 0 | { |
39 | 0 | } |
40 | | //--------------------------------------------------------------------- |
41 | | RotationalSpline::~RotationalSpline() |
42 | 0 | { |
43 | 0 | } |
44 | | //--------------------------------------------------------------------- |
45 | | void RotationalSpline::addPoint(const Quaternion& p) |
46 | 0 | { |
47 | 0 | mPoints.push_back(p); |
48 | 0 | if (mAutoCalc) |
49 | 0 | { |
50 | 0 | recalcTangents(); |
51 | 0 | } |
52 | 0 | } |
53 | | //--------------------------------------------------------------------- |
54 | | Quaternion RotationalSpline::interpolate(Real t, bool useShortestPath) |
55 | 0 | { |
56 | | // Work out which segment this is in |
57 | 0 | Real fSeg = t * (mPoints.size() - 1); |
58 | 0 | unsigned int segIdx = (unsigned int)fSeg; |
59 | | // Apportion t |
60 | 0 | t = fSeg - segIdx; |
61 | |
|
62 | 0 | return interpolate(segIdx, t, useShortestPath); |
63 | |
|
64 | 0 | } |
65 | | //--------------------------------------------------------------------- |
66 | | Quaternion RotationalSpline::interpolate(unsigned int fromIndex, Real t, |
67 | | bool useShortestPath) |
68 | 0 | { |
69 | | // Bounds check |
70 | 0 | assert (fromIndex < mPoints.size() && |
71 | 0 | "fromIndex out of bounds"); |
72 | |
|
73 | 0 | if ((fromIndex + 1) == mPoints.size()) |
74 | 0 | { |
75 | | // Duff request, cannot blend to nothing |
76 | | // Just return source |
77 | 0 | return mPoints[fromIndex]; |
78 | |
|
79 | 0 | } |
80 | | // Fast special cases |
81 | 0 | if (t == 0.0f) |
82 | 0 | { |
83 | 0 | return mPoints[fromIndex]; |
84 | 0 | } |
85 | 0 | else if(t == 1.0f) |
86 | 0 | { |
87 | 0 | return mPoints[fromIndex + 1]; |
88 | 0 | } |
89 | | |
90 | | // Real interpolation |
91 | | // Use squad using tangents we've already set up |
92 | 0 | Quaternion &p = mPoints[fromIndex]; |
93 | 0 | Quaternion &q = mPoints[fromIndex+1]; |
94 | 0 | Quaternion &a = mTangents[fromIndex]; |
95 | 0 | Quaternion &b = mTangents[fromIndex+1]; |
96 | | |
97 | | // NB interpolate to nearest rotation |
98 | 0 | return Quaternion::Squad(t, p, a, b, q, useShortestPath); |
99 | |
|
100 | 0 | } |
101 | | //--------------------------------------------------------------------- |
102 | | void RotationalSpline::recalcTangents(void) |
103 | 0 | { |
104 | | // ShoeMake (1987) approach |
105 | | // Just like Catmull-Rom really, just more gnarly |
106 | | // And no, I don't understand how to derive this! |
107 | | // |
108 | | // let p = point[i], pInv = p.Inverse |
109 | | // tangent[i] = p * exp( -0.25 * ( log(pInv * point[i+1]) + log(pInv * point[i-1]) ) ) |
110 | | // |
111 | | // Assume endpoint tangents are parallel with line with neighbour |
112 | |
|
113 | 0 | unsigned int i, numPoints; |
114 | 0 | bool isClosed; |
115 | |
|
116 | 0 | numPoints = (unsigned int)mPoints.size(); |
117 | |
|
118 | 0 | if (numPoints < 2) |
119 | 0 | { |
120 | | // Can't do anything yet |
121 | 0 | return; |
122 | 0 | } |
123 | | |
124 | 0 | mTangents.resize(numPoints); |
125 | |
|
126 | 0 | if (mPoints[0] == mPoints[numPoints-1]) |
127 | 0 | { |
128 | 0 | isClosed = true; |
129 | 0 | } |
130 | 0 | else |
131 | 0 | { |
132 | 0 | isClosed = false; |
133 | 0 | } |
134 | |
|
135 | 0 | Quaternion invp, part1, part2, preExp; |
136 | 0 | for(i = 0; i < numPoints; ++i) |
137 | 0 | { |
138 | 0 | Quaternion &p = mPoints[i]; |
139 | 0 | invp = p.Inverse(); |
140 | |
|
141 | 0 | if (i ==0) |
142 | 0 | { |
143 | | // special case start |
144 | 0 | part1 = (invp * mPoints[i+1]).Log(); |
145 | 0 | if (isClosed) |
146 | 0 | { |
147 | | // Use numPoints-2 since numPoints-1 == end == start == this one |
148 | 0 | part2 = (invp * mPoints[numPoints-2]).Log(); |
149 | 0 | } |
150 | 0 | else |
151 | 0 | { |
152 | 0 | part2 = (invp * p).Log(); |
153 | 0 | } |
154 | 0 | } |
155 | 0 | else if (i == numPoints-1) |
156 | 0 | { |
157 | | // special case end |
158 | 0 | if (isClosed) |
159 | 0 | { |
160 | | // Wrap to [1] (not [0], this is the same as end == this one) |
161 | 0 | part1 = (invp * mPoints[1]).Log(); |
162 | 0 | } |
163 | 0 | else |
164 | 0 | { |
165 | 0 | part1 = (invp * p).Log(); |
166 | 0 | } |
167 | 0 | part2 = (invp * mPoints[i-1]).Log(); |
168 | 0 | } |
169 | 0 | else |
170 | 0 | { |
171 | 0 | part1 = (invp * mPoints[i+1]).Log(); |
172 | 0 | part2 = (invp * mPoints[i-1]).Log(); |
173 | 0 | } |
174 | |
|
175 | 0 | preExp = -0.25 * (part1 + part2); |
176 | 0 | mTangents[i] = p * preExp.Exp(); |
177 | | |
178 | 0 | } |
179 | | |
180 | | |
181 | |
|
182 | 0 | } |
183 | | //--------------------------------------------------------------------- |
184 | | const Quaternion& RotationalSpline::getPoint(unsigned short index) const |
185 | 0 | { |
186 | 0 | assert (index < mPoints.size() && "Point index is out of bounds!!"); |
187 | |
|
188 | 0 | return mPoints[index]; |
189 | 0 | } |
190 | | //--------------------------------------------------------------------- |
191 | | unsigned short RotationalSpline::getNumPoints(void) const |
192 | 0 | { |
193 | 0 | return (unsigned short)mPoints.size(); |
194 | 0 | } |
195 | | //--------------------------------------------------------------------- |
196 | | void RotationalSpline::clear(void) |
197 | 0 | { |
198 | 0 | mPoints.clear(); |
199 | 0 | mTangents.clear(); |
200 | 0 | } |
201 | | //--------------------------------------------------------------------- |
202 | | void RotationalSpline::updatePoint(unsigned short index, const Quaternion& value) |
203 | 0 | { |
204 | 0 | assert (index < mPoints.size() && "Point index is out of bounds!!"); |
205 | |
|
206 | 0 | mPoints[index] = value; |
207 | 0 | if (mAutoCalc) |
208 | 0 | { |
209 | 0 | recalcTangents(); |
210 | 0 | } |
211 | 0 | } |
212 | | //--------------------------------------------------------------------- |
213 | | void RotationalSpline::setAutoCalculate(bool autoCalc) |
214 | 0 | { |
215 | 0 | mAutoCalc = autoCalc; |
216 | 0 | } |
217 | | //--------------------------------------------------------------------- |
218 | | |
219 | | |
220 | | |
221 | | } |
222 | | |
223 | | |
224 | | |
225 | | |