diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index edf23fe7fc..65e8ae9876 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,8 @@ A new header is inserted each time a *tag* is created. - engine: Option to automatically compute bent normals from SSAO & apply to specular AO [⚠️ **Material breakage**]. +- engine: New APIs: Light channels. Geometry and lights now have a channel associated to them, at + least one channel must match for lighting to occur [⚠️ **Material breakage**]. ## v1.11.2 diff --git a/android/filament-android/src/main/cpp/LightManager.cpp b/android/filament-android/src/main/cpp/LightManager.cpp index db19de6f2d..84df8ecd7f 100644 --- a/android/filament-android/src/main/cpp/LightManager.cpp +++ b/android/filament-android/src/main/cpp/LightManager.cpp @@ -189,6 +189,23 @@ Java_com_google_android_filament_LightManager_nBuilderHaloFalloff(JNIEnv*, jclas builder->sunHaloFalloff(haloFalloff); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nBuilderLightChannel(JNIEnv*, jclass, + jlong nativeBuilder, jint channel, jboolean enable) { + LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; + builder->lightChannel(channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv*, jclass, + jlong nativeBuilder, jlong nativeEngine, jint entity) { + LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; + Engine *engine = (Engine *) nativeEngine; + return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success); +} + +// ------------------------------------------------------------------------------------------------ + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_LightManager_nComputeUniformSplits(JNIEnv* env, jclass, jfloatArray splitPositions, jint cascades) { @@ -213,13 +230,7 @@ Java_com_google_android_filament_LightManager_nComputePracticalSplits(JNIEnv* en env->ReleaseFloatArrayElements(splitPositions, nativeSplits, 0); } -extern "C" JNIEXPORT jboolean JNICALL -Java_com_google_android_filament_LightManager_nBuilderBuild(JNIEnv*, jclass, - jlong nativeBuilder, jlong nativeEngine, jint entity) { - LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder; - Engine *engine = (Engine *) nativeEngine; - return jboolean(builder->build(*engine, (Entity &) entity) == LightManager::Builder::Success); -} +// ------------------------------------------------------------------------------------------------ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_LightManager_nGetType(JNIEnv* env, @@ -394,3 +405,17 @@ Java_com_google_android_filament_LightManager_nGetInnerConeAngle(JNIEnv*, jclass LightManager *lm = (LightManager *) nativeLightManager; return (jfloat)lm->getSpotLightInnerCone((LightManager::Instance) i); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_LightManager_nSetLightChannel(JNIEnv*, jclass, + jlong nativeLightManager, jint i, jint channel, jboolean enable) { + LightManager *lm = (LightManager *) nativeLightManager; + lm->setLightChannel((LightManager::Instance) i, channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_LightManager_nGetLightChannel(JNIEnv*, jclass, + jlong nativeLightManager, jint i, jint channel) { + LightManager const *lm = (LightManager const *) nativeLightManager; + return lm->getLightChannel((LightManager::Instance) i, channel); +} diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index fb77f0ed47..cba710637c 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -214,6 +214,15 @@ Java_com_google_android_filament_RenderableManager_nBuilderMorphing(JNIEnv*, jcl builder->morphing(enabled); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nBuilderLightChannel(JNIEnv*, jclass, + jlong nativeBuilder, jint channel, jboolean enable) { + RenderableManager::Builder *builder = (RenderableManager::Builder *) nativeBuilder; + builder->lightChannel(channel, (bool)enable); +} + +// ------------------------------------------------------------------------------------------------ + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_RenderableManager_nSetSkinningBuffer(JNIEnv*, jclass, jlong nativeRenderableManager, jint i, jlong nativeSkinningBuffer, jint count, jint offset) { @@ -404,3 +413,17 @@ Java_com_google_android_filament_RenderableManager_nGetEnabledAttributesAt(JNIEn AttributeBitset enabled = rm->getEnabledAttributesAt((RenderableManager::Instance) i, (size_t) primitiveIndex); return enabled.getValue(); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_RenderableManager_nSetLightChannel(JNIEnv*, jclass, + jlong nativeRenderableManager, jint i, jint channel, jboolean enable) { + RenderableManager *rm = (RenderableManager *) nativeRenderableManager; + rm->setLightChannel((RenderableManager::Instance) i, channel, (bool)enable); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_RenderableManager_nGetLightChannel(JNIEnv*, jclass, + jlong nativeRenderableManager, jint i, jint channel) { + RenderableManager const *rm = (RenderableManager const *) nativeRenderableManager; + return rm->getLightChannel((RenderableManager::Instance) i, channel); +} 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 4409345ce2..f3c4c5cb4a 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 @@ -430,6 +430,18 @@ public class LightManager { mFinalizer = new BuilderFinalizer(mNativeBuilder); } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + @NonNull + public Builder lightChannel(@IntRange(from = 0, to = 7) int channel, boolean enable) { + nBuilderLightChannel(mNativeBuilder, channel, enable); + return this; + } + /** * Whether this Light casts shadows (disabled by default) * @@ -785,6 +797,30 @@ public class LightManager { return type == Type.SPOT || type == Type.FOCUSED_SPOT; } + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to set + * @param enable true to enable, false to disable + * + * @see Builder#lightChannel + */ + public void setLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel, boolean enable) { + nSetLightChannel(mNativeObject, i, channel, enable); + } + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + public boolean getLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) { + return nGetLightChannel(mNativeObject, i, channel); + } + /** * Dynamically updates the light's position. * @@ -1108,6 +1144,7 @@ public class LightManager { private static native void nBuilderAngularRadius(long nativeBuilder, float angularRadius); private static native void nBuilderHaloSize(long nativeBuilder, float haloSize); private static native void nBuilderHaloFalloff(long nativeBuilder, float haloFalloff); + private static native void nBuilderLightChannel(long nativeBuilder, int channel, boolean enable); private static native void nComputeUniformSplits(float[] splitPositions, int cascades); private static native void nComputeLogSplits(float[] splitPositions, int cascades, float near, float far); @@ -1137,4 +1174,6 @@ public class LightManager { private static native boolean nIsShadowCaster(long nativeLightManager, int i); private static native float nGetOuterConeAngle(long nativeLightManager, int i); private static native float nGetInnerConeAngle(long nativeLightManager, int i); + private static native void nSetLightChannel(long nativeLightManager, int i, int channel, boolean enable); + private static native boolean nGetLightChannel(long nativeLightManager, int i, int channel); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java index 49e1570719..be9c6c6559 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java +++ b/android/filament-android/src/main/java/com/google/android/filament/RenderableManager.java @@ -266,6 +266,18 @@ public class RenderableManager { return this; } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + @NonNull + public Builder lightChannel(@IntRange(from = 0, to = 7) int channel, boolean enable) { + nBuilderLightChannel(mNativeBuilder, channel, enable); + return this; + } + /** * Controls if this renderable casts shadows, false by default. * @@ -531,6 +543,30 @@ public class RenderableManager { nSetCulling(mNativeObject, i, enabled); } + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to set + * @param enable true to enable, false to disable + * + * @see Builder#lightChannel + */ + public void setLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel, boolean enable) { + nSetLightChannel(mNativeObject, i, channel, enable); + } + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + public boolean getLightChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) { + return nGetLightChannel(mNativeObject, i, channel); + } + /** * Changes whether or not the renderable casts shadows. * @@ -718,6 +754,7 @@ public class RenderableManager { private static native void nBuilderSkinningBuffer(long nativeBuilder, long nativeSkinningBuffer, int boneCount, int offset); private static native void nBuilderMorphing(long nativeBuilder, boolean enabled); private static native void nEnableSkinningBuffers(long nativeBuilder, boolean enabled); + private static native void nBuilderLightChannel(long nativeRenderableManager, int channel, boolean enable); private static native void nSetSkinningBuffer(long nativeObject, int i, long nativeSkinningBuffer, int count, int offset); private static native int nSetBonesAsMatrices(long nativeObject, int i, Buffer matrices, int remaining, int boneCount, int offset); @@ -727,6 +764,8 @@ public class RenderableManager { private static native void nSetLayerMask(long nativeRenderableManager, int i, int select, int value); private static native void nSetPriority(long nativeRenderableManager, int i, int priority); private static native void nSetCulling(long nativeRenderableManager, int i, boolean enabled); + private static native void nSetLightChannel(long nativeRenderableManager, int i, int channel, boolean enable); + private static native boolean nGetLightChannel(long nativeRenderableManager, int i, int channel); private static native void nSetCastShadows(long nativeRenderableManager, int i, boolean enabled); private static native void nSetReceiveShadows(long nativeRenderableManager, int i, boolean enabled); private static native void nSetScreenSpaceContactShadows(long nativeRenderableManager, int i, boolean enabled); diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index 83d1da3c2e..a0e0441dd2 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -395,6 +395,15 @@ public: Builder& operator=(Builder const& rhs) noexcept; Builder& operator=(Builder&& rhs) noexcept; + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + * @return This Builder, for chaining calls. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + /** * Whether this Light casts shadows (disabled by default) * @@ -661,6 +670,21 @@ public: return type == Type::SPOT || type == Type::FOCUSED_SPOT; } + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * @param channel light channel to enable or disable, between 0 and 7. + * @param enable whether to enable (true) or disable (false) the specified light channel. + */ + void setLightChannel(Instance i, unsigned int channel, bool enable = true) noexcept; + + /** + * Returns whether a light channel is enabled on a specified light. + * @param i Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance i, unsigned int channel) const noexcept; + /** * Dynamically updates the light's position. * diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 8f2b6775bb..b9e0577124 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -218,6 +218,14 @@ public: */ Builder& culling(bool enable) noexcept; + /** + * Enables or disables a light channel. Light channel 0 is enabled by default. + * + * @param channel Light channel to enable or disable, between 0 and 7. + * @param enable Whether to enable or disable the light channel. + */ + Builder& lightChannel(unsigned int channel, bool enable = true) noexcept; + /** * Controls if this renderable casts shadows, false by default. * @@ -381,6 +389,22 @@ public: */ void setCulling(Instance instance, bool enable) noexcept; + /** + * Enables or disables a light channel. + * Light channel 0 is enabled by default. + * + * \see Builder::lightChannel() + */ + void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; + + /** + * Returns whether a light channel is enabled on a specified renderable. + * @param instance Instance of the component obtained from getInstance(). + * @param channel Light channel to query + * @return true if the light channel is enabled, false otherwise + */ + bool getLightChannel(Instance instance, unsigned int channel) const noexcept; + /** * Changes whether or not the renderable casts shadows. * diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index daef9d2584..6ea8144b67 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -140,6 +140,7 @@ void FScene::prepare(const mat4f& worldOriginTransform, bool shadowReceiversAreC worldAABB.center, // WORLD_AABB_CENTER 0, // VISIBLE_MASK rcm.getMorphWeights(ri), // MORPH_WEIGHTS + rcm.getChannels(ri), // CHANNELS rcm.getLayerMask(ri), // LAYERS worldAABB.halfExtent, // WORLD_AABB_EXTENT {}, // PRIMITIVES @@ -239,6 +240,7 @@ void FScene::updateUBOs(utils::Range visibleRenderables, backend::Hand FRenderableManager::Visibility visibility = sceneData.elementAt(i); hasContactShadows = hasContactShadows || visibility.screenSpaceContactShadows; + UniformBuffer::setUniform(buffer, offset + offsetof(PerRenderableUib, flags), PerRenderableUib::packFlags( @@ -250,9 +252,14 @@ void FScene::updateUBOs(utils::Range visibleRenderables, backend::Hand offset + offsetof(PerRenderableUib, morphWeights), sceneData.elementAt(i)); + UniformBuffer::setUniform(buffer, + offset + offsetof(PerRenderableUib, channels), + (uint32_t)sceneData.elementAt(i)); + // TODO: We need to find a better way to provide the scale information per object UniformBuffer::setUniform(buffer, - offset + offsetof(PerRenderableUib, userData), sceneData.elementAt(i)); + offset + offsetof(PerRenderableUib, userData), + sceneData.elementAt(i)); } // TODO: handle static objects separately @@ -309,7 +316,7 @@ void FScene::prepareDynamicLights(const CameraInfo& camera, ArenaScope& rootAren shadowInfo[i].contactShadows, shadowInfo[i].index, shadowInfo[i].layer); - lp[gpuIndex].channels = LightsUib::packChannels(1, shadowInfo[i].castsShadows); + lp[gpuIndex].channels = LightsUib::packChannels(lcm.getLightChannels(li), shadowInfo[i].castsShadows); lp[gpuIndex].reserved = {}; } diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 99e2bd8bbe..d77bcfd8e3 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -355,6 +355,7 @@ void FView::prepareLighting(FEngine& engine, FEngine::DriverApi& driver, ArenaSc s.lightDirection = l; s.lightColorIntensity = colorIntensity; + s.lightChannels = lcm.getLightChannels(directionalLight); const bool isSun = lcm.isSunLight(directionalLight); // The last parameter must be < 0.0f for regular directional lights diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 5972eb2a64..51d36baf51 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -24,6 +24,8 @@ #include #include +#include + using namespace filament::math; using namespace utils; @@ -36,6 +38,7 @@ struct LightManager::BuilderDetails { Type mType = Type::DIRECTIONAL; bool mCastShadows = false; bool mCastLight = true; + uint8_t mChannels = 1u; float3 mPosition = {}; float mFalloff = 1.0f; LinearColor mColor = LinearColor{ 1.0f }; @@ -134,6 +137,15 @@ LightManager::Builder& LightManager::Builder::sunHaloFalloff(float haloFalloff) return *this; } +LightManager::Builder& LightManager::Builder::lightChannel(unsigned int channel, bool enable) noexcept { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mImpl->mChannels &= ~mask; + mImpl->mChannels |= enable ? mask : 0u; + } + return *this; +} + LightManager::Builder::Result LightManager::Builder::build(Engine& engine, Entity entity) { upcast(engine).createLight(*this, entity); return Success; @@ -171,6 +183,8 @@ void FLightManager::create(const FLightManager::Builder& builder, utils::Entity lightType.shadowCaster = builder->mCastShadows; lightType.lightCaster = builder->mCastLight; + mManager[i].channels = builder->mChannels; + // set default values by calling the setters setShadowOptions(i, builder->mShadowOptions); setLocalPosition(i, builder->mPosition); @@ -227,16 +241,40 @@ void FLightManager::setShadowOptions(Instance i, ShadowOptions const& options) n params.options.vsm.blurWidth = std::max(0.0f, options.vsm.blurWidth); } +void FLightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept { + if (i) { + if (channel < 8) { + auto& manager = mManager; + const uint8_t mask = 1u << channel; + manager[i].channels &= ~mask; + manager[i].channels |= enable ? mask : 0u; + } + } +} + +bool FLightManager::getLightChannel(Instance i, unsigned int channel) const noexcept { + if (i) { + if (channel < 8) { + auto& manager = mManager; + const uint8_t mask = 1u << channel; + return bool(manager[i].channels & mask); + } + } + return false; +} + void FLightManager::setLocalPosition(Instance i, const float3& position) noexcept { - assert_invariant(i); - auto& manager = mManager; - manager[i].position = position; + if (i) { + auto& manager = mManager; + manager[i].position = position; + } } void FLightManager::setLocalDirection(Instance i, float3 direction) noexcept { - assert_invariant(i); - auto& manager = mManager; - manager[i].direction = direction; + if (i) { + auto& manager = mManager; + manager[i].direction = direction; + } } void FLightManager::setColor(Instance i, const LinearColor& color) noexcept { @@ -438,8 +476,16 @@ void LightManager::destroy(Entity e) noexcept { return upcast(this)->destroy(e); } +void LightManager::setLightChannel(Instance i, unsigned int channel, bool enable) noexcept { + upcast(this)->setLightChannel(i, channel, enable); +} + +bool LightManager::getLightChannel(LightManager::Instance i, unsigned int channel) const noexcept { + return upcast(this)->getLightChannel(i, channel); +} + void LightManager::setPosition(Instance i, const float3& position) noexcept { - return upcast(this)->setLocalPosition(i, position); + upcast(this)->setLocalPosition(i, position); } const float3& LightManager::getPosition(Instance i) const noexcept { diff --git a/filament/src/components/LightManager.h b/filament/src/components/LightManager.h index 9fb4b1b4f6..2081e5f008 100644 --- a/filament/src/components/LightManager.h +++ b/filament/src/components/LightManager.h @@ -96,6 +96,7 @@ public: UTILS_NOINLINE void setLocalPosition(Instance i, const math::float3& position) noexcept; UTILS_NOINLINE void setLocalDirection(Instance i, math::float3 direction) noexcept; + UTILS_NOINLINE void setLightChannel(Instance i, unsigned int channel, bool enable) 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, IntensityUnit unit) noexcept; @@ -105,6 +106,8 @@ public: UTILS_NOINLINE void setSunHaloSize(Instance i, float haloSize) noexcept; UTILS_NOINLINE void setSunHaloFalloff(Instance i, float haloFalloff) noexcept; + UTILS_NOINLINE bool getLightChannel(Instance i, unsigned int channel) const noexcept; + LightType const& getLightType(Instance i) const noexcept { return mManager[i].lightType; } @@ -209,6 +212,10 @@ public: return getSpotParams(i).radius; } + uint8_t getLightChannels(Instance i) const noexcept { + return mManager[i].channels; + } + const math::float3& getLocalPosition(Instance i) const noexcept { return mManager[i].position; } @@ -238,6 +245,7 @@ private: SUN_HALO_FALLOFF, // state for the directional light sun INTENSITY, FALLOFF, + CHANNELS, }; using Base = utils::SingleInstanceComponentManager< // 120 bytes @@ -251,7 +259,8 @@ private: float, // 4 float, // 4 float, // 4 - float // 4 + float, // 4 + uint8_t // 1 >; struct Sim : public Base { @@ -277,6 +286,7 @@ private: Field sunHaloFalloff; Field intensity; Field squaredFallOffInv; + Field channels; }; }; diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index b6353a8157..0c9e92fe59 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -45,6 +45,7 @@ struct RenderableManager::BuilderDetails { Box mAABB; uint8_t mLayerMask = 0x1; uint8_t mPriority = 0x4; + uint8_t mChannels = 1; bool mCulling : 1; bool mCastShadows : 1; bool mReceiveShadows : 1; @@ -133,6 +134,15 @@ RenderableManager::Builder& RenderableManager::Builder::culling(bool enable) noe return *this; } +RenderableManager::Builder& RenderableManager::Builder::lightChannel(unsigned int channel, bool enable) noexcept { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mImpl->mChannels &= ~mask; + mImpl->mChannels |= enable ? mask : 0u; + } + return *this; +} + RenderableManager::Builder& RenderableManager::Builder::castShadows(bool enable) noexcept { mImpl->mCastShadows = enable; return *this; @@ -307,6 +317,7 @@ void FRenderableManager::create( setSkinning(ci, false); setMorphing(ci, builder->mMorphingEnabled); setMorphWeights(ci, {0, 0, 0, 0}); + mManager[ci].channels = builder->mChannels; const uint32_t count = builder->mSkinningBoneCount; if (builder->mSkinningBufferMode) { @@ -557,6 +568,26 @@ void FRenderableManager::setMorphWeights(Instance ci, const float4& weights) noe } } +void FRenderableManager::setLightChannel(Instance ci, unsigned int channel, bool enable) noexcept { + if (ci) { + if (channel < 8) { + const uint8_t mask = 1u << channel; + mManager[ci].channels &= ~mask; + mManager[ci].channels |= enable ? mask : 0u; + } + } +} + +bool FRenderableManager::getLightChannel(Instance ci, unsigned int channel) const noexcept { + if (ci) { + if (channel < 8) { + const uint8_t mask = 1u << channel; + return bool(mManager[ci].channels & mask); + } + } + return false; +} + // ------------------------------------------------------------------------------------------------ // Trampoline calling into private implementation // ------------------------------------------------------------------------------------------------ @@ -666,9 +697,17 @@ void RenderableManager::setMorphWeights(Instance instance, float4 const& weights upcast(this)->setMorphWeights(instance, weights); } -void RenderableManager::setSkinningBuffer(RenderableManager::Instance instance, +void RenderableManager::setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept { upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset); } +void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept { + upcast(this)->setLightChannel(instance, channel, enable); +} + +bool RenderableManager::getLightChannel(Instance instance, unsigned int channel) const noexcept { + return upcast(this)->getLightChannel(instance, channel); +} + } // namespace filament diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 97ce2789c4..6104a621be 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -108,6 +108,9 @@ public: inline void setMorphWeights(Instance instance, const math::float4& weights) noexcept; inline void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept; + inline void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; + + inline bool getLightChannel(Instance instance, unsigned int channel) const noexcept; inline bool isShadowCaster(Instance instance) const noexcept; inline bool isShadowReceiver(Instance instance) const noexcept; @@ -120,6 +123,7 @@ public: inline uint8_t getLayerMask(Instance instance) const noexcept; inline uint8_t getPriority(Instance instance) const noexcept; inline math::float4 getMorphWeights(Instance instance) const noexcept; + inline uint8_t getChannels(Instance instance) const noexcept; struct SkinningBindingInfo { backend::Handle handle; @@ -163,6 +167,7 @@ private: AABB, // user data LAYERS, // user data MORPH_WEIGHTS, // user data + CHANNELS, // user data VISIBILITY, // user data PRIMITIVES, // user data BONES, // filament data, UBO storing a pointer to the bones information @@ -172,6 +177,7 @@ private: Box, // AABB uint8_t, // LAYERS math::float4, // MORPH_WEIGHTS + uint8_t, // CHANNELS Visibility, // VISIBILITY utils::Slice, // PRIMITIVES Bones // BONES @@ -192,6 +198,7 @@ private: Field aabb; Field layers; Field morphWeights; + Field channels; Field visibility; Field primitives; Field bones; @@ -317,6 +324,10 @@ math::float4 FRenderableManager::getMorphWeights(Instance instance) const noexce return mManager[instance].morphWeights; } +uint8_t FRenderableManager::getChannels(Instance instance) const noexcept { + return mManager[instance].channels; +} + Box const& FRenderableManager::getAABB(Instance instance) const noexcept { return mManager[instance].aabb; } diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 018c001285..33895af75c 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -108,6 +108,7 @@ public: WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable VISIBLE_MASK, // 1 | each bit represents a visibility in a pass MORPH_WEIGHTS, // 4 | floats for morphing + CHANNELS, // 1 | currently light channels only // These are not needed anymore after culling LAYERS, // 1 | layers @@ -130,6 +131,7 @@ public: math::float3, // WORLD_AABB_CENTER VisibleMaskType, // VISIBLE_MASK math::float4, // MORPH_WEIGHTS + uint8_t, // CHANNELS uint8_t, // LAYERS math::float3, // WORLD_AABB_EXTENT utils::Slice, // PRIMITIVES diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 2972f465e3..7277090bec 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -64,7 +64,8 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) filament::math::float4 sun; // cos(sunAngle), sin(sunAngle), 1/(sunAngle*HALO_SIZE-sunAngle), HALO_EXP - filament::math::float4 padding0; + filament::math::float3 padding0; + uint32_t lightChannels; filament::math::float3 lightDirection; uint32_t fParamsX; // stride-x @@ -142,7 +143,7 @@ struct alignas(256) PerRenderableUib { filament::math::mat3f worldFromModelNormalMatrix; // this gets expanded to 48 bytes during the copy to the UBO alignas(16) filament::math::float4 morphWeights; // morph weights (we could easily have 8 using half) uint32_t flags; // see packFlags() below - uint32_t reserved0; // 0 + uint32_t channels; // 0x000000ll uint32_t reserved1; // 0 // TODO: We need a better solution, this currently holds the average local scale for the renderable float userData; diff --git a/libs/filamat/src/UibGenerator.cpp b/libs/filamat/src/UibGenerator.cpp index d651820758..02521b4f3a 100644 --- a/libs/filamat/src/UibGenerator.cpp +++ b/libs/filamat/src/UibGenerator.cpp @@ -51,7 +51,8 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { // directional light .add("lightColorIntensity", 1, UniformInterfaceBlock::Type::FLOAT4) .add("sun", 1, UniformInterfaceBlock::Type::FLOAT4) - .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT4) + .add("padding0", 1, UniformInterfaceBlock::Type::FLOAT3) + .add("lightChannels", 1, UniformInterfaceBlock::Type::UINT) .add("lightDirection", 1, UniformInterfaceBlock::Type::FLOAT3) .add("fParamsX", 1, UniformInterfaceBlock::Type::UINT) // shadow @@ -121,7 +122,7 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept { .add("worldFromModelNormalMatrix", 1, UniformInterfaceBlock::Type::MAT3, Precision::HIGH) .add("morphWeights", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH) .add("flags", 1, UniformInterfaceBlock::Type::UINT) - .add("reserved0", 1, UniformInterfaceBlock::Type::UINT) + .add("channels", 1, UniformInterfaceBlock::Type::UINT) .add("reserved1", 1, UniformInterfaceBlock::Type::UINT) .add("userData", 1, UniformInterfaceBlock::Type::FLOAT) .build(); diff --git a/shaders/src/common_lighting.fs b/shaders/src/common_lighting.fs index 52cde9ec42..44b686c195 100644 --- a/shaders/src/common_lighting.fs +++ b/shaders/src/common_lighting.fs @@ -8,6 +8,7 @@ struct Light { bool contactShadows; uint shadowIndex; uint shadowLayer; + uint channels; }; struct PixelParams { diff --git a/shaders/src/light_directional.fs b/shaders/src/light_directional.fs index ec58092b2b..841cc02824 100644 --- a/shaders/src/light_directional.fs +++ b/shaders/src/light_directional.fs @@ -27,6 +27,7 @@ Light getDirectionalLight() { light.l = sampleSunAreaLight(frameUniforms.lightDirection); light.attenuation = 1.0; light.NoL = saturate(dot(shading_normal, light.l)); + light.channels = frameUniforms.lightChannels & 0xFFu; return light; } @@ -35,6 +36,11 @@ void evaluateDirectionalLight(const MaterialInputs material, Light light = getDirectionalLight(); + uint channels = objectUniforms.channels & 0xFFu; + if ((light.channels & channels) == 0u) { + return; + } + #if defined(MATERIAL_CAN_SKIP_LIGHTING) if (light.NoL <= 0.0) { return; diff --git a/shaders/src/light_punctual.fs b/shaders/src/light_punctual.fs index 646fbd8706..9aff62713d 100644 --- a/shaders/src/light_punctual.fs +++ b/shaders/src/light_punctual.fs @@ -160,6 +160,7 @@ Light getLight(const uint index) { light.contactShadows = false; light.shadowIndex = 0u; light.shadowLayer = 0u; + light.channels = channels; uint type = typeShadow & 0x1u; if (type == LIGHT_TYPE_SPOT) { @@ -198,6 +199,10 @@ void evaluatePunctualLights(const MaterialInputs material, // Iterate point lights for ( ; index < end; index++) { Light light = getLight(index); + if ((light.channels & channels) == 0u) { + continue; + } + #if defined(MATERIAL_CAN_SKIP_LIGHTING) if (light.NoL <= 0.0 || light.attenuation <= 0.0) { continue; diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 1a956185a7..febd79b244 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -980,6 +980,10 @@ class_("RenderableManager$Builder") (RenderableBuilder* builder, size_t index, uint16_t order), { return &builder->blendOrder(index, order); }) + .BUILDER_FUNCTION("lightChannel", RenderableBuilder, + (RenderableBuilder* builder, unsigned int channel, bool enable), { + return &builder->lightChannel(channel, enable); }) + .function("_build", EMBIND_LAMBDA(int, (RenderableBuilder* builder, Engine* engine, utils::Entity entity), { return (int) builder->build(*engine, entity); @@ -1006,6 +1010,8 @@ class_("RenderableManager") .function("setReceiveShadows", &RenderableManager::setReceiveShadows) .function("isShadowCaster", &RenderableManager::isShadowCaster) .function("isShadowReceiver", &RenderableManager::isShadowReceiver) + .function("setLightChannel", &RenderableManager::setLightChannel) + .function("getLightChannel", &RenderableManager::getLightChannel) .function("setBones", EMBIND_LAMBDA(void, (RenderableManager* self, RenderableManager::Instance instance, emscripten::val transforms, size_t offset), { @@ -1144,7 +1150,10 @@ class_("LightManager$Builder") .BUILDER_FUNCTION("sunHaloSize", LightBuilder, (LightBuilder* builder, float value), { return &builder->sunHaloSize(value); }) .BUILDER_FUNCTION("sunHaloFalloff", LightBuilder, - (LightBuilder* builder, float value), { return &builder->sunHaloFalloff(value); }); + (LightBuilder* builder, float value), { return &builder->sunHaloFalloff(value); }) + .BUILDER_FUNCTION("lightChannel", LightBuilder, + (LightBuilder* builder, unsigned int channel, bool enable), { + return &builder->lightChannel(channel, enable); }); class_("LightManager") .function("hasComponent", &LightManager::hasComponent) @@ -1191,6 +1200,8 @@ class_("LightManager") .function("getSunHaloFalloff", &LightManager::getSunHaloFalloff) .function("setShadowCaster", &LightManager::setShadowCaster) .function("isShadowCaster", &LightManager::isShadowCaster) + .function("setLightChannel", &LightManager::setLightChannel) + .function("getLightChannel", &LightManager::getLightChannel) ; /// LightManager$Instance ::class:: Component instance returned by [LightManager]