1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4 
5 Copyright (c) 2006-2021, assimp team
6 
7 
8 All rights reserved.
9 
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the
12 following conditions are met:
13 
14 * Redistributions of source code must retain the above
15   copyright notice, this list of conditions and the
16   following disclaimer.
17 
18 * Redistributions in binary form must reproduce the above
19   copyright notice, this list of conditions and the
20   following disclaimer in the documentation and/or other
21   materials provided with the distribution.
22 
23 * Neither the name of the assimp team, nor the names of its
24   contributors may be used to endorse or promote products
25   derived from this software without specific prior
26   written permission of the assimp team.
27 
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 
40 ----------------------------------------------------------------------
41 */
42 
43 #include "TargetAnimation.h"
44 #include <assimp/ai_assert.h>
45 #include <algorithm>
46 
47 using namespace Assimp;
48 
49 // ------------------------------------------------------------------------------------------------
KeyIterator(const std::vector<aiVectorKey> * _objPos,const std::vector<aiVectorKey> * _targetObjPos,const aiVector3D * defaultObjectPos,const aiVector3D * defaultTargetPos)50 KeyIterator::KeyIterator(const std::vector<aiVectorKey> *_objPos,
51     const std::vector<aiVectorKey> *_targetObjPos,
52     const aiVector3D *defaultObjectPos /*= nullptr*/,
53     const aiVector3D *defaultTargetPos /*= nullptr*/) :
54         reachedEnd(false),
55         curTime(-1.),
56         objPos(_objPos),
57         targetObjPos(_targetObjPos),
58         nextObjPos(0),
59         nextTargetObjPos(0) {
60     // Generate default transformation tracks if necessary
61     if (!objPos || objPos->empty()) {
62         defaultObjPos.resize(1);
63         defaultObjPos.front().mTime = 10e10;
64 
65         if (defaultObjectPos)
66             defaultObjPos.front().mValue = *defaultObjectPos;
67 
68         objPos = &defaultObjPos;
69     }
70     if (!targetObjPos || targetObjPos->empty()) {
71         defaultTargetObjPos.resize(1);
72         defaultTargetObjPos.front().mTime = 10e10;
73 
74         if (defaultTargetPos)
75             defaultTargetObjPos.front().mValue = *defaultTargetPos;
76 
77         targetObjPos = &defaultTargetObjPos;
78     }
79 }
80 
81 // ------------------------------------------------------------------------------------------------
82 template <class T>
Interpolate(const T & one,const T & two,ai_real val)83 inline T Interpolate(const T &one, const T &two, ai_real val) {
84     return one + (two - one) * val;
85 }
86 
87 // ------------------------------------------------------------------------------------------------
operator ++()88 void KeyIterator::operator++() {
89     // If we are already at the end of all keyframes, return
90     if (reachedEnd) {
91         return;
92     }
93 
94     // Now search in all arrays for the time value closest
95     // to our current position on the time line
96     double d0, d1;
97 
98     d0 = objPos->at(std::min(nextObjPos, static_cast<unsigned int>(objPos->size() - 1))).mTime;
99     d1 = targetObjPos->at(std::min(nextTargetObjPos, static_cast<unsigned int>(targetObjPos->size() - 1))).mTime;
100 
101     // Easiest case - all are identical. In this
102     // case we don't need to interpolate so we can
103     // return earlier
104     if (d0 == d1) {
105         curTime = d0;
106         curPosition = objPos->at(nextObjPos).mValue;
107         curTargetPosition = targetObjPos->at(nextTargetObjPos).mValue;
108 
109         // increment counters
110         if (objPos->size() != nextObjPos - 1)
111             ++nextObjPos;
112 
113         if (targetObjPos->size() != nextTargetObjPos - 1)
114             ++nextTargetObjPos;
115     }
116 
117     // An object position key is closest to us
118     else if (d0 < d1) {
119         curTime = d0;
120 
121         // interpolate the other
122         if (1 == targetObjPos->size() || !nextTargetObjPos) {
123             curTargetPosition = targetObjPos->at(0).mValue;
124         } else {
125             const aiVectorKey &last = targetObjPos->at(nextTargetObjPos);
126             const aiVectorKey &first = targetObjPos->at(nextTargetObjPos - 1);
127 
128             curTargetPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime)));
129         }
130 
131         if (objPos->size() != nextObjPos - 1)
132             ++nextObjPos;
133     }
134     // A target position key is closest to us
135     else {
136         curTime = d1;
137 
138         // interpolate the other
139         if (1 == objPos->size() || !nextObjPos) {
140             curPosition = objPos->at(0).mValue;
141         } else {
142             const aiVectorKey &last = objPos->at(nextObjPos);
143             const aiVectorKey &first = objPos->at(nextObjPos - 1);
144 
145             curPosition = Interpolate(first.mValue, last.mValue, (ai_real)((curTime - first.mTime) / (last.mTime - first.mTime)));
146         }
147 
148         if (targetObjPos->size() != nextTargetObjPos - 1)
149             ++nextTargetObjPos;
150     }
151 
152     if (nextObjPos >= objPos->size() - 1 &&
153             nextTargetObjPos >= targetObjPos->size() - 1) {
154         // We reached the very last keyframe
155         reachedEnd = true;
156     }
157 }
158 
159 // ------------------------------------------------------------------------------------------------
SetTargetAnimationChannel(const std::vector<aiVectorKey> * _targetPositions)160 void TargetAnimationHelper::SetTargetAnimationChannel(
161         const std::vector<aiVectorKey> *_targetPositions) {
162     ai_assert(nullptr != _targetPositions);
163 
164     targetPositions = _targetPositions;
165 }
166 
167 // ------------------------------------------------------------------------------------------------
SetMainAnimationChannel(const std::vector<aiVectorKey> * _objectPositions)168 void TargetAnimationHelper::SetMainAnimationChannel(
169         const std::vector<aiVectorKey> *_objectPositions) {
170     ai_assert(nullptr != _objectPositions);
171 
172     objectPositions = _objectPositions;
173 }
174 
175 // ------------------------------------------------------------------------------------------------
SetFixedMainAnimationChannel(const aiVector3D & fixed)176 void TargetAnimationHelper::SetFixedMainAnimationChannel(
177         const aiVector3D &fixed) {
178     objectPositions = nullptr; // just to avoid confusion
179     fixedMain = fixed;
180 }
181 
182 // ------------------------------------------------------------------------------------------------
Process(std::vector<aiVectorKey> * distanceTrack)183 void TargetAnimationHelper::Process(std::vector<aiVectorKey> *distanceTrack) {
184     ai_assert(nullptr != targetPositions);
185     ai_assert(nullptr != distanceTrack);
186 
187     // TODO: in most cases we won't need the extra array
188     std::vector<aiVectorKey> real;
189 
190     std::vector<aiVectorKey> *fill = (distanceTrack == objectPositions ? &real : distanceTrack);
191     fill->reserve(std::max(objectPositions->size(), targetPositions->size()));
192 
193     // Iterate through all object keys and interpolate their values if necessary.
194     // Then get the corresponding target position, compute the difference
195     // vector between object and target position. Then compute a rotation matrix
196     // that rotates the base vector of the object coordinate system at that time
197     // to match the diff vector.
198 
199     KeyIterator iter(objectPositions, targetPositions, &fixedMain);
200     for (; !iter.Finished(); ++iter) {
201         const aiVector3D &position = iter.GetCurPosition();
202         const aiVector3D &tposition = iter.GetCurTargetPosition();
203 
204         // diff vector
205         aiVector3D diff = tposition - position;
206         ai_real f = diff.Length();
207 
208         // output distance vector
209         if (f) {
210             fill->push_back(aiVectorKey());
211             aiVectorKey &v = fill->back();
212             v.mTime = iter.GetCurTime();
213             v.mValue = diff;
214 
215             diff /= f;
216         } else {
217             // FIXME: handle this
218         }
219 
220         // diff is now the vector in which our camera is pointing
221     }
222 
223     if (real.size()) {
224         *distanceTrack = real;
225     }
226 }
227