diff --git a/CMakeLists.txt b/CMakeLists.txt index cc5db2dba8..6db3af0533 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,6 +123,10 @@ if (ANDROID OR WEBGL OR IOS OR FILAMENT_LINUX_IS_MOBILE) set(IS_MOBILE_TARGET TRUE) endif() +if (ANDROID) + add_definitions(-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) +endif() + if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE) set(IS_HOST_PLATFORM TRUE) endif() @@ -295,7 +299,7 @@ if (LINUX) endif() if (ANDROID) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Werror=unguarded-availability") endif() if (CYGWIN) diff --git a/README.md b/README.md index d66791b606..a581cabf28 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.16.1' + implementation 'com.google.android.filament:filament-android:1.17.0' } ``` @@ -52,7 +52,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ``` -pod 'Filament', '~> 1.16.1' +pod 'Filament', '~> 1.17.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index abb366bba3..3ef724b10f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,7 +3,15 @@ This file contains one line summaries of commits that are worthy of mentioning in release notes. A new header is inserted each time a *tag* is created. -## v1.16.2 (currently main branch) +## v1.17.1 (currently main branch) + +## v1.17.0 + +- engine: Add experimental glossy screen-space reflections. +- engine: Add support for GPU morphing and 128 morph targets. +- engine: Fix crash with non-shadow receiving renderables and VSM. +- engine: Bring back the 3x3 filter for PCF shadows. +- engine: Correct AABB calculation for skinned glTF models. ## v1.16.1 diff --git a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java index 5017f18fc5..c52cd51e0c 100644 --- a/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java +++ b/android/filamat-android/src/main/java/com/google/android/filament/filamat/MaterialBuilder.java @@ -114,14 +114,14 @@ public class MaterialBuilder { BONE_INDICES, // indices of 4 bones (uvec4) BONE_WEIGHTS, // weights of the 4 bones (normalized float4) UNUSED, // reserved for future use - CUSTOM0, // custom or MORPH_POSITION_0 - CUSTOM1, // custom or MORPH_POSITION_1 - CUSTOM2, // custom or MORPH_POSITION_2 - CUSTOM3, // custom or MORPH_POSITION_3 - CUSTOM4, // custom or MORPH_TANGENTS_0 - CUSTOM5, // custom or MORPH_TANGENTS_1 - CUSTOM6, // custom or MORPH_TANGENTS_2 - CUSTOM7 // custom or MORPH_TANGENTS_3 + CUSTOM0, // custom + CUSTOM1, // custom + CUSTOM2, // custom + CUSTOM3, // custom + CUSTOM4, // custom + CUSTOM5, // custom + CUSTOM6, // custom + CUSTOM7 // custom } public enum BlendingMode { diff --git a/android/filament-android/src/main/cpp/RenderableManager.cpp b/android/filament-android/src/main/cpp/RenderableManager.cpp index cba710637c..8d3d43dcdd 100644 --- a/android/filament-android/src/main/cpp/RenderableManager.cpp +++ b/android/filament-android/src/main/cpp/RenderableManager.cpp @@ -270,9 +270,9 @@ Java_com_google_android_filament_RenderableManager_nSetMorphWeights(JNIEnv* env, jlong nativeRenderableManager, jint instance, jfloatArray weights) { RenderableManager *rm = (RenderableManager *) nativeRenderableManager; jfloat* vec = env->GetFloatArrayElements(weights, NULL); - math::float4 floatvec(vec[0], vec[1], vec[2], vec[3]); + jsize count = env->GetArrayLength(weights); + rm->setMorphWeights((RenderableManager::Instance)instance, vec, count); env->ReleaseFloatArrayElements(weights, vec, JNI_ABORT); - rm->setMorphWeights((RenderableManager::Instance)instance, floatvec); } extern "C" JNIEXPORT void JNICALL diff --git a/android/filament-android/src/main/cpp/ToneMapper.cpp b/android/filament-android/src/main/cpp/ToneMapper.cpp index 9318d450fd..ea040538cd 100644 --- a/android/filament-android/src/main/cpp/ToneMapper.cpp +++ b/android/filament-android/src/main/cpp/ToneMapper.cpp @@ -49,8 +49,8 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_ToneMapper_nCreateGenericToneMapper(JNIEnv*, jclass, - jfloat contrast, jfloat shoulder, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) { - return (jlong) new GenericToneMapper(contrast, shoulder, midGrayIn, midGrayOut, hdrMax); + jfloat contrast, jfloat midGrayIn, jfloat midGrayOut, jfloat hdrMax) { + return (jlong) new GenericToneMapper(contrast, midGrayIn, midGrayOut, hdrMax); } extern "C" JNIEXPORT jfloat JNICALL @@ -58,11 +58,6 @@ Java_com_google_android_filament_ToneMapper_nGenericGetContrast(JNIEnv*, jclass, return ((GenericToneMapper*) nativeObject)->getContrast(); } -extern "C" JNIEXPORT jfloat JNICALL -Java_com_google_android_filament_ToneMapper_nGenericGetShoulder(JNIEnv*, jclass, jlong nativeObject) { - return ((GenericToneMapper*) nativeObject)->getShoulder(); -} - extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_ToneMapper_nGenericGetMidGrayIn(JNIEnv*, jclass, jlong nativeObject) { return ((GenericToneMapper*) nativeObject)->getMidGrayIn(); @@ -84,12 +79,6 @@ Java_com_google_android_filament_ToneMapper_nGenericSetContrast(JNIEnv*, jclass, ((GenericToneMapper*) nativeObject)->setContrast(contrast); } -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_ToneMapper_nGenericSetShoulder(JNIEnv*, jclass, - jlong nativeObject, jfloat shoulder) { - ((GenericToneMapper*) nativeObject)->setShoulder(shoulder); -} - extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_ToneMapper_nGenericSetMidGrayIn(JNIEnv*, jclass, jlong nativeObject, jfloat midGrayIn) { 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 db4fdb8c83..1a5785ebe7 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 @@ -402,9 +402,6 @@ public class RenderableManager { /** * Controls if the renderable has vertex morphing targets, false by default. * - *

This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer - * must provide data in the appropriate VertexAttribute slots (MORPH_POSITION_0 etc).

- * *

