ue-mcp 1.2.3 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/action-class.js +56 -3
- package/dist/action-class.js.map +1 -1
- package/dist/bridge-timeouts.d.ts +64 -0
- package/dist/bridge-timeouts.js +118 -0
- package/dist/bridge-timeouts.js.map +1 -0
- package/dist/bridge.js +10 -2
- package/dist/bridge.js.map +1 -1
- package/dist/editor-control.d.ts +72 -5
- package/dist/editor-control.js +164 -140
- package/dist/editor-control.js.map +1 -1
- package/dist/editor-target.d.ts +55 -1
- package/dist/editor-target.js +107 -10
- package/dist/editor-target.js.map +1 -1
- package/dist/engine-observer.d.ts +59 -5
- package/dist/engine-observer.js +249 -29
- package/dist/engine-observer.js.map +1 -1
- package/dist/engine-root.d.ts +91 -0
- package/dist/engine-root.js +448 -0
- package/dist/engine-root.js.map +1 -0
- package/dist/epic-enrich.js +16 -1
- package/dist/epic-enrich.js.map +1 -1
- package/dist/feedback-cli.js +4 -0
- package/dist/feedback-cli.js.map +1 -1
- package/dist/feedback-deferred.d.ts +9 -0
- package/dist/feedback-deferred.js +2 -0
- package/dist/feedback-deferred.js.map +1 -1
- package/dist/feedback-fallback.d.ts +99 -0
- package/dist/feedback-fallback.js +173 -0
- package/dist/feedback-fallback.js.map +1 -0
- package/dist/flow/write-methods.js +20 -0
- package/dist/flow/write-methods.js.map +1 -1
- package/dist/index.js +41 -2
- package/dist/index.js.map +1 -1
- package/dist/lean-context.js +8 -3
- package/dist/lean-context.js.map +1 -1
- package/dist/locking.js +11 -2
- package/dist/locking.js.map +1 -1
- package/dist/plugin/loader.d.ts +4 -0
- package/dist/plugin/loader.js +22 -8
- package/dist/plugin/loader.js.map +1 -1
- package/dist/plugin/manifest.d.ts +72 -52
- package/dist/plugin/manifest.js +118 -21
- package/dist/plugin/manifest.js.map +1 -1
- package/dist/plugin-cli.js +16 -3
- package/dist/plugin-cli.js.map +1 -1
- package/dist/project.d.ts +11 -0
- package/dist/project.js +23 -2
- package/dist/project.js.map +1 -1
- package/dist/python-gate.d.ts +95 -0
- package/dist/python-gate.js +175 -0
- package/dist/python-gate.js.map +1 -0
- package/dist/tool-counts.json +16 -16
- package/dist/tools/animation.js +288 -24
- package/dist/tools/animation.js.map +1 -1
- package/dist/tools/asset.js +79 -14
- package/dist/tools/asset.js.map +1 -1
- package/dist/tools/audio.js +59 -5
- package/dist/tools/audio.js.map +1 -1
- package/dist/tools/blueprint.js +29 -12
- package/dist/tools/blueprint.js.map +1 -1
- package/dist/tools/chooser.js +4 -1
- package/dist/tools/chooser.js.map +1 -1
- package/dist/tools/editor.js +81 -43
- package/dist/tools/editor.js.map +1 -1
- package/dist/tools/feedback.js +214 -48
- package/dist/tools/feedback.js.map +1 -1
- package/dist/tools/foliage.js +27 -1
- package/dist/tools/foliage.js.map +1 -1
- package/dist/tools/gameplay.js +38 -11
- package/dist/tools/gameplay.js.map +1 -1
- package/dist/tools/gas.js +17 -5
- package/dist/tools/gas.js.map +1 -1
- package/dist/tools/landscape.js +22 -7
- package/dist/tools/landscape.js.map +1 -1
- package/dist/tools/level.js +322 -51
- package/dist/tools/level.js.map +1 -1
- package/dist/tools/material.js +10 -4
- package/dist/tools/material.js.map +1 -1
- package/dist/tools/niagara.js +3 -2
- package/dist/tools/niagara.js.map +1 -1
- package/dist/tools/pcg.js +6 -5
- package/dist/tools/pcg.js.map +1 -1
- package/dist/tools/plugins.js +2 -0
- package/dist/tools/plugins.js.map +1 -1
- package/dist/tools/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +24 -0
- package/dist/types.js +46 -3
- package/dist/types.js.map +1 -1
- package/dist/ue-mcp.default.yml +493 -125
- package/package.json +2 -2
- package/plugin/ue_mcp_bridge/NATIVE_RUNTIME_VISIBILITY.md +53 -0
- package/plugin/ue_mcp_bridge/NATIVE_STATIC_MESH_USAGE_PROGRESS.md +115 -0
- package/plugin/ue_mcp_bridge/README_MassHandlers.md +97 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/BridgeServer.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.cpp +21 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/GameThreadExecutor.h +9 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/HandlerJsonProperty.h +395 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.cpp +79 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers.h +25 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRig.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_ControlRigSequencer.cpp +3061 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRetargeterAuthoring.cpp +961 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_IKRigAuthoring.cpp +900 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Pose.cpp +769 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Sequence.cpp +44 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_SkeletalLive.cpp +217 -11
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_StateMachine.cpp +551 -24
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AnimationHandlers_Validation.cpp +735 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.cpp +352 -73
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers.h +8 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkProperties.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.cpp +503 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkRead.h +21 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_BulkUpsert.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Enum.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.cpp +902 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Geometry.h +33 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Import.cpp +478 -134
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Mesh.cpp +5 -5
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.cpp +933 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_MeshBoolean.h +35 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Redirectors.cpp +478 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_RenderTarget.cpp +1 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Sockets.cpp +4 -4
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Struct.cpp +2 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AssetHandlers_Subobject.cpp +319 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/AudioHandlers_SoundCue.cpp +3 -3
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.cpp +482 -35
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers.h +10 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.cpp +502 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Collision.h +43 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Functions.cpp +89 -7
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Graph.cpp +17 -51
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Internal.h +107 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Properties.cpp +10 -10
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/BlueprintHandlers_Search.cpp +648 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DemoHandlers.cpp +5 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.cpp +252 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/DiffHandlers.h +10 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.cpp +252 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers.h +4 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_Build.cpp +250 -27
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIE.cpp +303 -125
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_PIERuntime.cpp +73 -50
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/EditorHandlers_RuntimeVisibility.cpp +757 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.cpp +11 -2
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/FoliageHandlers_Batch.cpp +419 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.cpp +39 -161
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers.h +66 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BTAuthoring.cpp +1323 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GameplayHandlers_BehaviorTree.cpp +1211 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.cpp +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers.h +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/GasHandlers_Runtime.cpp +363 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/HandlerEditorState.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.cpp +1288 -36
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers.h +3 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LandscapeHandlers_Sculpt.cpp +41 -16
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.cpp +959 -213
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers.h +66 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_BatchWrite.cpp +1571 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Convert.cpp +316 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Inspection.cpp +280 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection.cpp +666 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_InstanceProjection_Internal.h +63 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Lights.cpp +43 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_MultiLevel.cpp +13 -47
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_PostProcess.cpp +527 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Query.cpp +1169 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Refresh.cpp +723 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Save.cpp +325 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Spatial.cpp +360 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Trace.cpp +178 -58
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Transient.cpp +337 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_Volumes.cpp +25 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartition.cpp +10 -9
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/LevelHandlers_WorldPartitionSettings.cpp +686 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.cpp +467 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MassHandlers.h +22 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.cpp +452 -175
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers.h +5 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/MaterialHandlers_Build.cpp +597 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/NiagaraHandlers.cpp +17 -15
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PCGHandlers.cpp +38 -168
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/PhysicsHandlers.cpp +39 -30
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.cpp +392 -78
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers.h +2 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SequencerHandlers_Internal.h +83 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.cpp +200 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SkeletalMeshHandlers.h +15 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/SplineHandlers.cpp +19 -12
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/StateTreeHandlers.cpp +31 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.cpp +307 -69
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers.h +84 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Extraction.cpp +8 -41
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Handlers/WidgetHandlers_Properties.cpp +33 -52
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/ActorSelectorTests.cpp +344 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationControlRigTimelineTests.cpp +60 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AnimationPoseTests.cpp +115 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetBulkReadTests.cpp +177 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetDeleteGuardTests.cpp +276 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPathResolutionTests.cpp +243 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetPropertyPersistTests.cpp +213 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AssetSubobjectTests.cpp +235 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/AutomationRunnerTests.cpp +72 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeAuthoringTests.cpp +526 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BehaviorTreeNodeTests.cpp +334 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BlueprintHandlerSurfaceTests.cpp +362 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/BridgeProtocolTests.cpp +14 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/CollisionQueryTests.cpp +288 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/DataTableRowWriteTests.cpp +453 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/EditorFunctionCallTests.cpp +223 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/FoliageBatchTests.cpp +147 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GameThreadGateTests.cpp +82 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/GasLiveAttributeTests.cpp +183 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LandscapeSampleTests.cpp +119 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelBatchWriteTests.cpp +176 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelComponentTreeInstanceCountTests.cpp +189 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelConvertBrushesTests.cpp +100 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelInstanceProjectionTests.cpp +265 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelPostProcessOverrideTests.cpp +258 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelQueryComponentsTests.cpp +195 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelRefreshTests.cpp +142 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelSaveReportTests.cpp +125 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelScriptHandlersTests.cpp +42 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelStaticMeshUsageTests.cpp +412 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTraceWorldScopeTests.cpp +133 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelTransientActorTests.cpp +205 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/LevelWorldPartitionSettingsTests.cpp +168 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTestTypes.h +51 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MCPInstancedStructPathTests.cpp +379 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MaterialBuildTests.cpp +238 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshBooleanTests.cpp +170 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/MeshGeometryTests.cpp +116 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageSaveExtensionTests.cpp +161 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/PackageWriteGuardTests.cpp +253 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/RuntimeVisibilityTests.cpp +143 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerHandlerTests.cpp +660 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SequencerScrubTests.cpp +50 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/SkeletonDiffTests.cpp +685 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/Tests/WidgetBlueprintResolveTests.cpp +145 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Private/UE_MCP_Bridge.cpp +80 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerFunctionCall.h +509 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerQuery.h +407 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/Public/HandlerUtils.h +1008 -77
- package/plugin/ue_mcp_bridge/Source/UE_MCP_Bridge/UE_MCP_Bridge.Build.cs +87 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/MCPEngineStatus.cpp +152 -14
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Private/Tests/MCPEngineStatusTests.cpp +65 -0
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/Public/MCPEngineStatus.h +32 -1
- package/plugin/ue_mcp_bridge/Source/UE_MCP_BridgeStatus/UE_MCP_BridgeStatus.Build.cs +2 -0
- package/skills/ue-mcp-animation/SKILL.md +154 -0
- package/skills/ue-mcp-blueprint/SKILL.md +71 -0
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
#include "AnimGraphNode_BlendSpacePlayer.h"
|
|
37
37
|
#include "Rig/IKRigDefinition.h"
|
|
38
38
|
#include "RigEditor/IKRigController.h"
|
|
39
|
+
#if UE_MCP_HAS_5_8_API
|
|
40
|
+
#include "Rig/Solvers/IKRigFullBodyIK.h"
|
|
41
|
+
#include "Retargeter/IKRetargetChainMapping.h"
|
|
42
|
+
#include "Retargeter/IKRetargetOps.h"
|
|
43
|
+
#include "Retargeter/IKRetargetProcessor.h"
|
|
44
|
+
#endif
|
|
39
45
|
#include "PoseSearch/PoseSearchDatabase.h"
|
|
40
46
|
#include "PoseSearch/PoseSearchSchema.h"
|
|
41
47
|
#include "PoseSearch/PoseSearchDerivedData.h"
|
|
@@ -53,12 +59,82 @@
|
|
|
53
59
|
#include "UObject/UObjectGlobals.h"
|
|
54
60
|
#include "EditorAssetLibrary.h"
|
|
55
61
|
#include "Editor.h"
|
|
62
|
+
#include "ScopedTransaction.h"
|
|
56
63
|
#include "Dom/JsonObject.h"
|
|
57
64
|
#include "Dom/JsonValue.h"
|
|
58
65
|
|
|
59
66
|
|
|
60
67
|
#define UE_MCP_HAS_POSESEARCH_DATABASE_ASSET_API (ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 4))
|
|
61
68
|
|
|
69
|
+
#if UE_MCP_HAS_5_8_API
|
|
70
|
+
static TSharedPtr<FJsonObject> AnimationVectorToJson(const FVector& Value)
|
|
71
|
+
{
|
|
72
|
+
TSharedPtr<FJsonObject> Json = MakeShared<FJsonObject>();
|
|
73
|
+
Json->SetNumberField(TEXT("x"), Value.X);
|
|
74
|
+
Json->SetNumberField(TEXT("y"), Value.Y);
|
|
75
|
+
Json->SetNumberField(TEXT("z"), Value.Z);
|
|
76
|
+
return Json;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static TSharedPtr<FJsonObject> AnimationQuaternionToJson(const FQuat& Value)
|
|
80
|
+
{
|
|
81
|
+
TSharedPtr<FJsonObject> Json = MakeShared<FJsonObject>();
|
|
82
|
+
Json->SetNumberField(TEXT("x"), Value.X);
|
|
83
|
+
Json->SetNumberField(TEXT("y"), Value.Y);
|
|
84
|
+
Json->SetNumberField(TEXT("z"), Value.Z);
|
|
85
|
+
Json->SetNumberField(TEXT("w"), Value.W);
|
|
86
|
+
return Json;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
static TSharedPtr<FJsonObject> AnimationTransformToJson(const FTransform& Value)
|
|
90
|
+
{
|
|
91
|
+
TSharedPtr<FJsonObject> Json = MakeShared<FJsonObject>();
|
|
92
|
+
Json->SetObjectField(TEXT("translation"), AnimationVectorToJson(Value.GetTranslation()));
|
|
93
|
+
Json->SetObjectField(TEXT("rotationQuaternion"), AnimationQuaternionToJson(Value.GetRotation()));
|
|
94
|
+
Json->SetObjectField(TEXT("scale"), AnimationVectorToJson(Value.GetScale3D()));
|
|
95
|
+
return Json;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Processor initialization temporarily redirects editor-instance pointers on
|
|
99
|
+
// the serialized op stack. Restore them after validation and batch execution.
|
|
100
|
+
class FScopedBatchRetargetEditorInstanceRestore
|
|
101
|
+
{
|
|
102
|
+
struct FState
|
|
103
|
+
{
|
|
104
|
+
FIKRetargetOpBase* Op = nullptr;
|
|
105
|
+
FIKRetargetOpBase* OpEditorInstance = nullptr;
|
|
106
|
+
FIKRetargetOpSettingsBase* Settings = nullptr;
|
|
107
|
+
FIKRetargetOpSettingsBase* SettingsEditorInstance = nullptr;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
public:
|
|
111
|
+
explicit FScopedBatchRetargetEditorInstanceRestore(UIKRetargeter* Retargeter)
|
|
112
|
+
{
|
|
113
|
+
if (!Retargeter) return;
|
|
114
|
+
States.Reserve(Retargeter->GetRetargetOps().Num());
|
|
115
|
+
for (const FInstancedStruct& OpStruct : Retargeter->GetRetargetOps())
|
|
116
|
+
{
|
|
117
|
+
FIKRetargetOpBase* Op = const_cast<FIKRetargetOpBase*>(OpStruct.GetPtr<FIKRetargetOpBase>());
|
|
118
|
+
if (!Op) continue;
|
|
119
|
+
FIKRetargetOpSettingsBase* Settings = Op->GetSettings();
|
|
120
|
+
States.Add({Op, Op->EditorInstance, Settings, Settings ? Settings->EditorInstance : nullptr});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
~FScopedBatchRetargetEditorInstanceRestore()
|
|
125
|
+
{
|
|
126
|
+
for (const FState& State : States)
|
|
127
|
+
{
|
|
128
|
+
State.Op->EditorInstance = State.OpEditorInstance;
|
|
129
|
+
if (State.Settings) State.Settings->EditorInstance = State.SettingsEditorInstance;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private:
|
|
134
|
+
TArray<FState> States;
|
|
135
|
+
};
|
|
136
|
+
#endif
|
|
137
|
+
|
|
62
138
|
static int32 GetPoseSearchAnimationAssetCount(const UPoseSearchDatabase* Database)
|
|
63
139
|
{
|
|
64
140
|
#if UE_MCP_HAS_POSESEARCH_DATABASE_ASSET_API
|
|
@@ -1278,6 +1354,9 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ReadIKRig(const TSharedPtr<FJsonObjec
|
|
|
1278
1354
|
ChainObj->SetStringField(TEXT("name"), Chain.ChainName.ToString());
|
|
1279
1355
|
ChainObj->SetStringField(TEXT("startBone"), Chain.StartBone.BoneName.ToString());
|
|
1280
1356
|
ChainObj->SetStringField(TEXT("endBone"), Chain.EndBone.BoneName.ToString());
|
|
1357
|
+
#if UE_MCP_HAS_5_8_API
|
|
1358
|
+
ChainObj->SetStringField(TEXT("goal"), Chain.IKGoalName.ToString());
|
|
1359
|
+
#endif
|
|
1281
1360
|
ChainsArray.Add(MakeShared<FJsonValueObject>(ChainObj));
|
|
1282
1361
|
}
|
|
1283
1362
|
Result->SetArrayField(TEXT("retargetChains"), ChainsArray);
|
|
@@ -1292,8 +1371,95 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ReadIKRig(const TSharedPtr<FJsonObjec
|
|
|
1292
1371
|
Result->SetStringField(TEXT("rootBone"), RigSkeleton.BoneNames[0].ToString());
|
|
1293
1372
|
}
|
|
1294
1373
|
|
|
1295
|
-
//
|
|
1374
|
+
// UE 5.8 replaced the legacy UObject solver array with typed instanced
|
|
1375
|
+
// structs. Keep the reflection fallback for older supported engines.
|
|
1296
1376
|
TArray<TSharedPtr<FJsonValue>> SolversArray;
|
|
1377
|
+
#if UE_MCP_HAS_5_8_API
|
|
1378
|
+
UIKRigController* Controller = UIKRigController::GetController(IKRig);
|
|
1379
|
+
if (!Controller)
|
|
1380
|
+
{
|
|
1381
|
+
return MCPError(TEXT("IKRigController unavailable"));
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
Result->SetStringField(TEXT("retargetRoot"), Controller->GetRetargetRoot().ToString());
|
|
1385
|
+
Result->SetStringField(TEXT("rootMotionBone"), Controller->GetRootMotionBone().ToString());
|
|
1386
|
+
Result->SetStringField(
|
|
1387
|
+
TEXT("skeletonRootBone"),
|
|
1388
|
+
RigSkeleton.BoneNames.IsEmpty() ? TEXT("") : RigSkeleton.BoneNames[0].ToString());
|
|
1389
|
+
|
|
1390
|
+
TArray<TSharedPtr<FJsonValue>> GoalsArray;
|
|
1391
|
+
for (UIKRigEffectorGoal* Goal : Controller->GetAllGoals())
|
|
1392
|
+
{
|
|
1393
|
+
if (!Goal) continue;
|
|
1394
|
+
TSharedPtr<FJsonObject> GoalObj = MakeShared<FJsonObject>();
|
|
1395
|
+
GoalObj->SetStringField(TEXT("name"), Goal->GoalName.ToString());
|
|
1396
|
+
GoalObj->SetStringField(TEXT("bone"), Goal->BoneName.ToString());
|
|
1397
|
+
GoalObj->SetNumberField(TEXT("positionAlpha"), Goal->PositionAlpha);
|
|
1398
|
+
GoalObj->SetNumberField(TEXT("rotationAlpha"), Goal->RotationAlpha);
|
|
1399
|
+
GoalObj->SetObjectField(TEXT("currentTransform"), AnimationTransformToJson(Goal->CurrentTransform));
|
|
1400
|
+
GoalObj->SetObjectField(TEXT("initialTransform"), AnimationTransformToJson(Goal->InitialTransform));
|
|
1401
|
+
|
|
1402
|
+
TArray<TSharedPtr<FJsonValue>> ConnectedSolvers;
|
|
1403
|
+
for (int32 SolverIndex = 0; SolverIndex < Controller->GetNumSolvers(); ++SolverIndex)
|
|
1404
|
+
{
|
|
1405
|
+
if (Controller->IsGoalConnectedToSolver(Goal->GoalName, SolverIndex))
|
|
1406
|
+
{
|
|
1407
|
+
ConnectedSolvers.Add(MakeShared<FJsonValueNumber>(SolverIndex));
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
GoalObj->SetArrayField(TEXT("connectedSolverIndices"), ConnectedSolvers);
|
|
1411
|
+
GoalsArray.Add(MakeShared<FJsonValueObject>(GoalObj));
|
|
1412
|
+
}
|
|
1413
|
+
Result->SetArrayField(TEXT("goals"), GoalsArray);
|
|
1414
|
+
|
|
1415
|
+
TArray<TSharedPtr<FJsonValue>> ExcludedBones;
|
|
1416
|
+
for (const FName BoneName : RigSkeleton.BoneNames)
|
|
1417
|
+
{
|
|
1418
|
+
if (Controller->GetBoneExcluded(BoneName))
|
|
1419
|
+
{
|
|
1420
|
+
ExcludedBones.Add(MakeShared<FJsonValueString>(BoneName.ToString()));
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
Result->SetArrayField(TEXT("excludedBones"), ExcludedBones);
|
|
1424
|
+
|
|
1425
|
+
for (int32 SolverIndex = 0; SolverIndex < Controller->GetNumSolvers(); ++SolverIndex)
|
|
1426
|
+
{
|
|
1427
|
+
FInstancedStruct* SolverStruct = Controller->GetSolverStructAtIndex(SolverIndex);
|
|
1428
|
+
const UScriptStruct* SolverType = SolverStruct ? SolverStruct->GetScriptStruct() : nullptr;
|
|
1429
|
+
TSharedPtr<FJsonObject> SolverObj = MakeShared<FJsonObject>();
|
|
1430
|
+
SolverObj->SetNumberField(TEXT("index"), SolverIndex);
|
|
1431
|
+
SolverObj->SetStringField(TEXT("name"), Controller->GetSolverUniqueName(SolverIndex));
|
|
1432
|
+
SolverObj->SetStringField(TEXT("type"), SolverType ? SolverType->GetPathName() : TEXT(""));
|
|
1433
|
+
SolverObj->SetBoolField(TEXT("enabled"), Controller->GetSolverEnabled(SolverIndex));
|
|
1434
|
+
SolverObj->SetStringField(TEXT("startBone"), Controller->GetStartBone(SolverIndex).ToString());
|
|
1435
|
+
SolverObj->SetStringField(TEXT("endBone"), Controller->GetEndBone(SolverIndex).ToString());
|
|
1436
|
+
|
|
1437
|
+
TArray<TSharedPtr<FJsonValue>> Effectors;
|
|
1438
|
+
if (UIKRigFBIKController* FBIKController = Cast<UIKRigFBIKController>(Controller->GetSolverController(SolverIndex)))
|
|
1439
|
+
{
|
|
1440
|
+
SolverObj->SetBoolField(TEXT("fullBodyIK"), true);
|
|
1441
|
+
for (UIKRigEffectorGoal* Goal : Controller->GetAllGoals())
|
|
1442
|
+
{
|
|
1443
|
+
if (!Goal || !Controller->IsGoalConnectedToSolver(Goal->GoalName, SolverIndex)) continue;
|
|
1444
|
+
const FIKRigFBIKGoalSettings Settings = FBIKController->GetGoalSettings(Goal->GoalName);
|
|
1445
|
+
TSharedPtr<FJsonObject> EffectorObj = MakeShared<FJsonObject>();
|
|
1446
|
+
EffectorObj->SetStringField(TEXT("goal"), Goal->GoalName.ToString());
|
|
1447
|
+
EffectorObj->SetStringField(TEXT("bone"), Settings.BoneName.ToString());
|
|
1448
|
+
EffectorObj->SetNumberField(TEXT("chainDepth"), Settings.ChainDepth);
|
|
1449
|
+
EffectorObj->SetNumberField(TEXT("strengthAlpha"), Settings.StrengthAlpha);
|
|
1450
|
+
EffectorObj->SetNumberField(TEXT("pullChainAlpha"), Settings.PullChainAlpha);
|
|
1451
|
+
EffectorObj->SetNumberField(TEXT("pinRotation"), Settings.PinRotation);
|
|
1452
|
+
Effectors.Add(MakeShared<FJsonValueObject>(EffectorObj));
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
else
|
|
1456
|
+
{
|
|
1457
|
+
SolverObj->SetBoolField(TEXT("fullBodyIK"), false);
|
|
1458
|
+
}
|
|
1459
|
+
SolverObj->SetArrayField(TEXT("effectors"), Effectors);
|
|
1460
|
+
SolversArray.Add(MakeShared<FJsonValueObject>(SolverObj));
|
|
1461
|
+
}
|
|
1462
|
+
#else
|
|
1297
1463
|
FProperty* SolversProp = IKRig->GetClass()->FindPropertyByName(TEXT("Solvers"));
|
|
1298
1464
|
if (SolversProp)
|
|
1299
1465
|
{
|
|
@@ -1307,6 +1473,7 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ReadIKRig(const TSharedPtr<FJsonObjec
|
|
|
1307
1473
|
SolversArray.Add(MakeShared<FJsonValueObject>(SolverInfo));
|
|
1308
1474
|
}
|
|
1309
1475
|
}
|
|
1476
|
+
#endif
|
|
1310
1477
|
Result->SetArrayField(TEXT("solvers"), SolversArray);
|
|
1311
1478
|
|
|
1312
1479
|
return MCPResult(Result);
|
|
@@ -1340,7 +1507,78 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateIKRetargeter(const TSharedPtr<F
|
|
|
1340
1507
|
auto Created = MCPCreateAssetIdempotent<UObject>(Name, PackagePath, OnConflict, TEXT("IKRetargeter"), RetargeterClass, Factory);
|
|
1341
1508
|
if (Created.EarlyReturn) return Created.EarlyReturn;
|
|
1342
1509
|
UObject* NewAsset = Created.Asset;
|
|
1510
|
+
const bool bAutoMap = OptionalBool(Params, TEXT("autoMapChains"), true);
|
|
1511
|
+
int32 ChainsMapped = 0;
|
|
1512
|
+
FString SrcErr;
|
|
1513
|
+
FString TgtErr;
|
|
1514
|
+
FString OpsWarning;
|
|
1515
|
+
|
|
1516
|
+
#if UE_MCP_HAS_5_8_API
|
|
1517
|
+
UIKRetargeter* Retargeter = Cast<UIKRetargeter>(NewAsset);
|
|
1518
|
+
if (!Retargeter)
|
|
1519
|
+
{
|
|
1520
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1521
|
+
return MCPError(TEXT("Created asset is not an IKRetargeter"));
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
UIKRigDefinition* SourceRig = SourceRigPath.IsEmpty()
|
|
1525
|
+
? nullptr
|
|
1526
|
+
: LoadObject<UIKRigDefinition>(nullptr, *SourceRigPath);
|
|
1527
|
+
UIKRigDefinition* TargetRig = TargetRigPath.IsEmpty()
|
|
1528
|
+
? nullptr
|
|
1529
|
+
: LoadObject<UIKRigDefinition>(nullptr, *TargetRigPath);
|
|
1530
|
+
if (!SourceRigPath.IsEmpty() && !SourceRig)
|
|
1531
|
+
{
|
|
1532
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1533
|
+
return MCPError(FString::Printf(TEXT("IKRig not found: %s"), *SourceRigPath));
|
|
1534
|
+
}
|
|
1535
|
+
if (!TargetRigPath.IsEmpty() && !TargetRig)
|
|
1536
|
+
{
|
|
1537
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1538
|
+
return MCPError(FString::Printf(TEXT("IKRig not found: %s"), *TargetRigPath));
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
UIKRetargeterController* Controller = UIKRetargeterController::GetController(Retargeter);
|
|
1542
|
+
if (!Controller)
|
|
1543
|
+
{
|
|
1544
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1545
|
+
return MCPError(TEXT("IKRetargeterController unavailable"));
|
|
1546
|
+
}
|
|
1343
1547
|
|
|
1548
|
+
// The factory does not install the operational stack. AddDefaultOps is
|
|
1549
|
+
// idempotent and must precede per-op rig assignment and chain mapping.
|
|
1550
|
+
Controller->AddDefaultOps();
|
|
1551
|
+
if (SourceRig)
|
|
1552
|
+
{
|
|
1553
|
+
Controller->SetIKRig(ERetargetSourceOrTarget::Source, SourceRig);
|
|
1554
|
+
Controller->AssignIKRigToAllOps(ERetargetSourceOrTarget::Source, SourceRig);
|
|
1555
|
+
}
|
|
1556
|
+
if (TargetRig)
|
|
1557
|
+
{
|
|
1558
|
+
Controller->SetIKRig(ERetargetSourceOrTarget::Target, TargetRig);
|
|
1559
|
+
Controller->AssignIKRigToAllOps(ERetargetSourceOrTarget::Target, TargetRig);
|
|
1560
|
+
}
|
|
1561
|
+
if (bAutoMap)
|
|
1562
|
+
{
|
|
1563
|
+
Controller->AutoMapChains(EAutoMapChainType::Exact, true);
|
|
1564
|
+
}
|
|
1565
|
+
Controller->CleanAsset();
|
|
1566
|
+
|
|
1567
|
+
if ((SourceRig && Controller->GetIKRig(ERetargetSourceOrTarget::Source) != SourceRig)
|
|
1568
|
+
|| (TargetRig && Controller->GetIKRig(ERetargetSourceOrTarget::Target) != TargetRig))
|
|
1569
|
+
{
|
|
1570
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1571
|
+
return MCPError(TEXT("IK Retargeter rig assignment failed readback validation"));
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
if (TargetRig)
|
|
1575
|
+
{
|
|
1576
|
+
for (const FBoneChain& Chain : TargetRig->GetRetargetChains())
|
|
1577
|
+
{
|
|
1578
|
+
if (!Controller->GetSourceChain(Chain.ChainName).IsNone()) ++ChainsMapped;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
#else
|
|
1344
1582
|
// Optionally set source / target IK Rigs via reflection
|
|
1345
1583
|
auto SetRigProperty = [&](const FString& PropName, const FString& Path) -> FString
|
|
1346
1584
|
{
|
|
@@ -1354,16 +1592,13 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateIKRetargeter(const TSharedPtr<F
|
|
|
1354
1592
|
return Prop->ImportText_Direct(*Export, Addr, NewAsset, PPF_None) ? TEXT("") : FString::Printf(TEXT("Failed to set %s"), *PropName);
|
|
1355
1593
|
};
|
|
1356
1594
|
|
|
1357
|
-
|
|
1358
|
-
|
|
1595
|
+
SrcErr = SetRigProperty(TEXT("SourceIKRigAsset"), SourceRigPath);
|
|
1596
|
+
TgtErr = SetRigProperty(TEXT("TargetIKRigAsset"), TargetRigPath);
|
|
1359
1597
|
|
|
1360
1598
|
// UE 5.7+ ops-stack initialization (#246). After CreateAsset the per-op
|
|
1361
1599
|
// IK Rig refs and chain mappings are unset, so the retargeter cannot be
|
|
1362
1600
|
// driven by an Anim Graph. Mirror what the Python workaround does:
|
|
1363
1601
|
// AssignIKRigToAllOps(SOURCE/TARGET) + AutoMapChains.
|
|
1364
|
-
bool bAutoMap = OptionalBool(Params, TEXT("autoMapChains"), true);
|
|
1365
|
-
int32 ChainsMapped = 0;
|
|
1366
|
-
FString OpsWarning;
|
|
1367
1602
|
if (bAutoMap)
|
|
1368
1603
|
{
|
|
1369
1604
|
UIKRetargeter* Retargeter = Cast<UIKRetargeter>(NewAsset);
|
|
@@ -1431,9 +1666,14 @@ TSharedPtr<FJsonValue> FAnimationHandlers::CreateIKRetargeter(const TSharedPtr<F
|
|
|
1431
1666
|
}
|
|
1432
1667
|
}
|
|
1433
1668
|
}
|
|
1669
|
+
#endif
|
|
1434
1670
|
|
|
1435
|
-
NewAsset
|
|
1436
|
-
|
|
1671
|
+
if (!SaveAssetPackage(NewAsset))
|
|
1672
|
+
{
|
|
1673
|
+
const FString FailedPackage = NewAsset->GetOutermost()->GetName();
|
|
1674
|
+
UEditorAssetLibrary::DeleteAsset(NewAsset->GetPathName());
|
|
1675
|
+
return MCPError(FString::Printf(TEXT("Failed to save IKRetargeter package '%s'"), *FailedPackage));
|
|
1676
|
+
}
|
|
1437
1677
|
|
|
1438
1678
|
auto Result = MCPSuccess();
|
|
1439
1679
|
MCPSetCreated(Result);
|
|
@@ -1477,6 +1717,93 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ReadIKRetargeter(const TSharedPtr<FJs
|
|
|
1477
1717
|
Result->SetStringField(TEXT("sourceRig"), SrcRig ? SrcRig->GetPathName() : TEXT(""));
|
|
1478
1718
|
Result->SetStringField(TEXT("targetRig"), TgtRig ? TgtRig->GetPathName() : TEXT(""));
|
|
1479
1719
|
|
|
1720
|
+
#if UE_MCP_HAS_5_8_API
|
|
1721
|
+
USkeletalMesh* SourcePreviewMesh = Controller->GetPreviewMesh(ERetargetSourceOrTarget::Source);
|
|
1722
|
+
USkeletalMesh* TargetPreviewMesh = Controller->GetPreviewMesh(ERetargetSourceOrTarget::Target);
|
|
1723
|
+
Result->SetStringField(TEXT("sourcePreviewMesh"), SourcePreviewMesh ? SourcePreviewMesh->GetPathName() : TEXT(""));
|
|
1724
|
+
Result->SetStringField(TEXT("targetPreviewMesh"), TargetPreviewMesh ? TargetPreviewMesh->GetPathName() : TEXT(""));
|
|
1725
|
+
|
|
1726
|
+
auto WritePoses = [&](const ERetargetSourceOrTarget Side, const TCHAR* CurrentField, const TCHAR* PosesField)
|
|
1727
|
+
{
|
|
1728
|
+
const FName CurrentPoseName = Controller->GetCurrentRetargetPoseName(Side);
|
|
1729
|
+
Result->SetStringField(CurrentField, CurrentPoseName.ToString());
|
|
1730
|
+
|
|
1731
|
+
TMap<FName, FIKRetargetPose>& Poses = Controller->GetRetargetPoses(Side);
|
|
1732
|
+
TArray<FName> PoseNames;
|
|
1733
|
+
Poses.GetKeys(PoseNames);
|
|
1734
|
+
PoseNames.Sort([](const FName& A, const FName& B)
|
|
1735
|
+
{
|
|
1736
|
+
return A.ToString() < B.ToString();
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
TArray<TSharedPtr<FJsonValue>> PosesArray;
|
|
1740
|
+
for (const FName PoseName : PoseNames)
|
|
1741
|
+
{
|
|
1742
|
+
const FIKRetargetPose* Pose = Poses.Find(PoseName);
|
|
1743
|
+
if (!Pose) continue;
|
|
1744
|
+
TSharedPtr<FJsonObject> PoseObj = MakeShared<FJsonObject>();
|
|
1745
|
+
PoseObj->SetStringField(TEXT("name"), PoseName.ToString());
|
|
1746
|
+
PoseObj->SetBoolField(TEXT("current"), PoseName == CurrentPoseName);
|
|
1747
|
+
PoseObj->SetObjectField(TEXT("rootTranslationOffset"), AnimationVectorToJson(Pose->GetRootTranslationDelta()));
|
|
1748
|
+
|
|
1749
|
+
TArray<FName> OffsetBones;
|
|
1750
|
+
Pose->GetAllDeltaRotations().GetKeys(OffsetBones);
|
|
1751
|
+
OffsetBones.Sort([](const FName& A, const FName& B)
|
|
1752
|
+
{
|
|
1753
|
+
return A.ToString() < B.ToString();
|
|
1754
|
+
});
|
|
1755
|
+
|
|
1756
|
+
TArray<TSharedPtr<FJsonValue>> RotationOffsets;
|
|
1757
|
+
for (const FName BoneName : OffsetBones)
|
|
1758
|
+
{
|
|
1759
|
+
const FQuat* Rotation = Pose->GetAllDeltaRotations().Find(BoneName);
|
|
1760
|
+
if (!Rotation) continue;
|
|
1761
|
+
TSharedPtr<FJsonObject> OffsetObj = MakeShared<FJsonObject>();
|
|
1762
|
+
OffsetObj->SetStringField(TEXT("bone"), BoneName.ToString());
|
|
1763
|
+
OffsetObj->SetObjectField(TEXT("rotationQuaternion"), AnimationQuaternionToJson(*Rotation));
|
|
1764
|
+
RotationOffsets.Add(MakeShared<FJsonValueObject>(OffsetObj));
|
|
1765
|
+
}
|
|
1766
|
+
PoseObj->SetArrayField(TEXT("rotationOffsets"), RotationOffsets);
|
|
1767
|
+
PosesArray.Add(MakeShared<FJsonValueObject>(PoseObj));
|
|
1768
|
+
}
|
|
1769
|
+
Result->SetArrayField(PosesField, PosesArray);
|
|
1770
|
+
};
|
|
1771
|
+
|
|
1772
|
+
WritePoses(ERetargetSourceOrTarget::Source, TEXT("currentSourcePose"), TEXT("sourcePoses"));
|
|
1773
|
+
WritePoses(ERetargetSourceOrTarget::Target, TEXT("currentTargetPose"), TEXT("targetPoses"));
|
|
1774
|
+
|
|
1775
|
+
TArray<TSharedPtr<FJsonValue>> RetargetOps;
|
|
1776
|
+
for (int32 OpIndex = 0; OpIndex < Controller->GetNumRetargetOps(); ++OpIndex)
|
|
1777
|
+
{
|
|
1778
|
+
const FName OpName = Controller->GetOpName(OpIndex);
|
|
1779
|
+
FInstancedStruct* OpStruct = Controller->GetRetargetOpStructAtIndex(OpIndex);
|
|
1780
|
+
const UScriptStruct* OpType = OpStruct ? OpStruct->GetScriptStruct() : nullptr;
|
|
1781
|
+
TSharedPtr<FJsonObject> OpObj = MakeShared<FJsonObject>();
|
|
1782
|
+
OpObj->SetNumberField(TEXT("index"), OpIndex);
|
|
1783
|
+
OpObj->SetStringField(TEXT("name"), OpName.ToString());
|
|
1784
|
+
OpObj->SetStringField(TEXT("type"), OpType ? OpType->GetPathName() : TEXT(""));
|
|
1785
|
+
OpObj->SetBoolField(TEXT("enabled"), Controller->GetRetargetOpEnabled(OpIndex));
|
|
1786
|
+
OpObj->SetStringField(TEXT("parentOp"), Controller->GetParentOpByName(OpName).ToString());
|
|
1787
|
+
const UIKRigDefinition* OpTargetRig = Controller->GetTargetIKRigForOp(OpName);
|
|
1788
|
+
OpObj->SetStringField(TEXT("targetRig"), OpTargetRig ? OpTargetRig->GetPathName() : TEXT(""));
|
|
1789
|
+
|
|
1790
|
+
TArray<TSharedPtr<FJsonValue>> OpMappings;
|
|
1791
|
+
if (const FRetargetChainMapping* ChainMapping = Controller->GetChainMapping(OpName))
|
|
1792
|
+
{
|
|
1793
|
+
for (const FRetargetChainPair& Pair : ChainMapping->GetChainPairs())
|
|
1794
|
+
{
|
|
1795
|
+
TSharedPtr<FJsonObject> MappingObj = MakeShared<FJsonObject>();
|
|
1796
|
+
MappingObj->SetStringField(TEXT("targetChain"), Pair.TargetChainName.ToString());
|
|
1797
|
+
MappingObj->SetStringField(TEXT("sourceChain"), Pair.SourceChainName.ToString());
|
|
1798
|
+
OpMappings.Add(MakeShared<FJsonValueObject>(MappingObj));
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
OpObj->SetArrayField(TEXT("chainMappings"), OpMappings);
|
|
1802
|
+
RetargetOps.Add(MakeShared<FJsonValueObject>(OpObj));
|
|
1803
|
+
}
|
|
1804
|
+
Result->SetArrayField(TEXT("retargetOps"), RetargetOps);
|
|
1805
|
+
#endif
|
|
1806
|
+
|
|
1480
1807
|
// Chain mappings: for each target chain, report the source chain it's mapped to.
|
|
1481
1808
|
TArray<TSharedPtr<FJsonValue>> Mappings;
|
|
1482
1809
|
if (TgtRig)
|
|
@@ -1841,6 +2168,10 @@ TSharedPtr<FJsonValue> FAnimationHandlers::SetIKRigMesh(const TSharedPtr<FJsonOb
|
|
|
1841
2168
|
{
|
|
1842
2169
|
FString RigPath;
|
|
1843
2170
|
if (auto Err = RequireStringAlt(Params, TEXT("rigPath"), TEXT("assetPath"), RigPath)) return Err;
|
|
2171
|
+
if (MCPIsProtectedAssetPath(RigPath))
|
|
2172
|
+
{
|
|
2173
|
+
return MCPError(FString::Printf(TEXT("Protected asset cannot be modified: %s"), *RigPath));
|
|
2174
|
+
}
|
|
1844
2175
|
FString MeshPath;
|
|
1845
2176
|
if (auto Err = RequireStringAlt(Params, TEXT("meshPath"), TEXT("skeletalMesh"), MeshPath)) return Err;
|
|
1846
2177
|
|
|
@@ -1877,9 +2208,18 @@ TSharedPtr<FJsonValue> FAnimationHandlers::SetIKRetargeterRig(const TSharedPtr<F
|
|
|
1877
2208
|
{
|
|
1878
2209
|
FString RetargeterPath;
|
|
1879
2210
|
if (auto Err = RequireStringAlt(Params, TEXT("retargeterPath"), TEXT("assetPath"), RetargeterPath)) return Err;
|
|
2211
|
+
if (MCPIsProtectedAssetPath(RetargeterPath))
|
|
2212
|
+
{
|
|
2213
|
+
return MCPError(FString::Printf(TEXT("Protected asset cannot be modified: %s"), *RetargeterPath));
|
|
2214
|
+
}
|
|
1880
2215
|
FString RigPath;
|
|
1881
2216
|
if (auto Err = RequireStringAlt(Params, TEXT("rigPath"), TEXT("ikRig"), RigPath)) return Err;
|
|
1882
2217
|
const FString Side = OptionalString(Params, TEXT("side"), TEXT("target"));
|
|
2218
|
+
if (!Side.Equals(TEXT("source"), ESearchCase::IgnoreCase)
|
|
2219
|
+
&& !Side.Equals(TEXT("target"), ESearchCase::IgnoreCase))
|
|
2220
|
+
{
|
|
2221
|
+
return MCPError(TEXT("'side' must be 'source' or 'target'"));
|
|
2222
|
+
}
|
|
1883
2223
|
|
|
1884
2224
|
UIKRetargeter* Retargeter = LoadObject<UIKRetargeter>(nullptr, *RetargeterPath);
|
|
1885
2225
|
if (!Retargeter) return MCPError(FString::Printf(TEXT("IKRetargeter not found: %s"), *RetargeterPath));
|
|
@@ -1888,8 +2228,38 @@ TSharedPtr<FJsonValue> FAnimationHandlers::SetIKRetargeterRig(const TSharedPtr<F
|
|
|
1888
2228
|
|
|
1889
2229
|
UIKRetargeterController* Controller = UIKRetargeterController::GetController(Retargeter);
|
|
1890
2230
|
if (!Controller) return MCPError(TEXT("IKRetargeterController unavailable"));
|
|
1891
|
-
|
|
1892
|
-
|
|
2231
|
+
const ERetargetSourceOrTarget SourceOrTarget = ParseSourceOrTarget(Side);
|
|
2232
|
+
bool bAssignmentFailed = false;
|
|
2233
|
+
{
|
|
2234
|
+
const FScopedTransaction Transaction(NSLOCTEXT("UE_MCP", "SetIKRetargeterRig", "Set IK Retargeter Rig"));
|
|
2235
|
+
Retargeter->Modify();
|
|
2236
|
+
#if UE_MCP_HAS_5_8_API
|
|
2237
|
+
if (Controller->GetNumRetargetOps() == 0)
|
|
2238
|
+
{
|
|
2239
|
+
Controller->AddDefaultOps();
|
|
2240
|
+
}
|
|
2241
|
+
Controller->SetIKRig(SourceOrTarget, IKRig);
|
|
2242
|
+
Controller->AssignIKRigToAllOps(SourceOrTarget, IKRig);
|
|
2243
|
+
Controller->CleanAsset();
|
|
2244
|
+
#else
|
|
2245
|
+
Controller->SetIKRig(SourceOrTarget, IKRig);
|
|
2246
|
+
#endif
|
|
2247
|
+
bAssignmentFailed = Controller->GetIKRig(SourceOrTarget) != IKRig;
|
|
2248
|
+
}
|
|
2249
|
+
if (bAssignmentFailed)
|
|
2250
|
+
{
|
|
2251
|
+
const bool bRolledBack = GEditor && GEditor->UndoTransaction();
|
|
2252
|
+
return MCPError(bRolledBack
|
|
2253
|
+
? TEXT("IK Retargeter rig assignment failed readback validation and was rolled back")
|
|
2254
|
+
: TEXT("IK Retargeter rig assignment failed readback validation and could not be rolled back"));
|
|
2255
|
+
}
|
|
2256
|
+
if (!SaveAssetPackage(Retargeter))
|
|
2257
|
+
{
|
|
2258
|
+
const bool bRolledBack = GEditor && GEditor->UndoTransaction();
|
|
2259
|
+
return MCPError(bRolledBack
|
|
2260
|
+
? TEXT("IK Retargeter rig assignment could not be saved and was rolled back")
|
|
2261
|
+
: TEXT("IK Retargeter rig assignment could not be saved and the editor transaction could not be rolled back"));
|
|
2262
|
+
}
|
|
1893
2263
|
|
|
1894
2264
|
auto Result = MCPSuccess();
|
|
1895
2265
|
MCPSetUpdated(Result);
|
|
@@ -1904,6 +2274,10 @@ TSharedPtr<FJsonValue> FAnimationHandlers::AutoAlignRetargetPose(const TSharedPt
|
|
|
1904
2274
|
{
|
|
1905
2275
|
FString RetargeterPath;
|
|
1906
2276
|
if (auto Err = RequireStringAlt(Params, TEXT("retargeterPath"), TEXT("assetPath"), RetargeterPath)) return Err;
|
|
2277
|
+
if (MCPIsProtectedAssetPath(RetargeterPath))
|
|
2278
|
+
{
|
|
2279
|
+
return MCPError(FString::Printf(TEXT("Protected asset cannot be modified: %s"), *RetargeterPath));
|
|
2280
|
+
}
|
|
1907
2281
|
const FString Side = OptionalString(Params, TEXT("side"), TEXT("target"));
|
|
1908
2282
|
|
|
1909
2283
|
UIKRetargeter* Retargeter = LoadObject<UIKRetargeter>(nullptr, *RetargeterPath);
|
|
@@ -1927,6 +2301,10 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ResetRetargetPose(const TSharedPtr<FJ
|
|
|
1927
2301
|
{
|
|
1928
2302
|
FString RetargeterPath;
|
|
1929
2303
|
if (auto Err = RequireStringAlt(Params, TEXT("retargeterPath"), TEXT("assetPath"), RetargeterPath)) return Err;
|
|
2304
|
+
if (MCPIsProtectedAssetPath(RetargeterPath))
|
|
2305
|
+
{
|
|
2306
|
+
return MCPError(FString::Printf(TEXT("Protected asset cannot be modified: %s"), *RetargeterPath));
|
|
2307
|
+
}
|
|
1930
2308
|
const FString Side = OptionalString(Params, TEXT("side"), TEXT("target"));
|
|
1931
2309
|
|
|
1932
2310
|
UIKRetargeter* Retargeter = LoadObject<UIKRetargeter>(nullptr, *RetargeterPath);
|
|
@@ -1950,6 +2328,13 @@ TSharedPtr<FJsonValue> FAnimationHandlers::ResetRetargetPose(const TSharedPtr<FJ
|
|
|
1950
2328
|
// ─── #701 batch_retarget_animations ─────────────────────────────────
|
|
1951
2329
|
TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TSharedPtr<FJsonObject>& Params)
|
|
1952
2330
|
{
|
|
2331
|
+
#if !(ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
|
|
2332
|
+
auto Result = MakeShared<FJsonObject>();
|
|
2333
|
+
Result->SetBoolField(TEXT("success"), false);
|
|
2334
|
+
Result->SetStringField(TEXT("errorCode"), TEXT("unsupported_engine_version"));
|
|
2335
|
+
Result->SetStringField(TEXT("error"), TEXT("batch_retarget_animations requires Unreal Engine 5.8 or newer"));
|
|
2336
|
+
return MCPResult(Result);
|
|
2337
|
+
#else
|
|
1953
2338
|
FString RetargeterPath;
|
|
1954
2339
|
if (auto Err = RequireStringAlt(Params, TEXT("retargeterPath"), TEXT("assetPath"), RetargeterPath)) return Err;
|
|
1955
2340
|
FString SourceMeshPath, TargetMeshPath;
|
|
@@ -1962,6 +2347,46 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TShared
|
|
|
1962
2347
|
if (!SourceMesh) return MCPError(FString::Printf(TEXT("Source mesh not found: %s"), *SourceMeshPath));
|
|
1963
2348
|
USkeletalMesh* TargetMesh = LoadObject<USkeletalMesh>(nullptr, *TargetMeshPath);
|
|
1964
2349
|
if (!TargetMesh) return MCPError(FString::Printf(TEXT("Target mesh not found: %s"), *TargetMeshPath));
|
|
2350
|
+
if (SourceMesh == TargetMesh) return MCPError(TEXT("sourceMesh and targetMesh must be different"));
|
|
2351
|
+
if (OptionalBool(Params, TEXT("overwrite"), false))
|
|
2352
|
+
{
|
|
2353
|
+
return MCPError(TEXT("overwrite=true is not supported; choose a new output name/path so existing assets are never replaced"));
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
bool bMappingInspectionAvailable = false;
|
|
2357
|
+
int32 TargetChainCount = 0;
|
|
2358
|
+
int32 MappedChainCount = 0;
|
|
2359
|
+
TArray<TSharedPtr<FJsonValue>> UnmappedTargetChains;
|
|
2360
|
+
if (UIKRetargeterController* Controller = UIKRetargeterController::GetController(Retargeter))
|
|
2361
|
+
{
|
|
2362
|
+
if (const UIKRigDefinition* TargetRig = Controller->GetIKRig(ERetargetSourceOrTarget::Target))
|
|
2363
|
+
{
|
|
2364
|
+
bMappingInspectionAvailable = true;
|
|
2365
|
+
for (const FBoneChain& TargetChain : TargetRig->GetRetargetChains())
|
|
2366
|
+
{
|
|
2367
|
+
++TargetChainCount;
|
|
2368
|
+
if (Controller->GetSourceChain(TargetChain.ChainName).IsNone())
|
|
2369
|
+
{
|
|
2370
|
+
UnmappedTargetChains.Add(MakeShared<FJsonValueString>(TargetChain.ChainName.ToString()));
|
|
2371
|
+
}
|
|
2372
|
+
else
|
|
2373
|
+
{
|
|
2374
|
+
++MappedChainCount;
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
const bool bRequireCompleteMapping = OptionalBool(Params, TEXT("requireCompleteMapping"), false);
|
|
2380
|
+
if (bRequireCompleteMapping && !bMappingInspectionAvailable)
|
|
2381
|
+
{
|
|
2382
|
+
return MCPError(TEXT("Cannot verify complete mapping because the retargeter has no target IK Rig"));
|
|
2383
|
+
}
|
|
2384
|
+
if (bRequireCompleteMapping && !UnmappedTargetChains.IsEmpty())
|
|
2385
|
+
{
|
|
2386
|
+
return MCPError(FString::Printf(
|
|
2387
|
+
TEXT("Retargeter has %d unmapped target chain(s) and requireCompleteMapping=true"),
|
|
2388
|
+
UnmappedTargetChains.Num()));
|
|
2389
|
+
}
|
|
1965
2390
|
|
|
1966
2391
|
const TArray<TSharedPtr<FJsonValue>>* AnimArr = nullptr;
|
|
1967
2392
|
if (!Params->TryGetArrayField(TEXT("animPaths"), AnimArr) || !AnimArr || AnimArr->Num() == 0)
|
|
@@ -1969,40 +2394,123 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TShared
|
|
|
1969
2394
|
return MCPError(TEXT("Missing 'animPaths' (array of AnimSequence paths to retarget)"));
|
|
1970
2395
|
}
|
|
1971
2396
|
|
|
1972
|
-
// The FIKRetargetBatchOperationInputs / UIKRetargetBatchOperation::RunBatchRetarget
|
|
1973
|
-
// batch API is UE 5.8+. The 5.7 batch-retarget API differs; rather than a
|
|
1974
|
-
// partial reimplementation, return a clear error below 5.8.
|
|
1975
|
-
#if !(ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 8))
|
|
1976
|
-
return MCPError(TEXT("batch_retarget_animations requires UE 5.8+ (the FIKRetargetBatchOperationInputs / RunBatchRetarget API is unavailable in this engine version)."));
|
|
1977
|
-
#else
|
|
1978
2397
|
FIKRetargetBatchOperationInputs Inputs;
|
|
1979
2398
|
Inputs.SourceMesh = SourceMesh;
|
|
1980
2399
|
Inputs.TargetMesh = TargetMesh;
|
|
1981
2400
|
Inputs.IKRetargetAsset = Retargeter;
|
|
1982
|
-
Inputs.bOverwriteExistingFiles =
|
|
2401
|
+
Inputs.bOverwriteExistingFiles = false;
|
|
2402
|
+
Inputs.bIncludeReferencedAssets = false;
|
|
1983
2403
|
Inputs.Prefix = OptionalString(Params, TEXT("prefix"));
|
|
1984
2404
|
Inputs.Suffix = OptionalString(Params, TEXT("suffix"), TEXT("_Retargeted"));
|
|
1985
2405
|
const FString TargetPath = OptionalString(Params, TEXT("outputPath"));
|
|
2406
|
+
if (!TargetPath.IsEmpty() && MCPIsProtectedAssetPath(TargetPath))
|
|
2407
|
+
{
|
|
2408
|
+
return MCPError(FString::Printf(TEXT("Protected output path is not allowed: %s"), *TargetPath));
|
|
2409
|
+
}
|
|
1986
2410
|
if (TargetPath.IsEmpty()) { Inputs.bUseSourcePath = true; }
|
|
1987
2411
|
else { Inputs.TargetPath = TargetPath; }
|
|
1988
2412
|
|
|
2413
|
+
TSet<FString> SeenPaths;
|
|
1989
2414
|
int32 Loaded = 0;
|
|
1990
|
-
for (
|
|
2415
|
+
for (int32 Index = 0; Index < AnimArr->Num(); ++Index)
|
|
1991
2416
|
{
|
|
2417
|
+
const TSharedPtr<FJsonValue>& V = (*AnimArr)[Index];
|
|
1992
2418
|
FString P;
|
|
1993
|
-
if (!V->TryGetString(P) || P.IsEmpty())
|
|
1994
|
-
if (UAnimSequence* Anim = LoadObject<UAnimSequence>(nullptr, *P))
|
|
2419
|
+
if (!V.IsValid() || !V->TryGetString(P) || P.IsEmpty())
|
|
1995
2420
|
{
|
|
1996
|
-
|
|
1997
|
-
++Loaded;
|
|
2421
|
+
return MCPError(FString::Printf(TEXT("animPaths[%d] must be a non-empty AnimSequence asset path"), Index));
|
|
1998
2422
|
}
|
|
2423
|
+
if (SeenPaths.Contains(P))
|
|
2424
|
+
{
|
|
2425
|
+
return MCPError(FString::Printf(TEXT("Duplicate animPaths entry: %s"), *P));
|
|
2426
|
+
}
|
|
2427
|
+
if (TargetPath.IsEmpty() && MCPIsProtectedAssetPath(P))
|
|
2428
|
+
{
|
|
2429
|
+
return MCPError(FString::Printf(TEXT("Cannot create a retargeted asset beside protected source path: %s"), *P));
|
|
2430
|
+
}
|
|
2431
|
+
UAnimSequence* Anim = LoadObject<UAnimSequence>(nullptr, *P);
|
|
2432
|
+
if (!Anim)
|
|
2433
|
+
{
|
|
2434
|
+
return MCPError(FString::Printf(TEXT("AnimSequence not found: %s"), *P));
|
|
2435
|
+
}
|
|
2436
|
+
if (!SourceMesh->GetSkeleton() || !Anim->GetSkeleton()
|
|
2437
|
+
|| !SourceMesh->GetSkeleton()->IsCompatibleForEditor(Anim->GetSkeleton()))
|
|
2438
|
+
{
|
|
2439
|
+
return MCPError(FString::Printf(
|
|
2440
|
+
TEXT("AnimSequence skeleton is incompatible with sourceMesh: %s"), *P));
|
|
2441
|
+
}
|
|
2442
|
+
SeenPaths.Add(P);
|
|
2443
|
+
Inputs.AssetsToRetarget.Add(FAssetData(Anim));
|
|
2444
|
+
++Loaded;
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
FScopedBatchRetargetEditorInstanceRestore RestoreEditorInstances(Retargeter);
|
|
2448
|
+
FIKRetargetProcessor ValidationProcessor;
|
|
2449
|
+
FRetargetInitParameters ValidationParameters;
|
|
2450
|
+
ValidationParameters.SourceSkeletalMesh = SourceMesh;
|
|
2451
|
+
ValidationParameters.TargetSkeletalMesh = TargetMesh;
|
|
2452
|
+
ValidationParameters.RetargeterAsset = Retargeter;
|
|
2453
|
+
ValidationParameters.bSuppressWarnings = false;
|
|
2454
|
+
ValidationProcessor.Initialize(ValidationParameters);
|
|
2455
|
+
const TArray<FText> ValidationErrors = ValidationProcessor.Log.GetErrors();
|
|
2456
|
+
if (!ValidationProcessor.IsInitialized() || !ValidationErrors.IsEmpty())
|
|
2457
|
+
{
|
|
2458
|
+
return MCPError(!ValidationErrors.IsEmpty()
|
|
2459
|
+
? FString::Printf(TEXT("IK Retargeter processor validation failed: %s"), *ValidationErrors[0].ToString())
|
|
2460
|
+
: TEXT("IK Retargeter processor validation failed to initialize"));
|
|
1999
2461
|
}
|
|
2000
|
-
if (Loaded == 0) return MCPError(TEXT("No valid AnimSequences resolved from animPaths"));
|
|
2001
2462
|
|
|
2002
2463
|
const TArray<FAssetData> Created = UIKRetargetBatchOperation::RunBatchRetarget(Inputs);
|
|
2464
|
+
auto DeleteCreatedAssets = [&Created]()
|
|
2465
|
+
{
|
|
2466
|
+
TArray<FString> ResidualPaths;
|
|
2467
|
+
for (const FAssetData& AssetData : Created)
|
|
2468
|
+
{
|
|
2469
|
+
const FString ObjectPath = AssetData.GetObjectPathString();
|
|
2470
|
+
if (ObjectPath.IsEmpty())
|
|
2471
|
+
{
|
|
2472
|
+
ResidualPaths.Add(TEXT("<unknown output path>"));
|
|
2473
|
+
continue;
|
|
2474
|
+
}
|
|
2475
|
+
UEditorAssetLibrary::DeleteAsset(ObjectPath);
|
|
2476
|
+
if (UEditorAssetLibrary::DoesAssetExist(ObjectPath)) ResidualPaths.Add(ObjectPath);
|
|
2477
|
+
}
|
|
2478
|
+
return ResidualPaths;
|
|
2479
|
+
};
|
|
2480
|
+
if (Created.Num() != Loaded)
|
|
2481
|
+
{
|
|
2482
|
+
const TArray<FString> ResidualPaths = DeleteCreatedAssets();
|
|
2483
|
+
if (!ResidualPaths.IsEmpty())
|
|
2484
|
+
{
|
|
2485
|
+
return MCPError(FString::Printf(
|
|
2486
|
+
TEXT("Batch retarget created %d of %d requested assets and cleanup failed for: %s"),
|
|
2487
|
+
Created.Num(), Loaded, *FString::Join(ResidualPaths, TEXT(", "))));
|
|
2488
|
+
}
|
|
2489
|
+
return MCPError(FString::Printf(
|
|
2490
|
+
TEXT("Batch retarget created %d of %d requested assets; all new outputs were deleted"),
|
|
2491
|
+
Created.Num(), Loaded));
|
|
2492
|
+
}
|
|
2003
2493
|
|
|
2004
2494
|
TArray<TSharedPtr<FJsonValue>> OutPaths;
|
|
2005
|
-
for (const FAssetData& AD : Created)
|
|
2495
|
+
for (const FAssetData& AD : Created)
|
|
2496
|
+
{
|
|
2497
|
+
UObject* CreatedAsset = AD.GetAsset();
|
|
2498
|
+
if (!CreatedAsset || !SaveAssetPackage(CreatedAsset))
|
|
2499
|
+
{
|
|
2500
|
+
const FString FailedPath = AD.GetObjectPathString();
|
|
2501
|
+
const TArray<FString> ResidualPaths = DeleteCreatedAssets();
|
|
2502
|
+
if (!ResidualPaths.IsEmpty())
|
|
2503
|
+
{
|
|
2504
|
+
return MCPError(FString::Printf(
|
|
2505
|
+
TEXT("Failed to save retargeted asset '%s' and cleanup failed for: %s"),
|
|
2506
|
+
*FailedPath, *FString::Join(ResidualPaths, TEXT(", "))));
|
|
2507
|
+
}
|
|
2508
|
+
return MCPError(FString::Printf(
|
|
2509
|
+
TEXT("Failed to save retargeted asset '%s'; all new outputs were deleted"),
|
|
2510
|
+
*FailedPath));
|
|
2511
|
+
}
|
|
2512
|
+
OutPaths.Add(MakeShared<FJsonValueString>(AD.GetObjectPathString()));
|
|
2513
|
+
}
|
|
2006
2514
|
|
|
2007
2515
|
auto Result = MCPSuccess();
|
|
2008
2516
|
MCPSetCreated(Result);
|
|
@@ -2010,6 +2518,25 @@ TSharedPtr<FJsonValue> FAnimationHandlers::BatchRetargetAnimations(const TShared
|
|
|
2010
2518
|
Result->SetNumberField(TEXT("requested"), Loaded);
|
|
2011
2519
|
Result->SetNumberField(TEXT("createdCount"), OutPaths.Num());
|
|
2012
2520
|
Result->SetArrayField(TEXT("createdAssets"), OutPaths);
|
|
2521
|
+
Result->SetBoolField(TEXT("includedReferencedAssets"), false);
|
|
2522
|
+
Result->SetBoolField(TEXT("requireCompleteMapping"), bRequireCompleteMapping);
|
|
2523
|
+
Result->SetBoolField(TEXT("mappingInspectionAvailable"), bMappingInspectionAvailable);
|
|
2524
|
+
if (bMappingInspectionAvailable)
|
|
2525
|
+
{
|
|
2526
|
+
Result->SetNumberField(TEXT("targetChainCount"), TargetChainCount);
|
|
2527
|
+
Result->SetNumberField(TEXT("mappedChainCount"), MappedChainCount);
|
|
2528
|
+
Result->SetArrayField(TEXT("unmappedTargetChains"), UnmappedTargetChains);
|
|
2529
|
+
Result->SetBoolField(TEXT("mappingComplete"), UnmappedTargetChains.IsEmpty());
|
|
2530
|
+
if (!UnmappedTargetChains.IsEmpty())
|
|
2531
|
+
{
|
|
2532
|
+
Result->SetStringField(TEXT("mappingWarning"), FString::Printf(
|
|
2533
|
+
TEXT("Retarget completed with %d unmapped target chain(s); inspect deformation and compare sampled poses before accepting the output"),
|
|
2534
|
+
UnmappedTargetChains.Num()));
|
|
2535
|
+
}
|
|
2536
|
+
}
|
|
2537
|
+
TSharedPtr<FJsonObject> RollbackPayload = MakeShared<FJsonObject>();
|
|
2538
|
+
RollbackPayload->SetArrayField(TEXT("assetPaths"), OutPaths);
|
|
2539
|
+
MCPSetRollback(Result, TEXT("delete_asset_batch"), RollbackPayload);
|
|
2013
2540
|
return MCPResult(Result);
|
|
2014
2541
|
#endif
|
|
2015
2542
|
}
|