Motive Animation System
An open source project by FPL.
 All Classes Functions Variables Typedefs Friends Pages
init.h
1 // Copyright 2014 Google Inc. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef MOTIVE_INIT_H_
16 #define MOTIVE_INIT_H_
17 
18 #include "mathfu/constants.h"
19 #include "motive/math/range.h"
20 #include "motive/math/vector_converter.h"
21 #include "motive/util.h"
22 
23 namespace motive {
24 
25 class RigAnim;
26 
27 enum MatrixOperationType {
28  kInvalidMatrixOperation,
29  kRotateAboutX,
30  kRotateAboutY,
31  kRotateAboutZ,
32  kTranslateX,
33  kTranslateY,
34  kTranslateZ,
35  kScaleX,
36  kScaleY,
37  kScaleZ,
38  kScaleUniformly,
39  kNumMatrixOperationTypes
40 };
41 
42 /// Returns true if the operation is a rotate.
43 inline bool RotateOp(MatrixOperationType op) {
44  return kRotateAboutX <= op && op <= kRotateAboutZ;
45 }
46 
47 /// Returns true if the operation is a translate.
48 inline bool TranslateOp(MatrixOperationType op) {
49  return kTranslateX <= op && op <= kTranslateZ;
50 }
51 
52 /// Returns true if the operation is a scale.
53 inline bool ScaleOp(MatrixOperationType op) {
54  return kScaleX <= op && op <= kScaleUniformly;
55 }
56 
57 /// Returns the default value of the operation. That is, the value of the
58 /// operation that does nothing to the transformation. Any operation that
59 /// constantly returns the default value can be removed.
60 inline float OperationDefaultValue(MatrixOperationType op) {
61  return ScaleOp(op) ? 1.0f : 0.0f;
62 }
63 
64 /// Returns the range of the matrix operation's spline. Most ranges are just
65 /// the extents of the splines, but rotations we want to normalize within
66 /// +-pi before blending to another curve.
67 inline Range RangeOfOp(MatrixOperationType op) {
68  return RotateOp(op) ? kAngleRange : kInvalidRange;
69 }
70 
71 /// Return a string with the operation name. Used for debugging.
72 const char* MatrixOpName(const MatrixOperationType op);
73 
74 /// @class SimpleInit
75 /// @brief Base class of Init classes for MotiveProcessors that derive from
76 /// SimpleProcessorTemplate.
77 ///
78 /// You cannot initialize a Motivator with this class because it has no
79 /// MotivatorType. Instead, use one of the Init classes below that derive from
80 /// SimpleInit.
81 struct SimpleInit : public MotivatorInit {
82  explicit SimpleInit(MotivatorType type)
83  : MotivatorInit(type),
84  start_values(nullptr),
85  start_derivatives(nullptr) {}
86 
87  SimpleInit(MotivatorType type, const float* start_values,
88  const float* start_derivatives = nullptr)
89  : MotivatorInit(type),
90  start_values(start_values),
92 
93  /// The starting value of each curve for each dimension. Array of length equal
94  /// to the number of dimensions. This points at external values and the caller
95  /// is responsible for ensuring these external values live as long as this
96  /// struct.
97  const float* start_values;
98 
99  /// The starting derivative of each curve for each dimension. Array of length
100  /// equal to the number of dimensions. This points at external values and the
101  /// caller is responsible for ensuring these external values live as long as
102  /// this struct.
103  const float* start_derivatives;
104 };
105 
106 /// @class SimpleInitTemplate
107 /// @brief A version of SimpleInit for Motivators with kDimensions.
108 /// Use this class to initialize a Motivator with vector types, instead of using
109 /// the float arrays required by the base class SimpleInit.
110 /// For example, use a derivation of SimpleInit3f to initialize a Motivator3f.
111 template <class BaseT, class VectorConverter, MotiveDimension kDimensionsParam>
112 struct SimpleInitTemplate : public BaseT {
113  static const MotiveDimension kDimensions = kDimensionsParam;
114 
115  typedef VectorConverter C;
116  typedef typename VectorT<C, kDimensions>::type Vec;
117 
119  : BaseT(C::ToPtr(start_values), C::ToPtr(start_derivatives)),
120  start_values(0.0f),
121  start_derivatives(0.0f) {}
122 
123  SimpleInitTemplate(const Vec& start_values_param,
124  const Vec& start_derivatives_param)
125  : BaseT(C::ToPtr(start_values), C::ToPtr(start_derivatives)),
126  start_values(start_values_param),
127  start_derivatives(start_derivatives_param) {}
128 
129  const Vec start_values;
130  const Vec start_derivatives;
131 };
132 
133 /// @class ConstInit
134 /// @brief Initialize a MotivatorNf that holds values and velocities that
135 /// never change.
136 ///
137 /// All calls to SetTarget functions are ignored.
138 struct ConstInit : public SimpleInit {
139  MOTIVE_INTERFACE();
140  ConstInit() : SimpleInit(kType) {}
141  explicit ConstInit(const float* start_values,
142  const float* start_derivatives = nullptr)
143  : SimpleInit(kType, start_values, start_derivatives) {}
144 };
145 
146 /// Use these types to initialize their corresponding MotivatorXfs using vector
147 /// types instead of float arrays.
149  ConstInit1f;
151  ConstInit2f;
153  ConstInit3f;
155  ConstInit4f;
156 
157 /// @class EaseInEaseOutInit
158 /// @brief Initialize a MotivatorNf move towards target using ease-in
159 /// ease-out math.
160 ///
161 /// Call @ref MotivatorNf::SetTargetWithShape to set the target the
162 /// curve moves towards.
163 struct EaseInEaseOutInit : public SimpleInit {
164  MOTIVE_INTERFACE();
165  EaseInEaseOutInit() : SimpleInit(kType) {}
166  explicit EaseInEaseOutInit(const float* start_values,
167  const float* start_derivatives = nullptr)
168  : SimpleInit(kType, start_values, start_derivatives) {}
169 };
170 
171 /// Use these types to initialize their corresponding MotivatorXfs using vector
172 /// types instead of float arrays.
181 
182 /// @class SpringInit
183 /// @brief Initialize a MotivatorNf move oscillate over a target.
184 ///
185 /// Call @ref MotivatorNf::SetTargetWithShape to set the target the
186 /// curve moves towards.
187 struct SpringInit : public SimpleInit {
188  MOTIVE_INTERFACE();
189  SpringInit() : SimpleInit(kType) {}
190  explicit SpringInit(const float* start_values,
191  const float* start_derivatives = nullptr)
192  : SimpleInit(kType, start_values, start_derivatives) {}
193 };
194 
195 /// Use these types to initialize their corresponding MotivatorXfs using vector
196 /// types instead of float arrays.
198  SpringInit1f;
200  SpringInit2f;
202  SpringInit3f;
204  SpringInit4f;
205 
206 /// @class OvershootInit
207 /// @brief Initialize a MotivatorNf move towards a target using spring physics.
208 ///
209 /// Call MotivatorNf::SetTargets() to set the target that we swing towards.
210 /// The name comes from the movement overshooting the target then coming
211 /// back, the way a dampened oscillator overshoots its resting point.
212 class OvershootInit : public MotivatorInit {
213  public:
214  MOTIVE_INTERFACE();
215 
216  OvershootInit()
217  : MotivatorInit(kType),
218  range_(Range::Full()),
219  modular_(false),
220  max_velocity_(0.0f),
221  accel_per_difference_(0.0f),
222  wrong_direction_multiplier_(0.0f),
223  max_delta_time_(0) {}
224 
225  /// Ensure velocity is within the reasonable limits.
226  float ClampVelocity(float velocity) const {
227  return mathfu::Clamp(velocity, -max_velocity_, max_velocity_);
228  }
229 
230  /// Ensure the Motivator's 'value' doesn't increment by more than 'max_delta'.
231  /// This is different from ClampVelocity because it is independent of time.
232  /// No matter how big the timestep, the delta will not be too great.
233  float ClampDelta(float delta) const {
234  return mathfu::Clamp(delta, -max_delta_, max_delta_);
235  }
236 
237  /// Return true if we're close to the target and almost stopped.
238  /// The definition of "close to" and "almost stopped" are given by the
239  /// "at_target" member.
240  bool AtTarget(float dist, float velocity) const {
241  return at_target_.Settled(dist, velocity);
242  }
243 
244  const Range& range() const { return range_; }
245  void set_range(const Range& r) { range_ = r; }
246  bool modular() const { return modular_; }
247  void set_modular(bool modular) { modular_ = modular; }
248  float max_velocity() const { return max_velocity_; }
249  float max_delta() const { return max_delta_; }
250  const Settled1f& at_target() const { return at_target_; }
251  Settled1f& at_target() { return at_target_; }
252  float accel_per_difference() const { return accel_per_difference_; }
253  float wrong_direction_multiplier() const {
254  return wrong_direction_multiplier_;
255  }
256  MotiveTime max_delta_time() const { return max_delta_time_; }
257 
258  void set_max_velocity(float max_velocity) { max_velocity_ = max_velocity; }
259  void set_max_delta(float max_delta) { max_delta_ = max_delta; }
260  void set_at_target(const Settled1f& at_target) { at_target_ = at_target; }
261  void set_accel_per_difference(float accel_per_difference) {
262  accel_per_difference_ = accel_per_difference;
263  }
264  void set_wrong_direction_multiplier(float wrong_direction_multiplier) {
265  wrong_direction_multiplier_ = wrong_direction_multiplier;
266  }
267  void set_max_delta_time(MotiveTime max_delta_time) {
268  max_delta_time_ = max_delta_time;
269  }
270 
271  private:
272  /// Minimum and maximum values for Motivator::Value().
273  /// Clamp (if modular_ is false) or wrap-around (if modular_ is true) when
274  /// we reach these boundaries.
275  Range range_;
276 
277  /// A modular value wraps around from min to max. For example, an angle
278  /// is modular, where -pi is equivalent to +pi. Setting this to true ensures
279  /// that arithmetic wraps around instead of clamping to min/max.
280  bool modular_;
281 
282  /// Maximum speed at which the value can change. That is, maximum value for
283  /// the Motivator::Velocity(). In units/tick.
284  /// For example, if the value is an angle, then this is the max angular
285  /// velocity, and the units are radians/tick.
286  float max_velocity_;
287 
288  /// Maximum that Motivator::Value() can be altered on a single call to
289  /// MotiveEngine::AdvanceFrame(), regardless of velocity or delta_time.
290  float max_delta_;
291 
292  /// Cutoff to determine if the Motivator's current state has settled on the
293  /// target. Once it has settled, Value() is set to TargetValue() and
294  /// Velocity() is set to zero.
295  Settled1f at_target_;
296 
297  /// Acceleration is a multiple of abs('state_.position' - 'target_.position').
298  /// Bigger differences cause faster acceleration.
299  float accel_per_difference_;
300 
301  /// When accelerating away from the target, we multiply our acceleration by
302  /// this amount. We need counter-acceleration to be stronger so that the
303  /// amplitude eventually dies down; otherwise, we'd just have a pendulum.
304  float wrong_direction_multiplier_;
305 
306  /// The algorithm is iterative. When the iteration step gets too big, the
307  /// behavior becomes erratic. This value clamps the iteration step.
308  MotiveTime max_delta_time_;
309 };
310 
311 /// @class SplineInit
312 /// @brief Initialize a MotivatorNf to follow a spline.
313 ///
314 /// Call MotivatorNf::SetSplines() to follow predefined splines,
315 /// or call MotivatorNf::SetTargets() to dynamically generate a spline that
316 /// travels through several key points.
317 class SplineInit : public MotivatorInit {
318  public:
319  MOTIVE_INTERFACE();
320 
321  SplineInit() : MotivatorInit(kType) {}
322 
323  /// @param range If using modular arithmetic, the normalized range.
324  /// If not using modular arithmetic, pass in an invalid range
325  /// such as Range().
326  explicit SplineInit(const Range& range) : MotivatorInit(kType), range_(range) {}
327 
328  const Range& range() const { return range_; }
329  void set_range(const Range& r) { range_ = r; }
330 
331  private:
332  /// If using modular arithmetic, the normalized range.
333  /// For example, for angles, the normalized range can be (pi, +pi].
334  /// Whenever a new spline segment is started, the internal logic resets
335  /// the value to the normalized range. Note, however, that it is possible
336  /// for the value to escape the normalized range. That is,
337  /// MotivatorNf::Value() may be outside of `range_`, though it will always
338  /// be close enough to normalize efficiently with
339  /// Range::NormalizeCloseValue().
340  ///
341  /// If not using modular arithmetic, set to an invalid range and ignored.
342  Range range_;
343 };
344 
345 /// @class MatrixOperationInit
346 /// @brief Init params for a basic operation on a matrix.
348  enum UnionType {
349  kUnionEmpty,
350  kUnionInitialValue,
351  kUnionTarget,
352  kUnionSpline
353  };
354 
355  /// Matrix operation never changes. Always use 'const_value'.
356  MatrixOperationInit(MatrixOpId id, MatrixOperationType type,
357  float const_value)
358  : init(nullptr),
359  id(id),
360  type(type),
361  union_type(kUnionInitialValue),
362  initial_value(const_value) {}
363 
364  /// Matrix operation is driven by Motivator defined by 'init'.
365  MatrixOperationInit(MatrixOpId id, MatrixOperationType type,
366  const MotivatorInit& init)
367  : init(&init), id(id), type(type), union_type(kUnionEmpty) {}
368 
369  /// Matrix operation is driven by Motivator defined by 'init'. Specify initial
370  /// value as well.
371  MatrixOperationInit(MatrixOpId id, MatrixOperationType type,
372  const MotivatorInit& init, float initial_value)
373  : init(&init),
374  id(id),
375  type(type),
376  union_type(kUnionInitialValue),
377  initial_value(initial_value) {}
378 
379  MatrixOperationInit(MatrixOpId id, MatrixOperationType type,
380  const MotivatorInit& init, const MotiveTarget1f& target)
381  : init(&init),
382  id(id),
383  type(type),
384  union_type(kUnionTarget),
385  target(&target) {}
386 
387  MatrixOperationInit(MatrixOpId id, MatrixOperationType type,
388  const MotivatorInit& init, const CompactSpline& spline)
389  : init(&init),
390  id(id),
391  type(type),
392  union_type(kUnionSpline),
393  spline(&spline) {}
394 
395  const MotivatorInit* init;
396  MatrixOpId id;
397  MatrixOperationType type;
398  UnionType union_type;
399  union {
400  float initial_value;
401  const MotiveTarget1f* target;
402  const CompactSpline* spline;
403  };
404 };
405 
406 /// @class MatrixInit
407 /// @brief Initialize a MatrixMotivator4f to generate its matrix from
408 /// a series of operations.
409 ///
410 /// Initialize a MatrixMotivator4f with these initialization parameters to
411 /// create a motivator that generates a 4x4 matrix from a series of basic
412 /// matrix operations. The basic matrix operations are driven by one dimensional
413 /// motivators.
414 ///
415 /// The series of operations can transform an object from the coordinate space
416 /// in which it was authored, to world (or local) space. For example, if you
417 /// have a penguin that is authored at (0,0,0) facing up the x-axis, you can
418 /// move it to it's target position with four operations:
419 ///
420 /// kScaleUniformly --> to make penguin the correct size
421 /// kRotateAboutY --> to make penguin face the correct direction
422 /// kTranslateX } --> to move penguin along to ground to target position
423 /// kTranslateZ }
425  public:
426  typedef std::vector<MatrixOperationInit> OpVector;
427 
428  // Guess at the number of operations we'll have. Better to high-ball a little
429  // so that we don't have to reallocate the `ops_` vector.
430  static const int kDefaultExpectedNumOps = 8;
431 
432  /// By default expect a relatively high number of ops. Cost for allocating
433  /// a bit too much temporary memory is small compared to cost of reallocating
434  /// that memory.
435  explicit MatrixOpArray(int expected_num_ops = kDefaultExpectedNumOps) {
436  ops_.reserve(expected_num_ops);
437  }
438 
439  /// Remove all matrix operations from the sequence.
440  void Clear(int expected_num_ops = kDefaultExpectedNumOps) {
441  ops_.clear();
442  ops_.reserve(expected_num_ops);
443  }
444 
445  /// Operation is constant. For example, use to put something flat on the
446  /// ground, with 'type' = kRotateAboutX and 'const_value' = pi/2.
447  void AddOp(MatrixOpId id, MatrixOperationType type, float const_value) {
448  ops_.push_back(MatrixOperationInit(id, type, const_value));
449  }
450 
451  /// Operation is driven by a one dimensional motivator. For example, you can
452  /// control the face angle of a standing object with 'type' = kRotateAboutY
453  /// and 'init' a curve specified by SplineInit.
454  void AddOp(MatrixOpId id, MatrixOperationType type,
455  const MotivatorInit& init) {
456  ops_.push_back(MatrixOperationInit(id, type, init));
457  }
458 
459  /// Operation is driven by a one dimensional motivator, and initial value
460  /// is specified.
461  void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit& init,
462  float initial_value) {
463  ops_.push_back(MatrixOperationInit(id, type, init, initial_value));
464  }
465 
466  /// Operation is driven by a one dimensional motivator, which is initialized
467  /// to traverse the key points specified in `target`.
468  void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit& init,
469  const MotiveTarget1f& target) {
470  ops_.push_back(MatrixOperationInit(id, type, init, target));
471  }
472 
473  /// Operation is driven by a one dimensional motivator, which is initialized
474  /// to follow the predefined curve specified in `spline`.
475  void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit& init,
476  const CompactSpline& spline) {
477  ops_.push_back(MatrixOperationInit(id, type, init, spline));
478  }
479 
480  // Maximum duration of any of the splines.
481  MotiveTime EndTime() const {
482  MotiveTime end_time = 0;
483  for (size_t i = 0; i < ops_.size(); ++i) {
484  const MatrixOperationInit& op = ops_[i];
485  if (op.union_type == MatrixOperationInit::kUnionSpline) {
486  end_time =
487  std::max(end_time, static_cast<MotiveTime>(op.spline->EndX()));
488  }
489  }
490  return end_time;
491  }
492 
493  const OpVector& ops() const { return ops_; }
494 
495  private:
496  OpVector ops_;
497 };
498 
499 class MatrixInit : public MotivatorInit {
500  public:
501  MOTIVE_INTERFACE();
502  typedef std::vector<MatrixOperationInit> OpVector;
503 
504  explicit MatrixInit(const MatrixOpArray& ops)
505  : MotivatorInit(kType), ops_(&ops) {}
506 
507  const OpVector& ops() const { return ops_->ops(); }
508 
509  private:
510  /// Reference to the union of all operations that this matrix will be able
511  /// to execute. Later calls to MotivatorMatrix4f::BlendToOps() must provide
512  /// operations that are a subset of those in `ops_`.
513  /// In `RigAnim`, these represent operations in the defining anim.
514  const MatrixOpArray* ops_;
515 };
516 
517 class RigInit : public MotivatorInit {
518  public:
519  MOTIVE_INTERFACE();
520 
521  RigInit(const RigAnim& defining_anim, const BoneIndex* bone_parents,
522  BoneIndex num_bones);
523  const RigAnim& defining_anim() const { return *defining_anim_; }
524  const mathfu::AffineTransform* bone_transforms() const {
525  return bone_transforms_;
526  }
527 
528  // Utility functions. Ensure that animations are compatible with rigs.
529  static bool MatchesHierarchy(const BoneIndex* parents_a, BoneIndex len_a,
530  const BoneIndex* parents_b, BoneIndex len_b);
531  static bool MatchesHierarchy(const RigAnim& anim, const BoneIndex* parents_b,
532  BoneIndex len_b);
533  static bool MatchesHierarchy(const RigAnim& anim_a, const RigAnim& anim_b);
534 
535  private:
536  /// Animation defining hierarchy and the union of matrix ops (across all
537  /// animations).
538  const RigAnim* defining_anim_;
539 
540  /// Array defining default pose. That is, the transformation from a bone to
541  /// its parent. With just these, you can reconstruct the model in the pose
542  /// it was exported in (i.e. its default pose).
543  /// These transforms are used as the `start_transform_`s of the underlying
544  /// `MatrixInit`s. All the matrix operations are applied from the origin of
545  /// the bone they're animating.
546  const mathfu::AffineTransform* bone_transforms_;
547 };
548 
549 } // namespace motive
550 
551 #endif // MOTIVE_INIT_H_
Definition: init.h:517
SplineInit(const Range &range)
Definition: init.h:326
bool AtTarget(float dist, float velocity) const
Definition: init.h:240
MotivatorInit(MotivatorType type)
The derived class's constructor should set 'type'.
Definition: common.h:95
MatrixOperationInit(MatrixOpId id, MatrixOperationType type, float const_value)
Matrix operation never changes. Always use 'const_value'.
Definition: init.h:356
Animation for a RigMotivator. Drives a fully rigged model.
Definition: anim.h:72
Initialize a MotivatorNf move oscillate over a target.
Definition: init.h:187
Initialize a MotivatorNf to follow a spline.
Definition: init.h:317
MatrixOperationInit(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init, float initial_value)
Definition: init.h:371
void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init)
Definition: init.h:454
MatrixOpArray(int expected_num_ops=kDefaultExpectedNumOps)
Definition: init.h:435
void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init, const CompactSpline &spline)
Definition: init.h:475
void Clear(int expected_num_ops=kDefaultExpectedNumOps)
Remove all matrix operations from the sequence.
Definition: init.h:440
Base class of Init classes for MotiveProcessors that derive from SimpleProcessorTemplate.
Definition: init.h:81
static RangeT< T > Full()
Returns the complete range. Every T is contained in this range.
Definition: range.h:479
bool Settled(float dist, float velocity) const
Definition: util.h:37
A version of SimpleInit for Motivators with kDimensions. Use this class to initialize a Motivator wit...
Definition: init.h:112
const float * start_values
Definition: init.h:97
Initialize a MatrixMotivator4f to generate its matrix from a series of operations.
Definition: init.h:499
float ClampDelta(float delta) const
Definition: init.h:233
void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init, const MotiveTarget1f &target)
Definition: init.h:468
Initialize a MotivatorNf that holds values and velocities that never change.
Definition: init.h:138
Definition: common.h:92
Initialize a MotivatorNf move towards a target using spring physics.
Definition: init.h:212
Set the current and/or target state for a one-dimensional Motivator.
Definition: target.h:98
Represent a smooth curve in a small amount of memory.
Definition: compact_spline.h:73
void AddOp(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init, float initial_value)
Definition: init.h:461
float ClampVelocity(float velocity) const
Ensure velocity is within the reasonable limits.
Definition: init.h:226
Initialize a MotivatorNf move towards target using ease-in ease-out math.
Definition: init.h:163
const float * start_derivatives
Definition: init.h:103
Definition: init.h:424
MatrixOperationInit(MatrixOpId id, MatrixOperationType type, const MotivatorInit &init)
Matrix operation is driven by Motivator defined by 'init'.
Definition: init.h:365
void AddOp(MatrixOpId id, MatrixOperationType type, float const_value)
Definition: init.h:447
Init params for a basic operation on a matrix.
Definition: init.h:347
Represent an interval on a number line.
Definition: range.h:37