From 1bf68222100a6e78df0899a1276d3f714f7124f7 Mon Sep 17 00:00:00 2001 From: luzhuang <364439895@qq.com> Date: Mon, 13 May 2024 14:07:19 +0800 Subject: [PATCH] Fix the bug of animation playback for multiple same type components under an Entity (#2095) * fix: multi subMesh animation bug --- e2e/case/animator-multiSubMeshBlendShape.ts | 108 ++++++++++++++++++ e2e/config.ts | 5 + ...imator_animator-multiSubMeshBlendShape.jpg | 3 + packages/core/src/animation/Animator.ts | 5 +- tests/src/core/Animator.test.ts | 8 +- 5 files changed, 122 insertions(+), 7 deletions(-) create mode 100644 e2e/case/animator-multiSubMeshBlendShape.ts create mode 100644 e2e/fixtures/originImage/Animator_animator-multiSubMeshBlendShape.jpg diff --git a/e2e/case/animator-multiSubMeshBlendShape.ts b/e2e/case/animator-multiSubMeshBlendShape.ts new file mode 100644 index 000000000..b20b29fab --- /dev/null +++ b/e2e/case/animator-multiSubMeshBlendShape.ts @@ -0,0 +1,108 @@ +/** + * @title Animation MultiSubMeshBlendShape + * @category Animation + */ +import { + Animator, + Camera, + DirectLight, + Logger, + SkinnedMeshRenderer, + SystemInfo, + Vector3, + WebGLEngine, + GLTFResource, + Keyframe, + AnimatorController, + AnimatorControllerLayer, + AnimatorStateMachine, + AnimationClip, + AnimationFloatArrayCurve +} from "@galacean/engine"; +import { initScreenshot, updateForE2E } from "./.mockForE2E"; + +Logger.enable(); +WebGLEngine.create({ canvas: "canvas" }).then((engine) => { + engine.canvas.width = window.innerWidth * SystemInfo.devicePixelRatio; + engine.canvas.height = window.innerHeight * SystemInfo.devicePixelRatio; + const scene = engine.sceneManager.activeScene; + const rootEntity = scene.createRootEntity(); + + // camera + const cameraEntity = rootEntity.createChild("camera_node"); + cameraEntity.transform.position = new Vector3(0, 1.5, 1); + const camera = cameraEntity.addComponent(Camera); + + const lightNode = rootEntity.createChild("light_node"); + lightNode.addComponent(DirectLight).intensity = 1.0; + lightNode.transform.lookAt(new Vector3(0, 0, 1)); + lightNode.transform.rotate(new Vector3(-45, -135, 0)); + + engine.resourceManager + .load( + "https://mdn.alipayobjects.com/oasis_be/afts/file/A*M_orSIoXP-QAAAAAAAAAAAAADkp5AQ/258To52bs_01.glb" + ) + .then((asset) => { + const { defaultSceneRoot } = asset; + rootEntity.addChild(defaultSceneRoot); + const entity = defaultSceneRoot; + defaultSceneRoot.transform.rotation = new Vector3(-90, -0, 0); + const animator = entity.addComponent(Animator); + + animator.animatorController = new AnimatorController(); + const layer = new AnimatorControllerLayer("base"); + animator.animatorController.addLayer(layer); + const stateMachine = (layer.stateMachine = new AnimatorStateMachine()); + const state = stateMachine.addState("blendShape"); + const clip = (state.clip = new AnimationClip("blendShape")); + + //custom blendShape curve + const blendShapeCurve = new AnimationFloatArrayCurve(); + let key1 = new Keyframe(); + key1.time = 0; + let array1 = new Float32Array(52); + for (let i = 0; i < array1.length; i++) { + array1[i] = 0; + if (i == 8) { + array1[i] = 0; + } + } + key1.value = array1; + + let key2 = new Keyframe(); + key2.time = 0.5; + let array2 = new Float32Array(52); + for (let i = 0; i < array2.length; i++) { + array2[i] = 0; + if (i == 8) { + array2[i] = 1; + } + } + key2.value = array2; + + let key3 = new Keyframe(); + key3.time = 1; + let array3 = new Float32Array(52); + for (let i = 0; i < array3.length; i++) { + array3[i] = 0; + if (i == 8) { + array3[i] = 0; + } + } + key3.value = array3; + + blendShapeCurve.addKey(key1); + blendShapeCurve.addKey(key2); + blendShapeCurve.addKey(key3); + const skinMeshRenders = entity.getComponentsIncludeChildren(SkinnedMeshRenderer, []); + + for (let i = 0, n = skinMeshRenders.length; i < n; i++) { + clip.addCurveBinding("", SkinnedMeshRenderer, i, "blendShapeWeights", blendShapeCurve); + } + animator.play("blendShape"); + + updateForE2E(engine, 500); + + initScreenshot(engine, camera); + }); +}); diff --git a/e2e/config.ts b/e2e/config.ts index 4ed3143fc..f0ce5ec80 100644 --- a/e2e/config.ts +++ b/e2e/config.ts @@ -30,6 +30,11 @@ export const E2E_CONFIG = { caseFileName: "animator-customBlendShape", threshold: 0.1 }, + multiSubMeshBlendShape: { + category: "Animator", + caseFileName: "animator-multiSubMeshBlendShape", + threshold: 0.1 + }, event: { category: "Animator", caseFileName: "animator-event", diff --git a/e2e/fixtures/originImage/Animator_animator-multiSubMeshBlendShape.jpg b/e2e/fixtures/originImage/Animator_animator-multiSubMeshBlendShape.jpg new file mode 100644 index 000000000..12b28fcb5 --- /dev/null +++ b/e2e/fixtures/originImage/Animator_animator-multiSubMeshBlendShape.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2e3237691c33d1fb0ca40b8d3076a87f29d4f1887ae6852cb3c256fe7605d91 +size 80713 diff --git a/packages/core/src/animation/Animator.ts b/packages/core/src/animation/Animator.ts index 83207e1b0..ce79f6674 100644 --- a/packages/core/src/animation/Animator.ts +++ b/packages/core/src/animation/Animator.ts @@ -300,7 +300,6 @@ export class Animator extends Component { const { relativePath } = curve; const targetEntity = curve.relativePath === "" ? entity : entity.findByPath(curve.relativePath); if (targetEntity) { - const propertyPath = `${curve.typeIndex}.` + curve.property; const component = curve.typeIndex > 0 ? targetEntity.getComponents(curve.type, AnimationCurveOwner._components)[curve.typeIndex] @@ -311,7 +310,7 @@ export class Animator extends Component { } const { property } = curve; - const { instanceId } = targetEntity; + const { instanceId } = component; // Get owner const propertyOwners = (curveOwnerPool[instanceId] ||= >>( Object.create(null) @@ -322,7 +321,7 @@ export class Animator extends Component { const layerPropertyOwners = (layerCurveOwnerPool[instanceId] ||= >( Object.create(null) )); - const layerOwner = (layerPropertyOwners[propertyPath] ||= curve._createCurveLayerOwner(owner)); + const layerOwner = (layerPropertyOwners[property] ||= curve._createCurveLayerOwner(owner)); if (mask && mask.pathMasks.length) { layerOwner.isActive = mask.getPathMask(relativePath)?.active ?? true; diff --git a/tests/src/core/Animator.test.ts b/tests/src/core/Animator.test.ts index 215eb9268..7c8cbe385 100644 --- a/tests/src/core/Animator.test.ts +++ b/tests/src/core/Animator.test.ts @@ -265,10 +265,10 @@ describe("Animator test", function () { ); let layerData = animator["_animatorLayersData"][1]; - const layerCurveOwner = layerData.curveOwnerPool[targetEntity.instanceId]["0.rotationQuaternion"]; - const parentLayerCurveOwner = layerData.curveOwnerPool[parentEntity.instanceId]["0.rotationQuaternion"]; + const layerCurveOwner = layerData.curveOwnerPool[targetEntity.transform.instanceId]["rotationQuaternion"]; + const parentLayerCurveOwner = layerData.curveOwnerPool[parentEntity.transform.instanceId]["rotationQuaternion"]; - let childLayerCurveOwner = layerData.curveOwnerPool[childEntity.instanceId]["0.rotationQuaternion"]; + let childLayerCurveOwner = layerData.curveOwnerPool[childEntity.transform.instanceId]["rotationQuaternion"]; expect(layerCurveOwner.isActive).to.eq(false); expect(parentLayerCurveOwner.isActive).to.eq(true); @@ -279,7 +279,7 @@ describe("Animator test", function () { animator.animatorController.addLayer(additiveLayer); animator.play("Run", 1); layerData = animator["_animatorLayersData"][1]; - childLayerCurveOwner = layerData.curveOwnerPool[childEntity.instanceId]["0.rotationQuaternion"]; + childLayerCurveOwner = layerData.curveOwnerPool[childEntity.transform.instanceId]["rotationQuaternion"]; expect(childLayerCurveOwner.isActive).to.eq(true); });