diff --git a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp index 17af1fd59e..fe837943e7 100644 --- a/android/filamat-android/src/main/cpp/MaterialBuilder.cpp +++ b/android/filamat-android/src/main/cpp/MaterialBuilder.cpp @@ -370,3 +370,10 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderVariant auto builder = (MaterialBuilder*) nativeBuilder; builder->variantFilter((uint8_t) variantFilter); } + +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUseLegacyMorphing(JNIEnv*, + jclass, jlong nativeBuilder) { + auto builder = (MaterialBuilder*) nativeBuilder; + builder->useLegacyMorphing(); +} 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 c52cd51e0c..bd69a0c593 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 - CUSTOM1, // custom - CUSTOM2, // custom - CUSTOM3, // custom - CUSTOM4, // custom - CUSTOM5, // custom - CUSTOM6, // custom - CUSTOM7 // custom + 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 } public enum BlendingMode { @@ -459,6 +459,19 @@ public class MaterialBuilder { @NonNull public MaterialBuilder variantFilter(int variantFilter) { nMaterialBuilderVariantFilter(mNativeObject, variantFilter); + useLegacyMorphing(); + return this; + } + + /** + * Legacy morphing uses the data in the {@link VertexBuffer.VertexAttribute} slots + * (MORPH_POSITION_0, etc) and is limited to 4 morph targets. + * + * @see RenderableManager.Builder#morphing() + */ + @NonNull + public MaterialBuilder useLegacyMorphing() { + nMaterialBuilderUseLegacyMorphing(mNativeObject); return this; } @@ -599,4 +612,5 @@ public class MaterialBuilder { private static native void nMaterialBuilderOptimization(long nativeBuilder, int optimization); private static native void nMaterialBuilderVariantFilter(long nativeBuilder, int variantFilter); + private static native void nMaterialBuilderUseLegacyMorphing(long nativeBuilder); } 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 1a5785ebe7..25b1553785 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 @@ -400,7 +400,21 @@ public class RenderableManager { } /** - * Controls if the renderable has vertex morphing targets, false by default. + * Controls if the renderable has vertex morphing targets, false by default. This is + * required to enable GPU morphing. + * + *

Filament supports two morphing modes: standard (default) and legacy.

+ * + *

