diff --git a/android/filament-android/src/main/cpp/MaterialInstance.cpp b/android/filament-android/src/main/cpp/MaterialInstance.cpp index abc5a0b7f5..458e458131 100644 --- a/android/filament-android/src/main/cpp/MaterialInstance.cpp +++ b/android/filament-android/src/main/cpp/MaterialInstance.cpp @@ -341,6 +341,14 @@ Java_com_google_android_filament_MaterialInstance_nSetDepthWrite(JNIEnv*, instance->setDepthWrite(enable); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilWrite(JNIEnv*, jclass, + jlong nativeMaterialInstance, jboolean enable) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilWrite(enable); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_MaterialInstance_nSetDepthCulling(JNIEnv*, @@ -349,6 +357,70 @@ Java_com_google_android_filament_MaterialInstance_nSetDepthCulling(JNIEnv*, instance->setDepthCulling(enable); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilCompareFunction(JNIEnv*, jclass, + jlong nativeMaterialInstance, jlong function, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilCompareFunction( + static_cast(function), + static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilOpStencilFail(JNIEnv*, jclass, + jlong nativeMaterialInstance, jlong op, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilOpStencilFail( + static_cast(op), + static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilOpDepthFail(JNIEnv*, jclass, + jlong nativeMaterialInstance, jlong op, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilOpDepthFail( + static_cast(op), + static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilOpDepthStencilPass(JNIEnv*, jclass, + jlong nativeMaterialInstance, jlong op, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilOpDepthStencilPass( + static_cast(op), + static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilReferenceValue(JNIEnv*, jclass, + jlong nativeMaterialInstance, jint value, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilReferenceValue(value, static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilReadMask(JNIEnv*, jclass, + jlong nativeMaterialInstance, jint readMask, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilReadMask(readMask, static_cast(face)); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_MaterialInstance_nSetStencilWriteMask(JNIEnv*, jclass, + jlong nativeMaterialInstance, jint writeMask, jlong face) { + MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + instance->setStencilWriteMask(writeMask, static_cast(face)); +} + extern "C" JNIEXPORT jstring JNICALL Java_com_google_android_filament_MaterialInstance_nGetName(JNIEnv* env, jclass, diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index f519ceeb49..3ef0d24560 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -462,6 +462,21 @@ Java_com_google_android_filament_View_nPick(JNIEnv* env, jclass, }, callback->getHandler()); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_View_nSetStencilBufferEnabled(JNIEnv *, jclass, jlong nativeView, + jboolean enabled) { + View* view = (View*) nativeView; + view->setStencilBufferEnabled(enabled); +} + +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_View_nIsStencilBufferEnabled(JNIEnv *, jclass, jlong nativeView) { + View* view = (View*) nativeView; + return view->isStencilBufferEnabled(); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass, diff --git a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java index c80f5fb22d..4fe0b16651 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java +++ b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java @@ -52,6 +52,54 @@ public class MaterialInstance { MAT4 } + /** + * Operations that control how the stencil buffer is updated. + */ + public enum StencilOperation { + /** + * Keeps the current value. + */ + KEEP, + /** + * Sets the value to 0. + */ + ZERO, + /** + * Sets the value to the stencil reference value. + */ + REPLACE, + /** + * Increments the current value. Clamps to the maximum representable unsigned value. + */ + INCR_CLAMP, + /** + * Increments the current value. Wraps value to zero when incrementing the maximum + * representable unsigned value. + */ + INCR_WRAP, + /** + * Decrements the current value. Clamps to 0. + */ + DECR_CLAMP, + /** + * Decrements the current value. Wraps value to the maximum representable unsigned value + * when decrementing a value of zero. + */ + DECR_WRAP, + /** + * Bitwise inverts the current value. + */ + INVERT, + } + + public enum StencilFace { + FRONT, + BACK, + FRONT_AND_BACK + } + // Converts the StencilFace enum ordinal to Filament's equivalent bit field. + static final int[] sStencilFaceMapping = {0x1, 0x2, 0x3}; + public MaterialInstance(Engine engine, long nativeMaterialInstance) { mNativeObject = nativeMaterialInstance; mNativeMaterial = nGetMaterial(mNativeObject); @@ -476,6 +524,10 @@ public class MaterialInstance { nSetDepthWrite(getNativeObject(), enable); } + public void setStencilWrite(boolean enable) { + nSetStencilWrite(getNativeObject(), enable); + } + /** * Overrides the default depth testing state that was set on the material. * @@ -487,6 +539,208 @@ public class MaterialInstance { nSetDepthCulling(getNativeObject(), enable); } + /** + * Sets the stencil comparison function (default is {@link TextureSampler.CompareFunction#ALWAYS}). + * + *

+ * It's possible to set separate stencil comparison functions; one for front-facing polygons, + * and one for back-facing polygons. The face parameter determines the comparison function(s) + * updated by this call. + *

+ * + * @param func the stencil comparison function + * @param face the faces to update the comparison function for + */ + public void setStencilCompareFunction(TextureSampler.CompareFunction func, StencilFace face) { + nSetStencilCompareFunction(getNativeObject(), func.ordinal(), + sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the stencil comparison function for both front and back-facing polygons. + * @see #setStencilCompareFunction(TextureSampler.CompareFunction, StencilFace) + */ + public void setStencilCompareFunction(TextureSampler.CompareFunction func) { + setStencilCompareFunction(func, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the stencil fail operation (default is {@link StencilOperation#KEEP}). + * + *

+ * The stencil fail operation is performed to update values in the stencil buffer when the + * stencil test fails. + *

+ * + *

+ * It's possible to set separate stencil fail operations; one for front-facing polygons, and one + * for back-facing polygons. The face parameter determines the stencil fail operation(s) updated + * by this call. + *

+ * + * @param op the stencil fail operation + * @param face the faces to update the stencil fail operation for + */ + public void setStencilOpStencilFail(StencilOperation op, StencilFace face) { + nSetStencilOpStencilFail(getNativeObject(), op.ordinal(), + sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the stencil fail operation for both front and back-facing polygons. + * @see #setStencilOpStencilFail(StencilOperation, StencilFace) + */ + public void setStencilOpStencilFail(StencilOperation op) { + setStencilOpStencilFail(op, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the depth fail operation (default is {@link StencilOperation#KEEP}). + * + *

+ * The depth fail operation is performed to update values in the stencil buffer when the depth + * test fails. + *

+ * + *

+ * It's possible to set separate depth fail operations; one for front-facing polygons, and one + * for back-facing polygons. The face parameter determines the depth fail operation(s) updated + * by this call. + *

+ * + * @param op the depth fail operation + * @param face the faces to update the depth fail operation for + */ + public void setStencilOpDepthFail(StencilOperation op, StencilFace face) { + nSetStencilOpDepthFail(getNativeObject(), op.ordinal(), + sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the depth fail operation for both front and back-facing polygons. + * @see #setStencilOpDepthFail(StencilOperation, StencilFace) + */ + public void setStencilOpDepthFail(StencilOperation op) { + setStencilOpDepthFail(op, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the depth-stencil pass operation (default is {@link StencilOperation#KEEP}). + * + *

+ * The depth-stencil pass operation is performed to update values in the stencil buffer when + * both the stencil test and depth test pass. + *

+ * + *

+ * It's possible to set separate depth-stencil pass operations; one for front-facing polygons, + * and one for back-facing polygons. The face parameter determines the depth-stencil pass + * operation(s) updated by this call. + *

+ * + * @param op the depth-stencil pass operation + * @param face the faces to update the depth-stencil operation for + */ + public void setStencilOpDepthStencilPass(StencilOperation op, StencilFace face) { + nSetStencilOpDepthStencilPass(getNativeObject(), op.ordinal(), + sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the depth-stencil pass operation for both front and back-facing polygons. + * @see #setStencilOpDepthStencilPass(StencilOperation, StencilFace) + */ + public void setStencilOpDepthStencilPass(StencilOperation op) { + setStencilOpDepthStencilPass(op, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the stencil reference value (default is 0). + * + *

+ * The stencil reference value is the left-hand side for stencil comparison tests. It's also + * used as the replacement stencil value when {@link StencilOperation} is + * {@link StencilOperation#REPLACE}. + *

+ * + *

+ * It's possible to set separate stencil reference values; one for front-facing polygons, and + * one for back-facing polygons. The face parameter determines the reference value(s) updated by + * this call. + *

+ * + * @param value the stencil reference value (only the least significant 8 bits are used) + * @param face the faces to update the reference value for + */ + public void setStencilReferenceValue(@IntRange(from = 0, to = 255) int value, StencilFace face) { + nSetStencilReferenceValue(getNativeObject(), value, sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the stencil reference value for both front and back-facing polygons. + * @see #setStencilReferenceValue(int, StencilFace) + */ + public void setStencilReferenceValue(@IntRange(from = 0, to = 255) int value) { + setStencilReferenceValue(value, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the stencil read mask (default is 0xFF). + * + *

+ * The stencil read mask masks the bits of the values participating in the stencil comparison + * test- both the value read from the stencil buffer and the reference value. + *

+ * + *

+ * It's possible to set separate stencil read masks; one for front-facing polygons, and one for + * back-facing polygons. The face parameter determines the stencil read mask(s) updated by this + * call. + *

+ * + * @param readMask the read mask (only the least significant 8 bits are used) + * @param face the faces to update the read mask for + */ + public void setStencilReadMask(@IntRange(from = 0, to = 255) int readMask, StencilFace face) { + nSetStencilReadMask(getNativeObject(), readMask, sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the stencil read mask for both front and back-facing polygons. + * @see #setStencilReadMask(int, StencilFace) + */ + public void setStencilReadMask(@IntRange(from = 0, to = 255) int readMask) { + setStencilReadMask(readMask, StencilFace.FRONT_AND_BACK); + } + + /** + * Sets the stencil write mask (default is 0xFF). + * + *

+ * The stencil write mask masks the bits in the stencil buffer updated by stencil operations. + *

+ * + *

+ * It's possible to set separate stencil write masks; one for front-facing polygons, and one for + * back-facing polygons. The face parameter determines the stencil write mask(s) updated by this + * call. + *

+ * + * @param writeMask the write mask (only the least significant 8 bits are used) + * @param face the faces to update the read mask for + */ + public void setStencilWriteMask(@IntRange(from = 0, to = 255) int writeMask, StencilFace face) { + nSetStencilWriteMask(getNativeObject(), writeMask, sStencilFaceMapping[face.ordinal()]); + } + + /** + * Sets the stencil write mask for both front and back-facing polygons. + * @see #setStencilWriteMask(int, StencilFace) + */ + public void setStencilWriteMask(int writeMask) { + setStencilWriteMask(writeMask, StencilFace.FRONT_AND_BACK); + } + public long getNativeObject() { if (mNativeObject == 0) { throw new IllegalStateException("Calling method on destroyed MaterialInstance"); @@ -559,8 +813,24 @@ public class MaterialInstance { private static native void nSetCullingMode(long nativeMaterialInstance, long mode); private static native void nSetColorWrite(long nativeMaterialInstance, boolean enable); private static native void nSetDepthWrite(long nativeMaterialInstance, boolean enable); + private static native void nSetStencilWrite(long nativeMaterialInstance, boolean enable); private static native void nSetDepthCulling(long nativeMaterialInstance, boolean enable); + private static native void nSetStencilCompareFunction(long nativeMaterialInstance, + long function, long face); + private static native void nSetStencilOpStencilFail(long nativeMaterialInstance, long op, + long face); + private static native void nSetStencilOpDepthFail(long nativeMaterialInstance, long op, + long face); + private static native void nSetStencilOpDepthStencilPass(long nativeMaterialInstance, long op, + long face); + private static native void nSetStencilReferenceValue(long nativeMaterialInstance, int value, + long face); + private static native void nSetStencilReadMask(long nativeMaterialInstance, int readMask, + long face); + private static native void nSetStencilWriteMask(long nativeMaterialInstance, int writeMask, + long face); + private static native String nGetName(long nativeMaterialInstance); private static native long nGetMaterial(long nativeMaterialInstance); diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java index 2f92509786..3767df704d 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/View.java +++ b/android/filament-android/src/main/java/com/google/android/filament/View.java @@ -1011,6 +1011,45 @@ public class View { return mDepthOfFieldOptions; } + /** + * Enables use of the stencil buffer. + * + *

+ * The stencil buffer is an 8-bit, per-fragment unsigned integer stored alongside the depth + * buffer. The stencil buffer is cleared at the beginning of a frame and discarded after the + * color pass. + *

+ * + *

+ * Each fragment's stencil value is set during rasterization by specifying stencil operations on + * a {@link Material}. The stencil buffer can be used as a mask for later rendering by setting a + * {@link Material}'s stencil comparison function and reference value. Fragments that don't pass + * the stencil test are then discarded. + *

+ * + *

+ * Post-processing must be enabled in order to use the stencil buffer. + *

+ * + *

+ * A renderable's priority (see {@link RenderableManager#setPriority(int, int)}) is useful to + * control the order in which primitives are drawn. + *

+ * + * @param enabled True to enable the stencil buffer, false disables it (default) + */ + public void setStencilBufferEnabled(boolean enabled) { + nSetStencilBufferEnabled(getNativeObject(), enabled); + } + + /** + * @return true if the stencil buffer is enabled. + * @see View#setStencilBufferEnabled(boolean) + */ + public boolean isStencilBufferEnabled() { + return nIsStencilBufferEnabled(getNativeObject()); + } + /** * A class containing the result of a picking query */ @@ -1131,6 +1170,8 @@ public class View { private static native void nSetGuardBandOptions(long nativeView, boolean enabled); private static native boolean nIsScreenSpaceRefractionEnabled(long nativeView); private static native void nPick(long nativeView, int x, int y, Object handler, InternalOnPickCallback internalCallback); + private static native void nSetStencilBufferEnabled(long nativeView, boolean enabled); + private static native boolean nIsStencilBufferEnabled(long nativeView); /** * List of available ambient occlusion techniques. diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index bc35e71b0f..74ce305c91 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -638,7 +638,7 @@ public: bool isFrontFaceWindingInverted() const noexcept; /** - * Enables use of the stencil buffer. This API is currently a WIP and experimental. + * Enables use of the stencil buffer. * * The stencil buffer is an 8-bit, per-fragment unsigned integer stored alongside the depth * buffer. The stencil buffer is cleared at the beginning of a frame and discarded after the