diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp index 4c77ac6a88..8a34162568 100644 --- a/android/filament-android/src/main/cpp/LightManager.cpp +++ b/android/filament-android/src/main/cpp/LightManager.cpp @@ -117,6 +117,13 @@ Java_com_google_android_filament_LightManager_nBuilderColor(JNIEnv*, jclass, builder->color({linearR, linearG, linearB}); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nBuilderIntensityCandela(JNIEnv*, jclass, + jlong nativeBuilder, jfloat intensity) { + LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; + builder->intensityCandela(intensity); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_LightManager_nBuilderIntensity__JF(JNIEnv*, jclass, jlong nativeBuilder, jfloat intensity) { @@ -243,6 +250,13 @@ Java_com_google_android_filament_LightManager_nSetIntensity__JIFF(JNIEnv*, jclas lm->setIntensity((LightManager::Instance) i, watts, efficiency); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nSetIntensityCandela(JNIEnv*, jclass, + jlong nativeLightManager, jint i, jfloat intensity) { + LightManager *lm = (LightManager *) nativeLightManager; + lm->setIntensityCandela((LightManager::Instance) i, intensity); +} + extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_LightManager_nGetIntensity(JNIEnv*, jclass, jlong nativeLightManager, jint i) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java index a8873cecb6..d522813de8 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/LightManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/LightManager.java @@ -401,6 +401,8 @@ public class LightManager { /** * Sets the initial intensity of a light. * + * This method overrides any prior calls to #intensity or #intensityCandela. + * * @param intensity This parameter depends on the {@link 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 @@ -435,6 +437,8 @@ public class LightManager { * Builder.intensity(efficiency * 683 * watts); * * + * This method overrides any prior calls to #intensity or #intensityCandela. + * * @param watts Energy consumed by a lightbulb. It is related to the energy produced * and ultimately the brightness by the efficiency parameter. * This value is often available on the packaging of commercial @@ -450,6 +454,24 @@ public class LightManager { return this; } + /** + * Sets the initial intensity of a spot or point light in candela. + * + * @param intensity Luminous intensity in candela. + * + * This method is equivalent to calling the plain intensity method for directional lights + * (Type.DIRECTIONAL or Type.SUN). + * + * This method overrides any prior calls to #intensity or #intensityCandela. + * + * @return This Builder, for chaining calls. + */ + @NonNull + public Builder intensityCandela(float intensity) { + nBuilderIntensityCandela(mNativeBuilder, intensity); + return this; + } + /** * Set the falloff distance for point lights and spot lights. *
@@ -726,7 +748,22 @@ public class LightManager { * @see Builder#intensity */ public void setIntensity(@EntityInstance int i, float intensity) { - nSetIntensity(mNativeObject, i , intensity); + nSetIntensity(mNativeObject, i, intensity); + } + + /** + * Dynamically updates the light's intensity in candela. The intensity can be negative. + * + * This method is equivalent to calling setIntensity for directional lights (Type.DIRECTIONAL + * or Type.SUN). + * + * @param i Instance of the component obtained from getInstance(). + * @param intensity Luminous intensity in candela. + * + * @see Builder#intensityCandela + */ + public void setIntensityCandela(@EntityInstance int i, float intensity) { + nSetIntensityCandela(mNativeObject, i, intensity); } /** @@ -914,6 +951,7 @@ public class LightManager { private static native void nBuilderColor(long nativeBuilder, float linearR, float linearG, float linearB); private static native void nBuilderIntensity(long nativeBuilder, float intensity); private static native void nBuilderIntensity(long nativeBuilder, float watts, float efficiency); + private static native void nBuilderIntensityCandela(long nativeBuilder, float intensity); private static native void nBuilderFalloff(long nativeBuilder, float radius); private static native void nBuilderSpotLightCone(long nativeBuilder, float inner, float outer); private static native void nBuilderAngularRadius(long nativeBuilder, float angularRadius); @@ -929,6 +967,7 @@ public class LightManager { private static native void nGetColor(long nativeLightManager, int i, float[] out); private static native void nSetIntensity(long nativeLightManager, int i, float intensity); private static native void nSetIntensity(long nativeLightManager, int i, float watts, float efficiency); + private static native void nSetIntensityCandela(long nativeLightManager, int i, float intensity); private static native float nGetIntensity(long nativeLightManager, int i); private static native void nSetFalloff(long nativeLightManager, int i, float falloff); private static native float nGetFalloff(long nativeLightManager, int i); diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index 790cf90899..bcb0e30a40 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -373,9 +373,26 @@ public: * * 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. * @@ -398,6 +415,8 @@ public: * * @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; @@ -622,6 +641,20 @@ public: 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. * diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 580609ecd7..165203ed05 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -42,6 +42,7 @@ struct LightManager::BuilderDetails { float mFalloff = 1.0f; LinearColor mColor = LinearColor{ 1.0f }; float mIntensity = 100000.0f; + FLightManager::IntensityUnit mIntensityUnit = FLightManager::IntensityUnit::LUMEN_LUX; float3 mDirection = { 0.0f, -1.0f, 0.0f }; float2 mSpotInnerOuter = { (float) F_PI, (float) F_PI }; float mSunAngle = 0.00951f; // 0.545° in radians @@ -94,11 +95,19 @@ LightManager::Builder& LightManager::Builder::color(const LinearColor& color) no LightManager::Builder& LightManager::Builder::intensity(float intensity) noexcept { mImpl->mIntensity = intensity; + mImpl->mIntensityUnit = FLightManager::IntensityUnit::LUMEN_LUX; + return *this; +} + +LightManager::Builder& LightManager::Builder::intensityCandela(float intensity) noexcept { + mImpl->mIntensity = intensity; + mImpl->mIntensityUnit = FLightManager::IntensityUnit::CANDELA; return *this; } LightManager::Builder& LightManager::Builder::intensity(float watts, float efficiency) noexcept { mImpl->mIntensity = efficiency * 683.0f * watts; + mImpl->mIntensityUnit = FLightManager::IntensityUnit::LUMEN_LUX; return *this; } @@ -187,7 +196,7 @@ void FLightManager::create(const FLightManager::Builder& builder, utils::Entity // this must be set before intensity setSpotLightCone(i, builder->mSpotInnerOuter.x, builder->mSpotInnerOuter.y); - setIntensity(i, builder->mIntensity); + setIntensity(i, builder->mIntensity, builder->mIntensityUnit); setFalloff(i, builder->mCastLight ? builder->mFalloff : 0); setSunAngularRadius(i, builder->mSunAngle); @@ -240,7 +249,7 @@ void FLightManager::setColor(Instance i, const LinearColor& color) noexcept { } } -void FLightManager::setIntensity(Instance i, float intensity) noexcept { +void FLightManager::setIntensity(Instance i, float intensity, IntensityUnit unit) noexcept { auto& manager = mManager; if (i) { Type type = getLightType(i).type; @@ -254,21 +263,41 @@ void FLightManager::setIntensity(Instance i, float intensity) noexcept { break; case Type::POINT: - // li = lp / (4*pi) - luminousIntensity = luminousPower * float(F_1_PI) * 0.25f; + if (unit == IntensityUnit::LUMEN_LUX) { + // li = lp / (4 * pi) + luminousIntensity = luminousPower * float(F_1_PI) * 0.25f; + } else { + assert(unit == IntensityUnit::CANDELA); + // intensity specified directly in candela, no conversion needed + luminousIntensity = luminousPower; + } break; case Type::FOCUSED_SPOT: { - // li = lp / (2 * pi * (1 - cos(cone_outer / 2))) SpotParams& spotParams = manager[i].spotParams; - spotParams.luminousPower = luminousPower; float cosOuter = std::sqrt(spotParams.cosOuterSquared); - luminousIntensity = luminousPower / (2.0f * float(F_PI) * (1.0f - cosOuter)); + if (unit == IntensityUnit::LUMEN_LUX) { + // li = lp / (2 * pi * (1 - cos(cone_outer / 2))) + luminousIntensity = luminousPower / (2.0f * float(F_PI) * (1.0f - cosOuter)); + } else { + assert(unit == IntensityUnit::CANDELA); + // intensity specified directly in candela, no conversion needed + luminousIntensity = luminousPower; + // lp = li * (2 * pi * (1 - cos(cone_outer / 2))) + luminousPower = luminousIntensity * (2.0f * float(F_PI) * (1.0f - cosOuter)); + } + spotParams.luminousPower = luminousPower; break; } case Type::SPOT: - // li = lp / pi - luminousIntensity = luminousPower * float(F_1_PI); + if (unit == IntensityUnit::LUMEN_LUX) { + // li = lp / pi + luminousIntensity = luminousPower * float(F_1_PI); + } else { + assert(unit == IntensityUnit::CANDELA); + // intensity specified directly in candela, no conversion needed + luminousIntensity = luminousPower; + } break; } manager[i].intensity = luminousIntensity; @@ -398,7 +427,11 @@ const float3& LightManager::getColor(Instance i) const noexcept { } void LightManager::setIntensity(Instance i, float intensity) noexcept { - upcast(this)->setIntensity(i, intensity); + upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::LUMEN_LUX); +} + +void LightManager::setIntensityCandela(Instance i, float intensity) noexcept { + upcast(this)->setIntensity(i, intensity, FLightManager::IntensityUnit::CANDELA); } float LightManager::getIntensity(Instance i) const noexcept { diff --git a/filament/src/components/LightManager.h b/filament/src/components/LightManager.h index 3d30aa4680..c51ebc1c64 100644 --- a/filament/src/components/LightManager.h +++ b/filament/src/components/LightManager.h @@ -86,6 +86,11 @@ public: math::float2 scaleOffset = {}; }; + enum class IntensityUnit { + LUMEN_LUX, // intensity specified in lumens (for punctual lights) or lux (for directional) + CANDELA // intensity specified in candela (only applicable to punctual lights) + }; + struct ShadowParams { LightManager::ShadowOptions options; }; @@ -94,7 +99,7 @@ public: UTILS_NOINLINE void setLocalDirection(Instance i, math::float3 direction) noexcept; UTILS_NOINLINE void setColor(Instance i, const LinearColor& color) noexcept; UTILS_NOINLINE void setSpotLightCone(Instance i, float inner, float outer) noexcept; - UTILS_NOINLINE void setIntensity(Instance i, float intensity) noexcept; + UTILS_NOINLINE void setIntensity(Instance i, float intensity, IntensityUnit unit) noexcept; UTILS_NOINLINE void setFalloff(Instance i, float radius) noexcept; UTILS_NOINLINE void setShadowCaster(Instance i, bool shadowCaster) noexcept; UTILS_NOINLINE void setSunAngularRadius(Instance i, float angularRadius) noexcept; diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 8006536be5..9a13de22dc 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -700,9 +700,7 @@ void FAssetLoader::createLight(const cgltf_node* node, Entity entity) { builder.intensity(light->intensity); break; case LightManager::Type::POINT: - // Convert from candelas (luminous intensity) to lumens (luminous power). - // lp = 4 * pi * li - builder.intensity(4.0f * F_PI * light->intensity); + builder.intensityCandela(light->intensity); break; case LightManager::Type::FOCUSED_SPOT: case LightManager::Type::SPOT: @@ -710,9 +708,7 @@ void FAssetLoader::createLight(const cgltf_node* node, Entity entity) { builder.spotLightCone( light->spot_inner_cone_angle, light->spot_outer_cone_angle); - // Convert from candelas (luminous intensity) to lumens (luminous power). - // lp = li * pi - builder.intensity(F_PI * light->intensity); + builder.intensityCandela(light->intensity); break; }