scene/schema/instancer.cpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // SPDX-License-Identifier: BSD-3-Clause | ||
| 2 | /// @file instancer.cpp | ||
| 3 | /// @brief The point instancer's registry entry and extent: its prototypes' bounds at each | ||
| 4 | /// instance, as UsdGeomPointInstancer computes it. | ||
| 5 | #include "ernie/scene/schema/instancer.h" | ||
| 6 | |||
| 7 | #include "ernie/util/algorithm.h" | ||
| 8 | |||
| 9 | #include <glm/gtc/matrix_transform.hpp> | ||
| 10 | #include <glm/gtc/quaternion.hpp> | ||
| 11 | |||
| 12 | #include <optional> | ||
| 13 | #include <vector> | ||
| 14 | |||
| 15 | namespace ernie { | ||
| 16 | namespace { | ||
| 17 | |||
| 18 | /// An instance's transform: translate, then rotate, then scale innermost. | ||
| 19 | 8 | glm::dmat4 InstanceTransform(std::span<const float> position, std::span<const float> rotation, | |
| 20 | std::span<const float> scale, bool wFirst) { | ||
| 21 | 8 | glm::dmat4 m = | |
| 22 | 8 | glm::translate(glm::dmat4(1.0), glm::dvec3(position[0], position[1], position[2])); | |
| 23 | 8 | if (!rotation.empty()) { | |
| 24 | 2 | const glm::dquat q = wFirst | |
| 25 | 2 | ? glm::dquat(rotation[0], rotation[1], rotation[2], rotation[3]) | |
| 26 | 2 | : glm::dquat(rotation[3], rotation[0], rotation[1], rotation[2]); | |
| 27 | 2 | m *= glm::dmat4(glm::mat3_cast(glm::normalize(q))); | |
| 28 | } | ||
| 29 | 8 | if (!scale.empty()) { | |
| 30 | 2 | m = glm::scale(m, glm::dvec3(scale[0], scale[1], scale[2])); | |
| 31 | } | ||
| 32 | 8 | return m; | |
| 33 | } | ||
| 34 | |||
| 35 | /// One row's instances: its prototypes' boxes, each placed at the instances that use it. | ||
| 36 | struct Instances { | ||
| 37 | std::vector<Box> prototypes; | ||
| 38 | std::vector<std::int64_t> indices; | ||
| 39 | std::vector<float> positions; | ||
| 40 | std::vector<float> rotations; | ||
| 41 | std::vector<float> scales; | ||
| 42 | bool wFirst = false; | ||
| 43 | |||
| 44 | 6 | Box Bounds() const { | |
| 45 | 6 | Box box; | |
| 46 | 14 | for (std::size_t i = 0; i < positions.size() / 3; ++i) { | |
| 47 | 8 | const std::int64_t p = i < indices.size() ? indices[i] : 0; | |
| 48 | 8 | if (p >= 0 && static_cast<std::size_t>(p) < prototypes.size()) { | |
| 49 | 8 | box.Extend(prototypes[static_cast<std::size_t>(p)].Transformed(InstanceTransform( | |
| 50 | 8 | Part(positions, i, 3), Part(rotations, i, 4), Part(scales, i, 3), wFirst))); | |
| 51 | } | ||
| 52 | } | ||
| 53 | 6 | return box; | |
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | /// @return Instance i's `n` values, or none when the array is unset or too short. | ||
| 58 | 24 | static std::span<const float> Part(const std::vector<float>& values, std::size_t i, | |
| 59 | std::size_t n) { | ||
| 60 | 24 | return values.size() >= (i + 1) * n ? std::span(values).subspan(i * n, n) | |
| 61 | 12 | : std::span<const float>(); | |
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | 6 | void InstancerExtent(const ExtentRows& rows, RowRange range, std::span<Box> out) { | |
| 66 | 6 | const auto prototypes = rows.Column<std::vector<Target>>(PointInstancer::kPrototypes); | |
| 67 | 6 | const auto indices = rows.Column<Tensor>(PointInstancer::kProtoIndices); | |
| 68 | 6 | const auto positions = rows.Column<Tensor>(PointInstancer::kPositions); | |
| 69 | 6 | const auto orientations = rows.Column<Tensor>(PointInstancer::kOrientations); | |
| 70 | 6 | const auto scales = rows.Column<Tensor>(PointInstancer::kScales); | |
| 71 | 6 | const auto order = rows.Column<Token>(PointInstancer::kQuaternionOrder); | |
| 72 | 6 | const auto authored = rows.Column<std::vector<float>>(Boundable::kExtent); | |
| 73 | 6 | ComputeRows(range, out, [&](std::uint32_t row) { | |
| 74 | 6 | const bool onHost = indices[row].OnHost() && positions[row].OnHost() && | |
| 75 | 12 | orientations[row].OnHost() && scales[row].OnHost(); | |
| 76 | 6 | if (!onHost) { | |
| 77 | ✗ | return AuthoredExtent(authored[row]); | |
| 78 | } | ||
| 79 | 6 | const Instances instances{ | |
| 80 | 6 | .prototypes = Map(prototypes[row], | |
| 81 | 6 | [&](const Target& target) { | |
| 82 | 8 | const std::optional<PrimId> prim = rows.Resolve(target); | |
| 83 | 8 | return prim ? rows.scene->BoundsInParent(*prim) : Box(); | |
| 84 | }), | ||
| 85 | 6 | .indices = CopyInts(indices[row], "protoIndices"), | |
| 86 | 6 | .positions = CopyFloats(positions[row], 3, "positions"), | |
| 87 | 6 | .rotations = CopyFloats(orientations[row], 4, "orientations"), | |
| 88 | 6 | .scales = CopyFloats(scales[row], 3, "scales"), | |
| 89 | 6 | .wFirst = rows.Text(order[row]) == "wxyz", | |
| 90 | 6 | }; | |
| 91 | 6 | return instances.Bounds(); | |
| 92 | 6 | }); | |
| 93 | 6 | } | |
| 94 | |||
| 95 | } // namespace | ||
| 96 | |||
| 97 | 3661 | ERNIE_DEFINE_SCHEMA(PointInstancer, Concrete, ComputedFromOtherPrims(InstancerExtent), | |
| 98 | ERNIE_POINT_INSTANCER_ATTRIBUTES, .openNamespaces = {tokens::kPrimvarsPrefix}) | ||
| 99 | |||
| 100 | } // namespace ernie | ||
| 101 |