182 lines
6.6 KiB
C++
182 lines
6.6 KiB
C++
//----------------------------------------------------------------------------//
|
|
// //
|
|
// ozz-animation is hosted at http://github.com/guillaumeblanc/ozz-animation //
|
|
// and distributed under the MIT License (MIT). //
|
|
// //
|
|
// Copyright (c) Guillaume Blanc //
|
|
// //
|
|
// Permission is hereby granted, free of charge, to any person obtaining a //
|
|
// copy of this software and associated documentation files (the "Software"), //
|
|
// to deal in the Software without restriction, including without limitation //
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
|
|
// and/or sell copies of the Software, and to permit persons to whom the //
|
|
// Software is furnished to do so, subject to the following conditions: //
|
|
// //
|
|
// The above copyright notice and this permission notice shall be included in //
|
|
// all copies or substantial portions of the Software. //
|
|
// //
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR //
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, //
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL //
|
|
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER //
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING //
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER //
|
|
// DEALINGS IN THE SOFTWARE. //
|
|
// //
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#include "ozz/animation/runtime/track_triggering_job.h"
|
|
#include "ozz/animation/runtime/track.h"
|
|
|
|
#include <algorithm>
|
|
#include <cassert>
|
|
|
|
namespace ozz {
|
|
namespace animation {
|
|
|
|
TrackTriggeringJob::TrackTriggeringJob()
|
|
: from(0.f), to(0.f), threshold(0.f), track(nullptr), iterator(nullptr) {}
|
|
|
|
bool TrackTriggeringJob::Validate() const {
|
|
bool valid = true;
|
|
valid &= track != nullptr;
|
|
valid &= iterator != nullptr;
|
|
return valid;
|
|
}
|
|
|
|
bool TrackTriggeringJob::Run() const {
|
|
if (!Validate()) {
|
|
return false;
|
|
}
|
|
|
|
// Triggering can only happen in a valid range of ratio.
|
|
if (from == to) {
|
|
*iterator = end();
|
|
return true;
|
|
}
|
|
|
|
*iterator = Iterator(this);
|
|
|
|
return true;
|
|
}
|
|
|
|
namespace {
|
|
inline bool DetectEdge(ptrdiff_t _i0, ptrdiff_t _i1, bool _forward,
|
|
const TrackTriggeringJob& _job,
|
|
TrackTriggeringJob::Edge* _edge) {
|
|
const span<const float>& values = _job.track->values();
|
|
|
|
const float vk0 = values[_i0];
|
|
const float vk1 = values[_i1];
|
|
|
|
bool detected = false;
|
|
if (vk0 <= _job.threshold && vk1 > _job.threshold) {
|
|
// Rising edge
|
|
_edge->rising = _forward;
|
|
detected = true;
|
|
} else if (vk0 > _job.threshold && vk1 <= _job.threshold) {
|
|
// Falling edge
|
|
_edge->rising = !_forward;
|
|
detected = true;
|
|
}
|
|
|
|
if (detected) {
|
|
const span<const float>& ratios = _job.track->ratios();
|
|
const span<const uint8_t>& steps = _job.track->steps();
|
|
|
|
const bool step = (steps[_i0 / 8] & (1 << (_i0 & 7))) != 0;
|
|
if (step) {
|
|
_edge->ratio = ratios[_i1];
|
|
} else {
|
|
assert(vk0 != vk1); // Won't divide by 0
|
|
|
|
if (_i1 == 0) {
|
|
_edge->ratio = 0.f;
|
|
} else {
|
|
// Finds where the curve crosses threshold value.
|
|
// This is the lerp equation, where we know the result and look for
|
|
// alpha, aka un-lerp.
|
|
const float alpha = (_job.threshold - vk0) / (vk1 - vk0);
|
|
|
|
// Remaps to keyframes actual times.
|
|
const float tk0 = ratios[_i0];
|
|
const float tk1 = ratios[_i1];
|
|
_edge->ratio = math::Lerp(tk0, tk1, alpha);
|
|
}
|
|
}
|
|
}
|
|
return detected;
|
|
}
|
|
} // namespace
|
|
|
|
TrackTriggeringJob::Iterator::Iterator(const TrackTriggeringJob* _job)
|
|
: job_(_job) {
|
|
// Outer loop initialization.
|
|
outer_ = floorf(job_->from);
|
|
|
|
// Search could start more closely to the "from" ratio, but it's not possible
|
|
// to ensure that floating point precision will not lead to missing a key
|
|
// (when from/to range is far from 0). This is less good in algorithmic
|
|
// complexity, but for consistency of forward and backward triggering, it's
|
|
// better to let iterator ++ implementation filter included and excluded
|
|
// edges.
|
|
inner_ = job_->from < job_->to ? 0 : _job->track->ratios().size() - 1;
|
|
|
|
// Evaluates first edge.
|
|
++*this;
|
|
}
|
|
|
|
const TrackTriggeringJob::Iterator& TrackTriggeringJob::Iterator::operator++() {
|
|
assert(*this != job_->end() && "Can't increment end iterator.");
|
|
|
|
const span<const float>& ratios = job_->track->ratios();
|
|
const ptrdiff_t num_keys = ratios.size();
|
|
|
|
if (job_->to > job_->from) {
|
|
for (; outer_ < job_->to; outer_ += 1.f) {
|
|
for (; inner_ < num_keys; ++inner_) {
|
|
const ptrdiff_t i0 = inner_ == 0 ? num_keys - 1 : inner_ - 1;
|
|
if (DetectEdge(i0, inner_, true, *job_, &edge_)) {
|
|
edge_.ratio += outer_; // Convert to global ratio space.
|
|
if (edge_.ratio >= job_->from &&
|
|
(edge_.ratio < job_->to || job_->to >= 1.f + outer_)) {
|
|
++inner_;
|
|
return *this; // Yield found edge.
|
|
}
|
|
// Won't find any further edge.
|
|
if (ratios[inner_] + outer_ >= job_->to) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
inner_ = 0; // Ready for next loop.
|
|
}
|
|
} else {
|
|
for (; outer_ + 1.f > job_->to; outer_ -= 1.f) {
|
|
for (; inner_ >= 0; --inner_) {
|
|
const ptrdiff_t i0 = inner_ == 0 ? num_keys - 1 : inner_ - 1;
|
|
if (DetectEdge(i0, inner_, false, *job_, &edge_)) {
|
|
edge_.ratio += outer_; // Convert to global ratio space.
|
|
if (edge_.ratio >= job_->to &&
|
|
(edge_.ratio < job_->from || job_->from >= 1.f + outer_)) {
|
|
--inner_;
|
|
return *this; // Yield found edge.
|
|
}
|
|
}
|
|
// Won't find any further edge.
|
|
if (ratios[inner_] + outer_ <= job_->to) {
|
|
break;
|
|
}
|
|
}
|
|
inner_ = ratios.size() - 1; // Ready for next loop.
|
|
}
|
|
}
|
|
|
|
// Set iterator to end position.
|
|
*this = job_->end();
|
|
|
|
return *this;
|
|
}
|
|
} // namespace animation
|
|
} // namespace ozz
|