/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef TNT_FILAMENT_LIGHTMANAGER_H #define TNT_FILAMENT_LIGHTMANAGER_H #include #include #include #include #include #include namespace utils { class Entity; } // namespace utils namespace filament { class Engine; class FEngine; class FLightManager; /** * LightManager allows to create a light source in the scene, such as a sun or street lights. * * At least one light must be added to a scene in order to see anything * (unless the Material.Shading.UNLIT is used). * * * Creation and destruction * ======================== * * A Light component is created using the LightManager::Builder and destroyed by calling * LightManager::destroy(utils::Entity). * * ~~~~~~~~~~~{.cpp} * filament::Engine* engine = filament::Engine::create(); * utils::Entity sun = utils::EntityManager.get().create(); * * filament::LightManager::Builder(Type::SUN) * .castShadows(true) * .build(*engine, sun); * * engine->getLightManager().destroy(sun); * ~~~~~~~~~~~ * * * Light types * =========== * * Lights come in three flavors: * - directional lights * - point lights * - spot lights * * * Directional lights * ------------------ * * Directional lights have a direction, but don't have a position. All light rays are * parallel and come from infinitely far away and from everywhere. Typically a directional light * is used to simulate the sun. * * Directional lights and spot lights are able to cast shadows. * * To create a directional light use Type.DIRECTIONAL or Type.SUN, both are similar, but the later * also draws a sun's disk in the sky and its reflection on glossy objects. * * @warning Currently, only a single directional light is supported. If several directional lights * are added to the scene, the dominant one will be used. * * @see Builder.direction(), Builder.sunAngularRadius() * * Point lights * ------------ * * Unlike directional lights, point lights have a position but emit light in all directions. * The intensity of the light diminishes with the inverse square of the distance to the light. * Builder.falloff() controls distance beyond which the light has no more influence. * * A scene can have multiple point lights. * * @see Builder.position(), Builder.falloff() * * Spot lights * ----------- * * Spot lights are similar to point lights but the light it emits is limited to a cone defined by * Builder.spotLightCone() and the light's direction. * * A spot light is therefore defined by a position, a direction and inner and outer cones. The * spot light's influence is limited to inside the outer cone. The inner cone defines the light's * falloff attenuation. * * A physically correct spot light is a little difficult to use because changing the outer angle * of the cone changes the illumination levels, as the same amount of light is spread over a * changing volume. The coupling of illumination and the outer cone means that an artist cannot * tweak the influence cone of a spot light without also changing the perceived illumination. * It therefore makes sense to provide artists with a parameter to disable this coupling. This * is the difference between Type.FOCUSED_SPOT and Type.SPOT. * * @see Builder.position(), Builder.direction(), Builder.falloff(), Builder.spotLightCone() * * Performance considerations * ========================== * * Generally, adding lights to the scene hurts performance, however filament is designed to be * able to handle hundreds of lights in a scene under certain conditions. Here are some tips * to keep performances high. * * 1. Prefer spot lights to point lights and use the smallest outer cone angle possible. * * 2. Use the smallest possible falloff distance for point and spot lights. * Performance is very sensitive to overlapping lights. The falloff distance essentially * defines a sphere of influence for the light, so try to position point and spot lights * such that they don't overlap too much. * * On the other hand, a scene can contain hundreds of non overlapping lights without * incurring a significant overhead. * */ class UTILS_PUBLIC LightManager : public FilamentAPI { struct BuilderDetails; public: using Instance = utils::EntityInstance; /** * Returns the number of component in the LightManager, not that component are not * guaranteed to be active. Use the EntityManager::isAlive() before use if needed. * * @return number of component in the LightManager */ size_t getComponentCount() const noexcept; /** * Returns the list of Entity for all components. Use getComponentCount() to know the size * of the list. * @return a pointer to Entity */ utils::Entity const* getEntities() const noexcept; /** * Returns whether a particular Entity is associated with a component of this LightManager * @param e An Entity. * @return true if this Entity has a component associated with this manager. */ bool hasComponent(utils::Entity e) const noexcept; /** * Gets an Instance representing the Light component associated with the given Entity. * @param e An Entity. * @return An Instance object, which represents the Light component associated with the Entity e. * @note Use Instance::isValid() to make sure the component exists. * @see hasComponent() */ Instance getInstance(utils::Entity e) const noexcept; // destroys this component from the given entity void destroy(utils::Entity e) noexcept; //! Denotes the type of the light being created. enum class Type : uint8_t { SUN, //!< Directional light that also draws a sun's disk in the sky. DIRECTIONAL, //!< Directional light, emits light in a given direction. POINT, //!< Point light, emits light from a position, in all directions. FOCUSED_SPOT, //!< Physically correct spot light. SPOT, //!< Spot light with coupling of outer cone and illumination disabled. }; /** * Control the quality / performance of the shadow map associated to this light */ struct ShadowOptions { /** Size of the shadow map in texels. Must be a power-of-two. */ uint32_t mapSize = 1024; /** * Number of shadow cascades to use for this light. Must be between 1 and 4 (inclusive). * A value greater than 1 turns on cascaded shadow mapping (CSM). * Only applicable to Type.SUN or Type.DIRECTIONAL lights. * * When using shadow cascades, cascadeSplitPositions must also be set. * * @see ShadowOptions::cascadeSplitPositions */ uint8_t shadowCascades = 1; /** * The split positions for shadow cascades. * * Cascaded shadow mapping (CSM) partitions the camera frustum into cascades. These values * determine the planes along the camera's Z axis to split the frustum. The camera near * plane is represented by 0.0f and the far plane represented by 1.0f. * * For example, if using 4 cascades, these values would set a uniform split scheme: * { 0.25f, 0.50f, 0.75f } * * For N cascades, N - 1 split positions will be read from this array. * * Filament provides utility methods inside LightManager::ShadowCascades to help set these * values. For example, to use a uniform split scheme: * * ~~~~~~~~~~~{.cpp} * LightManager::ShadowCascades::computeUniformSplits(options.splitPositions, 4); * ~~~~~~~~~~~ * * @see ShadowCascades::computeUniformSplits * @see ShadowCascades::computeLogSplits * @see ShadowCascades::computePracticalSplits */ float cascadeSplitPositions[3] = { 0.25f, 0.50f, 0.75f }; /** Constant bias in world units (e.g. meters) by which shadows are moved away from the * light. 1mm by default. */ float constantBias = 0.001f; /** Amount by which the maximum sampling error is scaled. The resulting value is used * to move the shadow away from the fragment normal. Should be 1.0. */ float normalBias = 1.0f; /** Distance from the camera after which shadows are clipped. this is used to clip * shadows that are too far and wouldn't contribute to the scene much, improving * performance and quality. This value is always positive. * Use 0.0f to use the camera far distance. */ float shadowFar = 0.0f; /** Optimize the quality of shadows from this distance from the camera. Shadows will * be rendered in front of this distance, but the quality may not be optimal. * This value is always positive. Use 0.0f to use the camera near distance. * The default of 1m works well with many scenes. The quality of shadows may drop * rapidly when this value decreases. */ float shadowNearHint = 1.0f; /** Optimize the quality of shadows in front of this distance from the camera. Shadows * will be rendered behind this distance, but the quality may not be optimal. * This value is always positive. Use std::numerical_limits::infinity() to * use the camera far distance. */ float shadowFarHint = 100.0f; /** * Controls whether the shadow map should be optimized for resolution or stability. * When set to true, all resolution enhancing features that can affect stability are * disabling, resulting in significantly lower resolution shadows, albeit stable ones. */ bool stable = false; /** * Constant bias in depth-resolution units by which shadows are moved away from the * light. The default value of 0.5 is used to round depth values up. * Generally this value shouldn't be changed or at least be small and positive. */ float polygonOffsetConstant = 0.5f; /** * Bias based on the change in depth in depth-resolution units by which shadows are moved * away from the light. The default value of 2.0 works well with SHADOW_SAMPLING_PCF_LOW. * Generally this value is between 0.5 and the size in texel of the PCF filter. * Setting this value correctly is essential for LISPSM shadow-maps. */ float polygonOffsetSlope = 2.0f; /** * Whether screen-space contact shadows are used. This applies regardless of whether a * Renderable is a shadow caster. * Screen-space contact shadows are typically useful in large scenes. * (off by default) */ bool screenSpaceContactShadows = false; /** * Number of ray-marching steps for screen-space contact shadows (8 by default). * * CAUTION: this parameter is ignored for all lights except the directional/sun light, * all other lights use the same value set for the directional/sun light. * */ uint8_t stepCount = 8; /** * Maximum shadow-occluder distance for screen-space contact shadows (world units). * (30 cm by default) * * CAUTION: this parameter is ignored for all lights except the directional/sun light, * all other lights use the same value set for the directional/sun light. * */ float maxShadowDistance = 0.3; /** * Options available when the View's ShadowType is set to VSM. * * @warning This API is still experimental and subject to change. * @see View::setShadowType */ struct { /** * The number of MSAA samples to use when rendering VSM shadow maps. * Must be a power-of-two and greater than or equal to 1. A value of 1 effectively turns * off MSAA. * Higher values may not be available depending on the underlying hardware. */ uint8_t msaaSamples = 1; } vsm; }; struct ShadowCascades { /** * Utility method to compute ShadowOptions::cascadeSplitPositions according to a uniform * split scheme. * * @param splitPositions a float array of at least size (cascades - 1) to write the split * positions into * @param cascades the number of shadow cascades, at most 4 */ static void computeUniformSplits(float* splitPositions, uint8_t cascades); /** * Utility method to compute ShadowOptions::cascadeSplitPositions according to a logarithmic * split scheme. * * @param splitPositions a float array of at least size (cascades - 1) to write the split * positions into * @param cascades the number of shadow cascades, at most 4 * @param near the camera near plane * @param far the camera far plane */ static void computeLogSplits(float* splitPositions, uint8_t cascades, float near, float far); /** * Utility method to compute ShadowOptions::cascadeSplitPositions according to a practical * split scheme. * * The practical split scheme uses uses a lambda value to interpolate between the logarithmic * and uniform split schemes. Start with a lambda value of 0.5f and adjust for your scene. * * See: Zhang et al 2006, "Parallel-split shadow maps for large-scale virtual environments" * * @param splitPositions a float array of at least size (cascades - 1) to write the split * positions into * @param cascades the number of shadow cascades, at most 4 * @param near the camera near plane * @param far the camera far plane * @param lambda a float in the range [0, 1] that interpolates between log and * uniform split schemes */ static void computePracticalSplits(float* splitPositions, uint8_t cascades, float near, float far, float lambda); }; //! Use Builder to construct a Light object instance class Builder : public BuilderBase { friend struct BuilderDetails; public: /** * Creates a light builder and set the light's #Type. * * @param type #Type of Light object to create. */ explicit Builder(Type type) noexcept; Builder(Builder const& rhs) noexcept; Builder(Builder&& rhs) noexcept; ~Builder() noexcept; Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; /** * Whether this Light casts shadows (disabled by default) * * @param enable Enables or disables casting shadows from this Light. * * @return This Builder, for chaining calls. * * @warning * - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows */ Builder& castShadows(bool enable) noexcept; /** * Sets the shadow-map options for this light. * * @return This Builder, for chaining calls. */ Builder& shadowOptions(const ShadowOptions& options) noexcept; /** * Whether this light casts light (enabled by default) * * @param enable Enables or disables lighting from this Light. * * @return This Builder, for chaining calls. * * @note * In some situations it can be useful to have a light in the scene that doesn't * actually emit light, but does cast shadows. */ Builder& castLight(bool enable) noexcept; /** * Sets the initial position of the light in world space. * * @param position Light's position in world space. The default is at the origin. * * @return This Builder, for chaining calls. * * @note * The Light's position is ignored for directional lights (Type.DIRECTIONAL or Type.SUN) */ Builder& position(const math::float3& position) noexcept; /** * Sets the initial direction of a light in world space. * * @param direction Light's direction in world space. Should be a unit vector. * The default is {0,-1,0}. * * @return This Builder, for chaining calls. * * @note * The Light's direction is ignored for Type.POINT lights. */ Builder& direction(const math::float3& direction) noexcept; /** * Sets the initial color of a light. * * @param color Color of the light specified in the linear sRGB color-space. * The default is white {1,1,1}. * * @return This Builder, for chaining calls. */ Builder& color(const LinearColor& color) noexcept; /** * Sets the initial intensity of a light. * @param intensity This parameter depends on the Light.Type: * - For directional lights, it specifies the illuminance in *lux* * (or *lumen/m^2*). * - For point lights and spot lights, it specifies the luminous power * in *lumen*. * * @return This Builder, for chaining calls. * * For example, the sun's illuminance is about 100,000 lux. * * This method overrides any prior calls to intensity or intensityCandela. * */ Builder& intensity(float intensity) noexcept; /** * Sets the initial intensity of a spot or point light in candela. * * @param intensity Luminous intensity in *candela*. * * @return This Builder, for chaining calls. * * @note * This method is equivalent to calling intensity(float intensity) for directional lights * (Type.DIRECTIONAL or Type.SUN). * * This method overrides any prior calls to intensity or intensityCandela. */ Builder& intensityCandela(float intensity) noexcept; /** * Sets the initial intensity of a light in watts. * * @param watts Energy consumed by a lightbulb. It is related to the energy produced * and ultimately the brightness by the \p efficiency parameter. * This value is often available on the packaging of commercial * lightbulbs. * * @param efficiency Efficiency in percent. This depends on the type of lightbulb used. * * Lightbulb type | Efficiency * ----------------:|-----------: * Incandescent | 2.2% * Halogen | 7.0% * LED | 8.7% * Fluorescent | 10.7% * * @return This Builder, for chaining calls. * * * @note * This call is equivalent to `Builder::intensity(efficiency * 683 * watts);` * * This method overrides any prior calls to intensity or intensityCandela. */ Builder& intensity(float watts, float efficiency) noexcept; /** * Set the falloff distance for point lights and spot lights. * * At the falloff distance, the light has no more effect on objects. * * The falloff distance essentially defines a *sphere of influence* around the light, and * therefore has an impact on performance. Larger falloffs might reduce performance * significantly, especially when many lights are used. * * Try to avoid having a large number of light's spheres of influence overlap. * * @param radius Falloff distance in world units. Default is 1 meter. * * @return This Builder, for chaining calls. * * @note * The Light's falloff is ignored for directional lights (Type.DIRECTIONAL or Type.SUN) */ Builder& falloff(float radius) noexcept; /** * Defines a spot light'st angular falloff attenuation. * * A spot light is defined by a position, a direction and two cones, \p inner and \p outer. * These two cones are used to define the angular falloff attenuation of the spot light * and are defined by the angle from the center axis to where the falloff begins (i.e. * cones are defined by their half-angle). * * @param inner inner cone angle in *radians* between 0 and @f$ \pi/2 @f$ * * @param outer outer cone angle in *radians* between \p inner and @f$ \pi/2 @f$ * * @return This Builder, for chaining calls. * * @note * The spot light cone is ignored for directional and point lights. * * @see Type.SPOT, Type.FOCUSED_SPOT */ Builder& spotLightCone(float inner, float outer) noexcept; /** * Defines the angular radius of the sun, in degrees, between 0.25° and 20.0° * * The Sun as seen from Earth has an angular size of 0.526° to 0.545° * * @param angularRadius sun's radius in degree. Default is 0.545°. * * @return This Builder, for chaining calls. */ Builder& sunAngularRadius(float angularRadius) noexcept; /** * Defines the halo radius of the sun. The radius of the halo is defined as a * multiplier of the sun angular radius. * * @param haloSize radius multiplier. Default is 10.0. * * @return This Builder, for chaining calls. */ Builder& sunHaloSize(float haloSize) noexcept; /** * Defines the halo falloff of the sun. The falloff is a dimensionless number * used as an exponent. * * @param haloFalloff halo falloff. Default is 80.0. * * @return This Builder, for chaining calls. */ Builder& sunHaloFalloff(float haloFalloff) noexcept; enum Result { Error = -1, Success = 0 }; /** * Adds the Light component to an entity. * * @param engine Reference to the filament::Engine to associate this light with. * @param entity Entity to add the light component to. * @return Success if the component was created successfully, Error otherwise. * * If exceptions are disabled and an error occurs, this function is a no-op. * Success can be checked by looking at the return value. * * If this component already exists on the given entity, it is first destroyed as if * destroy(utils::Entity e) was called. * * @warning * Currently, only 2048 lights can be created on a given Engine. * * @exception utils::PostConditionPanic if a runtime error occurred, such as running out of * memory or other resources. * @exception utils::PreConditionPanic if a parameter to a builder function was invalid. */ Result build(Engine& engine, utils::Entity entity); private: friend class FEngine; friend class FLightManager; }; static constexpr float EFFICIENCY_INCANDESCENT = 0.0220f; //!< Typical efficiency of an incandescent light bulb (2.2%) static constexpr float EFFICIENCY_HALOGEN = 0.0707f; //!< Typical efficiency of an halogen light bulb (7.0%) static constexpr float EFFICIENCY_FLUORESCENT = 0.0878f; //!< Typical efficiency of a fluorescent light bulb (8.7%) static constexpr float EFFICIENCY_LED = 0.1171f; //!< Typical efficiency of a LED light bulb (11.7%) Type getType(Instance i) const noexcept; /** * Helper function that returns if a light is a directional light * * @param i Instance of the component obtained from getInstance(). * @return true is this light is a type of directional light */ inline bool isDirectional(Instance i) const noexcept { Type type = getType(i); return type == Type::DIRECTIONAL || type == Type::SUN; } /** * Helper function that returns if a light is a point light * * @param i Instance of the component obtained from getInstance(). * @return true is this light is a type of point light */ inline bool isPointLight(Instance i) const noexcept { return getType(i) == Type::POINT; } /** * Helper function that returns if a light is a spot light * * @param i Instance of the component obtained from getInstance(). * @return true is this light is a type of spot light */ inline bool isSpotLight(Instance i) const noexcept { Type type = getType(i); return type == Type::SPOT || type == Type::FOCUSED_SPOT; } /** * Dynamically updates the light's position. * * @param i Instance of the component obtained from getInstance(). * @param position Light's position in world space. The default is at the origin. * * @see Builder.position() */ void setPosition(Instance i, const math::float3& position) noexcept; //! returns the light's position in world space const math::float3& getPosition(Instance i) const noexcept; /** * Dynamically updates the light's direction * * @param i Instance of the component obtained from getInstance(). * @param direction Light's direction in world space. Should be a unit vector. * The default is {0,-1,0}. * * @see Builder.direction() */ void setDirection(Instance i, const math::float3& direction) noexcept; //! returns the light's direction in world space const math::float3& getDirection(Instance i) const noexcept; /** * Dynamically updates the light's hue as linear sRGB * * @param i Instance of the component obtained from getInstance(). * @param color Color of the light specified in the linear sRGB color-space. * The default is white {1,1,1}. * * @see Builder.color(), getInstance() */ void setColor(Instance i, const LinearColor& color) noexcept; /** * @param i Instance of the component obtained from getInstance(). * @return the light's color in linear sRGB */ const math::float3& getColor(Instance i) const noexcept; /** * Dynamically updates the light's intensity. The intensity can be negative. * * @param i Instance of the component obtained from getInstance(). * @param intensity This parameter depends on the Light.Type: * - For directional lights, it specifies the illuminance in *lux* * (or *lumen/m^2*). * - For point lights and spot lights, it specifies the luminous power * in *lumen*. * * @see Builder.intensity() */ void setIntensity(Instance i, float intensity) noexcept; /** * Dynamically updates the light's intensity. The intensity can be negative. * * @param i Instance of the component obtained from getInstance(). * @param watts Energy consumed by a lightbulb. It is related to the energy produced * and ultimately the brightness by the \p efficiency parameter. * This value is often available on the packaging of commercial * lightbulbs. * @param efficiency Efficiency in percent. This depends on the type of lightbulb used. * * Lightbulb type | Efficiency * ----------------:|-----------: * Incandescent | 2.2% * Halogen | 7.0% * LED | 8.7% * Fluorescent | 10.7% * * @see Builder.intensity(float watts, float efficiency) */ void setIntensity(Instance i, float watts, float efficiency) noexcept { setIntensity(i, watts * 683.0f * efficiency); } /** * Dynamically updates the light's intensity in candela. The intensity can be negative. * * @param i Instance of the component obtained from getInstance(). * @param intensity Luminous intensity in *candela*. * * @note * This method is equivalent to calling setIntensity(float intensity) for directional lights * (Type.DIRECTIONAL or Type.SUN). * * @see Builder.intensityCandela(float intensity) */ void setIntensityCandela(Instance i, float intensity) noexcept; /** * returns the light's luminous intensity in lumen. * * @param i Instance of the component obtained from getInstance(). * * @note for Type.FOCUSED_SPOT lights, the returned value depends on the \p outer cone angle. * * @return luminous intensity in lumen. */ float getIntensity(Instance i) const noexcept; /** * Set the falloff distance for point lights and spot lights. * * @param i Instance of the component obtained from getInstance(). * @param radius falloff distance in world units. Default is 1 meter. * * @see Builder.falloff() */ void setFalloff(Instance i, float radius) noexcept; /** * returns the falloff distance of this light. * @param i Instance of the component obtained from getInstance(). * @return the falloff distance of this light. */ float getFalloff(Instance i) const noexcept; /** * Dynamically updates a spot light's cone as angles * * @param i Instance of the component obtained from getInstance(). * @param inner inner cone angle in *radians* between 0 and pi/2 * @param outer outer cone angle in *radians* between inner and pi/2 * * @see Builder.spotLightCone() */ void setSpotLightCone(Instance i, float inner, float outer) noexcept; float getSpotLightOuterCone(Instance i) const noexcept; /** * Dynamically updates the angular radius of a Type.SUN light * * The Sun as seen from Earth has an angular size of 0.526° to 0.545° * * @param i Instance of the component obtained from getInstance(). * @param angularRadius sun's radius in degrees. Default is 0.545°. */ void setSunAngularRadius(Instance i, float angularRadius) noexcept; /** * returns the angular radius if the sun in degrees. * @param i Instance of the component obtained from getInstance(). * @return the angular radius if the sun in degrees. */ float getSunAngularRadius(Instance i) const noexcept; /** * Dynamically updates the halo radius of a Type.SUN light. The radius * of the halo is defined as a multiplier of the sun angular radius. * * @param i Instance of the component obtained from getInstance(). * @param haloSize radius multiplier. Default is 10.0. */ void setSunHaloSize(Instance i, float haloSize) noexcept; /** * returns the halo size of a Type.SUN light as a multiplier of the * sun angular radius. * @param i Instance of the component obtained from getInstance(). * @return the halo size */ float getSunHaloSize(Instance i) const noexcept; /** * Dynamically updates the halo falloff of a Type.SUN light. The falloff * is a dimensionless number used as an exponent. * * @param i Instance of the component obtained from getInstance(). * @param haloFalloff halo falloff. Default is 80.0. */ void setSunHaloFalloff(Instance i, float haloFalloff) noexcept; /** * returns the halo falloff of a Type.SUN light as a dimensionless value. * @param i Instance of the component obtained from getInstance(). * @return the halo falloff */ float getSunHaloFalloff(Instance i) const noexcept; /** * returns the shadow-map options for a given light * @param i Instance of the component obtained from getInstance(). * @return A ShadowOption structure */ ShadowOptions const& getShadowOptions(Instance i) const noexcept; /** * sets the shadow-map options for a given light * @param i Instance of the component obtained from getInstance(). * @param options A ShadowOption structure */ void setShadowOptions(Instance i, ShadowOptions const& options) noexcept; /** * Whether this Light casts shadows (disabled by default) * * @param i Instance of the component obtained from getInstance(). * @param shadowCaster Enables or disables casting shadows from this Light. * * @warning * - Only a Type.DIRECTIONAL, Type.SUN, Type.SPOT, or Type.FOCUSED_SPOT light can cast shadows */ void setShadowCaster(Instance i, bool shadowCaster) noexcept; /** * returns whether this light casts shadows. * @param i Instance of the component obtained from getInstance(). */ bool isShadowCaster(Instance i) const noexcept; /** * Helper to process all components with a given function * @tparam F a void(Entity entity, Instance instance) * @param func a function of type F */ template void forEachComponent(F func) noexcept { utils::Entity const* const pEntity = getEntities(); for (size_t i = 0, c = getComponentCount(); i < c; i++) { // Instance 0 is the invalid instance func(pEntity[i], Instance(i + 1)); } } }; } // namespace filament #endif // TNT_FILAMENT_LIGHTMANAGER_H