GCC Code Coverage Report


ernie/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
65 / 65
100.0%
Branches:
15 / 28
53.6%
Functions:
64 / 64, 1 excl
100.0%

scene/schema/lux.h
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 /// @file lux.h
3 /// @brief UsdLux's lights and their shaping and shadow API schemas, Omniverse's photometric ones,
4 /// and Ernie's LaserLight: a Gaussian beam from datasheet parameters, and its patterns.
5 ///
6 /// References:
7 /// - Pixar, UsdLux, https://openusd.org/release/api/usd_lux_page_front.html
8 #pragma once
9
10 #include "ernie/scene/schema/geom.h"
11
12 // clang-format off
13 #define ERNIE_LIGHT_LENGTH ERNIE_NONNEGATIVE("Light")
14
15 /// LightAPI's attributes, which every light has as a built-in API schema.
16 #define ERNIE_LIGHT_API_ATTRIBUTES(X) \
17 X(std::string, ShaderId, "light:shaderId", "", "The light's shader, as renderers name it.", \
18 .group = "Light") \
19 X(std::string, MaterialSyncMode, "light:materialSyncMode", "noMaterialResponse", \
20 "For geometry that emits: whether its bound material's glow tints the light, is lit " \
21 "independently, or is ignored.", \
22 .group = "Light", \
23 .options = {"materialGlowTintsLight", "independent", "noMaterialResponse"}) \
24 X(double, Intensity, "inputs:intensity", 1.0, "Brightness, scaled by 2^exposure.", \
25 ERNIE_NONNEGATIVE("Light")) \
26 X(double, Exposure, "inputs:exposure", 0.0, "Stops of brightness, as photographers count them.", \
27 .group = "Light", .finite = true) \
28 X(double, Diffuse, "inputs:diffuse", 1.0, "Multiplier of its diffuse response.", \
29 .group = "Light", .lower = AtLeast(0.0)) \
30 X(double, Specular, "inputs:specular", 1.0, "Multiplier of its specular response.", \
31 .group = "Light", .lower = AtLeast(0.0)) \
32 X(bool, Normalize, "inputs:normalize", false, \
33 "Divides its power by its area, so resizing it leaves its brightness.", .group = "Light") \
34 X(glm::dvec3, Color, "inputs:color", glm::dvec3(1.0), "Linear RGB tint.", .group = "Light", \
35 .lower = AtLeast(0.0), .finite = true, .color = true) \
36 X(bool, EnableColorTemperature, "inputs:enableColorTemperature", false, \
37 "Tints it by its colour temperature too.", .group = "Light") \
38 X(double, ColorTemperature, "inputs:colorTemperature", 6500.0, "Kelvin, a black body's.", \
39 .group = "Light", .lower = Above(0.0), .finite = true) \
40 X(double, PolarizationDegree, "ernie:polarization:degree", 0.0, \
41 "The fraction of its light linearly polarized.", .label = "Polarized", \
42 ERNIE_FRACTION("Polarization")) \
43 X(double, PolarizationAngle, "ernie:polarization:angle", 0.0, \
44 "Degrees from the light's +x to its electric field.", .label = "Angle", \
45 .group = "Polarization", .finite = true) \
46 X(double, PulseRate, "ernie:pulse:rate", 0.0, "Pulses per second, a strobe's; 0 continuous.", \
47 ERNIE_NONNEGATIVE("Time")) \
48 X(double, PulseWidth, "ernie:pulse:width", 0.0, "Seconds each pulse lasts.", \
49 ERNIE_NONNEGATIVE("Time")) \
50 X(double, PulseDelay, "ernie:pulse:delay", 0.0, \
51 "Seconds on the sensor clock, or after the exposure opens, a pulse starts.", \
52 .group = "Time", .finite = true) \
53 X(double, PulseDecay, "ernie:pulse:decay", 0.0, \
54 "Seconds its level takes to fall by e over a pulse, as a xenon flash's; 0 flat.", \
55 ERNIE_NONNEGATIVE("Time")) \
56 X(double, PulseJitter, "ernie:pulse:jitter", 0.0, \
57 "Seconds each pulse may start early or late.", ERNIE_NONNEGATIVE("Time")) \
58 X(Choice<PulseTrigger>, PulseTrigger, "ernie:pulse:trigger", PulseTrigger::Clock, \
59 "The sensor clock, or the camera's exposure: one pulse a frame, its delay after it opens.", \
60 .group = "Time")
61
62 #define ERNIE_BOUNDABLE_LIGHT_BASE_ATTRIBUTES(X) \
63 X(double, Power, "ernie:power", 0.0, \
64 "Radiant flux in watts, scaling the illuminant's spectral shape; authored, it sets the " \
65 "brightness unless photometric values are authored.", \
66 ERNIE_NONNEGATIVE("Light"))
67
68 #define ERNIE_DISTANT_LIGHT_ATTRIBUTES(X) \
69 X(double, Angle, "inputs:angle", 0.53, "Angular diameter in degrees; the Sun's is 0.53.", \
70 .group = "Light", .lower = AtLeast(0.0), .upper = AtMost(360.0)) \
71 X(double, Irradiance, "ernie:irradiance", 1361.0, \
72 "W m^-2 at normal incidence (the Sun's at 1 au), scaling the illuminant's spectral shape; " \
73 "authored, it sets the brightness unless photometric values are authored.", \
74 ERNIE_NONNEGATIVE("Light"))
75
76 #define ERNIE_DISK_LIGHT_ATTRIBUTES(X) \
77 X(double, Radius, "inputs:radius", 0.5, "Metres; a disk facing -z.", ERNIE_LIGHT_LENGTH)
78
79 #define ERNIE_RECT_LIGHT_ATTRIBUTES(X) \
80 X(double, Width, "inputs:width", 1.0, "Metres along x; a rectangle facing -z.", \
81 ERNIE_LIGHT_LENGTH) \
82 X(double, Height, "inputs:height", 1.0, "Metres along y.", ERNIE_LIGHT_LENGTH) \
83 X(std::string, TextureFile, "inputs:texture:file", "", "An image it emits.", .group = "Light", \
84 .asset = true)
85
86 #define ERNIE_SPHERE_LIGHT_ATTRIBUTES(X) \
87 X(double, Radius, "inputs:radius", 0.5, "Metres.", ERNIE_LIGHT_LENGTH) \
88 X(bool, TreatAsPoint, "treatAsPoint", false, "Lights as a point however large it is.", \
89 .group = "Light")
90
91 #define ERNIE_CYLINDER_LIGHT_ATTRIBUTES(X) \
92 X(double, Length, "inputs:length", 1.0, "Metres along x.", ERNIE_LIGHT_LENGTH) \
93 X(double, Radius, "inputs:radius", 0.5, "Metres.", ERNIE_LIGHT_LENGTH) \
94 X(bool, TreatAsLine, "treatAsLine", false, "Lights as a line however thick it is.", \
95 .group = "Light")
96
97 #define ERNIE_DOME_LIGHT_ATTRIBUTES(X) \
98 X(std::string, TextureFile, "inputs:texture:file", "", "The environment it emits.", \
99 .group = "Light", .asset = true) \
100 X(std::string, TextureFormat, "inputs:texture:format", "automatic", "The texture's mapping.", \
101 .group = "Light", \
102 .options = {"automatic", "latlong", "mirroredBall", "angular", "cubeMapVerticalCross"}) \
103 X(Tensor, Texture, "ernie:texture", Tensor(), \
104 "float32 (rows, columns, 3 or 4) linear RGB in memory, latitude-longitude about +z, in " \
105 "place of the file.", \
106 .group = "Light", .ranks = {3}) \
107 X(std::vector<Path>, Portals, "portals", std::vector<Path>{}, \
108 "Portal lights its light enters through.", .group = "Light") \
109 X(double, GuideRadius, "guideRadius", 1.0e5, "Metres to the sphere drawn as its guide.", \
110 .group = "Light", .lower = Above(0.0))
111
112 #define ERNIE_PORTAL_LIGHT_ATTRIBUTES(X) \
113 X(double, Width, "inputs:width", 1.0, "Metres along x.", ERNIE_LIGHT_LENGTH) \
114 X(double, Height, "inputs:height", 1.0, "Metres along y.", ERNIE_LIGHT_LENGTH)
115
116 #define ERNIE_SHAPING_API_ATTRIBUTES(X) \
117 X(double, ShapingFocus, "inputs:shaping:focus", 0.0, "Concentrates its light towards -z.", \
118 .group = "Shaping", .lower = AtLeast(0.0)) \
119 X(glm::dvec3, ShapingFocusTint, "inputs:shaping:focusTint", glm::dvec3(0.0), \
120 "The colour of what the focus takes away.", .group = "Shaping", .color = true) \
121 X(double, ShapingConeAngle, "inputs:shaping:cone:angle", 90.0, "Degrees from -z it lights.", \
122 .group = "Shaping", .lower = AtLeast(0.0), .upper = AtMost(180.0)) \
123 X(double, ShapingConeSoftness, "inputs:shaping:cone:softness", 0.0, \
124 "How gradually the cone's edge falls off.", .group = "Shaping", .lower = AtLeast(0.0)) \
125 X(std::string, ShapingIesFile, "inputs:shaping:ies:file", "", "An IES LM-63 profile.", \
126 .group = "Shaping", .asset = true) \
127 X(double, ShapingIesAngleScale, "inputs:shaping:ies:angleScale", 0.0, \
128 "Widens or narrows the profile's angles.", .group = "Shaping") \
129 X(bool, ShapingIesNormalize, "inputs:shaping:ies:normalize", false, \
130 "Keeps the profile from changing the light's power.", .group = "Shaping")
131
132 // Omniverse's photometric and illuminant schemas (omni.usd.schema.physical_lighting). A light's
133 // spectral shape comes from its illuminant alone; its brightness from authored photometric values,
134 // else authored ernie:irradiance or ernie:power, else USD's intensity and exposure.
135 #define ERNIE_PHOTOMETRIC_SWITCH " Authored, it alone sets the light's brightness."
136
137 #define ERNIE_PHOTOMETRIC_AREA_LIGHT_API_ATTRIBUTES(X) \
138 X(double, PhotometricPower, "photometric:power", 1600.0, \
139 "Luminous flux in lumens, whatever the colour or cone." ERNIE_PHOTOMETRIC_SWITCH, \
140 ERNIE_NONNEGATIVE("Photometric")) \
141 X(double, PhotometricIlluminance, "photometric:illuminance", 0.0, \
142 "Lux on a surface facing it on its axis at the distance below, in place of the power when " \
143 "both are above 0.", \
144 ERNIE_NONNEGATIVE("Photometric")) \
145 X(double, PhotometricIlluminanceDistance, "photometric:illuminance:distance", 0.0, \
146 "Metres at which the illuminance holds; 0 uses the power.", ERNIE_NONNEGATIVE("Photometric"))
147
148 #define ERNIE_PHOTOMETRIC_ILLUMINANCE_ATTRIBUTES(X, where) \
149 X(double, PhotometricIlluminance, "photometric:illuminance", 10000.0, \
150 "Lux " where "." ERNIE_PHOTOMETRIC_SWITCH, ERNIE_NONNEGATIVE("Photometric"))
151
152 #define ERNIE_PHOTOMETRIC_DISTANT_LIGHT_API_ATTRIBUTES(X) \
153 ERNIE_PHOTOMETRIC_ILLUMINANCE_ATTRIBUTES(X, "on a surface facing the light")
154
155 #define ERNIE_PHOTOMETRIC_DOME_LIGHT_API_ATTRIBUTES(X) \
156 ERNIE_PHOTOMETRIC_ILLUMINANCE_ATTRIBUTES(X, "on an upward-facing surface under the dome")
157
158 #define ERNIE_PHYSICAL_LIGHT_ILLUMINANT_API_ATTRIBUTES(X) \
159 X(std::string, PhysicalIlluminant, "physical:illuminant", "", \
160 "The spectrum's shape: white (flat) or blackbody over 380 to 780 nm, illuminant D at the " \
161 "colour temperature, custom (the pairs below), or sun (TSIS-1, 115 nm to 200 um); unset, a " \
162 "black body when the temperature is used, else flat.", \
163 .group = "Spectrum", .options = {"white", "blackbody", "illuminant D", "custom", "sun"}) \
164 X(std::vector<float>, PhysicalCustomIlluminant, "physical:customIlluminant", \
165 std::vector<float>{}, \
166 "(nm, value) pairs, wavelengths increasing, of a custom illuminant's shape; 0 outside them.", \
167 .group = "Spectrum")
168
169 #define ERNIE_SHADOW_API_ATTRIBUTES(X) \
170 X(bool, ShadowEnable, "inputs:shadow:enable", true, "Whether it casts shadows.", \
171 .group = "Shadow") \
172 X(glm::dvec3, ShadowColor, "inputs:shadow:color", glm::dvec3(0.0), "The colour of shadows.", \
173 .group = "Shadow", .color = true) \
174 X(double, ShadowDistance, "inputs:shadow:distance", -1.0, \
175 "Metres shadow rays reach; -1 for no limit.", .group = "Shadow") \
176 X(double, ShadowFalloff, "inputs:shadow:falloff", -1.0, \
177 "Metres over which shadows fade near their limit; -1 for none.", .group = "Shadow") \
178 X(double, ShadowFalloffGamma, "inputs:shadow:falloffGamma", 1.0, "The fade's curve.", \
179 .group = "Shadow")
180
181 #define ERNIE_LASER_COUNT(X, Name, usdName, fallback, text) \
182 X(int, Name, usdName, fallback, text, .group = "Pattern", .lower = AtLeast(1.0))
183
184 #define ERNIE_LASER_LIGHT_ATTRIBUTES(X) \
185 X(double, Wavelength, "ernie:laser:wavelength", 532.0, "Centre wavelength, nm.", \
186 .group = "Laser", .lower = Above(0.0), .finite = true) \
187 X(double, Linewidth, "ernie:laser:linewidth", 0.1, "Spectral width (FWHM), nm.", \
188 ERNIE_NONNEGATIVE("Laser")) \
189 X(bool, Speckle, "ernie:laser:speckle", false, \
190 "Grain on rough surfaces, sized by the camera's aperture.", .group = "Laser") \
191 X(glm::dvec2, WaistRadius, "ernie:laser:waistRadius", glm::dvec2(0.0005), \
192 "1/e^2 radius at the waist along x and y, metres.", .group = "Beam", .lower = Above(0.0), \
193 .finite = true) \
194 X(double, WaistDistance, "ernie:laser:waistDistance", 0.0, \
195 "Metres from the aperture along the beam to its waist.", .group = "Beam", .finite = true) \
196 X(glm::dvec2, BeamQuality, "ernie:laser:beamQuality", glm::dvec2(1.0), \
197 "M^2 along x and y: 1 for an ideal beam.", .label = "M^2", .group = "Beam", \
198 .lower = AtLeast(1.0), .finite = true) \
199 X(double, ApertureRadius, "ernie:laser:apertureRadius", 0.0, \
200 "Metres at which the aperture clips the beam's tails; 0 none.", ERNIE_NONNEGATIVE("Beam")) \
201 X(std::string, Pattern, "ernie:laser:pattern", "spot", \
202 "The beam itself, a line fanned along x, a grid of beamlets, a field of dots, an image, or " \
203 "a scanned Lissajous figure.", \
204 .group = "Pattern", .options = {"spot", "line", "grid", "dots", "image", "scan"}) \
205 X(double, FanAngle, "ernie:laser:fanAngle", 60.0, "A line's full angle along x, degrees.", \
206 .group = "Pattern", .lower = Above(0.0), .upper = Below(170.0)) \
207 X(std::string, LineProfile, "ernie:laser:lineProfile", "flat", \
208 "Along a line: flat (a Powell lens) or Gaussian, 1/e^2 at the fan's edges.", \
209 .group = "Pattern", .options = {"flat", "gaussian"}) \
210 ERNIE_LASER_COUNT(X, GridColumns, "ernie:laser:gridColumns", 5, "A grid's beamlets along x.") \
211 ERNIE_LASER_COUNT(X, GridRows, "ernie:laser:gridRows", 5, "A grid's beamlets along y.") \
212 X(glm::dvec2, GridPitch, "ernie:laser:gridPitch", glm::dvec2(2.0), \
213 "Degrees between a grid's beamlets along x and y.", .group = "Pattern", .lower = Above(0.0)) \
214 ERNIE_LASER_COUNT(X, DotColumns, "ernie:laser:dotColumns", 64, "Dots per tile along x.") \
215 ERNIE_LASER_COUNT(X, DotRows, "ernie:laser:dotRows", 48, "Dots per tile along y.") \
216 ERNIE_LASER_COUNT(X, DotTileColumns, "ernie:laser:dotTileColumns", 1, \
217 "Tiles of the same dots along x.") \
218 ERNIE_LASER_COUNT(X, DotTileRows, "ernie:laser:dotTileRows", 1, \
219 "Tiles of the same dots along y.") \
220 X(double, DotJitter, "ernie:laser:dotJitter", 0.9, \
221 "How far a dot strays from its cell's centre, in cells: 0 a regular grid, 1 anywhere.", \
222 ERNIE_FRACTION("Pattern")) \
223 X(int, DotSeed, "ernie:laser:dotSeed", 1, "The seed the dots derive from.", \
224 .group = "Pattern", .lower = AtLeast(0.0)) \
225 X(glm::dvec2, ScanFrequencies, "ernie:laser:scanFrequencies", glm::dvec2(3.0, 2.0), \
226 "A scan's Lissajous turns along x and y each period, swept faster than cameras see.", \
227 .group = "Pattern", .lower = AtLeast(0.0)) \
228 X(double, ScanPhase, "ernie:laser:scanPhase", 90.0, "A scan's phase along x, degrees.", \
229 .group = "Pattern", .finite = true) \
230 X(double, Efficiency, "ernie:laser:efficiency", 1.0, \
231 "The fraction of the power that goes into the pattern.", ERNIE_FRACTION("Pattern")) \
232 X(double, ZeroOrder, "ernie:laser:zeroOrder", 0.0, \
233 "The fraction of the power left in a beam along the axis.", ERNIE_FRACTION("Pattern")) \
234 X(std::string, PatternImage, "ernie:laser:pattern:image", "", \
235 "The image pattern's relative power (linear), its top towards +y.", .group = "Pattern", \
236 .asset = true) \
237 X(Tensor, PatternImageArray, "ernie:laser:pattern:imageArray", Tensor(), \
238 "float32 (rows, columns) pattern image in place of the file, on the host or a device.", \
239 .group = "Pattern", .ranks = {2}) \
240 X(glm::dvec2, PatternFieldOfView, "ernie:laser:pattern:fieldOfView", glm::dvec2(40.0, 30.0), \
241 "Full angles an image, dots or a scan span along x and y, degrees.", .group = "Pattern", \
242 .lower = AtLeast(0.0), .upper = Below(170.0)) \
243 X(int, SequenceCount, "ernie:laser:sequence:count", 1, \
244 "Patterns shown in turn; 1 for one pattern.", .group = "Time", .lower = AtLeast(1.0)) \
245 X(double, SequencePeriod, "ernie:laser:sequence:period", 1.0 / 30.0, \
246 "Seconds each pattern of a sequence shows.", .group = "Time", .lower = Above(0.0), \
247 .finite = true) \
248 X(double, SequenceDelay, "ernie:laser:sequence:delay", 0.0, \
249 "Seconds on the sensor clock the first pattern shows.", .group = "Time", .finite = true)
250 // clang-format on
251
252 namespace ernie {
253
254 /// What times a pulsing light's pulses.
255 enum class PulseTrigger { Clock, Exposure };
256
257 template <> struct ChoiceTokens<PulseTrigger> {
258 static constexpr std::array<std::string_view, 2> kTokens{"clock", "exposure"};
259 };
260
261 // The light bases' views read LightAPI's attributes too, which every light has built in.
262 #define ERNIE_BOUNDABLE_LIGHT_VIEW(X) \
263 ERNIE_LIGHT_API_ATTRIBUTES(X) ERNIE_BOUNDABLE_LIGHT_BASE_ATTRIBUTES(X)
264
265 14 ERNIE_API_SCHEMA(LightAPI, ERNIE_LIGHT_API_ATTRIBUTES,
266 "What every light has: brightness, colour, and Ernie's polarization and pulses.");
267 10 ERNIE_SCHEMA(BoundableLightBase, Boundable, ERNIE_BOUNDABLE_LIGHT_VIEW,
268 "A light with a shape, bounded by it.");
269 9 ERNIE_SCHEMA(NonboundableLightBase, Xformable, ERNIE_LIGHT_API_ATTRIBUTES,
270 "A light at infinity or around everything, so unbounded.");
271 15 ERNIE_CONCRETE_SCHEMA(DistantLight, NonboundableLightBase, ERNIE_DISTANT_LIGHT_ATTRIBUTES,
272 "A light from infinitely far along +z, such as the Sun's.");
273 2 ERNIE_CONCRETE_SCHEMA(DiskLight, BoundableLightBase, ERNIE_DISK_LIGHT_ATTRIBUTES,
274 "A disk emitting towards -z.");
275 3 ERNIE_CONCRETE_SCHEMA(RectLight, BoundableLightBase, ERNIE_RECT_LIGHT_ATTRIBUTES,
276 "A rectangle emitting towards -z.");
277 22 ERNIE_CONCRETE_SCHEMA(SphereLight, BoundableLightBase, ERNIE_SPHERE_LIGHT_ATTRIBUTES,
278 "A sphere emitting outwards.");
279 3 ERNIE_CONCRETE_SCHEMA(CylinderLight, BoundableLightBase, ERNIE_CYLINDER_LIGHT_ATTRIBUTES,
280 "A cylinder along x emitting outwards.");
281 5 ERNIE_CONCRETE_SCHEMA(DomeLight, NonboundableLightBase, ERNIE_DOME_LIGHT_ATTRIBUTES,
282 "Light from all around, from an environment image.");
283 3 ERNIE_CONCRETE_SCHEMA(PortalLight, BoundableLightBase, ERNIE_PORTAL_LIGHT_ATTRIBUTES,
284 "A rectangle a dome light's light comes in through, guiding its samples.");
285
3/4
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✓ Branch 7 taken 1 time.
4 ERNIE_API_SCHEMA(MeshLightAPI, ERNIE_NO_ATTRIBUTES,
286 "Makes a Mesh emit, its bound material's glow tinting the light.");
287
2/4
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
3 ERNIE_API_SCHEMA(VolumeLightAPI, ERNIE_NO_ATTRIBUTES,
288 "Makes a Volume emit, its bound material's glow tinting the light.");
289
2/4
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
5 ERNIE_API_SCHEMA(ShapingAPI, ERNIE_SHAPING_API_ATTRIBUTES,
290 "Focuses a light, limits it to a cone, or shapes it by an IES profile.");
291
2/4
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
4 ERNIE_API_SCHEMA(ShadowAPI, ERNIE_SHADOW_API_ATTRIBUTES, "Controls a light's shadows.");
292
2/4
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
6 ERNIE_API_SCHEMA(PhotometricAreaLightAPI, ERNIE_PHOTOMETRIC_AREA_LIGHT_API_ATTRIBUTES,
293 "Omniverse's photometric units for shaped lights: lumens, or lux at a distance.");
294
2/4
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
4 ERNIE_API_SCHEMA(PhotometricDistantLightAPI, ERNIE_PHOTOMETRIC_DISTANT_LIGHT_API_ATTRIBUTES,
295 "Omniverse's photometric units for distant lights: lux on a facing surface.");
296 ERNIE_API_SCHEMA(PhotometricDomeLightAPI, ERNIE_PHOTOMETRIC_DOME_LIGHT_API_ATTRIBUTES,
297 "Omniverse's photometric units for dome lights: lux on an upward-facing "
298 "surface.");
299
2/4
✓ Branch 3 taken 2 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 2 times.
✗ Branch 7 not taken.
11 ERNIE_API_SCHEMA(PhysicalLightIlluminantAPI, ERNIE_PHYSICAL_LIGHT_ILLUMINANT_API_ATTRIBUTES,
300 "Omniverse's illuminant: the shape of a light's spectrum.");
301 5 ERNIE_CONCRETE_SCHEMA(LaserLight, BoundableLightBase, ERNIE_LASER_LIGHT_ATTRIBUTES,
302 "A laser along -z: a Gaussian beam and its pattern. Its power is "
303 "ernie:power, its pulses LightAPI's, and its extent the emitter, so framing "
304 "it frames the device.");
305
306 } // namespace ernie
307