scene/animation.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file animation.cpp | ||
| 3 | /// @brief Time-sampled attributes: their sparse samples per prim, and the clock that evaluates | ||
| 4 | /// them into the columns. | ||
| 5 | #include "ernie/scene/scene.h" | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | |||
| 9 | namespace ernie { | ||
| 10 | |||
| 11 | 19 | void Scene::SetAt(PrimId prim, std::string_view attribute, double time, const Value& value) { | |
| 12 | 19 | TimeSample sample{time, {}, {}}; | |
| 13 | 19 | Author(prim, attribute, value, &sample.value); | |
| 14 | 37 | AddSample(prim, attribute, std::move(sample)); | |
| 15 | 18 | } | |
| 16 | |||
| 17 | 7 | void Scene::WriteAt(PrimId prim, std::string_view attribute, double time, Tensor array) { | |
| 18 | 7 | CheckArray(prim, attribute, array); | |
| 19 | 6 | AddSample(prim, attribute, {time, {}, std::move(array)}); | |
| 20 | 12 | } | |
| 21 | |||
| 22 | 24 | void Scene::AddSample(PrimId prim, std::string_view attribute, TimeSample sample) { | |
| 23 | 24 | const AttributeId token = store_.Tokens().Intern(attribute); | |
| 24 | 24 | Animated& animated = animated_[prim.slot]; | |
| 25 | 24 | animated.prim = prim; | |
| 26 | 24 | std::vector<TimeSample>& samples = animated.samples[token]; | |
| 27 | // Samples are all values or all arrays: one of the other kind replaces them. | ||
| 28 | 24 | if (!samples.empty() && samples.front().tensor.Empty() != sample.tensor.Empty()) { | |
| 29 | ✗ | samples.clear(); | |
| 30 | } | ||
| 31 | 24 | const auto at = std::ranges::lower_bound(samples, sample.time, {}, &TimeSample::time); | |
| 32 | 24 | if (at != samples.end() && at->time == sample.time) { | |
| 33 | ✗ | *at = std::move(sample); | |
| 34 | } else { | ||
| 35 | 24 | samples.insert(at, std::move(sample)); | |
| 36 | } | ||
| 37 | 24 | Evaluate(prim, token, samples); | |
| 38 | 24 | } | |
| 39 | |||
| 40 | 5231 | std::span<const TimeSample> Scene::Samples(PrimId prim, std::string_view attribute) const { | |
| 41 | 5231 | const auto animated = animated_.find(prim.slot); | |
| 42 | 5231 | const std::optional<Token> token = store_.Tokens().Find(attribute); | |
| 43 | 5335 | if (animated == animated_.end() || animated->second.prim != prim || !token) { | |
| 44 | 5186 | return {}; | |
| 45 | } | ||
| 46 | 45 | const auto samples = animated->second.samples.find(*token); | |
| 47 | 90 | return samples == animated->second.samples.end() ? std::span<const TimeSample>() | |
| 48 | 45 | : std::span(samples->second); | |
| 49 | } | ||
| 50 | |||
| 51 | 1 | std::optional<Value> Scene::GetAt(PrimId prim, std::string_view attribute, double time) const { | |
| 52 | 1 | const std::span<const TimeSample> samples = Samples(prim, attribute); | |
| 53 | 1 | if (samples.empty()) { | |
| 54 | ✗ | return Get(prim, attribute); | |
| 55 | } | ||
| 56 | 2 | Value value = ernie::Evaluate(samples, time).value; | |
| 57 | 2 | return value.Empty() ? std::nullopt : std::optional(std::move(value)); | |
| 58 | 1 | } | |
| 59 | |||
| 60 | 5002 | bool Scene::ClearSamples(PrimId prim, std::string_view attribute) { | |
| 61 | 5002 | if (Samples(prim, attribute).empty()) { | |
| 62 | return false; | ||
| 63 | } | ||
| 64 | 1 | const auto animated = animated_.find(prim.slot); | |
| 65 | 1 | animated->second.samples.erase(*store_.Tokens().Find(attribute)); | |
| 66 | 1 | if (animated->second.samples.empty()) { | |
| 67 | ✗ | animated_.erase(animated); | |
| 68 | } | ||
| 69 | return true; | ||
| 70 | } | ||
| 71 | |||
| 72 | 5 | void Scene::SetTime(double timeCode) { | |
| 73 | 5 | time_ = timeCode; | |
| 74 | 15 | for (const auto& [slot, animated] : animated_) { | |
| 75 | 14 | for (const auto& [attribute, samples] : animated.samples) { | |
| 76 | 9 | Evaluate(animated.prim, attribute, samples); | |
| 77 | } | ||
| 78 | } | ||
| 79 | 5 | } | |
| 80 | |||
| 81 | 33 | void Scene::Evaluate(PrimId prim, AttributeId attribute, std::span<const TimeSample> samples) { | |
| 82 | 33 | const std::string_view name = store_.Tokens().Text(attribute); | |
| 83 | 33 | TimeSample now = ernie::Evaluate(samples, time_); | |
| 84 | 33 | if (!now.tensor.Empty()) { | |
| 85 | 7 | const Tensor* current = Read(prim, name); | |
| 86 | 7 | if (current == nullptr || current->Data() != now.tensor.Data()) { | |
| 87 | 4 | store_.Set(prim, attribute, std::move(now.tensor)); | |
| 88 | } | ||
| 89 | 26 | } else if (Get(prim, name) != now.value) { | |
| 90 | 10 | Author(prim, name, now.value); | |
| 91 | } | ||
| 92 | 33 | } | |
| 93 | |||
| 94 | } // namespace ernie | ||
| 95 |