For standard morphing, A {@link MorphTargetBuffer} must be created and provided via + * {@link RenderableManager#setMorphTargetBufferAt}. Standard morphing supports up to + * CONFIG_MAX_MORPH_TARGET_COUNT morph targets.

+ * + * For legacy morphing, the attached {@link VertexBuffer} must provide data in the + * appropriate {@link VertexBuffer.VertexAttribute} slots (MORPH_POSITION_0 etc). + * Legacy morphing only supports up to 4 morph targets and will be deprecated in the future. + * Legacy morphing must be enabled on the material definition: either via the + * legacyMorphing material attribute or by calling + * {@link MaterialBuilder::useLegacyMorphing}. * *

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

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

The renderable must be built with morphing enabled.

+ *

The renderable must be built with morphing enabled. In legacy morphing mode, only the + * first 4 weights are considered.

* * @see Builder#morphing */ diff --git a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java index 359505d369..d3494e81ef 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java +++ b/android/filament-android/src/main/java/com/google/android/filament/VertexBuffer.java @@ -66,14 +66,14 @@ public class VertexBuffer { BONE_INDICES, // indices of 4 bones (uvec4) BONE_WEIGHTS, // weights of the 4 bones (normalized float4) UNUSED, // reserved for future use - CUSTOM0, // custom - CUSTOM1, // custom - CUSTOM2, // custom - CUSTOM3, // custom - CUSTOM4, // custom - CUSTOM5, // custom - CUSTOM6, // custom - CUSTOM7 // custom + 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 } public enum AttributeType { diff --git a/docs/webgl/reference.html b/docs/webgl/reference.html index 6b7510906d..3fd55c7de6 100644 --- a/docs/webgl/reference.html +++ b/docs/webgl/reference.html @@ -1438,6 +1438,14 @@ This defines the following functions:

  • CUSTOM5
  • CUSTOM6
  • CUSTOM7
  • +
  • MORPH_POSITION_0
  • +
  • MORPH_POSITION_1
  • +
  • MORPH_POSITION_2
  • +
  • MORPH_POSITION_3
  • +
  • MORPH_TANGENTS_0
  • +
  • MORPH_TANGENTS_1
  • +
  • MORPH_TANGENTS_2
  • +
  • MORPH_TANGENTS_3
  • diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 47f4fc56d4..0b7c7f57a4 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -300,7 +300,20 @@ public: Builder& skinning(size_t boneCount) noexcept; //!< \overload /** - * Controls if the renderable has vertex morphing targets, false by default. + * Controls if the renderable has vertex morphing targets, false by default. This is + * required to enable GPU morphing. + * + * Filament supports two morphing modes: standard (default) and legacy. + * + * For standard morphing, A MorphTargetBuffer must be created and provided via + * RenderableManager::setMorphTargetBufferAt(). Standard morphing supports up to + * \c CONFIG_MAX_MORPH_TARGET_COUNT morph targets. + * + * For legacy morphing, the attached VertexBuffer must provide data in the + * appropriate VertexAttribute slots (\c MORPH_POSITION_0 etc). Legacy morphing only + * supports up to 4 morph targets and will be deprecated in the future. Legacy morphing must + * be enabled on the material definition: either via the legacyMorphing material attribute + * or by calling filamat::MaterialBuilder::useLegacyMorphing(). * * See also RenderableManager::setMorphWeights(), which can be called on a per-frame basis * to advance the animation. @@ -455,7 +468,8 @@ public: /** * Updates the vertex morphing weights on a renderable, all zeroes by default. * - * The renderable must be built with morphing enabled, see Builder::morphing(). + * The renderable must be built with morphing enabled, see Builder::morphing(). In legacy + * morphing mode, only the first 4 weights are considered. */ void setMorphWeights(Instance instance, float const* weights, size_t count) noexcept; diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 8521768ff5..14d3af6652 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -136,6 +136,17 @@ enum VertexAttribute : uint8_t { CUSTOM6 = 14, CUSTOM7 = 15, + // Aliases for legacy vertex morphing. + // See RenderableManager::Builder::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 }; diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index abdb3db2e5..000d9dfbc9 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -529,6 +529,12 @@ public: MaterialBuilder& enableFramebufferFetch() noexcept; + /** + * Legacy morphing uses the data in the VertexAttribute slots (\c MORPH_POSITION_0, etc) and is + * limited to 4 morph targets. See filament::RenderableManager::Builder::morphing(). + */ + MaterialBuilder& useLegacyMorphing() noexcept; + /** * Build the material. If you are using the Filament engine with this library, you should use * the job system provided by Engine. @@ -744,6 +750,8 @@ private: bool mEnableFramebufferFetch = false; + bool mUseLegacyMorphing = false; + PreprocessorDefineList mDefines; }; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 8018235ce8..86a861a759 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -905,6 +905,11 @@ MaterialBuilder& MaterialBuilder::enableFramebufferFetch() noexcept { return *this; } +MaterialBuilder& MaterialBuilder::useLegacyMorphing() noexcept { + mUseLegacyMorphing = true; + return *this; +} + Package MaterialBuilder::build(JobSystem& jobSystem) noexcept { if (materialBuilderClients == 0) { utils::slog.e << "Error: MaterialBuilder::init() must be called before build()." @@ -952,6 +957,8 @@ Package MaterialBuilder::build(JobSystem& jobSystem) noexcept { writeSurfaceChunks(container); } + info.useLegacyMorphing = mUseLegacyMorphing; + // Generate all shaders and write the shader chunks. const auto variants = mMaterialDomain == MaterialDomain::SURFACE ? determineSurfaceVariants(mVariantFilter, isLit(), mShadowMultiplier) : diff --git a/libs/filamat/src/shaders/MaterialInfo.h b/libs/filamat/src/shaders/MaterialInfo.h index bb66c99eda..708d98f71a 100644 --- a/libs/filamat/src/shaders/MaterialInfo.h +++ b/libs/filamat/src/shaders/MaterialInfo.h @@ -45,6 +45,7 @@ struct UTILS_PUBLIC MaterialInfo { bool multiBounceAOSet; bool specularAOSet; bool hasCustomSurfaceShading; + bool useLegacyMorphing; filament::SpecularAmbientOcclusion specularAO; filament::RefractionMode refractionMode; filament::RefractionType refractionType; diff --git a/libs/filamat/src/shaders/ShaderGenerator.cpp b/libs/filamat/src/shaders/ShaderGenerator.cpp index cfbc4b90e0..acc39ded88 100644 --- a/libs/filamat/src/shaders/ShaderGenerator.cpp +++ b/libs/filamat/src/shaders/ShaderGenerator.cpp @@ -187,6 +187,8 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel, CodeGenerator::generateDefine(vs, "FLIP_UV_ATTRIBUTE", material.flipUV); + CodeGenerator::generateDefine(vs, "LEGACY_MORPHING", material.useLegacyMorphing); + const bool litVariants = lit || material.hasShadowMultiplier; // note: even if the user vertex shader is empty, we can't use the "optimized" version if @@ -217,6 +219,16 @@ std::string ShaderGenerator::createVertexProgram(ShaderModel shaderModel, if (variant.hasSkinningOrMorphing()) { attributes.set(VertexAttribute::BONE_INDICES); attributes.set(VertexAttribute::BONE_WEIGHTS); + if (material.useLegacyMorphing) { + 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); diff --git a/libs/viewer/src/SimpleViewer.cpp b/libs/viewer/src/SimpleViewer.cpp index bd9a351462..eecdc91a58 100644 --- a/libs/viewer/src/SimpleViewer.cpp +++ b/libs/viewer/src/SimpleViewer.cpp @@ -997,7 +997,8 @@ void SimpleViewer::updateUserInterface() { ImGui::BeginDisabled(); } for (int i = 0; i != mMorphWeights.size(); ++i) { - ImGui::SliderFloat(mAsset->getMorphTargetNameAt(mCurrentMorphingEntity, i), + const char* name = mAsset->getMorphTargetNameAt(mCurrentMorphingEntity, i); + ImGui::SliderFloat(name ? name : "Unnamed target", &mMorphWeights[i], 0.0f, 1.0); } if (isAnimating) { diff --git a/shaders/src/getters.vs b/shaders/src/getters.vs index cde22bfee9..7a2f107950 100644 --- a/shaders/src/getters.vs +++ b/shaders/src/getters.vs @@ -101,7 +101,14 @@ vec4 getPosition() { #if defined(HAS_SKINNING_OR_MORPHING) if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) { + #if defined(LEGACY_MORPHING) + pos += morphingUniforms.weights[0] * mesh_custom0; + pos += morphingUniforms.weights[1] * mesh_custom1; + pos += morphingUniforms.weights[2] * mesh_custom2; + pos += morphingUniforms.weights[3] * mesh_custom3; + #else morphPosition(pos); + #endif } if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) { diff --git a/shaders/src/main.vs b/shaders/src/main.vs index 42b652a354..f2c9813ace 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -36,8 +36,20 @@ void main() { #if defined(HAS_SKINNING_OR_MORPHING) if ((objectUniforms.flags & FILAMENT_OBJECT_MORPHING_ENABLED_BIT) != 0u) { + #if defined(LEGACY_MORPHING) + vec3 normal0, normal1, normal2, normal3; + toTangentFrame(mesh_custom4, normal0); + toTangentFrame(mesh_custom5, normal1); + toTangentFrame(mesh_custom6, normal2); + toTangentFrame(mesh_custom7, normal3); + material.worldNormal += morphingUniforms.weights[0].xyz * normal0; + material.worldNormal += morphingUniforms.weights[1].xyz * normal1; + material.worldNormal += morphingUniforms.weights[2].xyz * normal2; + material.worldNormal += morphingUniforms.weights[3].xyz * normal3; + #else morphNormal(material.worldNormal); material.worldNormal = normalize(material.worldNormal); + #endif } if ((objectUniforms.flags & FILAMENT_OBJECT_SKINNING_ENABLED_BIT) != 0u) { diff --git a/tools/matc/src/matc/ParametersProcessor.cpp b/tools/matc/src/matc/ParametersProcessor.cpp index 0df2ab3cd1..33ae4d1ce7 100644 --- a/tools/matc/src/matc/ParametersProcessor.cpp +++ b/tools/matc/src/matc/ParametersProcessor.cpp @@ -590,6 +590,13 @@ static bool processFramebufferFetch(MaterialBuilder& builder, const JsonishValue return true; } +static bool processLegacyMorphing(MaterialBuilder& builder, const JsonishValue& value) { + if (value.toJsonBool()->getBool()) { + builder.useLegacyMorphing(); + } + return true; +} + static bool processCustomSurfaceShading(MaterialBuilder& builder, const JsonishValue& value) { builder.customSurfaceShading(value.toJsonBool()->getBool()); return true; @@ -759,6 +766,7 @@ ParametersProcessor::ParametersProcessor() { mParameters["refractionMode"] = { &processRefractionMode, Type::STRING }; mParameters["refractionType"] = { &processRefractionType, Type::STRING }; mParameters["framebufferFetch"] = { &processFramebufferFetch, Type::BOOL }; + mParameters["legacyMorphing"] = { &processLegacyMorphing, Type::BOOL }; mParameters["outputs"] = { &processOutputs, Type::ARRAY }; mParameters["quality"] = { &processQuality, Type::STRING }; mParameters["customSurfaceShading"] = { &processCustomSurfaceShading, Type::BOOL }; diff --git a/web/filament-js/filament.d.ts b/web/filament-js/filament.d.ts index 0a5887d432..865788ddb7 100644 --- a/web/filament-js/filament.d.ts +++ b/web/filament-js/filament.d.ts @@ -1001,6 +1001,14 @@ 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/jsenums.cpp b/web/filament-js/jsenums.cpp index 45647ba262..ed9d16fe55 100644 --- a/web/filament-js/jsenums.cpp +++ b/web/filament-js/jsenums.cpp @@ -61,7 +61,15 @@ enum_("VertexAttribute") .value("CUSTOM4", CUSTOM4) .value("CUSTOM5", CUSTOM5) .value("CUSTOM6", CUSTOM6) - .value("CUSTOM7", CUSTOM7); + .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); enum_("BufferObject$BindingType") .value("VERTEX", BufferObject::BindingType::VERTEX);