scene/scene.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file scene.h | ||
| 3 | /// @brief The scene: typed prims at paths, stored in columns by type. | ||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "ernie/export.h" | ||
| 7 | #include "ernie/geometry/bounds.h" | ||
| 8 | #include "ernie/scene/extent_cache.h" | ||
| 9 | #include "ernie/scene/holder_index.h" | ||
| 10 | #include "ernie/scene/schema.h" | ||
| 11 | #include "ernie/scene/storage.h" | ||
| 12 | #include "ernie/scene/time_samples.h" | ||
| 13 | #include "ernie/scene/value.h" | ||
| 14 | |||
| 15 | #include <glm/glm.hpp> | ||
| 16 | |||
| 17 | #include <map> | ||
| 18 | #include <optional> | ||
| 19 | #include <string> | ||
| 20 | #include <string_view> | ||
| 21 | #include <unordered_map> | ||
| 22 | #include <vector> | ||
| 23 | |||
| 24 | namespace ernie { | ||
| 25 | |||
| 26 | /// @brief Prims named by paths, each of a registered type, its attributes validated against the | ||
| 27 | /// type's schema and stored in the columns of the type's bucket. | ||
| 28 | class ERNIE_API Scene { | ||
| 29 | public: | ||
| 30 | Scene(); | ||
| 31 | Scene(const Scene&) = delete; | ||
| 32 | Scene& operator=(const Scene&) = delete; | ||
| 33 | |||
| 34 | /// @brief Defines a prim of a concrete type, or `""` for none; missing ancestors are defined | ||
| 35 | /// without a type. Redefining with another type keeps the authored values both types declare. | ||
| 36 | /// @throws std::invalid_argument for the root, an empty path or an unknown or abstract type. | ||
| 37 | PrimId Define(const Path& path, std::string_view type); | ||
| 38 | /// @brief Removes a prim and its descendants. | ||
| 39 | /// @return Whether there was a prim. | ||
| 40 | bool Remove(const Path& path); | ||
| 41 | |||
| 42 | std::optional<PrimId> Find(const Path& path) const; | ||
| 43 | /// @throws std::invalid_argument if there is no prim at `path`. | ||
| 44 | PrimId Require(const Path& path) const; | ||
| 45 | 48 | bool Has(const Path& path) const { | |
| 46 | 46 | return Find(path).has_value(); | |
| 47 | } | ||
| 48 | Path PrimPath(PrimId prim) const; | ||
| 49 | /// @return The prim's type name; empty for none. | ||
| 50 | std::string_view TypeName(PrimId prim) const; | ||
| 51 | /// @return The prim's schema, or null for a prim without a type. | ||
| 52 | const SchemaDef* Schema(PrimId prim) const; | ||
| 53 | std::vector<Path> Children(const Path& path) const; | ||
| 54 | /// @return Every prim whose type is `type` or derives from it; every prim for `""`. | ||
| 55 | std::vector<Path> Prims(std::string_view type = {}) const; | ||
| 56 | |||
| 57 | /// @brief Applies an API schema, `"MaterialBindingAPI"`, or an instance of a multiple-apply | ||
| 58 | /// one, `"CollectionAPI:lights"`, adding its attributes at their fallbacks; one the prim has | ||
| 59 | /// already, built in or applied, is left as it is. | ||
| 60 | /// @throws std::invalid_argument for an unknown API schema or a missing or extra instance. | ||
| 61 | void ApplyApi(PrimId prim, std::string_view api); | ||
| 62 | /// @brief Removes an applied API schema with the values of its attributes. | ||
| 63 | /// @return Whether it was applied. | ||
| 64 | bool RemoveApi(PrimId prim, std::string_view api); | ||
| 65 | bool HasApi(PrimId prim, std::string_view api) const; | ||
| 66 | /// @return The applied API schemas, as `ApplyApi` names them. | ||
| 67 | std::vector<std::string> AppliedApis(PrimId prim) const; | ||
| 68 | |||
| 69 | /// @brief Authors an attribute: a declared one converted to its type and checked, or an | ||
| 70 | /// undeclared one, typed by its first value, in a namespace the type leaves open or on a prim | ||
| 71 | /// without a type. | ||
| 72 | /// @throws std::invalid_argument for a missing prim, an undeclared attribute or a bad value. | ||
| 73 | void Set(PrimId prim, std::string_view attribute, const Value& value); | ||
| 74 | void Set(const Path& path, std::string_view attribute, const Value& value); | ||
| 75 | /// @return The authored value, or none. | ||
| 76 | std::optional<Value> Get(PrimId prim, std::string_view attribute) const; | ||
| 77 | /// @brief `Get` by the attribute's token, for readers that look it up once. | ||
| 78 | std::optional<Value> Get(PrimId prim, AttributeId attribute) const; | ||
| 79 | /// @return The authored value, else the fallback its schema declares; none when neither. | ||
| 80 | std::optional<Value> GetOrFallback(PrimId prim, std::string_view attribute) const; | ||
| 81 | std::optional<Value> Get(const Path& path, std::string_view attribute) const; | ||
| 82 | /// @return An inherited attribute's value as of the last `Update`: the prim's own or its | ||
| 83 | /// nearest authoring ancestor's (where "inherited" isn't authoring), else the fallback; none | ||
| 84 | /// for a prim that isn't Imageable. | ||
| 85 | std::optional<Value> GetResolved(PrimId prim, std::string_view attribute) const; | ||
| 86 | /// @brief Authors an array attribute with a tensor on the host or a device, shared, not copied. | ||
| 87 | /// @throws std::invalid_argument for a non-array attribute, or a tensor of the wrong type or | ||
| 88 | /// shape. | ||
| 89 | void Write(PrimId prim, std::string_view attribute, Tensor array); | ||
| 90 | /// @return The authored array, or null. | ||
| 91 | const Tensor* Read(PrimId prim, std::string_view attribute) const; | ||
| 92 | |||
| 93 | /// @brief Sets the attribute's value at a time code, a time sample, as `UsdAttribute::Set` | ||
| 94 | /// with a time does. A sampled attribute holds its samples' value at `Time`, interpolated | ||
| 95 | /// between the samples either side and held beyond the ends; `Set` or `Write` without a time | ||
| 96 | /// drops its samples. An attribute's samples are all values or all arrays. | ||
| 97 | /// @throws std::invalid_argument as `Set` does. | ||
| 98 | void SetAt(PrimId prim, std::string_view attribute, double time, const Value& value); | ||
| 99 | /// @brief `SetAt` for an array, kept and shared as `Write` keeps one. Device arrays between | ||
| 100 | /// samples hold the lower one, for their consumers to mix on the device (`Samples`). | ||
| 101 | void WriteAt(PrimId prim, std::string_view attribute, double time, Tensor array); | ||
| 102 | /// @return The attribute's samples in time order; empty when it has none. | ||
| 103 | std::span<const TimeSample> Samples(PrimId prim, std::string_view attribute) const; | ||
| 104 | /// @return The attribute's value at `time` without moving the clock: its samples', else its | ||
| 105 | /// authored value; none when unset or an array. | ||
| 106 | std::optional<Value> GetAt(PrimId prim, std::string_view attribute, double time) const; | ||
| 107 | /// @brief Drops the attribute's samples, keeping the value it holds now. | ||
| 108 | /// @return Whether it had any. | ||
| 109 | bool ClearSamples(PrimId prim, std::string_view attribute); | ||
| 110 | /// @brief Moves the clock, evaluating every sampled attribute and stamping those whose value | ||
| 111 | /// changes. | ||
| 112 | void SetTime(double timeCode); | ||
| 113 | 8 | double Time() const { | |
| 114 | 8 | return time_; | |
| 115 | } | ||
| 116 | /// @brief Unauthors an attribute and drops its samples. | ||
| 117 | /// @return Whether the attribute was authored. | ||
| 118 | bool Clear(PrimId prim, std::string_view attribute); | ||
| 119 | bool IsAuthored(PrimId prim, std::string_view attribute) const; | ||
| 120 | /// @return The declaration of the prim's attribute by its type or API schemas, or null. | ||
| 121 | const AttributeDef* Declaration(PrimId prim, std::string_view attribute) const; | ||
| 122 | /// @return The authored attributes' names, in column order. | ||
| 123 | std::vector<std::string> Attributes(const Path& path) const; | ||
| 124 | |||
| 125 | /// @return The prim's own transform from its ops; identity for prims that aren't Xformable. | ||
| 126 | glm::dmat4 LocalTransform(PrimId prim) const; | ||
| 127 | glm::dmat4 WorldTransform(PrimId prim) const; | ||
| 128 | /// @return The prim's extent in its own space; none when it isn't computed from attributes. | ||
| 129 | /// Cached since `Update` when its bucket hasn't changed, computed otherwise. | ||
| 130 | std::optional<Box> Extent(PrimId prim) const; | ||
| 131 | /// @brief Brings derived data up to date with the edits since the last update, once per step | ||
| 132 | /// before renderers pull changes: resolves inherited attributes for the subtrees whose | ||
| 133 | /// authored values or prims changed, re-indexes changed relationships, then recomputes the | ||
| 134 | /// changed rows' cached extents. | ||
| 135 | void Update(); | ||
| 136 | /// @return The prims whose relationships target `target` as of the last `Update`, resolved | ||
| 137 | /// material bindings included: what an edit to `target` reaches. | ||
| 138 | std::vector<PrimId> Holders(const Path& target) const; | ||
| 139 | /// @return The world box of the prim and its visible descendants of the default purpose. | ||
| 140 | /// Prims with extents bound their own subtrees, as UsdGeomBBoxCache prunes at Boundables. | ||
| 141 | Box WorldBounds(const Path& path) const; | ||
| 142 | /// @return The box of a prim's subtree in its parent's space, whatever its visibility: what a | ||
| 143 | /// point instancer places at each instance. | ||
| 144 | Box BoundsInParent(PrimId prim) const; | ||
| 145 | |||
| 146 | 81 | PrimStore& Store() { | |
| 147 | 79 | return store_; | |
| 148 | } | ||
| 149 | 10682 | const PrimStore& Store() const { | |
| 150 | 14120 | return store_; | |
| 151 | } | ||
| 152 | /// @return An id no other scene of this process has had, even one at the same address. | ||
| 153 | 5 | std::uint64_t Id() const { | |
| 154 | 5 | return id_; | |
| 155 | } | ||
| 156 | 52 | std::uint64_t Version() const { | |
| 157 | 51 | return store_.Version(); | |
| 158 | } | ||
| 159 | /// @return What a column change means to renderers: the attribute's declared category, or for | ||
| 160 | /// undeclared ones Transform for xformOps, Primvars for primvars and Params for the rest. | ||
| 161 | Dirty Classify(const ChangeSet::Values& change) const; | ||
| 162 | |||
| 163 | private: | ||
| 164 | 4186 | struct BucketSchema { | |
| 165 | const SchemaDef* schema = nullptr; | ||
| 166 | std::unordered_map<AttributeId, const AttributeDef*> declared; | ||
| 167 | }; | ||
| 168 | |||
| 169 | /// @return The bucket of a type and applied API schemas, made if new. | ||
| 170 | BucketId FindOrAddBucket(const SchemaDef* schema, std::vector<Token> apis); | ||
| 171 | /// Moves a prim to the bucket of its type and `apis`. | ||
| 172 | void MoveTo(PrimId prim, const SchemaDef* schema, std::vector<Token> apis); | ||
| 173 | const std::vector<Token>& Apis(PrimId prim) const; | ||
| 174 | /// @return A type's built-in API schemas, then the applied ones, each with those built into it, | ||
| 175 | /// each once. | ||
| 176 | std::vector<ApiName> AllApis(const SchemaDef* schema, std::span<const Token> applied) const; | ||
| 177 | /// @return The attribute's column when the prim authors it, or null. | ||
| 178 | const Column* AuthoredColumn(PrimId prim, std::string_view attribute) const; | ||
| 179 | const Column* AuthoredColumn(PrimId prim, AttributeId attribute) const; | ||
| 180 | /// @return The attribute's value when the prim authors it as a `T`, or null. | ||
| 181 | 413 | template <typename T> const T* Authored(PrimId prim, std::string_view attribute) const { | |
| 182 | 413 | const std::optional<Token> token = store_.Tokens().Find(attribute); | |
| 183 | 413 | return token ? store_.Get<T>(prim, *token) : nullptr; | |
| 184 | } | ||
| 185 | std::optional<Value> CellValue(PrimId prim, const Column* column) const; | ||
| 186 | /// Checks a value against the attribute and authors it; with `checked`, converts it there | ||
| 187 | /// instead, as `Get` would return it. | ||
| 188 | void Author(PrimId prim, std::string_view attribute, const Value& value, | ||
| 189 | Value* checked = nullptr); | ||
| 190 | /// @throws std::invalid_argument if the attribute takes no such array. | ||
| 191 | void CheckArray(PrimId prim, std::string_view attribute, const Tensor& array) const; | ||
| 192 | /// Adds a checked sample, then authors the attribute's value at the clock. | ||
| 193 | void AddSample(PrimId prim, std::string_view attribute, TimeSample sample); | ||
| 194 | /// Authors a sampled attribute's value at the clock, if it changed. | ||
| 195 | void Evaluate(PrimId prim, AttributeId attribute, std::span<const TimeSample> samples); | ||
| 196 | |||
| 197 | /// Resolves every inherited attribute for the subtrees changed since the last resolve. | ||
| 198 | void Resolve(); | ||
| 199 | const AttributeDef* Declared(BucketId bucket, AttributeId attribute) const; | ||
| 200 | /// @return Whether the prim's type or an applied API schema leaves the attribute's namespace | ||
| 201 | /// open; any attribute for a prim without a type. | ||
| 202 | bool TakesUndeclared(PrimId prim, std::string_view attribute) const; | ||
| 203 | /// @throws std::invalid_argument unless the prim takes the undeclared attribute. | ||
| 204 | void RequireUndeclared(PrimId prim, std::string_view attribute) const; | ||
| 205 | ColumnType TypeForUndeclared(PrimId prim, std::string_view attribute, const Value& value) const; | ||
| 206 | /// @return Whether the prim and its subtree are invisible or of a purpose bounds leave out. | ||
| 207 | bool LeavesOutOfBounds(PrimId prim) const; | ||
| 208 | /// @return GeomModelAPI's extentsHint: the default purpose's box, or with `allPurposes`, all. | ||
| 209 | Box ExtentsHint(PrimId prim, bool allPurposes) const; | ||
| 210 | /// Adds a subtree's extents under `parent`'s transform; with `visibleOnly`, only its visible | ||
| 211 | /// default-purpose prims'. | ||
| 212 | void AddTo(Box& bounds, PathId path, const glm::dmat4& parent, bool visibleOnly) const; | ||
| 213 | |||
| 214 | std::uint64_t id_; | ||
| 215 | PrimStore store_; | ||
| 216 | /// The store version inherited attributes were last resolved at. | ||
| 217 | std::uint64_t resolved_ = 0; | ||
| 218 | /// The sampled attributes of each prim, by its slot; sparse, as few attributes are animated. | ||
| 219 | 11 | struct Animated { | |
| 220 | PrimId prim; | ||
| 221 | std::map<AttributeId, std::vector<TimeSample>> samples; | ||
| 222 | }; | ||
| 223 | std::unordered_map<std::uint32_t, Animated> animated_; | ||
| 224 | double time_ = 0.0; | ||
| 225 | Token inherited_ = 0; | ||
| 226 | Token invisible_ = 0; | ||
| 227 | Token defaultPurpose_ = 0; | ||
| 228 | std::vector<BucketSchema> buckets_; | ||
| 229 | ExtentCache extents_; | ||
| 230 | HolderIndex holders_; | ||
| 231 | }; | ||
| 232 | |||
| 233 | } // namespace ernie | ||
| 234 |