See also {@link RenderableManager#setMorphWeights}, which can be called on a per-frame basis * to advance the animation.

*/ @@ -497,14 +494,11 @@ public class RenderableManager { /** * Updates the vertex morphing weights on a renderable, all zeroes by default. * - *

This is specified using a 4-tuple, one float per morph target. If the renderable has fewer - * than 4 morph targets, then clients should fill the unused components with zeroes.

- * *

The renderable must be built with morphing enabled.

* * @see Builder#morphing */ - public void setMorphWeights(@EntityInstance int i, @NonNull @Size(min = 4) float[] weights) { + public void setMorphWeights(@EntityInstance int i, @NonNull float[] weights) { nSetMorphWeights(mNativeObject, i, weights); } diff --git a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java index 71264c2d49..15800562e3 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java +++ b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java @@ -109,7 +109,6 @@ public class ToneMapper { * The tone mapping curve is defined by 5 parameters: *
diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 4049d39130..c9c996a9d3 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -28,6 +28,7 @@ set(PUBLIC_HDRS include/filament/LightManager.h include/filament/Material.h include/filament/MaterialInstance.h + include/filament/MorphTargetBuffer.h include/filament/Options.h include/filament/RenderTarget.h include/filament/RenderableManager.h @@ -69,6 +70,7 @@ set(SRCS src/Material.cpp src/MaterialInstance.cpp src/MaterialParser.cpp + src/MorphTargetBuffer.cpp src/PerViewUniforms.cpp src/PostProcessManager.cpp src/RenderPass.cpp @@ -140,6 +142,7 @@ set(PRIVATE_HDRS src/details/IndirectLight.h src/details/Material.h src/details/MaterialInstance.h + src/details/MorphTargetBuffer.h src/details/RenderTarget.h src/details/Renderer.h src/details/Scene.h diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index b521f3a4e0..abd38cc77a 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -809,10 +809,10 @@ using AttributeArray = std::array; //! Raster state descriptor struct RasterState { - using CullingMode = CullingMode; - using DepthFunc = SamplerCompareFunc; - using BlendEquation = BlendEquation; - using BlendFunction = BlendFunction; + using CullingMode = backend::CullingMode; + using DepthFunc = backend::SamplerCompareFunc; + using BlendEquation = backend::BlendEquation; + using BlendFunction = backend::BlendFunction; RasterState() noexcept { // NOLINT static_assert(sizeof(RasterState) == sizeof(uint32_t), diff --git a/filament/backend/src/android/ExternalTextureManagerAndroid.cpp b/filament/backend/src/android/ExternalTextureManagerAndroid.cpp index a3ccb93d5b..1892a666a3 100644 --- a/filament/backend/src/android/ExternalTextureManagerAndroid.cpp +++ b/filament/backend/src/android/ExternalTextureManagerAndroid.cpp @@ -95,33 +95,25 @@ void ExternalTextureManagerAndroid::destroy(ExternalTextureManagerAndroid* pExte // called on gl thread ExternalTextureManagerAndroid::ExternalTextureManagerAndroid() noexcept : mVm(VirtualMachineEnv::get()) { - // if we compile for API 26 (Oreo) and above, we're guaranteed to have AHardwareBuffer - // in all other cases, we need to get them at runtime. -#ifndef PLATFORM_HAS_HARDWAREBUFFER - // the following dlsym() calls don't work on API 19 - if (api_level() >= 21) { - loadSymbol(AHardwareBuffer_allocate, "AHardwareBuffer_allocate"); - loadSymbol(AHardwareBuffer_release, "AHardwareBuffer_release"); - } -#endif } // not quite sure on which thread this is going to be called ExternalTextureManagerAndroid::~ExternalTextureManagerAndroid() noexcept { -#ifndef PLATFORM_HAS_HARDWAREBUFFER - if (mGraphicBufferClass) { - JNIEnv* env = VirtualMachineEnv::getThreadEnvironment(); - if (env) { - env->DeleteGlobalRef(mGraphicBufferClass); + if (__builtin_available(android 26, *)) { + } else { + if (mGraphicBufferClass) { + JNIEnv* env = VirtualMachineEnv::getThreadEnvironment(); + if (env) { + env->DeleteGlobalRef(mGraphicBufferClass); + } } } -#endif } // called on gl thread backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::createExternalTexture() noexcept { -#ifndef PLATFORM_HAS_HARDWAREBUFFER - if (!AHardwareBuffer_allocate) { + if (__builtin_available(android 26, *)) { + } else { // initialize java stuff on-demand if (!mGraphicBufferClass) { JNIEnv* env = mVm.getEnvironment(); @@ -134,7 +126,6 @@ backend::Platform::ExternalTexture* ExternalTextureManagerAndroid::createExterna mGraphicBufferClass = static_cast(env->NewGlobalRef(mGraphicBufferClass)); } } -#endif EGLExternalTexture* ets = new EGLExternalTexture; return ets; } @@ -181,9 +172,7 @@ void ExternalTextureManagerAndroid::alloc( return; } -#ifndef PLATFORM_HAS_HARDWAREBUFFER - if (AHardwareBuffer_allocate) { -#endif + if (__builtin_available(android 26, *)) { // allocate new storage... AHardwareBuffer* buffer = nullptr; if (AHardwareBuffer_allocate(&desc, &buffer) < 0) { @@ -191,7 +180,6 @@ void ExternalTextureManagerAndroid::alloc( return; } ets->hardwareBuffer = buffer; -#ifndef PLATFORM_HAS_HARDWAREBUFFER } else { // note: This is called on the application thread (not the GL thread) JNIEnv* env = VirtualMachineEnv::getThreadEnvironment(); @@ -209,23 +197,17 @@ void ExternalTextureManagerAndroid::alloc( ets->clientBuffer = static_cast(gb); } } -#endif } // called on gl thread void ExternalTextureManagerAndroid::destroyStorage(backend::Platform::ExternalTexture* t) noexcept { EGLExternalTexture* ets = static_cast(t); - -#ifndef PLATFORM_HAS_HARDWAREBUFFER - if (AHardwareBuffer_allocate) { -#endif + if (__builtin_available(android 26, *)) { // destroy the current storage if any if (ets->hardwareBuffer) { AHardwareBuffer_release(ets->hardwareBuffer); ets->hardwareBuffer = nullptr; } - -#ifndef PLATFORM_HAS_HARDWAREBUFFER } else { if (ets->graphicBufferWrapper) { JNIEnv* env = VirtualMachineEnv::get().getEnvironment(); @@ -235,7 +217,6 @@ void ExternalTextureManagerAndroid::destroyStorage(backend::Platform::ExternalTe ets->graphicBufferWrapper = nullptr; } } -#endif } } // namespace filament diff --git a/filament/backend/src/android/ExternalTextureManagerAndroid.h b/filament/backend/src/android/ExternalTextureManagerAndroid.h index 925a7960b6..cfe1e6af34 100644 --- a/filament/backend/src/android/ExternalTextureManagerAndroid.h +++ b/filament/backend/src/android/ExternalTextureManagerAndroid.h @@ -25,10 +25,6 @@ #include #include -#if __ANDROID_API__ >= 26 -# define PLATFORM_HAS_HARDWAREBUFFER -#endif - namespace filament { /* @@ -78,16 +74,9 @@ private: VirtualMachineEnv& mVm; -#ifndef PLATFORM_HAS_HARDWAREBUFFER - // if we compile for API 26 (Oreo) and above, we're guaranteed to have AHardwareBuffer - // in all other cases, we need to get them at runtime. - int (*AHardwareBuffer_allocate)(const AHardwareBuffer_Desc*, AHardwareBuffer**) = nullptr; - void (*AHardwareBuffer_release)(AHardwareBuffer*) = nullptr; - jclass mGraphicBufferClass = nullptr; jmethodID mGraphicBuffer_nCreateGraphicBuffer = nullptr; jmethodID mGraphicBuffer_nDestroyGraphicBuffer = nullptr; -#endif }; diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 1cd646a8ac..fdf78e1d5f 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -272,6 +272,9 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept { for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) { BlockInfo blockInfo = blockInfos[i]; HwSamplerGroup const * const UTILS_RESTRICT hwsb = samplerBindings[blockInfo.binding]; + if (UTILS_UNLIKELY(!hwsb)) { + continue; + } SamplerGroup const& UTILS_RESTRICT sb = *(hwsb->sb); SamplerGroup::Sampler const* const UTILS_RESTRICT samplers = sb.getSamplers(); for (uint8_t j = 0, m = blockInfo.count ; j <= m; ++j, ++tmu) { // "<=" on purpose here diff --git a/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.cpp b/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.cpp index c8e1aca6c3..c74b8d311d 100644 --- a/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.cpp +++ b/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.cpp @@ -47,18 +47,7 @@ void ExternalStreamManagerAndroid::destroy(ExternalStreamManagerAndroid* pExtern ExternalStreamManagerAndroid::ExternalStreamManagerAndroid() noexcept : mVm(VirtualMachineEnv::get()) { - - // the following dlsym() calls don't work on API 19 - if (api_level() >= 21) { - loadSymbol(ASurfaceTexture_fromSurfaceTexture, "ASurfaceTexture_fromSurfaceTexture"); - loadSymbol(ASurfaceTexture_release, "ASurfaceTexture_release"); - loadSymbol(ASurfaceTexture_attachToGLContext, "ASurfaceTexture_attachToGLContext"); - loadSymbol(ASurfaceTexture_detachFromGLContext, "ASurfaceTexture_detachFromGLContext"); - loadSymbol(ASurfaceTexture_updateTexImage, "ASurfaceTexture_updateTexImage"); - loadSymbol(ASurfaceTexture_getTimestamp, "ASurfaceTexture_getTimestamp"); - } - - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { slog.d << "Using ASurfaceTexture" << io::endl; } } @@ -95,7 +84,7 @@ Stream* ExternalStreamManagerAndroid::acquire(jobject surfaceTexture) noexcept { } EGLStream* stream = new EGLStream(); stream->jSurfaceTexture = env->NewGlobalRef(surfaceTexture); - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { stream->nSurfaceTexture = ASurfaceTexture_fromSurfaceTexture(env, surfaceTexture); } return stream; @@ -103,7 +92,7 @@ Stream* ExternalStreamManagerAndroid::acquire(jobject surfaceTexture) noexcept { void ExternalStreamManagerAndroid::release(Stream* handle) noexcept { EGLStream* stream = static_cast(handle); - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { ASurfaceTexture_release(stream->nSurfaceTexture); } JNIEnv* const env = getEnvironment(); @@ -114,7 +103,7 @@ void ExternalStreamManagerAndroid::release(Stream* handle) noexcept { void ExternalStreamManagerAndroid::attach(Stream* handle, intptr_t tname) noexcept { EGLStream* stream = static_cast(handle); - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { // associate our GL texture to the SurfaceTexture ASurfaceTexture* const aSurfaceTexture = stream->nSurfaceTexture; if (UTILS_UNLIKELY(ASurfaceTexture_attachToGLContext(aSurfaceTexture, (uint32_t)tname))) { @@ -152,7 +141,7 @@ void ExternalStreamManagerAndroid::attach(Stream* handle, intptr_t tname) noexce void ExternalStreamManagerAndroid::detach(Stream* handle) noexcept { EGLStream* stream = static_cast(handle); - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { ASurfaceTexture_detachFromGLContext(stream->nSurfaceTexture); } else { JNIEnv* const env = mVm.getEnvironment(); @@ -164,14 +153,9 @@ void ExternalStreamManagerAndroid::detach(Stream* handle) noexcept { void ExternalStreamManagerAndroid::updateTexImage(Stream* handle, int64_t* timestamp) noexcept { EGLStream* stream = static_cast(handle); - if (ASurfaceTexture_fromSurfaceTexture) { + if (__builtin_available(android 28, *)) { ASurfaceTexture_updateTexImage(stream->nSurfaceTexture); - if (ASurfaceTexture_getTimestamp) { - *timestamp = ASurfaceTexture_getTimestamp(stream->nSurfaceTexture); - } else { - // if we're not at least on API 28, we may not have getTimestamp() - *timestamp = (std::chrono::steady_clock::now().time_since_epoch().count()); - } + *timestamp = ASurfaceTexture_getTimestamp(stream->nSurfaceTexture); } else { JNIEnv* const env = mVm.getEnvironment(); assert_invariant(env); // we should have called attach() by now diff --git a/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.h b/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.h index 4bbc5687ca..dee6dd002e 100644 --- a/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.h +++ b/filament/backend/src/opengl/platforms/ExternalStreamManagerAndroid.h @@ -84,13 +84,6 @@ private: jmethodID mSurfaceTextureClass_getTimestamp{}; jmethodID mSurfaceTextureClass_attachToGLContext{}; jmethodID mSurfaceTextureClass_detachFromGLContext{}; - - ASurfaceTexture* (*ASurfaceTexture_fromSurfaceTexture)(JNIEnv*, jobject){}; - void (*ASurfaceTexture_release)(ASurfaceTexture*){}; - int (*ASurfaceTexture_attachToGLContext)(ASurfaceTexture*, uint32_t){}; - int (*ASurfaceTexture_detachFromGLContext)(ASurfaceTexture*){}; - int (*ASurfaceTexture_updateTexImage)(ASurfaceTexture*){}; - int64_t (*ASurfaceTexture_getTimestamp)(ASurfaceTexture*){}; // available since api 28 }; } // namespace filament diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 4db5ae7576..0a31013f66 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -39,6 +39,7 @@ class SkinningBuffer; class IndirectLight; class Material; class MaterialInstance; +class MorphTargetBuffer; class Renderer; class RenderTarget; class Scene; @@ -421,6 +422,7 @@ public: bool destroy(const Fence* p); //!< Destroys a Fence object. bool destroy(const IndexBuffer* p); //!< Destroys an IndexBuffer object. bool destroy(const SkinningBuffer* p); //!< Destroys a SkinningBuffer object. + bool destroy(const MorphTargetBuffer* p); //!< Destroys a MorphTargetBuffer object. bool destroy(const IndirectLight* p); //!< Destroys an IndirectLight object. /** diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index a8722804b7..572de84454 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -193,9 +193,12 @@ public: //! Returns the refraction mode used by this material. RefractionMode getRefractionMode() const noexcept; - // Return the refraction type used by this material. + //! Return the refraction type used by this material. RefractionType getRefractionType() const noexcept; + //! Returns the reflection mode used by this material. + ReflectionMode getReflectionMode() const noexcept; + /** * Returns the number of parameters declared by this material. * The returned value can be 0. diff --git a/filament/include/filament/MorphTargetBuffer.h b/filament/include/filament/MorphTargetBuffer.h new file mode 100644 index 0000000000..f3c0877039 --- /dev/null +++ b/filament/include/filament/MorphTargetBuffer.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2021 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_MORPHTARGETBUFFER_H +#define TNT_FILAMENT_MORPHTARGETBUFFER_H + +#include + +#include + +#include + + +namespace filament { + +/** + * MorphTargetBuffer is used to hold morphing data (positions and tangents). + */ +class UTILS_PUBLIC MorphTargetBuffer : public FilamentAPI { + struct BuilderDetails; + +public: + class Builder : public BuilderBase { + friend struct BuilderDetails; + public: + Builder() noexcept; + Builder(Builder const& rhs) noexcept; + Builder(Builder&& rhs) noexcept; + ~Builder() noexcept; + Builder& operator=(Builder const& rhs) noexcept; + Builder& operator=(Builder&& rhs) noexcept; + + /** + * Size of the morph targets in vertex counts. + * @param vertexCount Number of vertex counts the morph targets can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& vertexCount(size_t vertexCount) noexcept; + + /** + * Size of the morph targets in targets. + * @param count Number of targets the morph targets can hold. + * @return A reference to this Builder for chaining calls. + */ + Builder& count(size_t count) noexcept; + + /** + * Creates the MorphTargetBuffer object and returns a pointer to it. + * + * @param engine Reference to the filament::Engine to associate this MorphTargetBuffer with. + * + * @return pointer to the newly created object or nullptr if exceptions are disabled and + * an error occurred. + * + * @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. + */ + MorphTargetBuffer* build(Engine& engine); + private: + friend class FMorphTargetBuffer; + }; + + /** + * Updates the position of morph target at the index. + * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. + * @param targetIndex the index of morph target to be updated. + * @param weights pointer to at least count positions + * @param count number of position elements in positions + */ + void setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count); + + /** + * Updates the position of morph target at the index. + * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. + * @param targetIndex the index of morph target to be updated. + * @param weights pointer to at least count positions + * @param count number of position elements in positions + */ + void setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count); + + /** + * Updates the position of morph target at the index. + * @param engine Reference to the filament::Engine associated with this MorphTargetBuffer. + * @param targetIndex the index of morph target to be updated. + * @param tangents pointer to at least count tangents + * @param count number of tangent elements in tangents + */ + void setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count); + + /** + * Returns the vertex count of this MorphTargetBuffer. + * @return The number of vertices the MorphTargetBuffer holds. + */ + size_t getVertexCount() const noexcept; + + /** + * Returns the target count of this MorphTargetBuffer. + * @return The number of targets the MorphTargetBuffer holds. + */ + size_t getCount() const noexcept; +}; + +} // namespace filament + +#endif //TNT_FILAMENT_MORPHTARGETBUFFER_H diff --git a/filament/include/filament/Options.h b/filament/include/filament/Options.h index 5904bc1e34..b43a5f349b 100644 --- a/filament/include/filament/Options.h +++ b/filament/include/filament/Options.h @@ -316,6 +316,18 @@ struct TemporalAntiAliasingOptions { bool enabled = false; //!< enables or disables temporal anti-aliasing }; +/** + * Options for Screen-space Reflections. + * @see setScreenSpaceReflectionsOptions() + */ +struct ScreenSpaceReflectionsOptions { + float thickness = 0.5f; //!< ray thickness, in world units + float bias = 0.01f; //!< bias, in world units, to prevent self-intersections + float maxDistance = 3.0f; //!< maximum distance, in world units, to raycast + float stride = 1.0f; //!< stride, in texels, for samples along the ray. + bool enabled = false; +}; + /** * List of available post-processing anti-aliasing techniques. * @see setAntiAliasing, getAntiAliasing, setSampleCount diff --git a/filament/include/filament/RenderTarget.h b/filament/include/filament/RenderTarget.h index cc4ae38a1c..950bbb8dba 100644 --- a/filament/include/filament/RenderTarget.h +++ b/filament/include/filament/RenderTarget.h @@ -37,6 +37,12 @@ class Texture; * An offscreen render target that can be associated with a View and contains * weak references to a set of attached Texture objects. * + * RenderTarget is intended to be used with the View's post-processing disabled for the most part. + * especially when a DEPTH attachment is also used (see Builder::texture()). + * + * Custom RenderTarget are ultimately intended to render into textures that might be used during + * the main render pass. + * * Clients are responsible for the lifetime of all associated Texture attachments. * * @see View @@ -87,6 +93,14 @@ public: * * All RenderTargets must have a non-null COLOR attachment. * + * When using a DEPTH attachment, it is important to always disable post-processing + * in the View. Failing to do so will cause the DEPTH attachment to be ignored in most + * cases. + * + * When the intention is to keep the content of the DEPTH attachment after rendering, + * Usage::SAMPLEABLE must be set on the DEPTH attachment, otherwise the content of the + * DEPTH buffer may be discarded. + * * @param attachment The attachment point of the texture. * @param texture The associated texture object. * @return A reference to this Builder for chaining calls. diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 46ef09e429..47f4fc56d4 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -41,9 +41,11 @@ class Engine; class IndexBuffer; class Material; class MaterialInstance; +class MorphTargetBuffer; class Renderer; class SkinningBuffer; class VertexBuffer; +class Texture; class FEngine; class FRenderPrimitive; @@ -300,9 +302,6 @@ public: /** * Controls if the renderable has vertex morphing targets, false by default. * - * This is required to enable GPU morphing for up to 4 attributes. The attached VertexBuffer - * must provide data in the appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). - * * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis * to advance the animation. */ @@ -456,12 +455,16 @@ public: /** * Updates the vertex morphing weights on a renderable, all zeroes by default. * - * This is specified using a 4-tuple, one float per morph target. If the renderable has fewer - * than 4 morph targets, then clients should fill the unused components with zeroes. - * * The renderable must be built with morphing enabled, see Builder::morphing(). */ - void setMorphWeights(Instance instance, math::float4 const& weights) noexcept; + void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept; + + + /** + * Associates a MorphTargetBuffer to the given primitive. + */ + void setMorphTargetBufferAt(Instance instance, + size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept; /** * Gets the bounding box used for frustum culling. diff --git a/filament/include/filament/ToneMapper.h b/filament/include/filament/ToneMapper.h index c0aa38d153..0bbe06fe3c 100644 --- a/filament/include/filament/ToneMapper.h +++ b/filament/include/filament/ToneMapper.h @@ -123,8 +123,6 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper { * * The tone mapping curve is defined by 5 parameters: * - contrast: controls the contrast of the curve - * - shoulder: controls the shoulder of the curve, i.e. how quickly scene - * referred values map to output white * - midGrayIn: sets the input middle gray * - midGrayOut: sets the output middle gray * - hdrMax: defines the maximum input value that will be mapped to @@ -138,18 +136,15 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { * * @param contrast: controls the contrast of the curve, must be > 0.0, values * in the range 0.5..2.0 are recommended. - * @param shoulder: controls the shoulder of the curve, i.e. how quickly scene - * referred values map to output white, between 0.0 and 1.0. * @param midGrayIn: sets the input middle gray, between 0.0 and 1.0. * @param midGrayOut: sets the output middle gray, between 0.0 and 1.0. * @param hdrMax: defines the maximum input value that will be mapped to * output white. Must be >= 1.0. */ explicit GenericToneMapper( - float contrast = 1.585f, - float shoulder = 0.5f, + float contrast = 1.55f, float midGrayIn = 0.18f, - float midGrayOut = 0.268f, + float midGrayOut = 0.215f, float hdrMax = 10.0f ) noexcept; ~GenericToneMapper() noexcept final; @@ -179,9 +174,6 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper { /** Sets the contrast of the curve, must be > 0.0, values in the range 0.5..2.0 are recommended. */ void setContrast(float contrast) noexcept; - /** Sets how quickly scene referred values map to output white, between 0.0 and 1.0. */ - void setShoulder(float shoulder) noexcept; - /** Sets the input middle gray, between 0.0 and 1.0. */ void setMidGrayIn(float midGrayIn) noexcept; diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index ef13813eeb..1083560c1e 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -83,6 +83,7 @@ public: using MultiSampleAntiAliasingOptions = MultiSampleAntiAliasingOptions; using VsmShadowOptions = VsmShadowOptions; using SoftShadowOptions = SoftShadowOptions; + using ScreenSpaceReflectionsOptions = ScreenSpaceReflectionsOptions; /** * Sets the View's name. Only useful for debugging. @@ -330,6 +331,20 @@ public: */ TemporalAntiAliasingOptions const& getTemporalAntiAliasingOptions() const noexcept; + /** + * Enables or disable screen-space reflections. Disabled by default. + * + * @param options screen-space reflections options + */ + void setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept; + + /** + * Returns screen-space reflections options. + * + * @return screen-space reflections options + */ + ScreenSpaceReflectionsOptions const& getScreenSpaceReflectionsOptions() const noexcept; + /** * Enables or disable multi-sample anti-aliasing (MSAA). Disabled by default. * @@ -564,15 +579,18 @@ public: * Enables or disables post processing. Enabled by default. * * Post-processing includes: + * - Depth-of-field * - Bloom - * - Tone-mapping & gamma encoding + * - Vignetting + * - Temporal Anti-aliasing (TAA) + * - Color grading & gamma encoding * - Dithering - * - MSAA * - FXAA * - Dynamic scaling * - * Disabling post-processing forgoes color correctness as well as anti-aliasing and - * should only be used experimentally (e.g., for UI overlays). + * Disabling post-processing forgoes color correctness as well as some anti-aliasing techniques + * and should only be used for debugging, UI overlays or when using custom render targets + * (see RenderTarget). * * @param enabled true enables post processing, false disables it. * diff --git a/filament/src/Allocators.h b/filament/src/Allocators.h index 7ee44286fc..25ccab736c 100644 --- a/filament/src/Allocators.h +++ b/filament/src/Allocators.h @@ -22,11 +22,11 @@ #include "private/backend/BackendUtils.h" #ifndef FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB -# define FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB 2 +# define FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB 3 #endif #ifndef FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB -# define FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB 1 +# define FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB 2 #endif namespace filament { diff --git a/filament/src/ColorGrading.cpp b/filament/src/ColorGrading.cpp index 67ae43a274..ba86dbfbd8 100644 --- a/filament/src/ColorGrading.cpp +++ b/filament/src/ColorGrading.cpp @@ -400,7 +400,7 @@ constexpr mat3f adaptationTransform(float2 whiteBalance) noexcept { float y = chromaticityCoordinateIlluminantD(x) + t * 0.066f; float3 lms = XYZ_to_CIECAT16 * xyY_to_XYZ({x, y, 1.0f}); - return LMS_CAT16_to_REC2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * REC2020_to_LMS_CAT16; + return LMS_CAT16_to_Rec2020 * mat3f{ILLUMINANT_D65_LMS_CAT16 / lms} * Rec2020_to_LMS_CAT16; } UTILS_ALWAYS_INLINE @@ -575,21 +575,21 @@ static mat3f selectColorGradingTransformIn(ColorGrading::ToneMapping toneMapping if (toneMapping == ColorGrading::ToneMapping::FILMIC) { return mat3f{}; } - return sRGB_to_REC2020; + return sRGB_to_Rec2020; } static mat3f selectColorGradingTransformOut(ColorGrading::ToneMapping toneMapping) noexcept { if (toneMapping == ColorGrading::ToneMapping::FILMIC) { return mat3f{}; } - return REC2020_to_sRGB; + return Rec2020_to_sRGB; } static float3 selectColorGradingLuminance(ColorGrading::ToneMapping toneMapping) noexcept { if (toneMapping == ColorGrading::ToneMapping::FILMIC) { - return LUMINANCE_REC709; + return LUMINANCE_Rec709; } - return LUMINANCE_REC2020; + return LUMINANCE_Rec2020; } #pragma clang diagnostic pop diff --git a/filament/src/ColorSpace.h b/filament/src/ColorSpace.h index 2e73f3e461..3ab62a70f1 100644 --- a/filament/src/ColorSpace.h +++ b/filament/src/ColorSpace.h @@ -52,13 +52,13 @@ constexpr mat3f sRGB_to_XYZ{ 0.1804380f, 0.0721750f, 0.9503040f }; -constexpr mat3f REC2020_to_XYZ{ +constexpr mat3f Rec2020_to_XYZ{ 0.6369530f, 0.2626983f, 0.0000000f, 0.1446169f, 0.6780088f, 0.0280731f, 0.1688558f, 0.0592929f, 1.0608272f }; -constexpr mat3f XYZ_to_REC2020{ +constexpr mat3f XYZ_to_Rec2020{ 1.7166634f, -0.6666738f, 0.0176425f, -0.3556733f, 1.6164557f, -0.0427770f, -0.2533681f, 0.0157683f, 0.9422433f @@ -130,6 +130,18 @@ constexpr mat3f sRGB_to_OkLab_LMS{ 0.0514459929f, 0.1073969566f, 0.6299787005f }; +constexpr mat3f XYZ_to_OkLab_LMS{ + 0.8189330101f, 0.3618667424f, -0.1288597137f, + 0.0329845436f, 0.9293118715f, 0.0361456387f, + 0.0482003018f, 0.2643662691f, 0.6338517070f +}; + +constexpr mat3f OkLab_LMS_to_XYZ{ + 1.227014f, -0.557800f, 0.281256f, + -0.040580f, 1.112257f, -0.071677f, + -0.076381f, -0.421482f, 1.586163f +}; + constexpr mat3f OkLab_LMS_to_OkLab{ 0.2104542553f, 1.9779984951f, 0.0259040371f, 0.7936177850f, -2.4285922050f, 0.7827717662f, @@ -148,21 +160,25 @@ constexpr mat3f OkLab_LMS_to_sRGB{ 0.2309699292f, -0.3413193965f, 1.7076147010f }; -constexpr mat3f sRGB_to_REC2020 = XYZ_to_REC2020 * sRGB_to_XYZ; +constexpr mat3f sRGB_to_Rec2020 = XYZ_to_Rec2020 * sRGB_to_XYZ; -constexpr mat3f REC2020_to_sRGB = XYZ_to_sRGB * REC2020_to_XYZ; +constexpr mat3f Rec2020_to_sRGB = XYZ_to_sRGB * Rec2020_to_XYZ; constexpr mat3f sRGB_to_LMS_CAT16 = XYZ_to_CIECAT16 * sRGB_to_XYZ; constexpr mat3f LMS_CAT16_to_sRGB = XYZ_to_sRGB * CIECAT16_to_XYZ; -constexpr mat3f REC2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * REC2020_to_XYZ; +constexpr mat3f Rec2020_to_LMS_CAT16 = XYZ_to_CIECAT16 * Rec2020_to_XYZ; -constexpr mat3f LMS_CAT16_to_REC2020 = XYZ_to_REC2020 * CIECAT16_to_XYZ; +constexpr mat3f LMS_CAT16_to_Rec2020 = XYZ_to_Rec2020 * CIECAT16_to_XYZ; -constexpr mat3f REC2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * REC2020_to_XYZ; +constexpr mat3f Rec2020_to_AP0 = AP1_to_AP0 * XYZ_to_AP1 * Rec2020_to_XYZ; -constexpr mat3f AP1_to_REC2020 = XYZ_to_REC2020 * AP1_to_XYZ; +constexpr mat3f AP1_to_Rec2020 = XYZ_to_Rec2020 * AP1_to_XYZ; + +constexpr mat3f Rec2020_to_OkLab_LMS = XYZ_to_OkLab_LMS * Rec2020_to_XYZ; + +constexpr mat3f OkLab_LMS_to_Rec2020 = XYZ_to_Rec2020 * OkLab_LMS_to_XYZ; //------------------------------------------------------------------------------ // Constants @@ -175,17 +191,17 @@ constexpr float3 ILLUMINANT_D65_xyY{0.31271f, 0.32902f, 1.0f}; // Result of: XYZ_to_CIECAT16 * xyY_to_XYZ(ILLUMINANT_D65_xyY); constexpr float3 ILLUMINANT_D65_LMS_CAT16{0.975533f, 1.016483f, 1.084837f}; -// RGB to luminance coefficients for Rec.2020, from REC2020_to_XYZ -constexpr float3 LUMINANCE_REC2020{0.2627002f, 0.6779981f, 0.0593017f}; +// RGB to luminance coefficients for Rec.2020, from Rec2020_to_XYZ +constexpr float3 LUMINANCE_Rec2020{0.2627002f, 0.6779981f, 0.0593017f}; // RGB to luminance coefficients for ACEScg (AP1), from AP1_to_XYZ constexpr float3 LUMINANCE_AP1{0.272229f, 0.674082f, 0.0536895f}; // RGB to luminance coefficients for Rec.709, from sRGB_to_XYZ -constexpr float3 LUMINANCE_REC709{0.2126730f, 0.7151520f, 0.0721750f}; +constexpr float3 LUMINANCE_Rec709{0.2126730f, 0.7151520f, 0.0721750f}; // RGB to luminance coefficients for Rec.709 with HK-like weighting -constexpr float3 LUMINANCE_HK_REC709{0.13913043f, 0.73043478f, 0.13043478f}; +constexpr float3 LUMINANCE_HK_Rec709{0.13913043f, 0.73043478f, 0.13043478f}; constexpr float MIDDLE_GRAY_ACEScg = 0.18f; @@ -212,21 +228,29 @@ inline constexpr XYZ xyY_to_XYZ(xyY v) noexcept { } inline constexpr xyY XYZ_to_xyY(XYZ v) noexcept { - return float3(v.xy / max(v.x + v.y + v.z, 1e-5f), v.y); -} - -inline float3 sRGB_to_OkLab(float3 x) noexcept { - return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x); + return {v.xy / max(v.x + v.y + v.z, 1e-5f), v.y}; } inline constexpr float3 pow3(float3 x) noexcept { return x * x * x; } +inline float3 sRGB_to_OkLab(float3 x) noexcept { + return OkLab_LMS_to_OkLab * cbrt(sRGB_to_OkLab_LMS * x); +} + +inline float3 Rec2020_to_OkLab(float3 x) noexcept { + return OkLab_LMS_to_OkLab * cbrt(Rec2020_to_OkLab_LMS * x); +} + inline float3 OkLab_to_sRGB(float3 x) noexcept { return OkLab_LMS_to_sRGB * pow3(OkLab_to_OkLab_LMS * x); } +inline float3 OkLab_to_Rec2020(float3 x) noexcept { + return OkLab_LMS_to_Rec2020 * pow3(OkLab_to_OkLab_LMS * x); +} + //------------------------------------------------------------------------------ // Conversion functions and encoding/decoding //------------------------------------------------------------------------------ diff --git a/filament/src/Engine.cpp b/filament/src/Engine.cpp index 4d7f3211bc..1edb08a520 100644 --- a/filament/src/Engine.cpp +++ b/filament/src/Engine.cpp @@ -333,6 +333,7 @@ void FEngine::shutdown() { cleanupResourceList(mBufferObjects); cleanupResourceList(mIndexBuffers); + cleanupResourceList(mMorphTargetBuffers); cleanupResourceList(mSkinningBuffers); cleanupResourceList(mVertexBuffers); cleanupResourceList(mTextures); @@ -567,6 +568,10 @@ FSkinningBuffer* FEngine::createSkinningBuffer(const SkinningBuffer::Builder& bu return create(mSkinningBuffers, builder); } +FMorphTargetBuffer* FEngine::createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept { + return create(mMorphTargetBuffers, builder); +} + FTexture* FEngine::createTexture(const Texture::Builder& builder) noexcept { return create(mTextures, builder); } @@ -753,6 +758,10 @@ bool FEngine::destroy(const FSkinningBuffer* p) { return terminateAndDestroy(p, mSkinningBuffers); } +bool FEngine::destroy(const FMorphTargetBuffer* p) { + return terminateAndDestroy(p, mMorphTargetBuffers); +} + inline bool FEngine::destroy(const FRenderer* p) { return terminateAndDestroy(p, mRenderers); } @@ -966,6 +975,10 @@ bool Engine::destroy(const SkinningBuffer* p) { return upcast(this)->destroy(upcast(p)); } +bool Engine::destroy(const MorphTargetBuffer* p) { + return upcast(this)->destroy(upcast(p)); +} + bool Engine::destroy(const IndirectLight* p) { return upcast(this)->destroy(upcast(p)); } diff --git a/filament/src/FrameHistory.h b/filament/src/FrameHistory.h index 3a654cdf81..208bf5e903 100644 --- a/filament/src/FrameHistory.h +++ b/filament/src/FrameHistory.h @@ -28,7 +28,7 @@ namespace filament { struct FrameHistoryEntry { FrameGraphTexture color; FrameGraphTexture::Descriptor colorDesc; - math::mat4f projection; + math::mat4f projection; // world space to clip space math::float2 jitter{}; uint32_t frameId = 0; @@ -60,6 +60,10 @@ public: return mCurrentEntry; } + const T& getCurrent() const noexcept { + return mCurrentEntry; + } + // This pushes the current frame info to the FIFO, effectively destroying // the oldest state (note: only the structure is destroyed, handles stored in it may // have to be destroyed prior to calling this). diff --git a/filament/src/Material.cpp b/filament/src/Material.cpp index b534f93c8a..78dad235f9 100644 --- a/filament/src/Material.cpp +++ b/filament/src/Material.cpp @@ -189,6 +189,7 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) parser->getRequiredAttributes(&mRequiredAttributes); parser->getRefractionMode(&mRefractionMode); parser->getRefractionType(&mRefractionType); + parser->getReflectionMode(&mReflectionMode); if (mBlendingMode == BlendingMode::MASKED) { parser->getMaskThreshold(&mMaskThreshold); @@ -378,6 +379,10 @@ Handle FMaterial::getSurfaceProgramSlow(uint8_t variantKey) const noe if (Variant(variantKey).hasSkinningOrMorphing()) { pb.setUniformBlock(BindingPoints::PER_RENDERABLE_BONES, PerRenderableUibBone::_name); + pb.setUniformBlock(BindingPoints::PER_RENDERABLE_MORPHING, PerRenderableMorphingUib::_name); + + addSamplerGroup(pb, BindingPoints::PER_RENDERABLE_MORPHING, + SibGenerator::getPerRenderPrimitiveMorphingSib(variantKey), mSamplerBindings); } addSamplerGroup(pb, BindingPoints::PER_VIEW, SibGenerator::getPerViewSib(variantKey), mSamplerBindings); @@ -649,6 +654,10 @@ RefractionType Material::getRefractionType() const noexcept { return upcast(this)->getRefractionType(); } +ReflectionMode Material::getReflectionMode() const noexcept { + return upcast(this)->getReflectionMode(); +} + bool Material::hasParameter(const char* name) const noexcept { return upcast(this)->hasParameter(name); } diff --git a/filament/src/MaterialParser.cpp b/filament/src/MaterialParser.cpp index 3339e19d80..fe34fa39f7 100644 --- a/filament/src/MaterialParser.cpp +++ b/filament/src/MaterialParser.cpp @@ -269,6 +269,12 @@ bool MaterialParser::getRefractionType(RefractionType* value) const noexcept { return mImpl.getFromSimpleChunk(ChunkType::MaterialRefractionType, (uint8_t*)value); } +bool MaterialParser::getReflectionMode(ReflectionMode* value) const noexcept { + static_assert(sizeof(ReflectionMode) == sizeof(uint8_t), + "ReflectionMode expected size is wrong"); + return mImpl.getFromSimpleChunk(ChunkType::MaterialReflectionMode, (uint8_t*)value); +} + bool MaterialParser::getShader(ShaderBuilder& shader, ShaderModel shaderModel, uint8_t variant, ShaderType stage) noexcept { return mImpl.mMaterialChunk.getShader(shader, diff --git a/filament/src/MaterialParser.h b/filament/src/MaterialParser.h index d55acdad6c..a16a1d47fb 100644 --- a/filament/src/MaterialParser.h +++ b/filament/src/MaterialParser.h @@ -85,6 +85,7 @@ public: bool getRequiredAttributes(AttributeBitset*) const noexcept; bool getRefractionMode(RefractionMode* value) const noexcept; bool getRefractionType(RefractionType* value) const noexcept; + bool getReflectionMode(ReflectionMode* value) const noexcept; bool hasCustomDepthShader(bool* value) const noexcept; bool hasSpecularAntiAliasing(bool* value) const noexcept; bool getSpecularAntiAliasingVariance(float* value) const noexcept; diff --git a/filament/src/MorphTargetBuffer.cpp b/filament/src/MorphTargetBuffer.cpp new file mode 100644 index 0000000000..3e123a9df9 --- /dev/null +++ b/filament/src/MorphTargetBuffer.cpp @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2021 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. + */ + +#include "details/MorphTargetBuffer.h" + +#include "private/filament/SibGenerator.h" + +#include "details/Engine.h" + +#include "FilamentAPI-impl.h" + +#include + +#include + +namespace filament { + +using namespace backend; +using namespace math; + +struct MorphTargetBuffer::BuilderDetails { + size_t mVertexCount = 0; + size_t mCount = 0; +}; + +using BuilderType = MorphTargetBuffer; +BuilderType::Builder::Builder() noexcept = default; +BuilderType::Builder::~Builder() noexcept = default; +BuilderType::Builder::Builder(BuilderType::Builder const& rhs) noexcept = default; +BuilderType::Builder::Builder(BuilderType::Builder&& rhs) noexcept = default; +BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder const& rhs) noexcept = default; +BuilderType::Builder& BuilderType::Builder::operator=(BuilderType::Builder&& rhs) noexcept = default; + +MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::vertexCount(size_t vertexCount) noexcept { + mImpl->mVertexCount = vertexCount; + return *this; +} + +MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::count(size_t count) noexcept { + mImpl->mCount = count; + return *this; +} + +MorphTargetBuffer* MorphTargetBuffer::Builder::build(Engine& engine) { + return upcast(engine).createMorphTargetBuffer(*this); +} + +// ------------------------------------------------------------------------------------------------ + +// This value is limited by ES3.0, ES3.0 only guarantees 2048. +// When you change this value, you must change MAX_MORPH_TARGET_BUFFER_WIDTH at getters.vs +constexpr size_t MAX_MORPH_TARGET_BUFFER_WIDTH = 2048; + +static const auto FREE_CALLBACK = [](void* mem, size_t, void*) { ::free(mem); }; + +static inline size_t getWidth(size_t vertexCount) noexcept { + return std::min(vertexCount, MAX_MORPH_TARGET_BUFFER_WIDTH); +} + +static inline size_t getHeight(size_t vertexCount) noexcept { + return (vertexCount + MAX_MORPH_TARGET_BUFFER_WIDTH) / MAX_MORPH_TARGET_BUFFER_WIDTH; +} + +static inline size_t getDepth(size_t targetCount) noexcept { + return targetCount * 2; +} + +static inline size_t getSize(size_t vertexCount) noexcept { + const size_t stride = getWidth(vertexCount) * sizeof(float4); + return Texture::PixelBufferDescriptor::computeDataSize( + Texture::PixelBufferDescriptor::PixelDataFormat::RGBA, + Texture::PixelBufferDescriptor::PixelDataType::FLOAT, + stride, getHeight(vertexCount), 1); +} + +static inline size_t getPositionIndex(size_t targetIndex) noexcept { + return targetIndex * 2 + 0; +} + +static inline size_t getTangentIndex(size_t targetIndex) noexcept { + return targetIndex * 2 + 1; +} + +FMorphTargetBuffer::FMorphTargetBuffer(FEngine& engine, const Builder& builder) + : mSBuffer(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT), + mVertexCount(builder->mVertexCount), + mCount(builder->mCount) { + FEngine::DriverApi& driver = engine.getDriverApi(); + + mSbHandle = driver.createSamplerGroup(PerRenderPrimitiveMorphingSib::SAMPLER_COUNT); + mTbHandle = driver.createTexture(SamplerType::SAMPLER_2D_ARRAY, 1, TextureFormat::RGBA32F, 1, + getWidth(mVertexCount), getHeight(mVertexCount), getDepth(mCount), + TextureUsage::DEFAULT); + + SamplerParams samplerParams{}; + samplerParams.filterMin = SamplerMinFilter::NEAREST; + samplerParams.filterMag = SamplerMagFilter::NEAREST; + mSBuffer.setSampler(PerRenderPrimitiveMorphingSib::TARGETS, mTbHandle, samplerParams); +} + +void FMorphTargetBuffer::terminate(FEngine& engine) { + FEngine::DriverApi& driver = engine.getDriverApi(); + + driver.destroySamplerGroup(mSbHandle); + driver.destroyTexture(mTbHandle); +} + +void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, math::float3 const* positions, size_t count) { + ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count"); + + auto size = getSize(mVertexCount); + + ASSERT_PRECONDITION((int)sizeof(math::float3) * count <= size, + "MorphTargetBuffer (size=%lu) overflow (size=%lu)", + size, sizeof(math::float3) * count); + + // We could use a pool instead of malloc() directly. + auto* out = (float4*) malloc(size); + std::transform(positions, positions + count, out, + [](const float3& p) { return float4(p, 1.0f); }); + + Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT, + FREE_CALLBACK); + + FEngine::DriverApi& driver = engine.getDriverApi(); + driver.update3DImage(mTbHandle, 0, 0, 0, getPositionIndex(targetIndex), + getWidth(mVertexCount), getHeight(mVertexCount), 1, + std::move(buffer)); +} + +void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t targetIndex, math::float4 const* positions, size_t count) { + ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count"); + + auto size = getSize(mVertexCount); + + ASSERT_PRECONDITION((int)sizeof(math::float4) * count <= size, + "MorphTargetBuffer (size=%lu) overflow (size=%lu)", + size, sizeof(math::float4) * count); + + // We could use a pool instead of malloc() directly. + auto* out = (float4*) malloc(size); + memcpy(out, positions, sizeof(math::float4) * count); + + Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT, + FREE_CALLBACK); + + FEngine::DriverApi& driver = engine.getDriverApi(); + driver.update3DImage(mTbHandle, 0, 0, 0, getPositionIndex(targetIndex), + getWidth(mVertexCount), getHeight(mVertexCount), 1, + std::move(buffer)); +} + +void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t targetIndex, math::short4 const* tangents, size_t count) { + ASSERT_PRECONDITION(targetIndex < mCount, "targetIndex must be < count"); + + auto size = getSize(mVertexCount); + + ASSERT_PRECONDITION((int)sizeof(math::short4) * count <= size, + "MorphTargetBuffer (size=%lu) overflow (size=%lu)", + size, sizeof(math::short4) * count); + + // We could use a pool instead of malloc() directly. + auto* out = (float4*) malloc(size); + std::transform(tangents, tangents + count, out, + [](const short4& t) { return unpackSnorm16(t); }); + + Texture::PixelBufferDescriptor buffer(out, size, Texture::Format::RGBA, Texture::Type::FLOAT, + FREE_CALLBACK); + + FEngine::DriverApi& driver = engine.getDriverApi(); + driver.update3DImage(mTbHandle, 0, 0, 0, getTangentIndex(targetIndex), + getWidth(mVertexCount), getHeight(mVertexCount), 1, + std::move(buffer)); +} + +void FMorphTargetBuffer::commit(FEngine& engine) const noexcept { + if (mSBuffer.isDirty()) { + FEngine::DriverApi& driver = engine.getDriverApi(); + driver.updateSamplerGroup(mSbHandle, std::move(mSBuffer.toCommandStream())); + } +} + +void FMorphTargetBuffer::bind(backend::DriverApi& driver) const noexcept { + driver.bindSamplers(BindingPoints::PER_RENDERABLE_MORPHING, mSbHandle); +} + +// ------------------------------------------------------------------------------------------------ +// Trampoline calling into private implementation +// ------------------------------------------------------------------------------------------------ + +void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex, math::float3 const* positions, size_t count) { + upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count); +} + +void MorphTargetBuffer::setPositionsAt(Engine& engine, size_t targetIndex, math::float4 const* positions, size_t count) { + upcast(this)->setPositionsAt(upcast(engine), targetIndex, positions, count); +} + +void MorphTargetBuffer::setTangentsAt(Engine& engine, size_t targetIndex, math::short4 const* tangents, size_t count) { + upcast(this)->setTangentsAt(upcast(engine), targetIndex, tangents, count); +} + +size_t MorphTargetBuffer::getVertexCount() const noexcept { + return upcast(this)->getVertexCount(); +} + +size_t MorphTargetBuffer::getCount() const noexcept { + return upcast(this)->getCount(); +} + +} // namespace filament + diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index 2e93cd3908..f9c71d58e9 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -177,6 +177,31 @@ void PerViewUniforms::prepareSSR(Handle ssr, float refractionLodOffse s.refractionLodOffset = refractionLodOffset; } +void PerViewUniforms::prepareSSReflections(TextureHandle ssr, math::mat4f const& historyProjection, + math::mat4f const& projectToPixelMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) noexcept { + mPerViewSb.setSampler(PerViewSib::SSR, ssr, { + .filterMag = SamplerMagFilter::LINEAR, + .filterMin = SamplerMinFilter::LINEAR + }); + auto& s = mPerViewUb.edit(); + s.ssrReprojection = historyProjection; + s.ssrProjectToPixelMatrix = projectToPixelMatrix; + s.ssrThickness = ssrOptions.thickness; + s.ssrBias = ssrOptions.bias; + s.ssrDistance = ssrOptions.maxDistance; + s.ssrStride = ssrOptions.stride; +} + +void PerViewUniforms::disableSSReflections() noexcept { + // We must bind a placeholder texture, even if reflections are disabled, to avoid a + // "no texture unit bound" warning. + Handle placeholder = mEngine.getPostProcessManager().getOneTexture(); + auto& s = mPerViewUb.edit(); + mPerViewSb.setSampler(PerViewSib::SSR, placeholder, {}); + s.ssrDistance = 0.0f; +} + void PerViewUniforms::prepareStructure(Handle structure) noexcept { // sampler must be NEAREST mPerViewSb.setSampler(PerViewSib::STRUCTURE, structure, {}); diff --git a/filament/src/PerViewUniforms.h b/filament/src/PerViewUniforms.h index 63d7ac17f7..76bc872ca3 100644 --- a/filament/src/PerViewUniforms.h +++ b/filament/src/PerViewUniforms.h @@ -35,6 +35,7 @@ namespace filament { struct AmbientOcclusionOptions; struct DynamicResolutionOptions; struct FogOptions; +struct ScreenSpaceReflectionsOptions; struct SoftShadowOptions; struct TemporalAntiAliasingOptions; struct VsmShadowOptions; @@ -72,6 +73,10 @@ public: void prepareStructure(TextureHandle structure) noexcept; void prepareSSAO(TextureHandle ssao, AmbientOcclusionOptions const& options) noexcept; void prepareSSR(TextureHandle ssr, float refractionLodOffset) noexcept; + void prepareSSReflections(TextureHandle ssr, math::mat4f const& historyProjection, + math::mat4f const& projectToPixelMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) noexcept; + void disableSSReflections() noexcept; void prepareShadowMapping(ShadowMappingUniforms const& shadowMappingUniforms, VsmShadowOptions const& options) noexcept; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 4fbef7c253..e267e0a12e 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -317,7 +317,8 @@ void PostProcessManager::commitAndRender(FrameGraphResources::RenderPassInfo con // ------------------------------------------------------------------------------------------------ FrameGraphId PostProcessManager::structure(FrameGraph& fg, - RenderPass const& pass, uint32_t width, uint32_t height, + RenderPass const& pass, uint8_t structureRenderFlags, + uint32_t width, uint32_t height, StructurePassConfig const& config) noexcept { const float scale = config.scale; @@ -367,10 +368,13 @@ FrameGraphId PostProcessManager::structure(FrameGraph& fg, .clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH }); }, - [=](FrameGraphResources const& resources, + [=, renderPass = pass](FrameGraphResources const& resources, auto const& data, DriverApi& driver) mutable { auto out = resources.getRenderPassInfo(); - pass.execute(resources.getPassName(), out.target, out.params); + renderPass.setRenderFlags(structureRenderFlags); + renderPass.appendCommands(RenderPass::CommandTypeFlags::SSAO); + renderPass.sortCommands(); + renderPass.execute(resources.getPassName(), out.target, out.params); }); auto depth = structurePass->depth; @@ -2167,12 +2171,8 @@ void PostProcessManager::prepareTaa(FrameHistory& frameHistory, auto& current = frameHistory.getCurrent(); // get sample position within a pixel [-0.5, 0.5] const float2 jitter = halton(previous.frameId) - 0.5f; - // compute the world-space to clip-space matrix for this frame - current.projection = cameraInfo.projection * (cameraInfo.view * cameraInfo.worldOrigin); // save this frame's sample position current.jitter = jitter; - // update frame id - current.frameId = previous.frameId + 1; } FrameGraphId PostProcessManager::taa(FrameGraph& fg, diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index 74c626cc9a..078c981f36 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -70,7 +70,8 @@ public: // methods below are ordered relative to their position in the pipeline (as much as possible) // structure (depth) pass - FrameGraphId structure(FrameGraph& fg, RenderPass const& pass, + FrameGraphId structure(FrameGraph& fg, + RenderPass const& pass, uint8_t structureRenderFlags, uint32_t width, uint32_t height, StructurePassConfig const& config) noexcept; // SSAO diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 708d563ff4..89a1d9d13c 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -381,6 +381,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags, FMaterialInstance const* const mi = primitive.getMaterialInstance(); if constexpr (isColorPass) { cmdColor.primitive.primitiveHandle = primitive.getHwHandle(); + cmdColor.primitive.morphTargetBuffer = primitive.getMorphTargetBuffer(); cmdColor.primitive.materialVariant = materialVariant; RenderPass::setupColorCommand(cmdColor, mi, inverseFrontFaces); @@ -470,6 +471,7 @@ void RenderPass::generateCommandsImpl(uint32_t extraFlags, // unconditionally write the command cmdDepth.primitive.primitiveHandle = primitive.getHwHandle(); cmdDepth.primitive.mi = mi; + cmdDepth.primitive.morphTargetBuffer = primitive.getMorphTargetBuffer(); cmdDepth.primitive.rasterState.culling = mi->getCullingMode(); // FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()? @@ -529,6 +531,7 @@ void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver, SYSTRACE_VALUE32("commandCount", last - first); auto const* const UTILS_RESTRICT soaSkinning = soa.data(); + auto const* const UTILS_RESTRICT soaMorphing = soa.data(); PolygonOffset dummyPolyOffset; PipelineState pipeline{ .polygonOffset = mPolygonOffset }; @@ -577,6 +580,18 @@ void RenderPass::Executor::recordDriverCommands(backend::DriverApi& driver, skinning.offset * sizeof(PerRenderableUibBone), CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone)); } + + auto morphTargetBuffer = info.morphTargetBuffer; + if (UTILS_UNLIKELY(morphTargetBuffer)) { + // Instead of using a UBO per primitive, we could also have a single UBO for all primitives + // and use bindUniformBufferRange which might be more efficient. + auto morphing = soaMorphing[info.index]; + assert_invariant(morphing.handle); + driver.bindUniformBuffer(BindingPoints::PER_RENDERABLE_MORPHING, morphing.handle); + assert_invariant(morphing.count <= morphTargetBuffer->getCount()); + morphTargetBuffer->bind(driver); + } + driver.draw(pipeline, info.primitiveHandle); } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index c1fa00eea2..5bd930f868 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -208,19 +208,20 @@ public: return boolish ? std::numeric_limits::max() : uint64_t(0); } - struct PrimitiveInfo { // 24 bytes + struct PrimitiveInfo { // 32 bytes FMaterialInstance const* mi = nullptr; // 8 bytes (4) + FMorphTargetBuffer const* morphTargetBuffer = nullptr; // 8 bytes (4) backend::Handle primitiveHandle; // 4 bytes backend::RasterState rasterState; // 4 bytes uint16_t index = 0; // 2 bytes Variant materialVariant; // 1 byte - uint8_t reserved[13 - sizeof(void*)] = {}; // 5 byte (9) + uint8_t reserved[21 - sizeof(void*) - sizeof(void*)] = {}; // 5 bytes (13) }; - static_assert(sizeof(PrimitiveInfo) == 24); + static_assert(sizeof(PrimitiveInfo) == 32); - struct alignas(8) Command { // 32 bytes + struct alignas(8) Command { // 40 bytes CommandKey key = 0; // 8 bytes - PrimitiveInfo primitive; // 24 bytes + PrimitiveInfo primitive; // 32 bytes bool operator < (Command const& rhs) const noexcept { return key < rhs.key; } // placement new declared as "throw" to avoid the compiler's null-check inline void* operator new (std::size_t, void* ptr) { @@ -228,7 +229,7 @@ public: return ptr; } }; - static_assert(sizeof(Command) == 32); + static_assert(sizeof(Command) == 40); static_assert(std::is_trivially_destructible_v, "Command isn't trivially destructible"); diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp index a6eae017d2..469bbb3e1d 100644 --- a/filament/src/RenderPrimitive.cpp +++ b/filament/src/RenderPrimitive.cpp @@ -83,4 +83,8 @@ void FRenderPrimitive::set(FEngine& engine, RenderableManager::PrimitiveType typ mPrimitiveType = type; } +void FRenderPrimitive::set(FMorphTargetBuffer* morphTargetBuffer) noexcept { + mMorphTargetBuffer = morphTargetBuffer; +} + } // namespace filament diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h index 70188c3238..cd7da72f0e 100644 --- a/filament/src/RenderPrimitive.h +++ b/filament/src/RenderPrimitive.h @@ -30,6 +30,7 @@ namespace filament { class FEngine; class FVertexBuffer; class FIndexBuffer; +class FMorphTargetBuffer; class FRenderer; class FRenderPrimitive { @@ -45,6 +46,8 @@ public: void set(FEngine& engine, RenderableManager::PrimitiveType type, size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept; + void set(FMorphTargetBuffer* morphTargetBuffer) noexcept; + // frees driver resources, object becomes invalid void terminate(FEngine& engine); @@ -53,6 +56,7 @@ public: backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; } AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; } uint16_t getBlendOrder() const noexcept { return mBlendOrder; } + FMorphTargetBuffer* getMorphTargetBuffer() const noexcept { return mMorphTargetBuffer; } void setMaterialInstance(FMaterialInstance const* mi) noexcept { mMaterialInstance = mi; } void setBlendOrder(uint16_t order) noexcept { @@ -65,6 +69,7 @@ private: backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::NONE; AttributeBitset mEnabledAttributes; uint16_t mBlendOrder = 0; + FMorphTargetBuffer* mMorphTargetBuffer = nullptr; }; } // namespace filament diff --git a/filament/src/Renderer.cpp b/filament/src/Renderer.cpp index 52d5628b2e..4e29c0693e 100644 --- a/filament/src/Renderer.cpp +++ b/filament/src/Renderer.cpp @@ -240,6 +240,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { auto taaOptions = view.getTemporalAntiAliasingOptions(); auto vignetteOptions = view.getVignetteOptions(); auto colorGrading = view.getColorGrading(); + auto ssReflectionsOptions = view.getScreenSpaceReflectionsOptions(); if (!hasPostProcess) { // disable all effects that are part of post-processing dofOptions.enabled = false; @@ -384,7 +385,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { .msaa = msaaSampleCount, .clearFlags = clearFlags, .clearColor = clearColor, - .hasContactShadows = scene.hasContactShadows() + .hasContactShadows = scene.hasContactShadows(), + .hasScreenSpaceReflections = ssReflectionsOptions.enabled }; // asSubpass is disabled with TAA (although it's supported) because performance was degraded @@ -416,6 +418,9 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // of RenderPass::setCamera / RenderPass::setGeometry calls. view.updatePrimitivesLod(engine, cameraInfo, scene.getRenderableData(), view.getVisibleRenderables()); + // updatePrimitivesMorphTargetBuffer must be run after updatePrimitivesLod. + view.updatePrimitivesMorphTargetBuffer(engine, cameraInfo, + scene.getRenderableData(), view.getVisibleRenderables()); pass.setCamera(cameraInfo); pass.setGeometry(scene.getRenderableData(), view.getVisibleRenderables(), scene.getRenderableUBO()); @@ -433,14 +438,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // Currently it consists of a simple depth pass. // This is normally used by SSAO and contact-shadows - // TODO: ideally this should be a FrameGraph pass to participate to automatic culling - RenderPass structurePass(pass); - structurePass.setRenderFlags(structureRenderFlags); - structurePass.appendCommands(RenderPass::CommandTypeFlags::SSAO); - structurePass.sortCommands(); - // TODO: the scaling should depends on all passes that need the structure pass - ppm.structure(fg, structurePass, svp.width, svp.height, { + ppm.structure(fg, pass, structureRenderFlags, svp.width, svp.height, { .scale = aoOptions.resolution, .picking = view.hasPicking() }); @@ -467,6 +466,17 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { }); } + // Store this frame's camera projection in the frame history. + if (UTILS_UNLIKELY(taaOptions.enabled || ssReflectionsOptions.enabled)) { + auto projection = cameraInfo.projection * (cameraInfo.view * cameraInfo.worldOrigin); + setHistoryProjection(view, projection); + // update frame id + auto& history = view.getFrameHistory(); + auto const& previous = history[0]; + auto& current = history.getCurrent(); + current.frameId = previous.frameId + 1; + } + // Apply the TAA jitter to everything after the structure pass, starting with the color pass. if (taaOptions.enabled) { auto& history = view.getFrameHistory(); @@ -494,7 +504,8 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { // -------------------------------------------------------------------------------------------- // Color passes - // TODO: ideally this should be a FrameGraph pass to participate to automatic culling + // This one doesn't need to be a FrameGraph pass because it always happens by construction + // (i.e. it won't be culled, unless everything is culled), so no need to complexify things. pass.setRenderFlags(colorRenderFlags); pass.appendCommands(RenderPass::COLOR); pass.sortCommands(); @@ -533,7 +544,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { colorGradingConfigForColor.asSubpass = colorGradingConfigForColor.asSubpass && !taaOptions.enabled; if (colorGradingConfigForColor.asSubpass) { - // append colorgrading subpass after all other passes + // append color grading subpass after all other passes pass.appendCustomCommand( RenderPass::Pass::BLENDED, RenderPass::CustomCommand::EPILOG, @@ -596,7 +607,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { struct ExportColorHistoryData { FrameGraphId color; }; - if (taaOptions.enabled) { + if (taaOptions.enabled || ssReflectionsOptions.enabled) { fg.addPass("Export color history", [&](FrameGraph::Builder& builder, auto& data) { // We need to use sideEffect here to ensure this pass won't be culled. The "output" of @@ -829,12 +840,25 @@ FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* FrameGraphId output; FrameGraphId depth; FrameGraphId ssao; - FrameGraphId ssr; + FrameGraphId ssr; // either screen-space reflections or refractions FrameGraphId structure; float4 clearColor{}; TargetBufferFlags clearFlags{}; }; + FrameGraphId colorHistory; + mat4f historyProjection; + if (config.hasScreenSpaceReflections) { + colorHistory = getColorHistory(fg, view.getFrameHistory()); + if (UTILS_UNLIKELY(!colorHistory)) { + // if we don't have a history yet, don't render reflections this frame + } else { + const FrameHistory& frameHistory = view.getFrameHistory(); + FrameHistoryEntry const& entry = frameHistory[0]; + historyProjection = entry.projection; + } + } + auto& colorPass = fg.addPass(name, [&](FrameGraph::Builder& builder, ColorPassData& data) { @@ -843,12 +867,24 @@ FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* TargetBufferFlags clearColorFlags = config.clearFlags & TargetBufferFlags::COLOR; data.shadows = blackboard.get("shadows"); - data.ssr = blackboard.get("ssr"); data.ssao = blackboard.get("ssao"); data.color = blackboard.get("color"); data.depth = blackboard.get("depth"); - if (config.hasContactShadows) { + // TODO: we currently only allow either screen-space reflections or refractions, + // but not both. If SS reflections are enabled, we prefer those. + if (config.hasScreenSpaceReflections && colorHistory) { + // Screen-space reflections + data.ssr = builder.sample(colorHistory); + } else { + // Screen-space refractions + data.ssr = blackboard.get("ssr"); + if (data.ssr) { + data.ssr = builder.sample(data.ssr); + } + } + + if (config.hasContactShadows || config.hasScreenSpaceReflections) { data.structure = blackboard.get("structure"); assert_invariant(data.structure); data.structure = builder.sample(data.structure); @@ -858,10 +894,6 @@ FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* data.shadows = builder.sample(data.shadows); } - if (data.ssr) { - data.ssr = builder.sample(data.ssr); - } - if (data.ssao) { data.ssao = builder.sample(data.ssao); } @@ -939,8 +971,32 @@ FrameGraphId FRenderer::colorPass(FrameGraph& fg, const char* view.prepareStructure(data.structure ? resources.getTexture(data.structure) : ppm.getOneTexture()); + // set screen-space reflections or screen-space refractions if (data.ssr) { - view.prepareSSR(resources.getTexture(data.ssr), config.refractionLodOffset); + // We currently only allow either SS reflections or refractions. + auto const& ssrOptions = view.getScreenSpaceReflectionsOptions(); + const bool reflections = config.hasScreenSpaceReflections && ssrOptions.enabled; + const bool refractions = !config.hasScreenSpaceReflections; + + if (reflections) { + const auto& cameraInfo = view.getCameraInfo(); + auto reprojection = + getClipSpaceToTextureSpaceMatrix() * + historyProjection * + inverse(cameraInfo.view * cameraInfo.worldOrigin); + + auto projectToPixelMatrix = + getClipSpaceToTextureSpaceMatrix() * + cameraInfo.projection; + + view.prepareSSReflections(resources.getTexture(data.ssr), reprojection, + projectToPixelMatrix, ssrOptions); + } else if (refractions) { // TODO: support both + view.prepareSSR(resources.getTexture(data.ssr), config.refractionLodOffset); + } + } else { + // Screen-space reflections must be explicitly disabled. + view.disableSSReflections(); } view.prepareViewport(static_cast(out.params.viewport)); @@ -1266,6 +1322,30 @@ void FRenderer::initializeClearFlags() { | TargetBufferFlags::DEPTH_AND_STENCIL; } +math::mat4f FRenderer::getClipSpaceToTextureSpaceMatrix() const noexcept { + // Compute a clip-space [-1 to 1] to texture space [0 to 1] matrix, taking into account + // API-level differences. + const bool textureSpaceYFlipped = + mEngine.getBackend() == Backend::METAL || mEngine.getBackend() == Backend::VULKAN; + return mat4f(textureSpaceYFlipped ? mat4f::row_major_init{ + 0.5f, 0.0f, 0.0f, 0.5f, + 0.0f, -0.5f, 0.0f, 0.5f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + } : mat4f::row_major_init{ + 0.5f, 0.0f, 0.0f, 0.5f, + 0.0f, 0.5f, 0.0f, 0.5f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }); +} + +void FRenderer::setHistoryProjection(FView& view, math::mat4f const& projection) { + auto& history = view.getFrameHistory(); + auto& current = history.getCurrent(); + current.projection = projection; +} + FrameGraphId FRenderer::getColorHistory(FrameGraph& fg, FrameHistory const& frameHistory) noexcept { // Here we import the previous frame's color output and cache it in the blackboard. This ensures diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index d5cca3c3c2..ca64d72c43 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -138,9 +138,9 @@ void FScene::prepare(const mat4& worldOriginTransform, bool shadowReceiversAreCa worldTransform, // WORLD_TRANSFORM visibility, // VISIBILITY_STATE rcm.getSkinningBufferInfo(ri), // SKINNING_BUFFER + rcm.getMorphingBufferInfo(ri), // MORPHING_BUFFER 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 @@ -254,8 +254,8 @@ void FScene::updateUBOs(utils::Range visibleRenderables, backend::Hand visibility.screenSpaceContactShadows)); UniformBuffer::setUniform(buffer, - offset + offsetof(PerRenderableUib, morphWeights), - sceneData.elementAt(i)); + offset + offsetof(PerRenderableUib, morphTargetCount), + sceneData.elementAt(i).count); UniformBuffer::setUniform(buffer, offset + offsetof(PerRenderableUib, channels), diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 1614e231cc..49fe876d10 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -264,6 +264,8 @@ void ShadowMapManager::render(FrameGraph& fg, FEngine& engine, backend::DriverAp // updatePrimitivesLod must be run before RenderPass::appendCommands. view.updatePrimitivesLod(engine, cameraInfo, scene->getRenderableData(), entry.range); + // updatePrimitivesMorphTargetBuffer must be run after updatePrimitivesLod. + view.updatePrimitivesMorphTargetBuffer(engine, cameraInfo, scene->getRenderableData(), entry.range); // generate and sort the commands for rendering the shadow map RenderPass entryPass(pass); diff --git a/filament/src/ToneMapper.cpp b/filament/src/ToneMapper.cpp index cae64d9acd..75cbdd74b8 100644 --- a/filament/src/ToneMapper.cpp +++ b/filament/src/ToneMapper.cpp @@ -111,7 +111,7 @@ inline float3 darkSurround_to_dimSurround(float3 linearCV) { float3 XYZ = AP1_to_XYZ * linearCV; float3 xyY = XYZ_to_xyY(XYZ); - xyY.z = clamp(xyY.z, 0.0f, (float)std::numeric_limits::max()); + xyY.z = clamp(xyY.z, 0.0f, (float) std::numeric_limits::max()); xyY.z = std::pow(xyY.z, DIM_SURROUND_GAMMA); XYZ = xyY_to_XYZ(xyY); @@ -135,7 +135,7 @@ float3 ACES(float3 color, float brightness) noexcept { constexpr float RRT_SAT_FACTOR = 0.96f; constexpr float ODT_SAT_FACTOR = 0.93f; - float3 ap0 = REC2020_to_AP0 * color; + float3 ap0 = Rec2020_to_AP0 * color; // Glow module float saturation = rgb_2_saturation(ap0); @@ -177,7 +177,7 @@ float3 ACES(float3 color, float brightness) noexcept { // Apply desaturation to compensate for luminance difference linearCV = mix(float3(dot(linearCV, LUMINANCE_AP1)), linearCV, ODT_SAT_FACTOR); - return AP1_to_REC2020 * linearCV; + return AP1_to_Rec2020 * linearCV; } } // namespace aces @@ -261,7 +261,7 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept { // The 5th color in the array (cyan) represents middle gray (18%) // Every stop above or below middle gray causes a color shift // TODO: This should depend on the working color grading color space - float v = log2(dot(c, LUMINANCE_REC2020) / 0.18f); + float v = log2(dot(c, LUMINANCE_Rec2020) / 0.18f); v = clamp(v + 5.0f, 0.0f, 15.0f); size_t index = size_t(v); @@ -272,71 +272,52 @@ float3 DisplayRangeToneMapper::operator()(math::float3 c) const noexcept { // Generic tone mapper //------------------------------------------------------------------------------ -// Lottes, 2016,"Advanced Techniques and Optimization of VDR Color Pipelines": -// https://gpuopen.com/wp-content/uploads/2016/03/GdcVdrLottes.pdf -// Includes fix from Bart Wronski: -// https://bartwronski.com/2016/09/01/dynamic-range-and-evs/ - struct GenericToneMapper::Options { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wshadow" void setParameters( float contrast, - float shoulder, float midGrayIn, float midGrayOut, float hdrMax ) { contrast = max(contrast, 1e-5f); - shoulder = saturate(shoulder); midGrayIn = clamp(midGrayIn, 1e-5f, 1.0f); midGrayOut = clamp(midGrayOut, 1e-5f, 1.0f); hdrMax = max(hdrMax, 1.0f); this->contrast = contrast; - this->shoulder = shoulder; this->midGrayIn = midGrayIn; this->midGrayOut = midGrayOut; this->hdrMax = hdrMax; - // remap shoulder - d = 0.8f + 0.4f * shoulder; + float a = pow(midGrayIn, contrast); + float b = pow(hdrMax, contrast); + float c = a - midGrayOut * b; - float mc = std::pow(midGrayIn, contrast); - float mcs = std::pow(mc, d); - - float hc = std::pow(hdrMax, contrast); - float hcs = std::pow(hc, d); - - float u = (hcs - mcs) * midGrayOut; - float v = mcs * midGrayOut; - - b = -((-mc + (midGrayOut * (hcs * mc - hc * v)) / u) / v); - c = (hcs * mc - hc * v) / u; + inputScale = (a * b * (midGrayOut - 1.0f)) / c; + outputScale = midGrayOut * (a - b) / c; } #pragma clang diagnostic pop float contrast; - float shoulder; float midGrayIn; float midGrayOut; float hdrMax; - // Computed fields, do not modify - float b; - float c; - float d; + // TEMP + float inputScale; + float outputScale; }; GenericToneMapper::GenericToneMapper( float contrast, - float shoulder, float midGrayIn, float midGrayOut, float hdrMax ) noexcept { mOptions = new Options(); - mOptions->setParameters(contrast, shoulder, midGrayIn, midGrayOut, hdrMax); + mOptions->setParameters(contrast, midGrayIn, midGrayOut, hdrMax); } GenericToneMapper::~GenericToneMapper() noexcept { @@ -354,12 +335,11 @@ GenericToneMapper& GenericToneMapper::operator=(GenericToneMapper&& rhs) noexcep } float3 GenericToneMapper::operator()(math::float3 x) const noexcept { - float3 xc = pow(clamp(x, 0.0f, mOptions->hdrMax), mOptions->contrast); - return saturate(xc / (pow(xc, mOptions->d) * mOptions->b + mOptions->c)); + x = pow(x, mOptions->contrast); + return mOptions->outputScale * x / (x + mOptions->inputScale); } float GenericToneMapper::getContrast() const noexcept { return mOptions->contrast; } -float GenericToneMapper::getShoulder() const noexcept { return mOptions->shoulder; } float GenericToneMapper::getMidGrayIn() const noexcept { return mOptions->midGrayIn; } float GenericToneMapper::getMidGrayOut() const noexcept { return mOptions->midGrayOut; } float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax; } @@ -367,27 +347,14 @@ float GenericToneMapper::getHdrMax() const noexcept { return mOptions->hdrMax; void GenericToneMapper::setContrast(float contrast) noexcept { mOptions->setParameters( contrast, - mOptions->shoulder, mOptions->midGrayIn, mOptions->midGrayOut, mOptions->hdrMax ); } - -void GenericToneMapper::setShoulder(float shoulder) noexcept { - mOptions->setParameters( - mOptions->contrast, - shoulder, - mOptions->midGrayIn, - mOptions->midGrayOut, - mOptions->hdrMax - ); -} - void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept { mOptions->setParameters( mOptions->contrast, - mOptions->shoulder, midGrayIn, mOptions->midGrayOut, mOptions->hdrMax @@ -397,7 +364,6 @@ void GenericToneMapper::setMidGrayIn(float midGrayIn) noexcept { void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept { mOptions->setParameters( mOptions->contrast, - mOptions->shoulder, mOptions->midGrayIn, midGrayOut, mOptions->hdrMax @@ -407,7 +373,6 @@ void GenericToneMapper::setMidGrayOut(float midGrayOut) noexcept { void GenericToneMapper::setHdrMax(float hdrMax) noexcept { mOptions->setParameters( mOptions->contrast, - mOptions->shoulder, mOptions->midGrayIn, mOptions->midGrayOut, hdrMax diff --git a/filament/src/View.cpp b/filament/src/View.cpp index 7b2a3ceacf..f0bab476ab 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -19,6 +19,7 @@ #include "Culler.h" #include "DFG.h" #include "Froxelizer.h" +#include "RenderPrimitive.h" #include "ResourceAllocator.h" #include "details/Engine.h" @@ -657,9 +658,23 @@ void FView::prepareSSAO(Handle ssao) const noexcept { } void FView::prepareSSR(Handle ssr, float refractionLodOffset) const noexcept { + // If screen-space refractions are enabled, make sure to disable screen-space reflections. + // disableSSReflections binds a dummy texture to light_ssr, but this is overridden by prepareSSR. + // TODO: support simultaneous screen-space refractions and reflections. + mPerViewUniforms.disableSSReflections(); mPerViewUniforms.prepareSSR(ssr, refractionLodOffset); } +void FView::disableSSReflections() const noexcept { + mPerViewUniforms.disableSSReflections(); +} + +void FView::prepareSSReflections(backend::Handle ssr, + math::mat4f historyProjection, math::mat4f projectToPixelMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept { + mPerViewUniforms.prepareSSReflections(ssr, historyProjection, projectToPixelMatrix, ssrOptions); +} + void FView::prepareStructure(Handle structure) const noexcept { // sampler must be NEAREST mPerViewUniforms.prepareStructure(structure); @@ -868,6 +883,19 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&, } } +void FView::updatePrimitivesMorphTargetBuffer(FEngine& engine, const CameraInfo&, + FScene::RenderableSoa& renderableData, Range visible) noexcept { + for (uint32_t index : visible) { + Slice primitives = renderableData.elementAt(index); + for (auto& primitive : primitives) { + auto morphTargetBuffer = primitive.getMorphTargetBuffer(); + if (morphTargetBuffer) { + morphTargetBuffer->commit(engine); + } + } + } +} + void FView::renderShadowMaps(FrameGraph& fg, FEngine& engine, FEngine::DriverApi& driver, RenderPass const& pass) noexcept { mShadowMapManager.render(fg, engine, driver, pass, *this); @@ -1016,6 +1044,14 @@ const View::MultiSampleAntiAliasingOptions& View::getMultiSampleAntiAliasingOpti return upcast(this)->getMultiSampleAntiAliasingOptions(); } +void View::setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept { + upcast(this)->setScreenSpaceReflectionsOptions(options); +} + +const View::ScreenSpaceReflectionsOptions& View::getScreenSpaceReflectionsOptions() const noexcept { + return upcast(this)->getScreenSpaceReflectionsOptions(); +} + void View::setColorGrading(ColorGrading* colorGrading) noexcept { return upcast(this)->setColorGrading(upcast(colorGrading)); } diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index 7208771bb6..d89cc4fb00 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -23,8 +23,11 @@ #include "details/Engine.h" #include "details/VertexBuffer.h" #include "details/IndexBuffer.h" +#include "details/Texture.h" #include "details/Material.h" +#include "private/filament/SibGenerator.h" + #include #include @@ -316,7 +319,6 @@ void FRenderableManager::create( setCulling(ci, builder->mCulling); setSkinning(ci, false); setMorphing(ci, builder->mMorphingEnabled); - setMorphWeights(ci, {0, 0, 0, 0}); mManager[ci].channels = builder->mChannels; const uint32_t count = builder->mSkinningBoneCount; @@ -372,6 +374,18 @@ void FRenderableManager::create( } } } + + if (builder->mMorphingEnabled) { + // Instead of using a UBO per primitive, we could also have a single UBO for all primitives + // and use bindUniformBufferRange which might be more efficient. + MorphWeights& morphWeights = manager[ci].morphWeights; + morphWeights = MorphWeights { + .handle = driver.createBufferObject( + sizeof(PerRenderableMorphingUib), + BufferObjectBinding::UNIFORM, + backend::BufferUsage::DYNAMIC), + .count = 0 }; + } } engine.flushIfNeeded(); } @@ -416,6 +430,12 @@ void FRenderableManager::destroyComponent(Instance ci) noexcept { if (bones.handle && !bones.skinningBufferMode) { driver.destroyBufferObject(bones.handle); } + + // destroy the weights structures if any + MorphWeights const& morphWeights = manager[ci].morphWeights; + if (morphWeights.handle) { + driver.destroyBufferObject(morphWeights.handle); + } } void FRenderableManager::destroyComponentPrimitives( @@ -562,9 +582,38 @@ void FRenderableManager::setSkinningBuffer(FRenderableManager::Instance ci, bones.offset = uint16_t(offset); } -void FRenderableManager::setMorphWeights(Instance ci, const float4& weights) noexcept { - if (ci) { - mManager[ci].morphWeights = weights; +static void updateMorphWeights(FEngine& engine, backend::Handle handle, + float const* weights, size_t count) noexcept { + auto& driver = engine.getDriverApi(); + auto size = sizeof(PerRenderableMorphingUib); + auto* UTILS_RESTRICT out = (PerRenderableMorphingUib*)driver.allocate(size); + memset(out, 0, size); + std::transform(weights, weights + count, out->weights, + [](float value) { return float4(value); }); + driver.updateBufferObject(handle, { out, size }, 0); +} + +void FRenderableManager::setMorphWeights(Instance instance, float const* weights, size_t count) noexcept { + if (instance) { + MorphWeights& morphWeights = mManager[instance].morphWeights; + morphWeights.count = count; + + ASSERT_PRECONDITION(count < CONFIG_MAX_MORPH_TARGET_COUNT, + "Only %d morph targets are supported (count=%d)", CONFIG_MAX_MORPH_TARGET_COUNT, count); + + if (morphWeights.handle) { + updateMorphWeights(mEngine, morphWeights.handle, weights, count); + } + } +} + +void FRenderableManager::setMorphTargetBufferAt(Instance instance, + size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer) noexcept { + if (instance) { + Slice& primitives = getRenderPrimitives(instance, 0); + if (primitiveIndex < primitives.size()) { + primitives[primitiveIndex].set(morphTargetBuffer); + } } } @@ -693,15 +742,20 @@ void RenderableManager::setBones(Instance instance, upcast(this)->setBones(instance, transforms, boneCount, offset); } -void RenderableManager::setMorphWeights(Instance instance, float4 const& weights) noexcept { - upcast(this)->setMorphWeights(instance, weights); -} - void RenderableManager::setSkinningBuffer(Instance instance, SkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept { upcast(this)->setSkinningBuffer(instance, upcast(skinningBuffer), count, offset); } +void RenderableManager::setMorphWeights(Instance instance, float const* weights, size_t count) noexcept { + upcast(this)->setMorphWeights(instance, weights, count); +} + +void RenderableManager::setMorphTargetBufferAt(Instance instance, + size_t primitiveIndex, MorphTargetBuffer* morphTargetBuffer) noexcept { + upcast(this)->setMorphTargetBufferAt(instance, primitiveIndex, upcast(morphTargetBuffer)); +} + void RenderableManager::setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept { upcast(this)->setLightChannel(instance, channel, enable); } diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index 364a2f3c05..38db7a3794 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -40,9 +40,11 @@ namespace filament { class FBufferObject; class FIndexBuffer; class FMaterialInstance; +class FMorphTargetBuffer; class FRenderPrimitive; class FSkinningBuffer; class FVertexBuffer; +class FTexture; class FRenderableManager : public RenderableManager { public: @@ -106,9 +108,11 @@ public: inline void setPrimitives(Instance instance, utils::Slice const& primitives) noexcept; inline void setBones(Instance instance, Bone const* transforms, size_t boneCount, size_t offset = 0) noexcept; inline void setBones(Instance instance, math::mat4f const* transforms, size_t boneCount, size_t offset = 0) noexcept; - inline void setMorphWeights(Instance instance, const math::float4& weights) noexcept; inline void setSkinningBuffer(Instance instance, FSkinningBuffer* skinningBuffer, size_t count, size_t offset) noexcept; + void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept; + void setMorphTargetBufferAt(Instance instance, + size_t primitiveIndex, FMorphTargetBuffer* morphTargetBuffer) noexcept; inline void setLightChannel(Instance instance, unsigned int channel, bool enable) noexcept; inline bool getLightChannel(Instance instance, unsigned int channel) const noexcept; @@ -123,7 +127,6 @@ public: inline Visibility getVisibility(Instance instance) const noexcept; 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 { @@ -134,6 +137,12 @@ public: inline SkinningBindingInfo getSkinningBufferInfo(Instance instance) const noexcept; inline uint32_t getBoneCount(Instance instance) const noexcept; + struct MorphingBindingInfo { + backend::Handle handle; + uint32_t count; + }; + inline MorphingBindingInfo getMorphingBufferInfo(Instance instance) const noexcept; + inline int getMorphTargetCount(Instance instance) const noexcept; utils::Entity getEntity(Instance instance) const noexcept { return mManager.getEntity(instance); @@ -165,13 +174,18 @@ private: uint16_t offset = 0; bool skinningBufferMode = false; }; - static_assert(sizeof(Bones) == 12); + struct MorphWeights { + backend::Handle handle; + uint32_t count = 0; + }; + static_assert(sizeof(MorphWeights) == 8); + enum { AABB, // user data LAYERS, // user data - MORPH_WEIGHTS, // user data + MORPH_WEIGHTS, // filament data, UBO storing a pointer to the morph weights information CHANNELS, // user data VISIBILITY, // user data PRIMITIVES, // user data @@ -181,7 +195,7 @@ private: using Base = utils::SingleInstanceComponentManager< Box, // AABB uint8_t, // LAYERS - math::float4, // MORPH_WEIGHTS + MorphWeights, // MORPH_WEIGHTS uint8_t, // CHANNELS Visibility, // VISIBILITY utils::Slice, // PRIMITIVES @@ -325,10 +339,6 @@ uint8_t FRenderableManager::getPriority(Instance instance) const noexcept { return getVisibility(instance).priority; } -math::float4 FRenderableManager::getMorphWeights(Instance instance) const noexcept { - return mManager[instance].morphWeights; -} - uint8_t FRenderableManager::getChannels(Instance instance) const noexcept { return mManager[instance].channels; } @@ -348,6 +358,17 @@ inline uint32_t FRenderableManager::getBoneCount(Instance instance) const noexce return bones.count; } +FRenderableManager::MorphingBindingInfo +FRenderableManager::getMorphingBufferInfo(Instance instance) const noexcept { + MorphWeights const& morphWeights = mManager[instance].morphWeights; + return { morphWeights.handle, morphWeights.count }; +} + +int FRenderableManager::getMorphTargetCount(Instance instance) const noexcept { + MorphWeights const& morphWeights = mManager[instance].morphWeights; + return morphWeights.count; +} + utils::Slice const& FRenderableManager::getRenderPrimitives( Instance instance, uint8_t level) const noexcept { return mManager[instance].primitives; diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index 6fd2777c45..9f6e1e1b0e 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -36,6 +36,7 @@ #include "details/IndexBuffer.h" #include "details/RenderTarget.h" #include "details/SkinningBuffer.h" +#include "details/MorphTargetBuffer.h" #include "details/Skybox.h" #include "private/backend/CommandBufferQueue.h" @@ -234,6 +235,7 @@ public: FVertexBuffer* createVertexBuffer(const VertexBuffer::Builder& builder) noexcept; FIndexBuffer* createIndexBuffer(const IndexBuffer::Builder& builder) noexcept; FSkinningBuffer* createSkinningBuffer(const SkinningBuffer::Builder& builder) noexcept; + FMorphTargetBuffer* createMorphTargetBuffer(const MorphTargetBuffer::Builder& builder) noexcept; FIndirectLight* createIndirectLight(const IndirectLight::Builder& builder) noexcept; FMaterial* createMaterial(const Material::Builder& builder) noexcept; FTexture* createTexture(const Texture::Builder& builder) noexcept; @@ -265,6 +267,7 @@ public: bool destroy(const FFence* p); bool destroy(const FIndexBuffer* p); bool destroy(const FSkinningBuffer* p); + bool destroy(const FMorphTargetBuffer* p); bool destroy(const FIndirectLight* p); bool destroy(const FMaterial* p); bool destroy(const FMaterialInstance* p); @@ -376,6 +379,7 @@ private: ResourceList mStreams{ "Stream" }; ResourceList mIndexBuffers{ "IndexBuffer" }; ResourceList mSkinningBuffers{ "SkinningBuffer" }; + ResourceList mMorphTargetBuffers{ "MorphTargetBuffer" }; ResourceList mVertexBuffers{ "VertexBuffer" }; ResourceList mIndirectLights{ "IndirectLight" }; ResourceList mMaterials{ "Material" }; diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index b75ecfefee..64031690b9 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -118,6 +118,7 @@ public: AttributeBitset getRequiredAttributes() const noexcept { return mRequiredAttributes; } RefractionMode getRefractionMode() const noexcept { return mRefractionMode; } RefractionType getRefractionType() const noexcept { return mRefractionType; } + ReflectionMode getReflectionMode() const noexcept { return mReflectionMode; } bool hasSpecularAntiAliasing() const noexcept { return mSpecularAntiAliasing; } float getSpecularAntiAliasingVariance() const noexcept { return mSpecularAntiAliasingVariance; } @@ -188,6 +189,7 @@ private: AttributeBitset mRequiredAttributes; RefractionMode mRefractionMode = RefractionMode::NONE; RefractionType mRefractionType = RefractionType::SOLID; + ReflectionMode mReflectionMode = ReflectionMode::DEFAULT; uint64_t mMaterialProperties = 0; float mMaskThreshold = 0.4f; diff --git a/filament/src/details/MorphTargetBuffer.h b/filament/src/details/MorphTargetBuffer.h new file mode 100644 index 0000000000..5ba5fe1fdd --- /dev/null +++ b/filament/src/details/MorphTargetBuffer.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 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_DETAILS_MORPHTARGETBUFFER_H +#define TNT_FILAMENT_DETAILS_MORPHTARGETBUFFER_H + +#include "upcast.h" + +#include + +#include "private/backend/DriverApiForward.h" + +#include "private/backend/SamplerGroup.h" + +#include + +#include + +#include + +namespace filament { + +class FEngine; + +class FMorphTargetBuffer : public MorphTargetBuffer { +public: + FMorphTargetBuffer(FEngine& engine, const Builder& builder); + + // frees driver resources, object becomes invalid + void terminate(FEngine& engine); + + void setPositionsAt(FEngine& engine, size_t targetIndex, math::float3 const* positions, size_t count); + void setPositionsAt(FEngine& engine, size_t targetIndex, math::float4 const* positions, size_t count); + void setTangentsAt(FEngine& engine, size_t targetIndex, math::short4 const* tangents, size_t count); + inline size_t getVertexCount() const noexcept { return mVertexCount; } + inline size_t getCount() const noexcept { return mCount; } + +private: + friend class FView; + friend class RenderPass; + + void commit(FEngine& engine) const noexcept; + void bind(backend::DriverApi& driver) const noexcept; + + backend::Handle mSbHandle; + backend::Handle mTbHandle; + backend::SamplerGroup mSBuffer; + size_t mVertexCount; + size_t mCount; +}; + +FILAMENT_UPCAST(MorphTargetBuffer) + +} // namespace filament + +#endif //TNT_FILAMENT_DETAILS_MORPHTARGETBUFFER_H diff --git a/filament/src/details/Renderer.h b/filament/src/details/Renderer.h index 026915e8ad..ffbb37e91f 100644 --- a/filament/src/details/Renderer.h +++ b/filament/src/details/Renderer.h @@ -152,6 +152,7 @@ private: math::float4 clearColor = {}; float refractionLodOffset; bool hasContactShadows; + bool hasScreenSpaceReflections; }; FrameGraphId colorPass(FrameGraph& fg, const char* name, @@ -187,6 +188,10 @@ private: return clock::now() - getUserEpoch(); } + math::mat4f getClipSpaceToTextureSpaceMatrix() const noexcept; + + void setHistoryProjection(FView& view, math::mat4f const& projection); + static FrameGraphId getColorHistory(FrameGraph& fg, FrameHistory const& frameHistory) noexcept; diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 03833a86dc..f8f66afbf6 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -105,9 +105,9 @@ public: WORLD_TRANSFORM, // 16 | instance of the Transform component VISIBILITY_STATE, // 1 | visibility data of the component SKINNING_BUFFER, // 8 | bones uniform buffer handle, count, offset + MORPHING_BUFFER, // 8 | weights uniform buffer handle, count WORLD_AABB_CENTER, // 12 | world-space bounding box center of the renderable VISIBLE_MASK, // 2 | 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 @@ -127,9 +127,9 @@ public: math::mat4f, // WORLD_TRANSFORM FRenderableManager::Visibility, // VISIBILITY_STATE FRenderableManager::SkinningBindingInfo, // SKINNING_BUFFER + FRenderableManager::MorphingBindingInfo, // MORPHING_BUFFER math::float3, // WORLD_AABB_CENTER VisibleMaskType, // VISIBLE_MASK - math::float4, // MORPH_WEIGHTS uint8_t, // CHANNELS uint8_t, // LAYERS math::float3, // WORLD_AABB_EXTENT diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 68b0447573..639192e2ec 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -172,6 +172,10 @@ public: void prepareSSAO(backend::Handle ssao) const noexcept; void prepareSSR(backend::Handle ssr, float refractionLodOffset) const noexcept; + void disableSSReflections() const noexcept; + void prepareSSReflections(backend::Handle ssr, + math::mat4f historyProjection, math::mat4f projectToPixelMatrix, + ScreenSpaceReflectionsOptions const& ssrOptions) const noexcept; void prepareStructure(backend::Handle structure) const noexcept; void prepareShadow(backend::Handle structure) const noexcept; void prepareShadowMap() const noexcept; @@ -198,6 +202,10 @@ public: FEngine& engine, const CameraInfo& camera, FScene::RenderableSoa& renderableData, Range visible) noexcept; + void updatePrimitivesMorphTargetBuffer( + FEngine& engine, const CameraInfo&, + FScene::RenderableSoa& renderableData, Range visible) noexcept; + void setShadowingEnabled(bool enabled) noexcept { mShadowingEnabled = enabled; } bool isShadowingEnabled() const noexcept { return mShadowingEnabled; } @@ -255,6 +263,18 @@ public: return mMultiSampleAntiAliasingOptions; } + void setScreenSpaceReflectionsOptions(ScreenSpaceReflectionsOptions options) noexcept { + options.thickness = std::max(0.0f, options.thickness); + options.bias = std::max(0.0f, options.bias); + options.maxDistance = std::max(0.0f, options.maxDistance); + options.stride = std::max(1.0f, options.stride); + mScreenSpaceReflectionsOptions = options; + } + + const ScreenSpaceReflectionsOptions& getScreenSpaceReflectionsOptions() const noexcept { + return mScreenSpaceReflectionsOptions; + } + void setColorGrading(FColorGrading* colorGrading) noexcept { mColorGrading = colorGrading == nullptr ? mDefaultColorGrading : colorGrading; } @@ -570,6 +590,7 @@ private: VignetteOptions mVignetteOptions; TemporalAntiAliasingOptions mTemporalAntiAliasingOptions; MultiSampleAntiAliasingOptions mMultiSampleAntiAliasingOptions; + ScreenSpaceReflectionsOptions mScreenSpaceReflectionsOptions; BlendMode mBlendMode = BlendMode::OPAQUE; const FColorGrading* mColorGrading = nullptr; const FColorGrading* mDefaultColorGrading = nullptr; diff --git a/filament/src/materials/ssao/depthUtils.fs b/filament/src/materials/ssao/depthUtils.fs index 1aaa4a61ed..235fe4f87f 100644 --- a/filament/src/materials/ssao/depthUtils.fs +++ b/filament/src/materials/ssao/depthUtils.fs @@ -21,7 +21,7 @@ highp float linearizeDepth(highp float depth) { // Our far plane is at infinity, which causes a division by zero below, which in turn // causes some issues on some GPU. We workaround it by replacing "infinity" by the closest // value representable in a 24 bit depth buffer. - const float preventDiv0 = 1.0 / 16777216.0; + const highp float preventDiv0 = 1.0 / 16777216.0; mat4 p = getViewFromClipMatrix(); // this works with perspective and ortho projections, for a perspective projection // this resolves to -near/depth, for an ortho projection this resolves to depth*(far - near) - far diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 759d3047e7..88c9b84e2b 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.16.1" + spec.version = "1.17.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.16.1/filament-v1.16.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.17.0/filament-v1.17.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/filabridge/include/filament/MaterialChunkType.h b/libs/filabridge/include/filament/MaterialChunkType.h index 56d6eb50d5..b80e635451 100644 --- a/libs/filabridge/include/filament/MaterialChunkType.h +++ b/libs/filabridge/include/filament/MaterialChunkType.h @@ -62,6 +62,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t { MaterialDomain = charTo64bitNum("MAT_DOMN"), MaterialRefraction = charTo64bitNum("MAT_REFM"), MaterialRefractionType = charTo64bitNum("MAT_REFT"), + MaterialReflectionMode = charTo64bitNum("MAT_REFL"), MaterialRequiredAttributes = charTo64bitNum("MAT_REQA"), MaterialDepthWriteSet = charTo64bitNum("MAT_DEWS"), diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 9e2ed51906..c4ef79b5d4 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -27,7 +27,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 16; +static constexpr size_t MATERIAL_VERSION = 17; /** * Supported shading models @@ -136,20 +136,10 @@ enum VertexAttribute : uint8_t { CUSTOM6 = 14, CUSTOM7 = 15, - // Aliases for vertex morphing. - MORPH_POSITION_0 = CUSTOM0, - MORPH_POSITION_1 = CUSTOM1, - MORPH_POSITION_2 = CUSTOM2, - MORPH_POSITION_3 = CUSTOM3, - MORPH_TANGENTS_0 = CUSTOM4, - MORPH_TANGENTS_1 = CUSTOM5, - MORPH_TANGENTS_2 = CUSTOM6, - MORPH_TANGENTS_3 = CUSTOM7, - // this is limited by driver::MAX_VERTEX_ATTRIBUTE_COUNT }; -static constexpr size_t MAX_MORPH_TARGETS = 4; +static constexpr size_t MAX_MORPH_TARGETS = 128; // this is limited by filament::CONFIG_MAX_MORPH_TARGET_COUNT static constexpr size_t MAX_CUSTOM_ATTRIBUTES = 8; /** @@ -186,6 +176,14 @@ enum class RefractionType : uint8_t { THIN = 1, //!< refraction through thin objects (e.g. window) }; +/** + * Reflection mode + */ +enum class ReflectionMode : uint8_t { + DEFAULT = 0, //! reflections sample from the scene's IBL only + SCREEN_SPACE = 1, //! reflections sample from screen space, and fallback to the scene's IBL +}; + // can't really use std::underlying_type::type because the driver takes a uint32_t using AttributeBitset = utils::bitset32; diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 30a3fb343f..ccdace432b 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -33,14 +33,15 @@ enum class PostProcessVariant : uint8_t { // Binding points for uniform buffers and sampler buffers. // Effectively, these are just names. namespace BindingPoints { - constexpr uint8_t PER_VIEW = 0; // uniforms/samplers updated per view - constexpr uint8_t PER_RENDERABLE = 1; // uniforms/samplers updated per renderable - constexpr uint8_t PER_RENDERABLE_BONES = 2; // bones data, per renderable - constexpr uint8_t LIGHTS = 3; // lights data array - constexpr uint8_t SHADOW = 4; // punctual shadow data - constexpr uint8_t FROXEL_RECORDS = 5; - constexpr uint8_t PER_MATERIAL_INSTANCE = 6; // uniforms/samplers updates per material - constexpr uint8_t COUNT = 7; + constexpr uint8_t PER_VIEW = 0; // uniforms/samplers updated per view + constexpr uint8_t PER_RENDERABLE = 1; // uniforms/samplers updated per renderable + constexpr uint8_t PER_RENDERABLE_BONES = 2; // bones data, per renderable + constexpr uint8_t PER_RENDERABLE_MORPHING = 3; // morphing uniform/sampler updated per render primitive + constexpr uint8_t LIGHTS = 4; // lights data array + constexpr uint8_t SHADOW = 5; // punctual shadow data + constexpr uint8_t FROXEL_RECORDS = 6; + constexpr uint8_t PER_MATERIAL_INSTANCE = 7; // uniforms/samplers updates per material + constexpr uint8_t COUNT = 8; // These are limited by Program::UNIFORM_BINDING_COUNT (currently 8) } @@ -66,6 +67,11 @@ constexpr size_t CONFIG_MAX_SHADOW_CASCADES = 4; // We store 64 bytes per bone. constexpr size_t CONFIG_MAX_BONE_COUNT = 256; +// The maximum number of morph target count. +// This value is limited by ES3.0, ES3.0 only guarantees 256 layers in an array texture. +// For morphing, 128 layers are used for the positions and others are used for tangents. +constexpr int CONFIG_MAX_MORPH_TARGET_COUNT = 128; + } // namespace filament #endif // TNT_FILAMENT_driver/EngineEnums.h diff --git a/libs/filabridge/include/private/filament/SibGenerator.h b/libs/filabridge/include/private/filament/SibGenerator.h index 25c9f7362c..610ee07191 100644 --- a/libs/filabridge/include/private/filament/SibGenerator.h +++ b/libs/filabridge/include/private/filament/SibGenerator.h @@ -30,6 +30,7 @@ class SamplerInterfaceBlock; class SibGenerator { public: static SamplerInterfaceBlock const& getPerViewSib(uint8_t variantKey) noexcept; + static SamplerInterfaceBlock const& getPerRenderPrimitiveMorphingSib(uint8_t variantKey) noexcept; static SamplerInterfaceBlock const* getSib(uint8_t bindingPoint, uint8_t variantKey) noexcept; // When adding a sampler block here, make sure to also update // FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed @@ -48,5 +49,14 @@ struct PerViewSib { static constexpr size_t SAMPLER_COUNT = 7; }; +struct PerRenderPrimitiveMorphingSib { + // Morph positions and normals are packed into one texture. + // Because some materials exceed MAX_SAMPLER_COUNT(16) + // when morph positions and normals are bound to samplers separately. + static constexpr size_t TARGETS = 0; // morph positions and normals + + static constexpr size_t SAMPLER_COUNT = 1; +}; + } #endif // TNT_FILABRIDGE_SIBGENERATOR_H diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 4a25bac3d0..42c2edcfc0 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -136,8 +136,16 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float nearOverFarMinusNear; // n / (f-n), always positive float temporalNoise; // noise [0,1] when TAA is used, 0 otherwise + // Screen-space reflections + math::mat4f ssrReprojection; + math::mat4f ssrProjectToPixelMatrix; + float ssrThickness; // ssr thickness, in world units + float ssrBias; // ssr bias, in world units + float ssrDistance; // ssr world raycast distance, 0 when ssr is off + float ssrStride; // ssr texel stride, >= 1.0 + // bring PerViewUib to 2 KiB - math::float4 padding2[58]; + math::float4 padding3[49]; }; // 2 KiB == 128 float4s @@ -149,7 +157,7 @@ struct alignas(256) PerRenderableUib { // NOLINT(cppcoreguidelines-pro-type-memb static constexpr utils::StaticString _name{ "ObjectUniforms" }; math::mat4f worldFromModelMatrix; math::mat3f worldFromModelNormalMatrix; // this gets expanded to 48 bytes during the copy to the UBO - alignas(16) math::float4 morphWeights; // morph weights (we could easily have 8 using half) + alignas(16) uint32_t morphTargetCount; uint32_t flags; // see packFlags() below uint32_t channels; // 0x000000ll uint32_t objectId; // used for picking @@ -227,6 +235,14 @@ struct PerRenderableUibBone { // NOLINT(cppcoreguidelines-pro-type-member-init) static_assert(CONFIG_MAX_BONE_COUNT * sizeof(PerRenderableUibBone) <= 16384, "PerRenderableUibBone exceed max UBO size"); +struct alignas(16) PerRenderableMorphingUib { + static constexpr utils::StaticString _name{ "MorphingUniforms" }; + // The array stride(the bytes between array elements) is always rounded up to the size of a vec4 in std140. + math::float4 weights[CONFIG_MAX_MORPH_TARGET_COUNT]; +}; +static_assert(sizeof(PerRenderableMorphingUib) <= 16384, + "PerRenderableMorphingUib exceed max UBO size"); + } // namespace filament #endif // TNT_FILABRIDGE_UIBSTRUCTS_H diff --git a/libs/filabridge/include/private/filament/Variant.h b/libs/filabridge/include/private/filament/Variant.h index 596acfbba3..efe12ddc39 100644 --- a/libs/filabridge/include/private/filament/Variant.h +++ b/libs/filabridge/include/private/filament/Variant.h @@ -185,6 +185,10 @@ namespace filament { if (isValidDepthVariant(variantKey)) { return variantKey; } + // if shadow receiver is disabled, turn off VSM + if (isLit && !(variantKey & SHADOW_RECEIVER)) { + return variantKey & ~VSM; + } // when the shading mode is unlit, remove all the lighting variants return isLit ? variantKey : (variantKey & UNLIT_MASK); } diff --git a/libs/filabridge/src/SibGenerator.cpp b/libs/filabridge/src/SibGenerator.cpp index 14b1bab85f..a361f701c9 100644 --- a/libs/filabridge/src/SibGenerator.cpp +++ b/libs/filabridge/src/SibGenerator.cpp @@ -66,12 +66,27 @@ SamplerInterfaceBlock const& SibGenerator::getPerViewSib(uint8_t variantKey) noe return v.hasVsm() ? sibVsm : sibPcf; } +SamplerInterfaceBlock const& SibGenerator::getPerRenderPrimitiveMorphingSib(uint8_t variantKey) noexcept { + using Type = SamplerInterfaceBlock::Type; + using Format = SamplerInterfaceBlock::Format; + using Precision = SamplerInterfaceBlock::Precision; + + static SamplerInterfaceBlock sib = SamplerInterfaceBlock::Builder() + .name("Morphing") + .add("targets", Type::SAMPLER_2D_ARRAY, Format::FLOAT, Precision::HIGH) + .build(); + + return sib; +} + SamplerInterfaceBlock const* SibGenerator::getSib(uint8_t bindingPoint, uint8_t variantKey) noexcept { switch (bindingPoint) { case BindingPoints::PER_VIEW: return &getPerViewSib(variantKey); case BindingPoints::PER_RENDERABLE: return nullptr; + case BindingPoints::PER_RENDERABLE_MORPHING: + return &getPerRenderPrimitiveMorphingSib(variantKey); case BindingPoints::LIGHTS: return nullptr; default: diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 6a7077fe00..1650fab63a 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -190,6 +190,7 @@ public: using MaterialDomain = filament::MaterialDomain; using RefractionMode = filament::RefractionMode; using RefractionType = filament::RefractionType; + using ReflectionMode = filament::ReflectionMode; using VertexAttribute = filament::VertexAttribute; using ShaderQuality = filament::ShaderQuality; @@ -461,6 +462,9 @@ public: //! Specify the refraction type MaterialBuilder& refractionType(RefractionType refractionType) noexcept; + //! Specifies how reflections should be rendered (default is DEFAULT). + MaterialBuilder& reflectionMode(ReflectionMode mode) noexcept; + //! Specifies how transparent objects should be rendered (default is DEFAULT). MaterialBuilder& transparencyMode(TransparencyMode mode) noexcept; @@ -702,6 +706,7 @@ private: MaterialDomain mMaterialDomain = MaterialDomain::SURFACE; RefractionMode mRefractionMode = RefractionMode::NONE; RefractionType mRefractionType = RefractionType::SOLID; + ReflectionMode mReflectionMode = ReflectionMode::DEFAULT; Interpolation mInterpolation = Interpolation::SMOOTH; VertexDomain mVertexDomain = VertexDomain::OBJECT; TransparencyMode mTransparencyMode = TransparencyMode::DEFAULT; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 93f46a5b42..5df7f6894b 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -374,6 +374,11 @@ MaterialBuilder& MaterialBuilder::transparencyMode(TransparencyMode mode) noexce return *this; } +MaterialBuilder& MaterialBuilder::reflectionMode(ReflectionMode mode) noexcept { + mReflectionMode = mode; + return *this; +} + MaterialBuilder& MaterialBuilder::platform(Platform platform) noexcept { mPlatform = platform; return *this; @@ -480,6 +485,7 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { info.specularAOSet = mSpecularAOSet; info.refractionMode = mRefractionMode; info.refractionType = mRefractionType; + info.reflectionMode = mReflectionMode; info.quality = mShaderQuality; info.hasCustomSurfaceShading = mCustomSurfaceShading; } @@ -1026,6 +1032,7 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& container.addSimpleChild(ChunkType::MaterialBlendingMode, static_cast(mBlendingMode)); container.addSimpleChild(ChunkType::MaterialTransparencyMode, static_cast(mTransparencyMode)); + container.addSimpleChild(ChunkType::MaterialReflectionMode, static_cast(mReflectionMode)); container.addSimpleChild(ChunkType::MaterialDepthWriteSet, mDepthWriteSet); container.addSimpleChild(ChunkType::MaterialColorWrite, mColorWrite); container.addSimpleChild(ChunkType::MaterialDepthWrite, mDepthWrite); diff --git a/libs/filamat/src/UibGenerator.cpp b/libs/filamat/src/UibGenerator.cpp index 8708be1d1f..2af505d3be 100644 --- a/libs/filamat/src/UibGenerator.cpp +++ b/libs/filamat/src/UibGenerator.cpp @@ -117,8 +117,16 @@ UniformInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { .add("nearOverFarMinusNear", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) .add("temporalNoise", 1, UniformInterfaceBlock::Type::FLOAT, Precision::HIGH) + // Screen-space reflection parameters + .add("ssrReprojection", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH) + .add("ssrProjectToPixelMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH) + .add("ssrThickness", 1, UniformInterfaceBlock::Type::FLOAT) + .add("ssrBias", 1, UniformInterfaceBlock::Type::FLOAT) + .add("ssrDistance", 1, UniformInterfaceBlock::Type::FLOAT) + .add("ssrStride", 1, UniformInterfaceBlock::Type::FLOAT) + // bring PerViewUib to 2 KiB - .add("padding2", 58, UniformInterfaceBlock::Type::FLOAT4) + .add("padding3", 49, UniformInterfaceBlock::Type::FLOAT4) .build(); return uib; } @@ -128,7 +136,7 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableUib() noexcept { .name(PerRenderableUib::_name) .add("worldFromModelMatrix", 1, UniformInterfaceBlock::Type::MAT4, Precision::HIGH) .add("worldFromModelNormalMatrix", 1, UniformInterfaceBlock::Type::MAT3, Precision::HIGH) - .add("morphWeights", 1, UniformInterfaceBlock::Type::FLOAT4, Precision::HIGH) + .add("morphTargetCount", 1, UniformInterfaceBlock::Type::UINT) .add("flags", 1, UniformInterfaceBlock::Type::UINT) .add("channels", 1, UniformInterfaceBlock::Type::UINT) .add("objectId", 1, UniformInterfaceBlock::Type::UINT) @@ -161,6 +169,14 @@ UniformInterfaceBlock const& UibGenerator::getPerRenderableBonesUib() noexcept { return uib; } +UniformInterfaceBlock const& UibGenerator::getPerRenderableMorphingUib() noexcept { + static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder() + .name(PerRenderableMorphingUib::_name) + .add("weights", CONFIG_MAX_MORPH_TARGET_COUNT, UniformInterfaceBlock::Type::FLOAT4) + .build(); + return uib; +} + UniformInterfaceBlock const& UibGenerator::getFroxelRecordUib() noexcept { static UniformInterfaceBlock uib = UniformInterfaceBlock::Builder() .name(FroxelRecordUib::_name) diff --git a/libs/filamat/src/UibGenerator.h b/libs/filamat/src/UibGenerator.h index 4007c5c76b..8c39ebcc96 100644 --- a/libs/filamat/src/UibGenerator.h +++ b/libs/filamat/src/UibGenerator.h @@ -28,6 +28,7 @@ public: static UniformInterfaceBlock const& getLightsUib() noexcept; static UniformInterfaceBlock const& getShadowUib() noexcept; static UniformInterfaceBlock const& getPerRenderableBonesUib() noexcept; + static UniformInterfaceBlock const& getPerRenderableMorphingUib() noexcept; static UniformInterfaceBlock const& getFroxelRecordUib() noexcept; // When adding an UBO here, make sure to also update // FMaterial::getSurfaceProgramSlow and FMaterial::getPostProcessProgramSlow if needed diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index 28bf33c1de..f4334b4639 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -611,6 +611,7 @@ io::sstream& CodeGenerator::generateShaderLit(io::sstream& out, ShaderType type, if (shading != Shading::UNLIT) { out << SHADERS_AMBIENT_OCCLUSION_FS_DATA; + out << SHADERS_LIGHT_REFLECTIONS_FS_DATA; out << SHADERS_LIGHT_INDIRECT_FS_DATA; } if (variant.hasDirectionalLighting()) { diff --git a/libs/filamat/src/shaders/MaterialInfo.h b/libs/filamat/src/shaders/MaterialInfo.h index 0ff7eae638..bb66c99eda 100644 --- a/libs/filamat/src/shaders/MaterialInfo.h +++ b/libs/filamat/src/shaders/MaterialInfo.h @@ -48,6 +48,7 @@ struct UTILS_PUBLIC MaterialInfo { filament::SpecularAmbientOcclusion specularAO; filament::RefractionMode refractionMode; filament::RefractionType refractionType; + filament::ReflectionMode reflectionMode; filament::AttributeBitset requiredAttributes; filament::BlendingMode blendingMode; filament::BlendingMode postLightingBlendingMode; diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index fa4449e2bb..8f7f72bb5e 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -218,14 +218,6 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel, if (variant.hasSkinningOrMorphing()) { attributes.set(VertexAttribute::BONE_INDICES); attributes.set(VertexAttribute::BONE_WEIGHTS); - attributes.set(VertexAttribute::MORPH_POSITION_0); - attributes.set(VertexAttribute::MORPH_POSITION_1); - attributes.set(VertexAttribute::MORPH_POSITION_2); - attributes.set(VertexAttribute::MORPH_POSITION_3); - attributes.set(VertexAttribute::MORPH_TANGENTS_0); - attributes.set(VertexAttribute::MORPH_TANGENTS_1); - attributes.set(VertexAttribute::MORPH_TANGENTS_2); - attributes.set(VertexAttribute::MORPH_TANGENTS_3); } CodeGenerator::generateShaderInputs(vs, ShaderType::VERTEX, attributes, interpolation); @@ -247,6 +239,12 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel, cg.generateUniforms(vs, ShaderType::VERTEX, BindingPoints::PER_RENDERABLE_BONES, UibGenerator::getPerRenderableBonesUib()); + cg.generateUniforms(vs, ShaderType::VERTEX, + BindingPoints::PER_RENDERABLE_MORPHING, + UibGenerator::getPerRenderableMorphingUib()); + cg.generateSamplers(vs, + material.samplerBindings.getBlockOffset(BindingPoints::PER_RENDERABLE_MORPHING), + SibGenerator::getPerRenderPrimitiveMorphingSib(variantKey)); } cg.generateUniforms(vs, ShaderType::VERTEX, BindingPoints::PER_MATERIAL_INSTANCE, material.uib); @@ -310,6 +308,11 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel, auto specularAO = material.specularAOSet ? material.specularAO : defaultSpecularAO; CodeGenerator::generateDefine(fs, "SPECULAR_AMBIENT_OCCLUSION", uint32_t(specularAO)); + // Currently, we only support either screen-space refractions or reflections. + // The HAS_REFRACTION/HAS_REFLECTIONS defines signify if refraction/reflections are supported by + // the material, yet only one technique will be selected at runtime. + // TODO: support simultaneous screen-space refractions and reflections. + CodeGenerator::generateDefine(fs, "HAS_REFRACTION", material.refractionMode != RefractionMode::NONE); if (material.refractionMode != RefractionMode::NONE) { CodeGenerator::generateDefine(fs, "REFRACTION_MODE_CUBEMAP", uint32_t(RefractionMode::CUBEMAP)); @@ -337,6 +340,14 @@ std::string ShaderGenerator::createFragmentProgram(ShaderModel shaderModel, } } + CodeGenerator::generateDefine(fs, "HAS_REFLECTIONS", material.reflectionMode != ReflectionMode::DEFAULT); + if (material.reflectionMode != ReflectionMode::DEFAULT) { + CodeGenerator::generateDefine(fs, "REFLECTION_MODE_SCREEN_SPACE", uint32_t(ReflectionMode::SCREEN_SPACE)); + if (material.reflectionMode == ReflectionMode::SCREEN_SPACE) { + CodeGenerator::generateDefine(fs, "REFLECTION_MODE", "REFLECTION_MODE_SCREEN_SPACE"); + } + } + bool multiBounceAO = material.multiBounceAOSet ? material.multiBounceAO : !isMobileTarget(shaderModel); CodeGenerator::generateDefine(fs, "MULTI_BOUNCE_AMBIENT_OCCLUSION", multiBounceAO ? 1u : 0u); diff --git a/libs/gltfio/include/gltfio/FilamentAsset.h b/libs/gltfio/include/gltfio/FilamentAsset.h index 87daa3c38e..2b0a65863b 100644 --- a/libs/gltfio/include/gltfio/FilamentAsset.h +++ b/libs/gltfio/include/gltfio/FilamentAsset.h @@ -203,6 +203,16 @@ public: */ Animator* getAnimator() noexcept; + /** + * Updates the morphing weights in the given entity. + */ + void setMorphWeights(utils::Entity entity, const float* weights, size_t count); + + /** + * Gets the number of morphing in the given entity. + */ + int getMorphTargetCount(utils::Entity entity) noexcept; + /** * Lazily creates a single LINES renderable that draws the transformed bounding-box hierarchy * for diagnostic purposes. The wireframe is owned by the asset so clients should not delete it. diff --git a/libs/gltfio/include/gltfio/ResourceLoader.h b/libs/gltfio/include/gltfio/ResourceLoader.h index b69a16e565..a92a4f8517 100644 --- a/libs/gltfio/include/gltfio/ResourceLoader.h +++ b/libs/gltfio/include/gltfio/ResourceLoader.h @@ -52,6 +52,10 @@ struct ResourceConfiguration { //! If true, computes the bounding boxes of all \c POSITION attibutes. Well formed glTF files //! do not need this, but it is useful for robustness. bool recomputeBoundingBoxes; + + //! If true, ignore skinned primitives bind transform when compute bounding box. Implicitly true + //! for instanced asset. Only applicable when recomputeBoundingBoxes is set to true + bool ignoreBindTransform; }; /** diff --git a/libs/gltfio/materials/gltflite.mat.in b/libs/gltfio/materials/gltflite.mat.in index ea7a9cec7a..ef91f582b8 100644 --- a/libs/gltfio/materials/gltflite.mat.in +++ b/libs/gltfio/materials/gltflite.mat.in @@ -8,6 +8,7 @@ material { flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, + reflections : screenspace, parameters : [ // Base Color diff --git a/libs/gltfio/materials/sheen.mat.in b/libs/gltfio/materials/sheen.mat.in index e621e4b966..2fc3562e57 100644 --- a/libs/gltfio/materials/sheen.mat.in +++ b/libs/gltfio/materials/sheen.mat.in @@ -9,6 +9,7 @@ material { flipUV : false, specularAmbientOcclusion : simple, specularAntiAliasing : true, + reflections : screenspace, parameters : [ // Base Color diff --git a/libs/gltfio/materials/transmission.mat.in b/libs/gltfio/materials/transmission.mat.in index 210bfe896d..0b7fd40d26 100644 --- a/libs/gltfio/materials/transmission.mat.in +++ b/libs/gltfio/materials/transmission.mat.in @@ -10,6 +10,7 @@ material { specularAntiAliasing : true, refractionMode: screenspace, refractionType: thin, + reflections: screenspace, parameters : [ // Base Color diff --git a/libs/gltfio/materials/ubershader.mat.in b/libs/gltfio/materials/ubershader.mat.in index 62fedd0ab6..e525e0b8b2 100644 --- a/libs/gltfio/materials/ubershader.mat.in +++ b/libs/gltfio/materials/ubershader.mat.in @@ -9,6 +9,7 @@ material { specularAmbientOcclusion : simple, specularAntiAliasing : true, clearCoatIorChange : false, + reflections : screenspace, parameters : [ { type : float3, name : specularFactor }, diff --git a/libs/gltfio/materials/volume.mat.in b/libs/gltfio/materials/volume.mat.in index 37baf7b233..570d41e68c 100644 --- a/libs/gltfio/materials/volume.mat.in +++ b/libs/gltfio/materials/volume.mat.in @@ -10,6 +10,7 @@ material { specularAntiAliasing : true, refractionMode: screenspace, refractionType: solid, + reflections: screenspace, parameters : [ // Base Color diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp index 4879a637fc..0102f4c682 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -176,7 +176,6 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) { mImpl->instance = instance; mImpl->renderableManager = &asset->mEngine->getRenderableManager(); mImpl->transformManager = &asset->mEngine->getTransformManager(); - mImpl->morpher = new MorphHelper(asset, instance); const cgltf_data* srcAsset = asset->mSourceAsset->hierarchy; const cgltf_animation* srcAnims = srcAsset->animations; @@ -222,6 +221,8 @@ Animator::Animator(FFilamentAsset* asset, FFilamentInstance* instance) { } } } + + mImpl->morpher = asset->mMorpher; } void Animator::addInstance(FFilamentInstance* instance) { @@ -235,7 +236,6 @@ void Animator::addInstance(FFilamentInstance* instance) { } Animator::~Animator() { - delete mImpl->morpher; delete mImpl; } @@ -461,7 +461,7 @@ void AnimatorImpl::applyAnimation(const Channel& channel, float t, size_t prevIn } } - morpher->applyWeights(channel.targetEntity, weights.data(), weights.size()); + morpher->setWeights(channel.targetEntity, weights.data(), weights.size()); return; } } diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 13ef84b3c3..6a855aa2b0 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -520,14 +521,15 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const // node weights are provided, they override the ones specified on the mesh. if (numMorphTargets > 0) { RenderableManager::Instance renderable = mRenderableManager.getInstance(entity); - float4 weights(0, 0, 0, 0); - for (cgltf_size i = 0; i < std::min(MAX_MORPH_TARGETS, mesh->weights_count); ++i) { + const auto size = std::min(MAX_MORPH_TARGETS, std::max(mesh->weights_count, node->weights_count)); + std::vector weights(size); + for (cgltf_size i = 0, c = std::min(size, mesh->weights_count); i < c; ++i) { weights[i] = mesh->weights[i]; } - for (cgltf_size i = 0; i < std::min(MAX_MORPH_TARGETS, node->weights_count); ++i) { + for (cgltf_size i = 0, c = std::min(size, node->weights_count); i < c; ++i) { weights[i] = node->weights[i]; } - mRenderableManager.setMorphWeights(renderable, weights); + mRenderableManager.setMorphWeights(renderable, weights.data(), size); } } @@ -688,15 +690,11 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out cgltf_size targetsCount = inPrim->targets_count; - // There is no need to emit a warning if there are more than 4 targets. This is only the base - // VertexBuffer and more might be created by MorphHelper. if (targetsCount > MAX_MORPH_TARGETS) { + utils::slog.w << "WARNING: Exceeded max morph target count of " << MAX_MORPH_TARGETS << utils::io::endl; targetsCount = MAX_MORPH_TARGETS; } - constexpr int baseTangentsAttr = (int) VertexAttribute::MORPH_TANGENTS_0; - constexpr int basePositionAttr = (int) VertexAttribute::MORPH_POSITION_0; - for (cgltf_size targetIndex = 0; targetIndex < targetsCount; targetIndex++) { const cgltf_morph_target& morphTarget = inPrim->targets[targetIndex]; for (cgltf_size aindex = 0; aindex < morphTarget.attributes_count; aindex++) { @@ -705,17 +703,8 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out const cgltf_attribute_type atype = attribute.type; const int morphId = targetIndex + 1; - // The glTF tangent data is ignored here, but honored in ResourceLoader. - if (atype == cgltf_attribute_type_tangent) { - continue; - } - - if (atype == cgltf_attribute_type_normal) { - VertexAttribute attr = (VertexAttribute) (baseTangentsAttr + targetIndex); - vbb.attribute(attr, slot, VertexBuffer::AttributeType::SHORT4); - vbb.normalized(attr); - outPrim->morphTangents[targetIndex] = slot; - addBufferSlot({&mResult->mGenerateTangents, atype, slot++, morphId}); + // The glTF normal and tangent data are ignored here, but honored in ResourceLoader. + if (atype == cgltf_attribute_type_normal || atype == cgltf_attribute_type_tangent) { continue; } @@ -736,13 +725,6 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive* inPrim, Primitive* out slog.e << "Unsupported accessor type in " << name << io::endl; return false; } - const int stride = (fatype == actualType) ? accessor->stride : 0; - - VertexAttribute attr = (VertexAttribute) (basePositionAttr + targetIndex); - vbb.attribute(attr, slot, fatype, 0, stride); - vbb.normalized(attr, accessor->normalized); - outPrim->morphPositions[targetIndex] = slot; - addBufferSlot({accessor, atype, slot++, morphId}); } } diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index c548d20bfc..a4061fe524 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -65,13 +65,13 @@ namespace gltfio { class Animator; class Wireframe; +class MorphHelper; // Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer(). struct BufferSlot { const cgltf_accessor* accessor; cgltf_attribute_type attribute; int bufferIndex; // for vertex buffers only - int morphTarget; // 0 if no morphing, otherwise 1-based index filament::VertexBuffer* vertexBuffer; filament::IndexBuffer* indexBuffer; }; @@ -97,8 +97,6 @@ struct Primitive { filament::IndexBuffer* indices = nullptr; filament::Aabb aabb; // object-space bounding box UvMap uvmap; // mapping from each glTF UV set to either UV0 or UV1 (8 bytes) - uint8_t morphPositions[4] = {}; // Buffer indices for MORPH_POSITION_0, MORPH_POSITION_1 etc. - uint8_t morphTangents[4] = {}; // Buffer indices for MORPH_TANGENTS_0, MORPH_TANGENTS_1, etc. }; using MeshCache = tsl::robin_map>; @@ -194,6 +192,10 @@ struct FFilamentAsset : public FilamentAsset { Animator* getAnimator() noexcept; + void setMorphWeights(utils::Entity entity , const float* weights, size_t count) noexcept; + + int getMorphTargetCount(utils::Entity entity) noexcept; + utils::Entity getWireframe() noexcept; filament::Engine* getEngine() const noexcept { @@ -243,6 +245,7 @@ struct FFilamentAsset : public FilamentAsset { std::vector mInstances; SkinVector mSkins; // unused for instanced assets Animator* mAnimator = nullptr; + MorphHelper* mMorpher = nullptr; Wireframe* mWireframe = nullptr; bool mResourcesLoaded = false; DependencyGraph mDependencyGraph; diff --git a/libs/gltfio/src/FilamentAsset.cpp b/libs/gltfio/src/FilamentAsset.cpp index ed6a9a25ef..843a5aec2b 100644 --- a/libs/gltfio/src/FilamentAsset.cpp +++ b/libs/gltfio/src/FilamentAsset.cpp @@ -22,6 +22,7 @@ #include #include +#include "MorphHelper.h" #include "Wireframe.h" using namespace filament; @@ -41,6 +42,7 @@ FFilamentAsset::~FFilamentAsset() { delete mAnimator; delete mWireframe; + delete mMorpher; mEngine->destroy(mRoot); mEntityManager->destroy(mRoot); @@ -95,6 +97,19 @@ Animator* FFilamentAsset::getAnimator() noexcept { return mAnimator; } +void FFilamentAsset::setMorphWeights(utils::Entity entity, const float* weights, size_t count) noexcept { + if (mResourcesLoaded) { + mMorpher->setWeights(entity, weights, count); + } +} + +int FFilamentAsset::getMorphTargetCount(utils::Entity entity) noexcept { + if (mResourcesLoaded) { + return mMorpher->getTargetCount(entity); + } + return 0; +} + Entity FFilamentAsset::getWireframe() noexcept { if (!mWireframe) { mWireframe = new Wireframe(this); @@ -274,6 +289,14 @@ Animator* FilamentAsset::getAnimator() noexcept { return upcast(this)->getAnimator(); } +void FilamentAsset::setMorphWeights(utils::Entity entity, const float* weights, size_t count) { + return upcast(this)->setMorphWeights(entity, weights, count); +} + +int FilamentAsset::getMorphTargetCount(utils::Entity entity) noexcept { + return upcast(this)->getMorphTargetCount(entity); +} + Entity FilamentAsset::getWireframe() noexcept { return upcast(this)->getWireframe(); } diff --git a/libs/gltfio/src/MaterialGenerator.cpp b/libs/gltfio/src/MaterialGenerator.cpp index 2f6aea933b..c832fe3893 100644 --- a/libs/gltfio/src/MaterialGenerator.cpp +++ b/libs/gltfio/src/MaterialGenerator.cpp @@ -324,6 +324,7 @@ static Material* createMaterial(Engine* engine, const MaterialKey& config, const .transparencyMode(config.doubleSided ? MaterialBuilder::TransparencyMode::TWO_PASSES_TWO_SIDES : MaterialBuilder::TransparencyMode::DEFAULT) + .reflectionMode(MaterialBuilder::ReflectionMode::SCREEN_SPACE) .targetApi(filamat::targetApiFromBackend(engine->getBackend())); if (!optimizeShaders) { diff --git a/libs/gltfio/src/MorphHelper.cpp b/libs/gltfio/src/MorphHelper.cpp index b2680607f7..abad68525c 100644 --- a/libs/gltfio/src/MorphHelper.cpp +++ b/libs/gltfio/src/MorphHelper.cpp @@ -16,11 +16,12 @@ #include "MorphHelper.h" -#include #include #include +#include +#include +#include -#include "GltfEnums.h" #include "TangentsJob.h" using namespace filament; @@ -45,14 +46,6 @@ static bool isSubsetOf(ubyte4 a, ubyte4 b) { return true; } -static int indexOf(int a, ubyte4 b) { - if (a == b.x) return 0; - if (a == b.y) return 1; - if (a == b.z) return 2; - if (a == b.w) return 3; - return -1; -} - MorphHelper::MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst) : mAsset(asset), mInstance(inst) { NodeMap& sourceNodes = asset->isInstanced() ? asset->mInstances[0]->nodeMap : asset->mNodeMap; @@ -62,7 +55,7 @@ MorphHelper::MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst) : mAsse if (mesh) { cgltf_primitive const* prims = mesh->primitives; for (cgltf_size pi = 0, count = mesh->primitives_count; pi < count; ++pi) { - addPrimitive(mesh, pi, &mMorphTable[pair.second]); + addPrimitive(mesh, pi, pair.second); } } } @@ -72,96 +65,50 @@ MorphHelper::~MorphHelper() { auto engine = mAsset->mEngine; for (auto& entry : mMorphTable) { for (auto& prim : entry.second.primitives) { - for (auto& target : prim.targets) { - engine->destroy(target.bufferObject); + if (prim.targets) { + engine->destroy(prim.targets); } } } } -void MorphHelper::applyWeights(Entity entity, float const* weights, size_t count) noexcept { +void MorphHelper::setWeights(Entity entity, float const* weights, int count) noexcept { auto& engine = *mAsset->mEngine; - auto renderableManager = &engine.getRenderableManager(); - auto renderable = renderableManager->getInstance(entity); + auto& rcm = engine.getRenderableManager(); + rcm.setMorphWeights(rcm.getInstance(entity), weights, count); +} - // If there are 4 or fewer targets, we can simply re-use the original VertexBuffer. - if (count <= 4) { - float4 vec{}; - for (size_t i = 0; i < count; i++) { - vec[i] = weights[i]; - } - renderableManager->setMorphWeights(renderable, vec); - return; - } - - // We honor up to 255 weights because our set representation is a 4-tuple of bytes, with one - // slot reserved for a sentinel value. Note that 255 is much more than the glTF min spec of 4. - count = std::min(count, size_t(255)); - - // Make a copy of the weights because we want to re-order them. - auto& sorted = mPartiallySortedWeights; - sorted.clear(); - sorted.insert(sorted.begin(), weights, weights + count); - - // Find the four highest weights in O(n) by doing a partial sort. - std::nth_element(sorted.begin(), sorted.begin() + 4, sorted.end(), [](float a, float b) { - return a > b; - }); - - // Find the "primary indices" which are the indices of the four highest weights. This is O(n). - ubyte4 primaryIndices = {kUnused, kUnused, kUnused, kUnused}; - for (size_t index = 0, primary = 0; index < count && primary < 4; ++index) { - const float w = weights[index]; - if (w > 0 && (w == sorted[0] || w == sorted[1] || w == sorted[2] || w == sorted[3])) { - primaryIndices[primary++] = index; +int MorphHelper::getTargetCount(Entity entity) const noexcept { + if (mMorphTable.count(entity)) { + auto& primitive = mMorphTable.at(entity).primitives; + if (!primitive.empty() && primitive[0].targets) { + return primitive[0].targets->getCount(); } } - - // Swap out the buffer objects for the primary indices. - for (const auto& prim : mMorphTable[entity].primitives) { - for (const auto& target : prim.targets) { - const int index = indexOf(target.morphTargetIndex, primaryIndices); - if (index > -1) { - assert_invariant(primaryIndices[index] == target.morphTargetIndex); - VertexBuffer* vb = prim.vertexBuffer; - const int bufferObjectSlot = target.type == cgltf_attribute_type_position ? - prim.positions[index] : prim.tangents[index]; - - // Slot 0 is always used for the base position so if it's getting clobbered, then - // something is seriously wrong. Should never occur, assert just in case. - assert_invariant(bufferObjectSlot != 0); - - vb->setBufferObjectAt(engine, bufferObjectSlot, target.bufferObject); - - // Do not break out early because there could be more than one target entry for this - // particular target index (e.g. positions + tangents). - } - } - } - - // Finally, set the 4-tuple uniform for the weight values by derefing the primary indices. - // Note that we first create a "safe set" by replacing the unused sentinel with zero. - float4 highest; - for (int i = 0; i < 4; i++) { - highest[i] = (primaryIndices[i] == kUnused) ? 0 : weights[primaryIndices[i]]; - } - renderableManager->setMorphWeights(renderable, highest); + return 0; } // This method copies various morphing-related data from the FilamentAsset MeshCache primitive // (which lives in transient memory) into the MorphHelper primitive (which will stay resident). -void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry) { +void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Entity entity){ + auto& entry = mMorphTable[entity]; auto& engine = *mAsset->mEngine; const cgltf_primitive& prim = mesh->primitives[primitiveIndex]; const auto& gltfioPrim = mAsset->mMeshCache.at(mesh)[primitiveIndex]; VertexBuffer* vertexBuffer = gltfioPrim.vertices; + entry.primitives.push_back({ nullptr }); - entry->primitives.push_back({ vertexBuffer }); - auto& morphHelperPrim = entry->primitives.back(); + auto& morphHelperPrim = entry.primitives.back(); - for (int i = 0; i < 4; i++) { - morphHelperPrim.positions[i] = gltfioPrim.morphPositions[i]; - morphHelperPrim.tangents[i] = gltfioPrim.morphTangents[i]; + if (prim.targets_count) { + morphHelperPrim.targets = MorphTargetBuffer::Builder() + .vertexCount(vertexBuffer->getVertexCount()) + .count(prim.targets_count) + .build(engine); + + auto& rcm = engine.getRenderableManager(); + rcm.setMorphTargetBufferAt(rcm.getInstance(entity), + primitiveIndex, morphHelperPrim.targets); } const cgltf_accessor* previous = nullptr; @@ -175,19 +122,15 @@ void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Table continue; } if (atype == cgltf_attribute_type_normal) { - // TODO: use JobSystem for this, like what we do for non-morph tangents. TangentsJob job; TangentsJob::Params params = { .in = { &prim, targetIndex } }; TangentsJob::run(¶ms); if (params.out.results) { - const size_t size = params.out.vertexCount * sizeof(short4); - BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine); - VertexBuffer::BufferDescriptor bd(params.out.results, size, FREE_CALLBACK); - bufferObject->setBuffer(engine, std::move(bd)); - params.out.results = nullptr; - morphHelperPrim.targets.push_back({bufferObject, targetIndex, atype}); + morphHelperPrim.targets->setTangentsAt(engine, targetIndex, + params.out.results, params.out.vertexCount); + free(params.out.results); } continue; } @@ -201,19 +144,16 @@ void MorphHelper::addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Table if (accessor->buffer_view) { auto bufferData = (const uint8_t*) accessor->buffer_view->buffer->data; assert_invariant(bufferData); - const uint8_t* data = computeBindingOffset(accessor) + bufferData; - const uint32_t size = computeBindingSize(accessor); + auto* data = computeBindingOffset(accessor) + bufferData; - // This creates a copy because we don't know when the user will free the cgltf - // source data. For non-morphed vertex buffers, we use a sharing mechanism to - // prevent copies, but here we just want to keep it as simple as possible. - uint8_t* clone = (uint8_t*) malloc(size); - memcpy(clone, data, size); - - BufferObject* bufferObject = BufferObject::Builder().size(size).build(engine); - VertexBuffer::BufferDescriptor bd(clone, size, FREE_CALLBACK); - bufferObject->setBuffer(engine, std::move(bd)); - morphHelperPrim.targets.push_back({bufferObject, targetIndex, atype}); + if (accessor->type == cgltf_type_vec3) { + morphHelperPrim.targets->setPositionsAt(engine, targetIndex, + (const float3*) data, vertexBuffer->getVertexCount()); + } else { + assert_invariant(accessor->type == cgltf_type_vec4); + morphHelperPrim.targets->setPositionsAt(engine, targetIndex, + (const float4*) data, vertexBuffer->getVertexCount()); + } } } } diff --git a/libs/gltfio/src/MorphHelper.h b/libs/gltfio/src/MorphHelper.h index 18f9a6fe48..45c51ba06f 100644 --- a/libs/gltfio/src/MorphHelper.h +++ b/libs/gltfio/src/MorphHelper.h @@ -14,9 +14,14 @@ * limitations under the License. */ +#ifndef GLTFIO_MORPHHELPER_H +#define GLTFIO_MORPHHELPER_H + #include "FFilamentAsset.h" #include "FFilamentInstance.h" +#include + #include #include @@ -44,35 +49,25 @@ public: MorphHelper(FFilamentAsset* asset, FFilamentInstance* inst); ~MorphHelper(); - /** - * Picks the 4 most influential weights and applies them to the target entity. - */ - void applyWeights(Entity targetEntity, float const* weights, size_t count) noexcept; + void setWeights(Entity entity, float const* weights, int count) noexcept; + int getTargetCount(Entity entity) const noexcept; private: - struct GltfTarget { - filament::BufferObject* bufferObject; - int morphTargetIndex; - cgltf_attribute_type type; - }; - struct GltfPrimitive { - filament::VertexBuffer* vertexBuffer; - uint8_t positions[4]; - uint8_t tangents[4]; - std::vector targets; // TODO: flatten this? + filament::MorphTargetBuffer* targets; }; struct TableEntry { std::vector primitives; // TODO: flatten this? }; - void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, TableEntry* entry); + void addPrimitive(cgltf_mesh const* mesh, int primitiveIndex, Entity entity); - std::vector mPartiallySortedWeights; tsl::robin_map mMorphTable; const FFilamentAsset* mAsset; const FFilamentInstance* mInstance; }; } // namespace gltfio + +#endif // GLTFIO_MORPHHELPER_H diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 179bea3e06..e98785f205 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -20,6 +20,7 @@ #include "GltfEnums.h" #include "FFilamentAsset.h" #include "TangentsJob.h" +#include "MorphHelper.h" #include "upcast.h" #include @@ -86,13 +87,19 @@ struct ResourceLoader::Impl { mEngine = config.engine; mNormalizeSkinningWeights = config.normalizeSkinningWeights; mRecomputeBoundingBoxes = config.recomputeBoundingBoxes; + mIgnoreBindTransform = config.ignoreBindTransform; } Engine* mEngine; bool mNormalizeSkinningWeights; bool mRecomputeBoundingBoxes; + bool mIgnoreBindTransform; std::string mGltfPath; + // This is used to calculate skinIndex when updateBoundingBoxes, so that the correspondency between + // cgltf_node* and FFilamentInstance::Skin can be retrieved. This pointer doesn't need to be freed. + cgltf_skin* cgltfSkinBaseAddress; + // User-provided resource data with URI string keys, populated with addResourceData(). // This is used on platforms without traditional file systems, such as Android, iOS, and WebGL. UriDataCache mUriDataCache; @@ -458,6 +465,11 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) { } if (pImpl->mRecomputeBoundingBoxes) { + // asset->mSkins is unused for instanced assets + if (!pImpl->mIgnoreBindTransform) { + pImpl->mIgnoreBindTransform = asset->isInstanced(); + } + pImpl->cgltfSkinBaseAddress = &gltf->skins[0]; updateBoundingBoxes(asset); } @@ -861,15 +873,10 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { // Collect all TANGENT vertex attribute slots that need to be populated. tsl::robin_map baseTangents; - tsl::robin_map morphTangents[4]; for (auto slot : asset->mBufferSlots) { if (slot.accessor != kGenerateTangents && slot.accessor != kGenerateNormals) { continue; } - if (slot.morphTarget) { - morphTangents[slot.morphTarget - 1][slot.vertexBuffer] = slot.bufferIndex; - continue; - } baseTangents[slot.vertexBuffer] = slot.bufferIndex; } @@ -885,13 +892,6 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { if (iter != baseTangents.end()) { jobParams.emplace_back(Params {{ pair.first }, {vb, iter->second }}); } - for (int morphTarget = 0; morphTarget < 4; morphTarget++) { - const auto& tangents = morphTangents[morphTarget]; - auto iter = tangents.find(vb); - if (iter != tangents.end()) { - jobParams.emplace_back(Params {{ pair.first, morphTarget }, {vb, iter->second }}); - } - } } // Kick off jobs for computing tangent frames. @@ -912,6 +912,8 @@ void ResourceLoader::Impl::computeTangents(FFilamentAsset* asset) { params.out.results, bo->getByteCount(), FREE_CALLBACK)); params.context.vb->setBufferObjectAt(*mEngine, params.context.slot, bo); } + + asset->mMorpher = new MorphHelper(asset, nullptr); } ResourceLoader::Impl::~Impl() { @@ -1005,27 +1007,99 @@ void ResourceLoader::updateBoundingBoxes(FFilamentAsset* asset) const { *result = aabb; }; - // Collect all mesh primitives that we wish to find bounds for. - std::vector prims; + const size_t posAttrSize = cgltf_num_components(cgltf_type_vec3); + const size_t skinningAttrSize = cgltf_num_components(cgltf_type_vec4); + auto computeBoundingBoxSkinned = [&](const cgltf_primitive* prim, const Skin* skin, Aabb* result) { + Aabb aabb; + std::vector inverseGlobalTransforms(skin->targets.size()); + for (size_t i = 0; i < skin->targets.size(); i++) { + auto xformable = tm.getInstance(skin->targets[i]); + if (xformable) { + inverseGlobalTransforms[i] = inverse(tm.getWorldTransform(xformable)); + } + } + std::vector verts; + std::vector rawJoints; + std::vector weights; + for (cgltf_size slot = 0; slot < prim->attributes_count; slot++) { + const cgltf_attribute& attr = prim->attributes[slot]; + const cgltf_accessor* accessor = attr.data; + if (attr.type == cgltf_attribute_type_position && cgltf_num_components(accessor->type) >= posAttrSize) { + verts.resize(accessor->count * posAttrSize); + cgltf_accessor_unpack_floats(accessor, &verts[0], accessor->count * posAttrSize); + } + if (attr.type == cgltf_attribute_type_joints && cgltf_num_components(accessor->type) >= skinningAttrSize) { + rawJoints.resize(accessor->count * skinningAttrSize); + cgltf_accessor_unpack_floats(accessor, &rawJoints[0], accessor->count * skinningAttrSize); + } + if (attr.type == cgltf_attribute_type_weights && cgltf_num_components(accessor->type) >= skinningAttrSize) { + weights.resize(accessor->count * skinningAttrSize); + cgltf_accessor_unpack_floats(accessor, &weights[0], accessor->count * skinningAttrSize); + } + } + std::vector jointIndices(rawJoints.begin(), rawJoints.end()); + auto primitiveCount = static_cast(verts.size() / posAttrSize); + for (size_t i = 0; i < primitiveCount; i++) { + float3 point(verts[posAttrSize * i], verts[posAttrSize * i + 1], verts[posAttrSize * i + 2]); + mat4f tmp = mat4f(0.0f); + for (size_t j = 0; j < skinningAttrSize; j++) { + size_t jointIndex = jointIndices[skinningAttrSize * i + j]; + float jointWeight = weights[skinningAttrSize * i + j]; + Entity jointEntity = skin->joints[jointIndex]; + mat4f globalJointTransform = tm.getWorldTransform(tm.getInstance(jointEntity)); + mat4f inverseBindMatrix = skin->inverseBindMatrices[jointIndex]; + tmp += jointWeight * globalJointTransform * inverseBindMatrix; + } + for (const auto& inverseGlobalTransform: inverseGlobalTransforms) { + mat4f skinMatrix = inverseGlobalTransform * tmp; + if (!pImpl->mNormalizeSkinningWeights) { + skinMatrix /= skinMatrix[3].w; + } + float3 skinnedPoint = (point.x * skinMatrix[0] + point.y * skinMatrix[1] + point.z * skinMatrix[2] + skinMatrix[3]).xyz; + aabb.min = min(aabb.min, skinnedPoint); + aabb.max = max(aabb.max, skinnedPoint); + } + } + *result = aabb; + }; + + // Collect all mesh primitives that we wish to find bounds for. For each mesh primitive, we also + // collect the skin it bound to (nullptr if not skinned) for bounds computation. + std::vector> primitives; for (auto iter : nodeMap) { - const cgltf_mesh* mesh = iter.first->mesh; - if (mesh) { - for (cgltf_size index = 0, nprims = mesh->primitives_count; index < nprims; ++index) { - prims.push_back(&mesh->primitives[index]); + const Skin* skin = nullptr; + cgltf_skin* const cgltfSkin = iter.first->skin; + if (cgltfSkin) { + // importSkins unpacked cgltfSkin into FFilamentInstance::SkinVector bijectively so that + // the unpacked Skin can be retrieved given cgltfSkin index + int skinIndex = cgltfSkin - pImpl->cgltfSkinBaseAddress; + skin = &asset->mSkins[skinIndex]; + } + const cgltf_mesh* cgltfMesh = iter.first->mesh; + if (cgltfMesh) { + for (cgltf_size index = 0, nprims = cgltfMesh->primitives_count; index < nprims; ++index) { + primitives.push_back({&cgltfMesh->primitives[index], skin}); } } } // Kick off a bounding box job for every primitive. - std::vector bounds(prims.size()); + std::vector bounds(primitives.size()); JobSystem* js = &pImpl->mEngine->getJobSystem(); JobSystem::Job* parent = js->createJob(); - for (size_t i = 0; i < prims.size(); ++i) { - cgltf_primitive const* prim = prims[i]; + for (size_t i = 0; i < primitives.size(); ++i) { Aabb* result = &bounds[i]; - js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] { - computeBoundingBox(prim, result); - })); + if (pImpl->mIgnoreBindTransform || !primitives[i].second) { + cgltf_primitive const* prim = primitives[i].first; + js->run(jobs::createJob(*js, parent, [prim, result, computeBoundingBox] { + computeBoundingBox(prim, result); + })); + } else { + std::pair skinnedPrimitive = primitives[i]; + js->run(jobs::createJob(*js, parent, [skinnedPrimitive, result, computeBoundingBoxSkinned] { + computeBoundingBoxSkinned(skinnedPrimitive.first, skinnedPrimitive.second, result); + })); + } } js->runAndWait(parent); diff --git a/libs/gltfio/src/UbershaderLoader.cpp b/libs/gltfio/src/UbershaderLoader.cpp index c7874462c1..b9323b2b88 100644 --- a/libs/gltfio/src/UbershaderLoader.cpp +++ b/libs/gltfio/src/UbershaderLoader.cpp @@ -131,7 +131,7 @@ Material* UbershaderLoader::getMaterial(const MaterialKey& config) const { case MATINDEX(LIT, AlphaMode::OPAQUE, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_OPAQUE); break; #endif - #if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_BLEND_DATA) + #if !GLTFIO_LITE || defined(GLTFRESOURCES_LITE_LIT_FADE_DATA) case MATINDEX(LIT, AlphaMode::BLEND, false, false, false): mMaterials[matindex] = CREATE_MATERIAL(LIT_FADE); break; #endif diff --git a/libs/math/include/math/TVecHelpers.h b/libs/math/include/math/TVecHelpers.h index 43b346381b..f213dc714d 100644 --- a/libs/math/include/math/TVecHelpers.h +++ b/libs/math/include/math/TVecHelpers.h @@ -477,6 +477,13 @@ private: return v; } + friend inline VECTOR MATH_PURE sign(VECTOR v) { + for (size_t i = 0; i < v.size(); i++) { + v[i] = std::copysign(T(1), v[i]); + } + return v; + } + friend inline VECTOR MATH_PURE pow(VECTOR v, T p) { for (size_t i = 0; i < v.size(); i++) { v[i] = std::pow(v[i], p); diff --git a/libs/utils/CMakeLists.txt b/libs/utils/CMakeLists.txt index 468dd51fdd..f2cb243566 100644 --- a/libs/utils/CMakeLists.txt +++ b/libs/utils/CMakeLists.txt @@ -73,6 +73,9 @@ set(SRCS if (WIN32) list(APPEND SRCS src/win32/Path.cpp) endif() +if (ANDROID) + list(APPEND SRCS src/android/ThermalManager.cpp) +endif() if (LINUX OR ANDROID) list(APPEND SRCS src/linux/Condition.cpp) list(APPEND SRCS src/linux/Mutex.cpp) diff --git a/libs/utils/include/utils/ThermalManager.h b/libs/utils/include/utils/ThermalManager.h new file mode 100644 index 0000000000..fc38d88edf --- /dev/null +++ b/libs/utils/include/utils/ThermalManager.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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_UTILS_THERMALMANAGER_H +#define TNT_UTILS_THERMALMANAGER_H + +#if defined(__ANDROID__) +#include +#else +#include +#endif + +#endif // TNT_UTILS_THERMALMANAGER_H diff --git a/libs/utils/include/utils/android/ThermalManager.h b/libs/utils/include/utils/android/ThermalManager.h new file mode 100644 index 0000000000..6c303b04bb --- /dev/null +++ b/libs/utils/include/utils/android/ThermalManager.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 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_UTILS_ANDROID_THERMALMANAGER_H +#define TNT_UTILS_ANDROID_THERMALMANAGER_H + +#include + +#include + +struct AThermalManager; + +namespace utils { + +class ThermalManager { +public: + enum class ThermalStatus : int8_t { + ERROR = -1, + NONE, + LIGHT, + MODERATE, + SEVERE, + CRITICAL, + EMERGENCY, + SHUTDOWN + }; + + ThermalManager(); + ~ThermalManager(); + + // Movable + ThermalManager(ThermalManager&& rhs) noexcept; + ThermalManager& operator=(ThermalManager&& rhs) noexcept; + + // not copiable + ThermalManager(ThermalManager const& rhs) = delete; + ThermalManager& operator=(ThermalManager const& rhs) = delete; + + ThermalStatus getCurrentThermalStatus() const noexcept; + +private: + AThermalManager* mThermalManager = nullptr; +}; + +} // namespace utils + +#endif // TNT_UTILS_ANDROID_THERMALMANAGER_H diff --git a/libs/utils/include/utils/generic/ThermalManager.h b/libs/utils/include/utils/generic/ThermalManager.h new file mode 100644 index 0000000000..2d0088e561 --- /dev/null +++ b/libs/utils/include/utils/generic/ThermalManager.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 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_UTILS_GENERIC_THERMALMANAGER_H +#define TNT_UTILS_GENERIC_THERMALMANAGER_H + +#include + +#include + +namespace utils { + +class ThermalManager { +public: + enum class ThermalStatus : int8_t { + ERROR = -1, + NONE, + LIGHT, + MODERATE, + SEVERE, + CRITICAL, + EMERGENCY, + SHUTDOWN + }; + + ThermalManager() = default; + + // Movable + ThermalManager(ThermalManager&& rhs) noexcept = default; + ThermalManager& operator=(ThermalManager&& rhs) noexcept = default; + + // not copiable + ThermalManager(ThermalManager const& rhs) = delete; + ThermalManager& operator=(ThermalManager const& rhs) = delete; + + ThermalStatus getCurrentThermalStatus() const noexcept { + return ThermalStatus::NONE; + } +}; + +} // namespace utils + +#endif // TNT_UTILS_GENERIC_THERMALMANAGER_H diff --git a/libs/utils/include/utils/ostream.h b/libs/utils/include/utils/ostream.h index 4d1a9b9035..1c5e26029d 100644 --- a/libs/utils/include/utils/ostream.h +++ b/libs/utils/include/utils/ostream.h @@ -17,13 +17,14 @@ #ifndef TNT_UTILS_OSTREAM_H #define TNT_UTILS_OSTREAM_H +#include #include +#include #include #include // ssize_t is a POSIX type. -namespace utils { -namespace io { +namespace utils::io { class UTILS_PUBLIC ostream { public: @@ -70,19 +71,27 @@ protected: Buffer(const Buffer&) = delete; Buffer& operator=(const Buffer&) = delete; + const char* get() const noexcept { return buffer; } + + std::pair grow(size_t s) noexcept; + void advance(ssize_t n) noexcept; + void reset() noexcept; + + private: + void reserve(size_t newSize) noexcept; + char* buffer = nullptr; // buffer address char* curr = nullptr; // current pointer size_t size = 0; // size remaining size_t capacity = 0; // total capacity of the buffer - const char* get() const noexcept { return buffer; } - void advance(ssize_t n) noexcept; - void reset() noexcept; - void reserve(size_t newSize) noexcept; }; + std::mutex mLock; Buffer mData; Buffer& getBuffer() noexcept { return mData; } + ostream& print(const char* format, ...) noexcept; + private: virtual ostream& flush() noexcept = 0; @@ -96,13 +105,9 @@ private: LONG_DOUBLE }; - bool mShowHex = false; - const char* getFormat(type t) const noexcept; + inline const char* getFormat(type t) const noexcept; - /* - * Checks that the buffer has room for s additional bytes, growing the allocation if necessary. - */ - void growBufferIfNeeded(size_t s) noexcept; + bool mShowHex = false; }; // handles std::string @@ -129,8 +134,6 @@ inline ostream& dec(ostream& s) noexcept { return s.dec(); } inline ostream& endl(ostream& s) noexcept { s << "\n"; return s.flush(); } inline ostream& flush(ostream& s) noexcept { return s.flush(); } -} // namespace io - -} // namespace utils +} // namespace utils::io #endif // TNT_UTILS_OSTREAM_H diff --git a/libs/utils/src/Log.cpp b/libs/utils/src/Log.cpp index 4d3234193d..321707672a 100644 --- a/libs/utils/src/Log.cpp +++ b/libs/utils/src/Log.cpp @@ -46,6 +46,7 @@ private: }; ostream& LogStream::flush() noexcept { + std::lock_guard lock(mLock); Buffer& buf = getBuffer(); #ifdef __ANDROID__ switch (mPriority) { @@ -86,6 +87,14 @@ ostream& LogStream::flush() noexcept { return *this; } + +/* + * We can't use thread_local because on Android we're currently using several dynamic libraries + * including this .o (via libutils.a), which violates the ODR and ends-up with only one of + * the thread_local instance initialized. + * For this reason, ostream is protected by a mutex instead. + */ + static LogStream cout(LogStream::Priority::LOG_DEBUG); static LogStream cerr(LogStream::Priority::LOG_ERROR); static LogStream cwarn(LogStream::Priority::LOG_WARNING); diff --git a/libs/utils/src/android/ThermalManager.cpp b/libs/utils/src/android/ThermalManager.cpp new file mode 100644 index 0000000000..6009abc927 --- /dev/null +++ b/libs/utils/src/android/ThermalManager.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 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. + */ + +#include + +#include + +#include + +namespace utils { + +ThermalManager::ThermalManager() { + if (__builtin_available(android 30, *)) { + mThermalManager = AThermal_acquireManager(); + } +} + +ThermalManager::~ThermalManager() { + if (__builtin_available(android 30, *)) { + AThermal_releaseManager(mThermalManager); + } +} + +ThermalManager::ThermalManager(ThermalManager&& rhs) noexcept + : mThermalManager(rhs.mThermalManager) { + rhs.mThermalManager = nullptr; +} + +ThermalManager& ThermalManager::operator=(ThermalManager&& rhs) noexcept { + std::swap(mThermalManager, rhs.mThermalManager); + return *this; +} + +ThermalManager::ThermalStatus ThermalManager::getCurrentThermalStatus() const noexcept { + if (__builtin_available(android 30, *)) { + return (ThermalManager::ThermalStatus)AThermal_getCurrentThermalStatus(mThermalManager); + } else { + return ThermalStatus::NONE; + } +} + +} // namespace utils diff --git a/libs/utils/src/ostream.cpp b/libs/utils/src/ostream.cpp index 5eeaf67f2c..ec154a8824 100644 --- a/libs/utils/src/ostream.cpp +++ b/libs/utils/src/ostream.cpp @@ -18,11 +18,10 @@ #include #include +#include #include -namespace utils { - -namespace io { +namespace utils::io { ostream::~ostream() = default; @@ -35,7 +34,6 @@ ostream::Buffer::~Buffer() noexcept { free(buffer); } -UTILS_NOINLINE void ostream::Buffer::advance(ssize_t n) noexcept { if (n > 0) { size_t written = n < size ? size_t(n) : size; @@ -44,7 +42,6 @@ void ostream::Buffer::advance(ssize_t n) noexcept { } } -UTILS_NOINLINE void ostream::Buffer::reserve(size_t newSize) noexcept { size_t offset = curr - buffer; if (buffer == nullptr) { @@ -58,7 +55,6 @@ void ostream::Buffer::reserve(size_t newSize) noexcept { size = capacity - offset; } -UTILS_NOINLINE void ostream::Buffer::reset() noexcept { // aggressively shrink the buffer if (capacity > 1024) { @@ -70,6 +66,17 @@ void ostream::Buffer::reset() noexcept { size = capacity; } + +std::pair ostream::Buffer::grow(size_t s) noexcept { + if (UTILS_UNLIKELY(size < s)) { + size_t used = curr - buffer; + size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum + reserve(newCapacity); + assert(size >= s); + } + return { curr, size }; +} + const char* ostream::getFormat(ostream::type t) const noexcept { switch (t) { case type::SHORT: return mShowHex ? "0x%hx" : "%hd"; @@ -87,94 +94,86 @@ const char* ostream::getFormat(ostream::type t) const noexcept { } } -void ostream::growBufferIfNeeded(size_t s) noexcept { - Buffer& buf = getBuffer(); - if (UTILS_UNLIKELY(buf.size < s)) { - size_t used = buf.curr - buf.buffer; - size_t newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum - buf.reserve(newCapacity); - assert(buf.size >= s); +UTILS_NOINLINE +ostream& ostream::print(const char* format, ...) noexcept { + va_list args0; + va_list args1; + + // figure out how much size to we need + va_start(args0, format); + va_copy(args1, args0); + ssize_t s = vsnprintf(nullptr, 0, format, args0); + va_end(args0); + + + { // scope for the lock + std::lock_guard lock(mLock); + + Buffer& buf = getBuffer(); + + // grow the buffer to the needed size + auto[curr, size] = buf.grow(s + 1); // +1 to include the null-terminator + + // print into the buffer + vsnprintf(curr, size, format, args1); + + // advance the buffer + buf.advance(s); } + + va_end(args1); + + return *this; } ostream& ostream::operator<<(short value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::SHORT), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::SHORT), value)); - return *this; + const char* format = getFormat(type::SHORT); + return print(format, value); } ostream& ostream::operator<<(unsigned short value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::USHORT), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::USHORT), value)); - return *this; + const char* format = getFormat(type::USHORT); + return print(format, value); } ostream& ostream::operator<<(char value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::CHAR), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::CHAR), value)); - return *this; + const char* format = getFormat(type::CHAR); + return print(format, value); } ostream& ostream::operator<<(unsigned char value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::UCHAR), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UCHAR), value)); - return *this; + const char* format = getFormat(type::UCHAR); + return print(format, value); } ostream& ostream::operator<<(int value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::INT), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::INT), value)); - return *this; + const char* format = getFormat(type::INT); + return print(format, value); } ostream& ostream::operator<<(unsigned int value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::UINT), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::UINT), value)); - return *this; + const char* format = getFormat(type::UINT); + return print(format, value); } ostream& ostream::operator<<(long value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::LONG), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG), value)); - return *this; + const char* format = getFormat(type::LONG); + return print(format, value); } ostream& ostream::operator<<(unsigned long value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::ULONG), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG), value)); - return *this; + const char* format = getFormat(type::ULONG); + return print(format, value); } ostream& ostream::operator<<(long long value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::LONG_LONG), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_LONG), value)); - return *this; + const char* format = getFormat(type::LONG_LONG); + return print(format, value); } ostream& ostream::operator<<(unsigned long long value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::ULONG_LONG), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::ULONG_LONG), value)); - return *this; + const char* format = getFormat(type::ULONG_LONG); + return print(format, value); } ostream& ostream::operator<<(float value) noexcept { @@ -182,19 +181,13 @@ ostream& ostream::operator<<(float value) noexcept { } ostream& ostream::operator<<(double value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::DOUBLE), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::DOUBLE), value)); - return *this; + const char* format = getFormat(type::DOUBLE); + return print(format, value); } ostream& ostream::operator<<(long double value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, getFormat(type::LONG_DOUBLE), value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, getFormat(type::LONG_DOUBLE), value)); - return *this; + const char* format = getFormat(type::LONG_DOUBLE); + return print(format, value); } ostream& ostream::operator<<(bool value) noexcept { @@ -202,27 +195,15 @@ ostream& ostream::operator<<(bool value) noexcept { } ostream& ostream::operator<<(const char* string) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, "%s", string); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, "%s", string)); - return *this; + return print("%s", string); } ostream& ostream::operator<<(const unsigned char* string) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, "%s", string); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, "%s", string)); - return *this; + return print("%s", string); } ostream& ostream::operator<<(const void* value) noexcept { - Buffer& buf = getBuffer(); - size_t s = snprintf(nullptr, 0, "%p", value); - growBufferIfNeeded(s + 1); // +1 to include the null-terminator - buf.advance(snprintf(buf.curr, buf.size, "%p", value)); - return *this; + return print("%p", value); } ostream& ostream::hex() noexcept { @@ -235,6 +216,4 @@ ostream& ostream::dec() noexcept { return *this; } -} // namespace io - -} // namespace utils +} // namespace utils::io diff --git a/libs/viewer/include/viewer/Settings.h b/libs/viewer/include/viewer/Settings.h index c17cbedbb5..248553f15a 100644 --- a/libs/viewer/include/viewer/Settings.h +++ b/libs/viewer/include/viewer/Settings.h @@ -59,6 +59,7 @@ enum class ToneMapping : uint8_t { }; using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions; +using ScreenSpaceReflectionsOptions = filament::View::ScreenSpaceReflectionsOptions; using AntiAliasing = filament::View::AntiAliasing; using BloomOptions = filament::View::BloomOptions; using DepthOfFieldOptions = filament::View::DepthOfFieldOptions; @@ -105,10 +106,9 @@ private: }; struct GenericToneMapperSettings { - float contrast = 1.585f; - float shoulder = 0.5f; + float contrast = 1.55f; float midGrayIn = 0.18f; - float midGrayOut = 0.268f; + float midGrayOut = 0.215f; float hdrMax = 10.0f; bool operator!=(const GenericToneMapperSettings &rhs) const { return !(rhs == *this); } bool operator==(const GenericToneMapperSettings &rhs) const; @@ -161,6 +161,7 @@ struct ViewSettings { // View Options (sorted) AmbientOcclusionOptions ssao; + ScreenSpaceReflectionsOptions screenSpaceReflections; BloomOptions bloom; DepthOfFieldOptions dof; DynamicResolutionOptions dsr; diff --git a/libs/viewer/include/viewer/SimpleViewer.h b/libs/viewer/include/viewer/SimpleViewer.h index f3787f2d8f..859942e231 100644 --- a/libs/viewer/include/viewer/SimpleViewer.h +++ b/libs/viewer/include/viewer/SimpleViewer.h @@ -35,6 +35,8 @@ #include #include +#include + namespace filagui { class ImGuiHelper; } @@ -241,6 +243,7 @@ private: Settings mSettings; int mSidebarWidth; uint32_t mFlags; + std::vector mMorphWeights; // 0 is the default "free camera". Additional cameras come from the gltf file (1-based index). int mCurrentCamera = 0; diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index 5f4621aa68..cd9471f149 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -336,8 +336,6 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, GenericT CHECK_KEY(tok); if (compare(tok, jsonChunk, "contrast") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->contrast); - } else if (compare(tok, jsonChunk, "shoulder") == 0) { - i = parse(tokens, i + 1, jsonChunk, &out->shoulder); } else if (compare(tok, jsonChunk, "midGrayIn") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->midGrayIn); } else if (compare(tok, jsonChunk, "midGrayOut") == 0) { @@ -467,6 +465,33 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, return i; } +static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, + ScreenSpaceReflectionsOptions* out) { + CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); + int size = tokens[i++].size; + for (int j = 0; j < size; ++j) { + const jsmntok_t tok = tokens[i]; + CHECK_KEY(tok); + if (compare(tok, jsonChunk, "enabled") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->enabled); + } else if (compare(tok, jsonChunk, "thickness") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->thickness); + } else if (compare(tok, jsonChunk, "bias") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->bias); + } else if (compare(tok, jsonChunk, "maxDistance") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->maxDistance); + } else { + slog.w << "Invalid screen-space reflections key: '" << STR(tok, jsonChunk) << "'" << io::endl; + i = parse(tokens, i + 1); + } + if (i < 0) { + slog.e << "Invalid screen-space reflections value: '" << STR(tok, jsonChunk) << "'" << io::endl; + return i; + } + } + return i; +} + static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, AmbientOcclusionOptions* out) { CHECK_TOKTYPE(tokens[i], JSMN_OBJECT); @@ -738,6 +763,8 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ViewSett i = parse(tokens, i + 1, jsonChunk, &out->colorGrading); } else if (compare(tok, jsonChunk, "ssao") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->ssao); + } else if (compare(tok, jsonChunk, "screenSpaceReflections") == 0) { + i = parse(tokens, i + 1, jsonChunk, &out->screenSpaceReflections); } else if (compare(tok, jsonChunk, "bloom") == 0) { i = parse(tokens, i + 1, jsonChunk, &out->bloom); } else if (compare(tok, jsonChunk, "fog") == 0) { @@ -1011,6 +1038,7 @@ void applySettings(const ViewSettings& settings, View* dest) { dest->setMultiSampleAntiAliasingOptions(settings.msaa); dest->setDynamicResolutionOptions(settings.dsr); dest->setAmbientOcclusionOptions(settings.ssao); + dest->setScreenSpaceReflectionsOptions(settings.screenSpaceReflections); dest->setBloomOptions(settings.bloom); dest->setFogOptions(settings.fog); dest->setDepthOfFieldOptions(settings.dof); @@ -1101,7 +1129,6 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe case ToneMapping::FILMIC: return new FilmicToneMapper; case ToneMapping::GENERIC: return new GenericToneMapper( settings.genericToneMapper.contrast, - settings.genericToneMapper.shoulder, settings.genericToneMapper.midGrayIn, settings.genericToneMapper.midGrayOut, settings.genericToneMapper.hdrMax @@ -1265,7 +1292,6 @@ static std::ostream& operator<<(std::ostream& out, const TemporalAntiAliasingOpt static std::ostream& operator<<(std::ostream& out, const GenericToneMapperSettings& in) { return out << "{\n" << "\"contrast\": " << (in.contrast) << ",\n" - << "\"shoulder\": " << (in.shoulder) << ",\n" << "\"midGrayIn\": " << (in.midGrayIn) << ",\n" << "\"midGrayOut\": " << (in.midGrayOut) << ",\n" << "\"hdrMax\": " << (in.hdrMax) << "\n" @@ -1319,6 +1345,15 @@ static std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions << "}"; } +static std::ostream& operator<<(std::ostream& out, const ScreenSpaceReflectionsOptions& in) { + return out << "{\n" + << "\"enabled\": " << to_string(in.enabled) << ",\n" + << "\"thickness\": " << in.thickness << ",\n" + << "\"bias\": " << in.bias << ",\n" + << "\"maxDistance\": " << in.maxDistance << ",\n" + << "}"; +} + static std::ostream& operator<<(std::ostream& out, const AmbientOcclusionOptions& in) { return out << "{\n" << "\"radius\": " << (in.radius) << ",\n" @@ -1521,6 +1556,7 @@ static std::ostream& operator<<(std::ostream& out, const ViewSettings& in) { << "\"dsr\": " << in.dsr << ",\n" << "\"colorGrading\": " << (in.colorGrading) << ",\n" << "\"ssao\": " << (in.ssao) << ",\n" + << "\"screenSpaceReflections\": " << (in.screenSpaceReflections) << ",\n" << "\"bloom\": " << (in.bloom) << ",\n" << "\"fog\": " << (in.fog) << ",\n" << "\"dof\": " << (in.dof) << ",\n" @@ -1544,9 +1580,8 @@ static std::ostream& operator<<(std::ostream& out, const Settings& in) { } bool GenericToneMapperSettings::operator==(const GenericToneMapperSettings &rhs) const { - static_assert(sizeof(GenericToneMapperSettings) == 20, "Please update Settings.cpp"); + static_assert(sizeof(GenericToneMapperSettings) == 16, "Please update Settings.cpp"); return contrast == rhs.contrast && - shoulder == rhs.shoulder && midGrayIn == rhs.midGrayIn && midGrayOut == rhs.midGrayOut && hdrMax == rhs.hdrMax; @@ -1555,7 +1590,7 @@ bool GenericToneMapperSettings::operator==(const GenericToneMapperSettings &rhs) bool ColorGradingSettings::operator==(const ColorGradingSettings &rhs) const { // If you had to fix the following codeline, then you likely also need to update the // implementation of operator==. - static_assert(sizeof(ColorGradingSettings) == 232, "Please update Settings.cpp"); + static_assert(sizeof(ColorGradingSettings) == 228, "Please update Settings.cpp"); return enabled == rhs.enabled && quality == rhs.quality && toneMapping == rhs.toneMapping && diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index a1eba1c066..a56f398c3c 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -34,6 +34,7 @@ #include #include +#include using namespace filagui; using namespace filament::math; @@ -137,7 +138,6 @@ static void computeToneMapPlot(ColorGradingSettings& settings, float* plot) { case ToneMapping::GENERIC: mapper = new GenericToneMapper( settings.genericToneMapper.contrast, - settings.genericToneMapper.shoulder, settings.genericToneMapper.midGrayIn, settings.genericToneMapper.midGrayOut, settings.genericToneMapper.hdrMax @@ -197,7 +197,6 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo if (ImGui::CollapsingHeader("Tonemap parameters")) { GenericToneMapperSettings& generic = colorGrading.genericToneMapper; ImGui::SliderFloat("Contrast##genericToneMapper", &generic.contrast, 1e-5f, 3.0f); - ImGui::SliderFloat("Shoulder##genericToneMapper", &generic.shoulder, 0.0f, 1.0f); ImGui::SliderFloat("Mid-gray in##genericToneMapper", &generic.midGrayIn, 0.0f, 1.0f); ImGui::SliderFloat("Mid-gray out##genericToneMapper", &generic.midGrayOut, 0.0f, 1.0f); ImGui::SliderFloat("HDR max", &generic.hdrMax, 1.0f, 64.0f); @@ -408,6 +407,13 @@ void SimpleViewer::populateScene(FilamentAsset* asset, FilamentInstance* instan mAnimator = instanceToAnimate ? instanceToAnimate->getAnimator() : asset->getAnimator(); updateRootTransform(); mScene->addEntities(asset->getLightEntities(), asset->getLightEntityCount()); + + int morphCount = 0; + for (size_t i = 0, c = asset->getEntityCount(); i < c; ++i) { + auto entity = asset->getEntities()[i]; + morphCount = std::max(morphCount, asset->getMorphTargetCount(entity)); + } + mMorphWeights.resize(std::min(morphCount, 128), 0); } auto& tcm = mEngine->getRenderableManager(); @@ -429,6 +435,7 @@ void SimpleViewer::removeAsset() { } mAsset = nullptr; mAnimator = nullptr; + mMorphWeights.clear(); } void SimpleViewer::setIndirectLight(filament::IndirectLight* ibl, @@ -579,8 +586,11 @@ void SimpleViewer::updateUserInterface() { } auto instance = rm.getInstance(entity); bool scaster = rm.isShadowCaster(instance); + bool sreceiver = rm.isShadowReceiver(instance); ImGui::Checkbox("casts shadows", &scaster); rm.setCastShadows(instance, scaster); + ImGui::Checkbox("receives shadows", &sreceiver); + rm.setReceiveShadows(instance, sreceiver); size_t numPrims = rm.getPrimitiveCount(instance); for (size_t prim = 0; prim < numPrims; ++prim) { const char* mname = rm.getMaterialInstanceAt(instance, prim)->getName(); @@ -730,6 +740,15 @@ void SimpleViewer::updateUserInterface() { ssao.ssct.sampleCount = sampleCount; } } + + ImGui::Checkbox("Screen-space reflections", &mSettings.view.screenSpaceReflections.enabled); + if (ImGui::CollapsingHeader("Screen-space reflections Options")) { + auto& ssrefl = mSettings.view.screenSpaceReflections; + ImGui::SliderFloat("Ray thickness", &ssrefl.thickness, 0.001f, 0.2f); + ImGui::SliderFloat("Bias", &ssrefl.bias, 0.001f, 0.5f); + ImGui::SliderFloat("Max distance", &ssrefl.maxDistance, 0.1, 10.0f); + ImGui::SliderFloat("Stride", &ssrefl.stride, 1.0, 10.0f); + } ImGui::Unindent(); } @@ -969,6 +988,19 @@ void SimpleViewer::updateUserInterface() { ImGui::Unindent(); } + if (!mMorphWeights.empty() && ImGui::CollapsingHeader("Morphing")) { + for (int i = 0; i != mMorphWeights.size(); ++i) { + std::stringstream ss; + ss << i + 1 << " Weight"; + std::string label = ss.str(); + ImGui::SliderFloat(label.data(), &mMorphWeights[i], 0.0f, 1.0); + } + for (size_t i = 0, c = mAsset->getEntityCount(); i != c; ++i) { + mAsset->setMorphWeights(mAsset->getEntities()[i], + mMorphWeights.data(), mMorphWeights.size()); + } + } + if (mEnableWireframe) { mScene->addEntity(mAsset->getWireframe()); } else { diff --git a/libs/viewer/tests/test_settings.cpp b/libs/viewer/tests/test_settings.cpp index 9ad89d27b2..fda3ff27e7 100644 --- a/libs/viewer/tests/test_settings.cpp +++ b/libs/viewer/tests/test_settings.cpp @@ -52,7 +52,6 @@ static const char* JSON_TEST_DEFAULTS = R"TXT( "toneMapping": "ACES_LEGACY", "genericToneMapper": { "contrast": 1.0, - "shoulder": 1.0, "midGrayIn": 1.0, "midGrayOut": 1.0, "hdrMax": 16.0 diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 60d886ebb5..140271ec56 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -85,6 +85,7 @@ struct App { gltfio::ResourceLoader* resourceLoader = nullptr; bool recomputeAabb = false; + bool ignoreBindTransform = false; bool actualSize = false; @@ -133,6 +134,8 @@ static void printUsage(char* name) { " Do not scale the model to fit into a unit cube\n\n" " --recompute-aabb, -r\n" " Ignore the min/max attributes in the glTF file\n\n" + " --ignore-bind-transform, -g\n" + " Ignore bind transform when recomputing aabb\n\n" " --settings=, -t\n" " Apply the settings in the given JSON file\n\n" " --ubershader, -u\n" @@ -161,19 +164,20 @@ static std::ifstream::pos_type getFileSize(const char* filename) { } static int handleCommandLineArguments(int argc, char* argv[], App* app) { - static constexpr const char* OPTSTR = "ha:i:usc:rt:b:ev"; + static constexpr const char* OPTSTR = "ha:i:usc:rgt:b:ev"; static const struct option OPTIONS[] = { - { "help", no_argument, nullptr, 'h' }, - { "api", required_argument, nullptr, 'a' }, - { "batch", required_argument, nullptr, 'b' }, - { "headless", no_argument, nullptr, 'e' }, - { "ibl", required_argument, nullptr, 'i' }, - { "ubershader", no_argument, nullptr, 'u' }, - { "actual-size", no_argument, nullptr, 's' }, - { "camera", required_argument, nullptr, 'c' }, - { "recompute-aabb", no_argument, nullptr, 'r' }, - { "settings", required_argument, nullptr, 't' }, - { "split-view", no_argument, nullptr, 'v' }, + { "help", no_argument, nullptr, 'h' }, + { "api", required_argument, nullptr, 'a' }, + { "batch", required_argument, nullptr, 'b' }, + { "headless", no_argument, nullptr, 'e' }, + { "ibl", required_argument, nullptr, 'i' }, + { "ubershader", no_argument, nullptr, 'u' }, + { "actual-size", no_argument, nullptr, 's' }, + { "camera", required_argument, nullptr, 'c' }, + { "recompute-aabb", no_argument, nullptr, 'r' }, + { "ignore-bind-transform", no_argument, nullptr, 'g' }, + { "settings", required_argument, nullptr, 't' }, + { "split-view", no_argument, nullptr, 'v' }, { nullptr, 0, nullptr, 0 } }; int opt; @@ -220,6 +224,9 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) { case 'r': app->recomputeAabb = true; break; + case 'g': + app->ignoreBindTransform = true; + break; case 't': app->settingsFile = arg; break; @@ -412,6 +419,7 @@ int main(int argc, char** argv) { configuration.engine = app.engine; configuration.gltfPath = gltfPath.c_str(); configuration.recomputeBoundingBoxes = app.recomputeAabb; + configuration.ignoreBindTransform = app.ignoreBindTransform; configuration.normalizeSkinningWeights = true; if (!app.resourceLoader) { app.resourceLoader = new gltfio::ResourceLoader(configuration); diff --git a/samples/rendertarget.cpp b/samples/rendertarget.cpp index 36af1ff4d2..60dc9f1416 100644 --- a/samples/rendertarget.cpp +++ b/samples/rendertarget.cpp @@ -51,11 +51,6 @@ struct Vertex { float2 uv; }; -enum class ReflectionMode { - RENDERABLES, - CAMERA, -}; - struct App { utils::Entity lightEntity; Material* meshMaterial; @@ -71,6 +66,11 @@ struct App { Scene* offscreenScene = nullptr; Camera* offscreenCamera = nullptr; + enum class ReflectionMode { + RENDERABLES, + CAMERA, + }; + ReflectionMode mode = ReflectionMode::CAMERA; Config config; @@ -105,14 +105,14 @@ static mat4f reflectionMatrix(float4 plane) { return transpose(m); } -static void setReflectionMode(App& app, ReflectionMode mode) { +static void setReflectionMode(App& app, App::ReflectionMode mode) { switch (mode) { - case ReflectionMode::RENDERABLES: + case App::ReflectionMode::RENDERABLES: app.offscreenScene->addEntity(app.reflectedMonkey); app.offscreenScene->remove(app.monkeyMesh.renderable); app.offscreenView->setFrontFaceWindingInverted(false); break; - case ReflectionMode::CAMERA: + case App::ReflectionMode::CAMERA: app.offscreenScene->addEntity(app.monkeyMesh.renderable); app.offscreenScene->remove(app.reflectedMonkey); app.offscreenView->setFrontFaceWindingInverted(true); @@ -173,9 +173,9 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) { break; case 'm': if (arg == "camera") { - app->mode = ReflectionMode::CAMERA; + app->mode = App::ReflectionMode::CAMERA; } else if (arg == "renderables") { - app->mode = ReflectionMode::RENDERABLES; + app->mode = App::ReflectionMode::RENDERABLES; } else { std::cerr << "Unrecognized mode. Must be 'camera'|'renderables'.\n"; exit(1); @@ -286,7 +286,7 @@ int main(int argc, char** argv) { rcm.setCastShadows(rcm.getInstance(app.monkeyMesh.renderable), false); scene->addEntity(app.monkeyMesh.renderable); - // Create a reflected monkey, which is used only for ReflectionMode::RENDERABLES. + // Create a reflected monkey, which is used only for App::ReflectionMode::RENDERABLES. app.reflectedMonkey = em.create(); RenderableManager::Builder(1) .boundingBox({{ -2, -2, -2 }, { 2, 2, 2 }}) @@ -362,11 +362,11 @@ int main(int argc, char** argv) { const auto cullingProjection = camera.getCullingProjectionMatrix(); app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection, camera.getNear(), camera.getCullingFar()); switch (app.mode) { - case ReflectionMode::RENDERABLES: + case App::ReflectionMode::RENDERABLES: tcm.setTransform(tcm.getInstance(app.reflectedMonkey), reflection * xform); app.offscreenCamera->setModelMatrix(model); break; - case ReflectionMode::CAMERA: + case App::ReflectionMode::CAMERA: app.offscreenCamera->setModelMatrix(reflection * model); break; } diff --git a/shaders/CMakeLists.txt b/shaders/CMakeLists.txt index 6695237285..32c4a4459d 100644 --- a/shaders/CMakeLists.txt +++ b/shaders/CMakeLists.txt @@ -29,6 +29,7 @@ set(SHADERS src/inputs.vs src/light_directional.fs src/light_indirect.fs + src/light_reflections.fs src/light_punctual.fs src/main.fs src/main.vs diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index 4ba481a8da..cde22bfee9 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -20,6 +20,15 @@ mat3 getWorldFromModelNormalMatrix() { // Attributes access //------------------------------------------------------------------------------ +/** @public-api */ +int getVertexIndex() { +#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) + return gl_VertexIndex; +#else + return gl_VertexID; +#endif +} + #if defined(HAS_SKINNING_OR_MORPHING) vec3 mulBoneNormal(vec3 n, uint i) { @@ -63,6 +72,26 @@ void skinPosition(inout vec3 p, const uvec4 ids, const vec4 weights) { + mulBoneVertex(p, ids.z) * weights.z + mulBoneVertex(p, ids.w) * weights.w; } + +#define MAX_MORPH_TARGET_BUFFER_WIDTH 2048 + +void morphPosition(inout vec4 p) { + ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); + for (uint i = 0u; i < objectUniforms.morphTargetCount; ++i) { + texcoord.z = int(i) * 2 + 0; + p += morphingUniforms.weights[i] * texelFetch(morphing_targets, texcoord, 0); + } +} + +void morphNormal(inout vec3 n) { + ivec3 texcoord = ivec3(getVertexIndex() % MAX_MORPH_TARGET_BUFFER_WIDTH, getVertexIndex() / MAX_MORPH_TARGET_BUFFER_WIDTH, 0); + for (uint i = 0u; i < objectUniforms.morphTargetCount; ++i) { + texcoord.z = int(i) * 2 + 1; + vec3 normal; + toTangentFrame(texelFetch(morphing_targets, texcoord, 0), normal); + n += morphingUniforms.weights[i].xyz * normal; + } +} #endif /** @public-api */ @@ -72,10 +101,7 @@ vec4 getPosition() { #if defined(HAS_SKINNING_OR_MORPHING) if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) { - pos += objectUniforms.morphWeights.x * mesh_custom0; - pos += objectUniforms.morphWeights.y * mesh_custom1; - pos += objectUniforms.morphWeights.z * mesh_custom2; - pos += objectUniforms.morphWeights.w * mesh_custom3; + morphPosition(pos); } if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) { @@ -112,15 +138,6 @@ vec4 getCustom6() { return mesh_custom6; } vec4 getCustom7() { return mesh_custom7; } #endif -/** @public-api */ -int getVertexIndex() { -#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) - return gl_VertexIndex; -#else - return gl_VertexID; -#endif -} - //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ diff --git a/shaders/src/light_indirect.fs b/shaders/src/light_indirect.fs index fdfd848c71..68efd64875 100644 --- a/shaders/src/light_indirect.fs +++ b/shaders/src/light_indirect.fs @@ -539,20 +539,30 @@ void applyRefraction( // which is not the case when we'll read from the screen-space buffer vec3 Ft = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance; #else - // compute the point where the ray exits the medium, if needed - vec4 p = vec4(frameUniforms.clipFromWorldMatrix * vec4(ray.position, 1.0)); - p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5); + vec3 Ft; +#if defined(HAS_REFLECTIONS) && REFLECTION_MODE == REFLECTION_MODE_SCREEN_SPACE + if (frameUniforms.ssrDistance > 0.0f) { + // if screen-space reflections is enabled, we can't read from the screen-space buffer, so + // fall back to cubemap refractions + Ft = prefilteredRadiance(ray.direction, perceptualRoughness) * frameUniforms.iblLuminance; + } else +#endif + { + // compute the point where the ray exits the medium, if needed + vec4 p = vec4(frameUniforms.clipFromWorldMatrix * vec4(ray.position, 1.0)); + p.xy = uvToRenderTargetUV(p.xy * (0.5 / p.w) + 0.5); - // perceptualRoughness to LOD - // Empirical factor to compensate for the gaussian approximation of Dggx, chosen so - // cubemap and screen-space modes match at perceptualRoughness 0.125 - // TODO: Remove this factor temporarily until we find a better solution - // This overblurs many scenes and needs a more principled approach - // float tweakedPerceptualRoughness = perceptualRoughness * 1.74; - float tweakedPerceptualRoughness = perceptualRoughness; - float lod = max(0.0, 2.0 * log2(tweakedPerceptualRoughness) + frameUniforms.refractionLodOffset); + // perceptualRoughness to LOD + // Empirical factor to compensate for the gaussian approximation of Dggx, chosen so + // cubemap and screen-space modes match at perceptualRoughness 0.125 + // TODO: Remove this factor temporarily until we find a better solution + // This overblurs many scenes and needs a more principled approach + // float tweakedPerceptualRoughness = perceptualRoughness * 1.74; + float tweakedPerceptualRoughness = perceptualRoughness; + float lod = max(0.0, 2.0 * log2(tweakedPerceptualRoughness) + frameUniforms.refractionLodOffset); - vec3 Ft = textureLod(light_ssr, p.xy, lod).rgb; + Ft = textureLod(light_ssr, p.xy, lod).rgb; + } #endif // base color changes the amount of light passing through the boundary @@ -586,13 +596,39 @@ void combineDiffuseAndSpecular( void evaluateIBL(const MaterialInputs material, const PixelParams pixel, inout vec3 color) { // specular layer vec3 Fr; + + // screen-space reflections +#if defined(HAS_REFLECTIONS) && REFLECTION_MODE == REFLECTION_MODE_SCREEN_SPACE + vec4 ssr = vec4(0.0f); + // evaluateScreenSpaceReflections will set the value of ssr if there's a hit. + // ssr.a contains the reflection's contribution. + // TODO: do we want iblLuminance to control screen-space reflections? + if (pixel.roughness <= 0.01f && frameUniforms.ssrDistance > 0.0f) { + vec3 r = getReflectedVector(pixel, shading_view, shading_normal); + evaluateScreenSpaceReflections(r, ssr); + } +#else + const vec4 ssr = vec4(0.0f); +#endif + + // If screen-space reflections are turned on and have full contribution (ssr.a == 1.0f), then we + // skip sampling the IBL down below. + #if IBL_INTEGRATION == IBL_INTEGRATION_PREFILTERED_CUBEMAP vec3 E = specularDFG(pixel); vec3 r = getReflectedVector(pixel, shading_normal); - Fr = E * prefilteredRadiance(r, pixel.perceptualRoughness); + vec3 ibl = vec3(0.0f); + if (ssr.a < 1.0f) { + ibl = E * prefilteredRadiance(r, pixel.perceptualRoughness); + } + Fr = (1.0f - ssr.a) * ibl + ssr.rgb; #elif IBL_INTEGRATION == IBL_INTEGRATION_IMPORTANCE_SAMPLING vec3 E = vec3(0.0); // TODO: fix for importance sampling - Fr = isEvaluateSpecularIBL(pixel, shading_normal, shading_view, shading_NoV); + vec3 ibl = vec3(0.0f); + if (ssr.a < 1.0f) { + ibl = isEvaluateSpecularIBL(pixel, shading_normal, shading_view, shading_NoV); + } + Fr = (1.0f - ssr.a) * ibl + ssr.rgb; #endif SSAOInterpolationCache interpolationCache; diff --git a/shaders/src/light_reflections.fs b/shaders/src/light_reflections.fs new file mode 100644 index 0000000000..a69474872f --- /dev/null +++ b/shaders/src/light_reflections.fs @@ -0,0 +1,255 @@ +//------------------------------------------------------------------------------ +// Screen-space reflections +//------------------------------------------------------------------------------ + +#if defined(HAS_REFLECTIONS) && REFLECTION_MODE == REFLECTION_MODE_SCREEN_SPACE + +// Copied from depthUtils.fs +highp float linearizeDepth(highp float depth) { + // Our far plane is at infinity, which causes a division by zero below, which in turn + // causes some issues on some GPU. We workaround it by replacing "infinity" by the closest + // value representable in a 24 bit depth buffer. + const highp float preventDiv0 = 1.0 / 16777216.0; + highp mat4 p = getViewFromClipMatrix(); + // this works with perspective and ortho projections, for a perspective projection + // this resolves to -near/depth, for an ortho projection this resolves to depth*(far - near) - far + return (depth * p[2].z + p[3].z) / max(depth * p[2].w + p[3].w, preventDiv0); +} + +// Code adapted from McGuire and Mara, Efficient GPU Screen-Space Ray Tracing, Journal of Computer +// Graphics Techniques, 2014 +// +// Copyright (c) 2014, Morgan McGuire and Michael Mara +// All rights reserved. +// +// This software is open source under the "BSD 2-clause license": +// +// Redistribution and use in source and binary forms, with or without modification, are permitted +// provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this list of +// conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +void swap(inout highp float a, inout highp float b) { + highp float temp = a; + a = b; + b = temp; +} + +highp float distanceSquared(highp vec2 a, highp vec2 b) { + a -= b; + return dot(a, a); +} + +// Note: McGuire and Mara use the "cs" prefix to stand for "camera space", equivalent to Filament's +// "view space". "cs" has been replaced with "vs" to avoid confusion. +bool traceScreenSpaceRay(const highp vec3 vsOrigin, const highp vec3 vsDirection, + const highp mat4 projectToPixelMatrix, const highp sampler2D vsZBuffer, + const float vsZThickness, const highp float nearPlaneZ, const float stride, + const float jitterFraction, const highp float maxSteps, const float maxRayTraceDistance, + out highp vec2 hitPixel, out highp vec3 vsHitPoint) { + // Clip ray to a near plane in 3D (doesn't have to be *the* near plane, although that would be a + // good idea) + highp float rayLength = ((vsOrigin.z + vsDirection.z * maxRayTraceDistance) > nearPlaneZ) ? + (nearPlaneZ - vsOrigin.z) / vsDirection.z : maxRayTraceDistance; + highp vec3 vsEndPoint = vsDirection * rayLength + vsOrigin; + + // Project into screen space + highp vec4 H0 = mulMat4x4Float3(projectToPixelMatrix, vsOrigin); + highp vec4 H1 = mulMat4x4Float3(projectToPixelMatrix, vsEndPoint); + + // There are a lot of divisions by w that can be turned into multiplications at some minor + // precision loss...and we need to interpolate these 1/w values anyway. + // + // Because the caller was required to clip to the near plane, this homogeneous division + // (projecting from 4D to 2D) is guaranteed to succeed. + highp float k0 = 1.0 / H0.w; + highp float k1 = 1.0 / H1.w; + + // Switch the original points to values that interpolate linearly in 2D + highp vec3 Q0 = vsOrigin * k0; + highp vec3 Q1 = vsEndPoint * k1; + + // Screen-space endpoints + highp vec2 P0 = H0.xy * k0; + highp vec2 P1 = H1.xy * k1; + + // TODO: + // [Optional clipping to frustum sides here] + + // Initialize to off screen + hitPixel = vec2(-1.0, -1.0); + + // If the line is degenerate, make it cover at least one pixel to avoid handling zero-pixel + // extent as a special case later + P1 += vec2((distanceSquared(P0, P1) < 0.0001) ? 0.01 : 0.0); + + highp vec2 delta = P1 - P0; + + // Permute so that the primary iteration is in x to reduce large branches later + bool permute = false; + if (abs(delta.x) < abs(delta.y)) { + // More-vertical line. Create a permutation that swaps x and y in the output + permute = true; + // Directly swizzle the inputs + delta = delta.yx; + P1 = P1.yx; + P0 = P0.yx; + } + + // From now on, "x" is the primary iteration direction and "y" is the secondary one + + float stepDirection = sign(delta.x); + highp float invdx = stepDirection / delta.x; + highp vec2 dP = vec2(stepDirection, invdx * delta.y); + + // Track the derivatives of Q and k + highp vec3 dQ = (Q1 - Q0) * invdx; + highp float dk = (k1 - k0) * invdx; + + // Scale derivatives by the desired pixel stride + dP *= stride; dQ *= stride; dk *= stride; + + // Offset the starting values by the jitter fraction + P0 += dP * jitterFraction; Q0 += dQ * jitterFraction; k0 += dk * jitterFraction; + + // Slide P from P0 to P1, (now-homogeneous) Q from Q0 to Q1, and k from k0 to k1 + highp vec3 Q = Q0; + highp float k = k0; + + // We track the ray depth at +/- 1/2 pixel to treat pixels as clip-space solid voxels. Because + // the depth at -1/2 for a given pixel will be the same as at +1/2 for the previous iteration, + // we actually only have to compute one value per iteration. + highp float prevZMaxEstimate = vsOrigin.z; + highp float stepCount = 0.0; + highp float rayZMax = prevZMaxEstimate; + highp float rayZMin = prevZMaxEstimate; + highp float sceneZMax = rayZMax + 1e4; + + // P1.x is never modified after this point, so pre-scale it by the step direction for a signed + // comparison + highp float end = P1.x * stepDirection; + + // We only advance the z field of Q in the inner loop, since Q.xy is never used until after the + // loop terminates. + + for (highp vec2 P = P0; + ((P.x * stepDirection) <= end) && + (stepCount < maxSteps) && + ((rayZMax < sceneZMax - vsZThickness) || + (rayZMin > sceneZMax)) && + (sceneZMax != 0.0); + P += dP, Q.z += dQ.z, k += dk, stepCount += 1.0) { + + hitPixel = permute ? P.yx : P; + + // The depth range that the ray covers within this loop iteration. Assume that the ray is + // moving in increasing z and swap if backwards. Because one end of the interval is shared + // between adjacent iterations, we track the previous value and then swap as needed to + // ensure correct ordering + rayZMin = prevZMaxEstimate; + + // Compute the value at 1/2 pixel into the future + rayZMax = (dQ.z * 0.5 + Q.z) / (dk * 0.5 + k); + prevZMaxEstimate = rayZMax; + if (rayZMin > rayZMax) { swap(rayZMin, rayZMax); } + + // View-space z of the background + // TODO: explore if we can avoid the linearization of the depth sample here. + sceneZMax = linearizeDepth(texelFetch(vsZBuffer, int2(hitPixel), 0).r); + } // pixel on ray + + Q.xy += dQ.xy * stepCount; + vsHitPoint = Q * (1.0 / k); + // Matches the new loop condition: + return (rayZMax >= sceneZMax - vsZThickness) && (rayZMin <= sceneZMax); +} + +// -- end "BSD 2-clause license" ------------------------------------------------------------------- + +highp mat4 scaleMatrix(const highp float x, const highp float y) { + mat4 m = mat4(1.0f); + m[0].x = x; + m[1].y = y; + m[2].z = 1.0f; + m[3].w = 1.0f; + return m; +} + +/** + * Evaluates screen-space reflections, storing a premultiplied color in Fr.rgb if there's a hit. + * r is the desired reflected vector. + * + * Fr.a is set to a value between [0, 1] representing the "opacity" of the reflection. 1.0f is full + * screen-space reflection. Values < 1.0f should be blended with the scene's IBL. Fr.rgb is + * premultiplied by Fr.a. + * + * If there is no hit, Fr is unmodified. + */ +void evaluateScreenSpaceReflections(vec3 r, inout vec4 Fr) { + vec3 wsRayDirection = r; + highp vec3 wsRayStart = shading_position + frameUniforms.ssrBias * wsRayDirection; + + // ray start/end in view space + highp vec4 vsRayStart = mulMat4x4Float3(getViewFromWorldMatrix(), wsRayStart); + highp vec4 vsRayDirection = getViewFromWorldMatrix() * vec4(wsRayDirection, 0.0); + + highp vec3 vsOrigin = vsRayStart.xyz; + highp vec3 vsDirection = vsRayDirection.xyz; + float vsZThickness = frameUniforms.ssrThickness; + highp float nearPlaneZ = -frameUniforms.nearOverFarMinusNear / frameUniforms.oneOverFarMinusNear; + float stride = frameUniforms.ssrStride; + // TODO: jitterFraction should be between 0 and 1, but anything < 1 gives banding artifacts. + const float jitterFraction = 1.0f; + float maxRayTraceDistance = frameUniforms.ssrDistance; + + highp vec2 res = vec2(textureSize(light_structure, 0).xy); + highp mat4 projectToPixelMatrix = + scaleMatrix(res.x, res.y) * + frameUniforms.ssrProjectToPixelMatrix; + + highp float maxSteps = float(max(res.x, res.y)); + + // Outputs from the traceScreenSpaceRay function. + highp vec2 hitPixel; // not currently used + highp vec3 vsHitPoint; + + if (traceScreenSpaceRay(vsOrigin, vsDirection, projectToPixelMatrix, light_structure, + vsZThickness, nearPlaneZ, stride, jitterFraction, maxSteps, + maxRayTraceDistance, hitPixel, vsHitPoint)) { + highp vec4 reprojected = frameUniforms.ssrReprojection * vec4(vsHitPoint, 1.0f); + reprojected *= (1.0 / reprojected.w); + + // Compute the screen-space reflection's contribution. + // TODO: parameterize fadeRate. + const float fadeRate = 12.0f; + + // Fade the reflections out near the edges. + vec2 edgeFactor = max(fadeRate * abs(reprojected.xy - 0.5f) - (fadeRate / 2.0f - 1.0f), 0.0f); + float edgeFade = saturate(1.0 - dot(edgeFactor, edgeFactor)); + + // Fade the reflections out near maxRayTraceDistance. + float t = distance(vsOrigin, vsHitPoint) / maxRayTraceDistance; + float distFactor = max(fadeRate * (t - 0.5f) - (fadeRate / 2.0f - 1.0f), 0.0f); + float distanceFade = saturate(1.0 - (distFactor, distFactor)); + + float fade = edgeFade * distanceFade; + Fr = vec4(textureLod(light_ssr, reprojected.xy, 0.0f).rgb * fade, fade); + } +} + +#endif // screen-space reflections diff --git a/shaders/src/main.vs b/shaders/src/main.vs index 32637d1c71..42b652a354 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -36,15 +36,7 @@ void main() { #if defined(HAS_SKINNING_OR_MORPHING) if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) { - vec3 normal0, normal1, normal2, normal3; - toTangentFrame(mesh_custom4, normal0); - toTangentFrame(mesh_custom5, normal1); - toTangentFrame(mesh_custom6, normal2); - toTangentFrame(mesh_custom7, normal3); - material.worldNormal += objectUniforms.morphWeights.x * normal0; - material.worldNormal += objectUniforms.morphWeights.y * normal1; - material.worldNormal += objectUniforms.morphWeights.z * normal2; - material.worldNormal += objectUniforms.morphWeights.w * normal3; + morphNormal(material.worldNormal); material.worldNormal = normalize(material.worldNormal); } diff --git a/shaders/src/shadowing.fs b/shaders/src/shadowing.fs index 11f80a4558..e4b02bf063 100644 --- a/shaders/src/shadowing.fs +++ b/shaders/src/shadowing.fs @@ -8,6 +8,11 @@ #define SHADOW_SAMPLING_RUNTIME_DPCF 2u #define SHADOW_SAMPLING_RUNTIME_PCSS 3u +// TODO: this should be user-settable, maybe at the material level +#define SHADOW_SAMPLING_PCF_HARD 0 +#define SHADOW_SAMPLING_PCF_LOW 1 +#define SHADOW_SAMPLING_METHOD SHADOW_SAMPLING_PCF_LOW + //------------------------------------------------------------------------------ // PCF Shadow Sampling //------------------------------------------------------------------------------ @@ -20,30 +25,70 @@ float sampleDepth(const mediump sampler2DArrayShadow map, const uint layer, return texture(map, vec4(uv, layer, saturate(depth))); } +#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD // use hardware assisted PCF -float ShadowSample_PCF(const mediump sampler2DArrayShadow map, +float ShadowSample_PCF_Hard(const mediump sampler2DArrayShadow map, const uint layer, const highp vec4 shadowPosition) { highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w); // note: shadowPosition.z is in the [1, 0] range (reversed Z) return sampleDepth(map, layer, position.xy, position.z); } +#endif -// use manual PCF -float ShadowSample_PCF(const mediump sampler2DArray shadowMap, +#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW +// use hardware assisted PCF + 3x3 gaussian filter +float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map, const uint layer, const highp vec4 shadowPosition) { highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w); // note: shadowPosition.z is in the [1, 0] range (reversed Z) - highp vec2 size = vec2(textureSize(shadowMap, 0)); + highp vec2 size = vec2(textureSize(map, 0)); + highp vec2 texelSize = vec2(1.0) / size; + + // CastaƱo, 2013, "Shadow Mapping Summary Part 1" + float depth = position.z; + + // clamp position to avoid overflows below, which cause some GPUs to abort + position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0)); + + vec2 offset = vec2(0.5); + highp vec2 uv = (position.xy * size) + offset; + highp vec2 base = (floor(uv) - offset) * texelSize; + highp vec2 st = fract(uv); + + vec2 uw = vec2(3.0 - 2.0 * st.x, 1.0 + 2.0 * st.x); + vec2 vw = vec2(3.0 - 2.0 * st.y, 1.0 + 2.0 * st.y); + + highp vec2 u = vec2((2.0 - st.x) / uw.x - 1.0, st.x / uw.y + 1.0); + highp vec2 v = vec2((2.0 - st.y) / vw.x - 1.0, st.y / vw.y + 1.0); + + u *= texelSize.x; + v *= texelSize.y; + + float sum = 0.0; + sum += uw.x * vw.x * sampleDepth(map, layer, base + vec2(u.x, v.x), depth); + sum += uw.y * vw.x * sampleDepth(map, layer, base + vec2(u.y, v.x), depth); + sum += uw.x * vw.y * sampleDepth(map, layer, base + vec2(u.x, v.y), depth); + sum += uw.y * vw.y * sampleDepth(map, layer, base + vec2(u.y, v.y), depth); + return sum * (1.0 / 16.0); +} +#endif + +// use manual PCF +float ShadowSample_PCF(const mediump sampler2DArray map, + const uint layer, const highp vec4 shadowPosition) { + highp vec3 position = shadowPosition.xyz * (1.0 / shadowPosition.w); + // note: shadowPosition.z is in the [1, 0] range (reversed Z) + highp vec2 size = vec2(textureSize(map, 0)); highp vec2 st = position.xy * size - 0.5; vec4 d; #if defined(FILAMENT_HAS_FEATURE_TEXTURE_GATHER) - d = textureGather(shadowMap, vec3(position.xy, layer), 0); // 01, 11, 10, 00 + d = textureGather(map, vec3(position.xy, layer), 0); // 01, 11, 10, 00 #else highp ivec3 tc = ivec3(st, layer); - d[0] = texelFetchOffset(shadowMap, tc, 0, ivec2(0, 1)).r; - d[1] = texelFetchOffset(shadowMap, tc, 0, ivec2(1, 1)).r; - d[2] = texelFetchOffset(shadowMap, tc, 0, ivec2(1, 0)).r; - d[3] = texelFetchOffset(shadowMap, tc, 0, ivec2(0, 0)).r; + d[0] = texelFetchOffset(map, tc, 0, ivec2(0, 1)).r; + d[1] = texelFetchOffset(map, tc, 0, ivec2(1, 1)).r; + d[2] = texelFetchOffset(map, tc, 0, ivec2(1, 0)).r; + d[3] = texelFetchOffset(map, tc, 0, ivec2(0, 0)).r; #endif vec4 pcf = step(0.0, position.zzzz - d); highp vec2 grad = fract(st); @@ -454,8 +499,11 @@ float shadow(const bool DIRECTIONAL, shadowPosition = getSpotLightSpacePosition(index, zLight); #endif } - - return ShadowSample_PCF(shadowMap, layer, shadowPosition); +#if SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_HARD + return ShadowSample_PCF_Hard(shadowMap, layer, shadowPosition); +#elif SHADOW_SAMPLING_METHOD == SHADOW_SAMPLING_PCF_LOW + return ShadowSample_PCF_Low(shadowMap, layer, shadowPosition); +#endif } // Shadow requiring a sampler2D sampler (VSM, DPCF and PCSS) diff --git a/third_party/spirv-cross/spirv_glsl.cpp b/third_party/spirv-cross/spirv_glsl.cpp index 6feac01ad0..38d7f5ef5f 100644 --- a/third_party/spirv-cross/spirv_glsl.cpp +++ b/third_party/spirv-cross/spirv_glsl.cpp @@ -176,11 +176,23 @@ void CompilerGLSL::init() const struct lconv *conv = localeconv(); if (conv && conv->decimal_point) current_locale_radix_character = *conv->decimal_point; -#elif defined(__ANDROID__) && __ANDROID_API__ < 26 - // nl_langinfo is not supported on this platform, fall back to the worse alternative. - const struct lconv *conv = localeconv(); - if (conv && conv->decimal_point) - current_locale_radix_character = *conv->decimal_point; +#elif defined(__ANDROID__) + #if __ANDROID_API__ >= 26 + // nl_langinfo is not even defined if __ANDROID_API__ is not at least 26! + if (__builtin_available(android 26, *)) { + // localeconv, the portable function is not MT safe ... + const char *decimal_point = nl_langinfo(RADIXCHAR); + if (decimal_point && *decimal_point != '\0') + current_locale_radix_character = *decimal_point; + } else + #endif + if (__builtin_available(android 21, *)) { + const struct lconv *conv = localeconv(); + if (conv && conv->decimal_point) + current_locale_radix_character = *conv->decimal_point; + } else { + // we do nothing + } #else // localeconv, the portable function is not MT safe ... const char *decimal_point = nl_langinfo(RADIXCHAR); diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 29e68a6b3f..c75a28faa4 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -659,6 +659,20 @@ static bool processRefractionMode(MaterialBuilder& builder, const JsonishValue& return true; } +static bool processReflectionMode(MaterialBuilder& builder, const JsonishValue& value) { + static const std::unordered_map strToEnum { + { "default", MaterialBuilder::ReflectionMode ::DEFAULT }, + { "screenspace", MaterialBuilder::ReflectionMode::SCREEN_SPACE }, + }; + auto jsonString = value.toJsonString(); + if (!isStringValidEnum(strToEnum, jsonString->getString())) { + return logEnumIssue("reflection_mode", *jsonString, strToEnum); + } + + builder.reflectionMode(stringToEnum(strToEnum, jsonString->getString())); + return true; +} + static bool processRefractionType(MaterialBuilder& builder, const JsonishValue& value) { static const std::unordered_map strToEnum { { "solid", MaterialBuilder::RefractionType::SOLID }, @@ -728,6 +742,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["depthCulling"] = { &processDepthCull, Type::BOOL }; mParameters["doubleSided"] = { &processDoubleSided, Type::BOOL }; mParameters["transparency"] = { &processTransparencyMode, Type::STRING }; + mParameters["reflections"] = { &processReflectionMode, Type::STRING }; mParameters["maskThreshold"] = { &processMaskThreshold, Type::NUMBER }; mParameters["shadowMultiplier"] = { &processShadowMultiplier, Type::BOOL }; mParameters["transparentShadow"] = { &processTransparentShadow, Type::BOOL }; diff --git a/web/filament-js/extensions.js b/web/filament-js/extensions.js index 1684e11b75..346ff848dd 100644 --- a/web/filament-js/extensions.js +++ b/web/filament-js/extensions.js @@ -578,7 +578,8 @@ Filament.loadClassExtensions = function() { const interval = asyncInterval || 30; const defaults = { normalizeSkinningWeights: true, - recomputeBoundingBoxes: false + recomputeBoundingBoxes: false, + ignoreBindTransform: false }; config = Object.assign(defaults, config || {}); @@ -601,7 +602,8 @@ Filament.loadClassExtensions = function() { // Construct a resource loader and start decoding after all textures are fetched. const resourceLoader = new Filament.gltfio$ResourceLoader(engine, config.normalizeSkinningWeights, - config.recomputeBoundingBoxes); + config.recomputeBoundingBoxes, + config.ignoreBindTransform); const onComplete = () => { resourceLoader.asyncBeginLoad(asset); diff --git a/web/filament-js/filament-viewer.js b/web/filament-js/filament-viewer.js index 594f6ca535..79f2f6b989 100644 --- a/web/filament-js/filament-viewer.js +++ b/web/filament-js/filament-viewer.js @@ -307,6 +307,7 @@ class FilamentViewer extends LitElement { const config = { normalizeSkinningWeights: true, recomputeBoundingBoxes: false, + ignoreBindTransform: false, asyncInterval: 30 }; @@ -333,7 +334,8 @@ class FilamentViewer extends LitElement { const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine, config.normalizeSkinningWeights, - config.recomputeBoundingBoxes); + config.recomputeBoundingBoxes, + config.ignoreBindTransform); let remaining = Object.keys(this.srcBlobResources).length; for (const name in this.srcBlobResources) { diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 9163ca7c7c..46e6b3ee55 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -992,14 +992,6 @@ export enum VertexAttribute { CUSTOM5 = 13, CUSTOM6 = 14, CUSTOM7 = 15, - MORPH_POSITION_0 = CUSTOM0, - MORPH_POSITION_1 = CUSTOM1, - MORPH_POSITION_2 = CUSTOM2, - MORPH_POSITION_3 = CUSTOM3, - MORPH_TANGENTS_0 = CUSTOM4, - MORPH_TANGENTS_1 = CUSTOM5, - MORPH_TANGENTS_2 = CUSTOM6, - MORPH_TANGENTS_3 = CUSTOM7, } export enum VertexBuffer$AttributeType { diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index f2694ecd2b..6a54edcc54 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -1045,11 +1045,10 @@ class_("RenderableManager") } self->setBones(instance, bones.data(), bones.size(), offset); }), allow_raw_pointers()) - - // NOTE: this cannot take a float4 due to a binding issue. + .function("setMorphWeights", EMBIND_LAMBDA(void, (RenderableManager* self, - RenderableManager::Instance instance, float x, float y, float z, float w), { - self->setMorphWeights(instance, {x, y, z, w}); + RenderableManager::Instance instance, float* weights, size_t count), { + self->setMorphWeights(instance, weights, count); }), allow_raw_pointers()) .function("getAxisAlignedBoundingBox", &RenderableManager::getAxisAlignedBoundingBox) @@ -1910,12 +1909,13 @@ class_("gltfio$AssetLoader") class_("gltfio$ResourceLoader") .constructor(EMBIND_LAMBDA(ResourceLoader*, (Engine* engine, bool normalizeSkinningWeights, - bool recomputeBoundingBoxes), { + bool recomputeBoundingBoxes, bool ignoreBindTransform), { return new ResourceLoader({ .engine = engine, .gltfPath = nullptr, .normalizeSkinningWeights = normalizeSkinningWeights, - .recomputeBoundingBoxes = recomputeBoundingBoxes + .recomputeBoundingBoxes = recomputeBoundingBoxes, + .ignoreBindTransform = ignoreBindTransform }); }), allow_raw_pointers()) diff --git a/web/filament-js/jsenums.cpp b/web/filament-js/jsenums.cpp index ed9d16fe55..45647ba262 100644 --- a/web/filament-js/jsenums.cpp +++ b/web/filament-js/jsenums.cpp @@ -61,15 +61,7 @@ enum_("VertexAttribute") .value("CUSTOM4", CUSTOM4) .value("CUSTOM5", CUSTOM5) .value("CUSTOM6", CUSTOM6) - .value("CUSTOM7", CUSTOM7) - .value("MORPH_POSITION_0", MORPH_POSITION_0) - .value("MORPH_POSITION_1", MORPH_POSITION_1) - .value("MORPH_POSITION_2", MORPH_POSITION_2) - .value("MORPH_POSITION_3", MORPH_POSITION_3) - .value("MORPH_TANGENTS_0", MORPH_TANGENTS_0) - .value("MORPH_TANGENTS_1", MORPH_TANGENTS_1) - .value("MORPH_TANGENTS_2", MORPH_TANGENTS_2) - .value("MORPH_TANGENTS_3", MORPH_TANGENTS_3); + .value("CUSTOM7", CUSTOM7); enum_("BufferObject$BindingType") .value("VERTEX", BufferObject::BindingType::VERTEX); diff --git a/web/filament-js/package.json b/web/filament-js/package.json index bd0ec5807c..bf1082bc9e 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.16.1", + "version": "1.17.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",