GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
49 / 50
98.0%
Branches:
41 / 74
55.4%
Functions:
7 / 7
100.0%

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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
8 if (!rotation.empty()) {
24 2 const glm::dquat q = wFirst
25
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
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
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
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
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 for (std::size_t i = 0; i < positions.size() / 3; ++i) {
47
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 const std::int64_t p = i < indices.size() ? indices[i] : 0;
48
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
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
3/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
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
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
6 const bool onHost = indices[row].OnHost() && positions[row].OnHost() &&
75
3/6
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
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
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 [&](const Target& target) {
82 8 const std::optional<PrimId> prim = rows.Resolve(target);
83
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 return prim ? rows.scene->BoundsInParent(*prim) : Box();
84 }),
85
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 .indices = CopyInts(indices[row], "protoIndices"),
86
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 .positions = CopyFloats(positions[row], 3, "positions"),
87
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 .rotations = CopyFloats(orientations[row], 4, "orientations"),
88
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 .scales = CopyFloats(scales[row], 3, "scales"),
89 6 .wFirst = rows.Text(order[row]) == "wxyz",
90
5/10
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 6 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 6 times.
✗ Branch 11 not taken.
✓ Branch 13 taken 6 times.
✗ Branch 14 not taken.
6 };
91 6 return instances.Bounds();
92 6 });
93 6 }
94
95 } // namespace
96
97
10/20
✓ Branch 0 taken 146 times.
✓ Branch 1 taken 11 times.
✓ Branch 3 taken 146 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 146 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 146 times.
✗ Branch 10 not taken.
✓ Branch 21 taken 146 times.
✗ Branch 22 not taken.
✓ Branch 25 taken 146 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 146 times.
✗ Branch 29 not taken.
✓ Branch 32 taken 1752 times.
✓ Branch 33 taken 146 times.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 85 not taken.
✗ Branch 86 not taken.
3661 ERNIE_DEFINE_SCHEMA(PointInstancer, Concrete, ComputedFromOtherPrims(InstancerExtent),
98 ERNIE_POINT_INSTANCER_ATTRIBUTES, .openNamespaces = {tokens::kPrimvarsPrefix})
99
100 } // namespace ernie
101