ue-mcp 1.2.4 → 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/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/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/project.js +47 -20
- package/dist/tools/project.js.map +1 -1
- package/dist/types.d.ts +21 -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
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
#include "Materials/MaterialExpressionFresnel.h"
|
|
34
34
|
#include "Factories/MaterialInstanceConstantFactoryNew.h"
|
|
35
35
|
#include "Factories/MaterialFactoryNew.h"
|
|
36
|
+
#include "MaterialEditingLibrary.h"
|
|
36
37
|
#include "AssetToolsModule.h"
|
|
37
38
|
#include "IAssetTools.h"
|
|
38
39
|
#include "EditorScriptingUtilities/Public/EditorAssetLibrary.h"
|
|
@@ -87,6 +88,9 @@ void FMaterialHandlers::RegisterHandlers(FMCPHandlerRegistry& Registry)
|
|
|
87
88
|
Registry.RegisterHandler(TEXT("begin_material_transaction"), &BeginMaterialTransaction);
|
|
88
89
|
Registry.RegisterHandler(TEXT("end_material_transaction"), &EndMaterialTransaction);
|
|
89
90
|
|
|
91
|
+
// #946: texture-set build with automatic virtual/UDIM sampler selection.
|
|
92
|
+
Registry.RegisterHandler(TEXT("build_material"), &BuildMaterial);
|
|
93
|
+
|
|
90
94
|
Registry.RegisterHandler(TEXT("create_material_simple"), &CreateMaterialSimple);
|
|
91
95
|
Registry.RegisterHandler(TEXT("set_material_usage"), &SetMaterialUsage);
|
|
92
96
|
|
|
@@ -162,6 +166,108 @@ namespace
|
|
|
162
166
|
return Obj;
|
|
163
167
|
}
|
|
164
168
|
|
|
169
|
+
// A colour object, in any of the spellings a client might use: {r,g,b,a},
|
|
170
|
+
// {R,G,B,A} or {x,y,z,w}. Missing components fall back to 0, with alpha 1.
|
|
171
|
+
bool TryReadMaterialColorObject(const TSharedPtr<FJsonObject>& Obj, FLinearColor& OutColor)
|
|
172
|
+
{
|
|
173
|
+
if (!Obj.IsValid()) return false;
|
|
174
|
+
double R = 0.0, G = 0.0, B = 0.0, A = 1.0;
|
|
175
|
+
bool bAny = false;
|
|
176
|
+
auto Pick = [&Obj, &bAny](const TCHAR* Lower, const TCHAR* Upper, const TCHAR* Alt, double& Slot)
|
|
177
|
+
{
|
|
178
|
+
double V = 0.0;
|
|
179
|
+
if (Obj->TryGetNumberField(Lower, V) || Obj->TryGetNumberField(Upper, V) || Obj->TryGetNumberField(Alt, V))
|
|
180
|
+
{
|
|
181
|
+
Slot = V;
|
|
182
|
+
bAny = true;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
Pick(TEXT("r"), TEXT("R"), TEXT("x"), R);
|
|
186
|
+
Pick(TEXT("g"), TEXT("G"), TEXT("y"), G);
|
|
187
|
+
Pick(TEXT("b"), TEXT("B"), TEXT("z"), B);
|
|
188
|
+
Pick(TEXT("a"), TEXT("A"), TEXT("w"), A);
|
|
189
|
+
if (!bAny) return false;
|
|
190
|
+
OutColor = FLinearColor((float)R, (float)G, (float)B, (float)A);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// #952: a colour arrives in whatever shape the calling client serialised it
|
|
195
|
+
// in - an object {r,g,b,a}, an array [r,g,b,a], a re-encoded JSON string, or
|
|
196
|
+
// UE struct text "(R=..,G=..,B=..,A=..)". Accept every one of them under a
|
|
197
|
+
// single named field so no caller has to guess the wire format.
|
|
198
|
+
bool TryParseMaterialColorField(
|
|
199
|
+
const TSharedPtr<FJsonObject>& Params,
|
|
200
|
+
const TCHAR* FieldName,
|
|
201
|
+
FLinearColor& OutColor)
|
|
202
|
+
{
|
|
203
|
+
if (!Params.IsValid()) return false;
|
|
204
|
+
|
|
205
|
+
const TSharedPtr<FJsonObject>* AsObject = nullptr;
|
|
206
|
+
if (Params->TryGetObjectField(FieldName, AsObject) && AsObject)
|
|
207
|
+
{
|
|
208
|
+
return TryReadMaterialColorObject(*AsObject, OutColor);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const TArray<TSharedPtr<FJsonValue>>* AsArray = nullptr;
|
|
212
|
+
if (Params->TryGetArrayField(FieldName, AsArray) && AsArray && AsArray->Num() > 0)
|
|
213
|
+
{
|
|
214
|
+
double Components[4] = { 0.0, 0.0, 0.0, 1.0 };
|
|
215
|
+
for (int32 Index = 0; Index < AsArray->Num() && Index < 4; ++Index)
|
|
216
|
+
{
|
|
217
|
+
(*AsArray)[Index]->TryGetNumber(Components[Index]);
|
|
218
|
+
}
|
|
219
|
+
OutColor = FLinearColor((float)Components[0], (float)Components[1], (float)Components[2], (float)Components[3]);
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
FString AsString;
|
|
224
|
+
if (Params->TryGetStringField(FieldName, AsString) && !AsString.IsEmpty())
|
|
225
|
+
{
|
|
226
|
+
TSharedPtr<FJsonObject> Reparsed;
|
|
227
|
+
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(AsString);
|
|
228
|
+
if (FJsonSerializer::Deserialize(Reader, Reparsed) && TryReadMaterialColorObject(Reparsed, OutColor))
|
|
229
|
+
{
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
FLinearColor Parsed;
|
|
233
|
+
if (Parsed.InitFromString(AsString))
|
|
234
|
+
{
|
|
235
|
+
OutColor = Parsed;
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// #952: agents reach for `color` as readily as `value` when the parameter is
|
|
244
|
+
// a colour, and rejecting the synonym is indistinguishable from the whole
|
|
245
|
+
// action being unsupported. Try both, and report which one was honoured.
|
|
246
|
+
bool TryParseMaterialColorParam(
|
|
247
|
+
const TSharedPtr<FJsonObject>& Params,
|
|
248
|
+
FLinearColor& OutColor,
|
|
249
|
+
FString& OutSourceField,
|
|
250
|
+
bool bAllowTopLevelComponents = false)
|
|
251
|
+
{
|
|
252
|
+
static const TCHAR* Candidates[] = { TEXT("value"), TEXT("color"), TEXT("colour") };
|
|
253
|
+
for (const TCHAR* Candidate : Candidates)
|
|
254
|
+
{
|
|
255
|
+
if (TryParseMaterialColorField(Params, Candidate, OutColor))
|
|
256
|
+
{
|
|
257
|
+
OutSourceField = Candidate;
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Raw bridge callers reach the handler without the tool schema in front
|
|
262
|
+
// of them and have historically passed the components at the top level.
|
|
263
|
+
if (bAllowTopLevelComponents && TryReadMaterialColorObject(Params, OutColor))
|
|
264
|
+
{
|
|
265
|
+
OutSourceField = TEXT("(top-level components)");
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
|
|
165
271
|
TSharedPtr<FJsonObject> MaterialInstanceOverrideCounts(UMaterialInstanceConstant* Instance)
|
|
166
272
|
{
|
|
167
273
|
TSharedPtr<FJsonObject> Counts = MakeShared<FJsonObject>();
|
|
@@ -248,6 +354,170 @@ namespace
|
|
|
248
354
|
Result->SetObjectField(TEXT("overrideCounts"), MaterialInstanceOverrideCounts(Instance));
|
|
249
355
|
Result->SetNumberField(TEXT("overrideCount"), CountTotalMaterialInstanceOverrides(Instance));
|
|
250
356
|
}
|
|
357
|
+
|
|
358
|
+
// #952: `read` and `list_parameters` used to insist on a base UMaterial, so
|
|
359
|
+
// the asset an agent actually edits - the instance - could be written to and
|
|
360
|
+
// never read back, and verifying a write meant dropping to python. Report
|
|
361
|
+
// what the instance resolves to now, what its parent would give it, and
|
|
362
|
+
// which of the two the instance is actually overriding.
|
|
363
|
+
void AddMaterialInterfaceParameters(TSharedPtr<FJsonObject> Result, UMaterialInterface* Material)
|
|
364
|
+
{
|
|
365
|
+
if (!Result.IsValid() || !Material)
|
|
366
|
+
{
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
UMaterialInstance* Instance = Cast<UMaterialInstance>(Material);
|
|
371
|
+
UMaterialInterface* Parent = nullptr;
|
|
372
|
+
if (Instance)
|
|
373
|
+
{
|
|
374
|
+
Parent = Instance->Parent;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
{
|
|
378
|
+
TArray<FMaterialParameterInfo> Infos;
|
|
379
|
+
TArray<FGuid> Guids;
|
|
380
|
+
Material->GetAllScalarParameterInfo(Infos, Guids);
|
|
381
|
+
TArray<TSharedPtr<FJsonValue>> Entries;
|
|
382
|
+
for (int32 Index = 0; Index < Infos.Num(); ++Index)
|
|
383
|
+
{
|
|
384
|
+
const FMaterialParameterInfo& Info = Infos[Index];
|
|
385
|
+
TSharedPtr<FJsonObject> Obj = MaterialParameterInfoToJson(Info);
|
|
386
|
+
|
|
387
|
+
float Value = 0.0f;
|
|
388
|
+
if (Material->GetScalarParameterValue(Info, Value))
|
|
389
|
+
{
|
|
390
|
+
Obj->SetNumberField(TEXT("value"), Value);
|
|
391
|
+
}
|
|
392
|
+
float Default = 0.0f;
|
|
393
|
+
if (Parent && Parent->GetScalarParameterValue(Info, Default))
|
|
394
|
+
{
|
|
395
|
+
Obj->SetNumberField(TEXT("defaultValue"), Default);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
bool bOverridden = false;
|
|
399
|
+
if (Instance)
|
|
400
|
+
{
|
|
401
|
+
for (const FScalarParameterValue& Override : Instance->ScalarParameterValues)
|
|
402
|
+
{
|
|
403
|
+
if (Override.ParameterInfo == Info) { bOverridden = true; break; }
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
Obj->SetBoolField(TEXT("overridden"), bOverridden);
|
|
407
|
+
if (Guids.IsValidIndex(Index))
|
|
408
|
+
{
|
|
409
|
+
Obj->SetStringField(TEXT("expressionGuid"), Guids[Index].ToString(EGuidFormats::DigitsWithHyphens));
|
|
410
|
+
}
|
|
411
|
+
Entries.Add(MakeShared<FJsonValueObject>(Obj));
|
|
412
|
+
}
|
|
413
|
+
Result->SetArrayField(TEXT("scalarParameters"), Entries);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
{
|
|
417
|
+
TArray<FMaterialParameterInfo> Infos;
|
|
418
|
+
TArray<FGuid> Guids;
|
|
419
|
+
Material->GetAllVectorParameterInfo(Infos, Guids);
|
|
420
|
+
TArray<TSharedPtr<FJsonValue>> Entries;
|
|
421
|
+
for (int32 Index = 0; Index < Infos.Num(); ++Index)
|
|
422
|
+
{
|
|
423
|
+
const FMaterialParameterInfo& Info = Infos[Index];
|
|
424
|
+
TSharedPtr<FJsonObject> Obj = MaterialParameterInfoToJson(Info);
|
|
425
|
+
|
|
426
|
+
FLinearColor Value;
|
|
427
|
+
if (Material->GetVectorParameterValue(Info, Value))
|
|
428
|
+
{
|
|
429
|
+
Obj->SetObjectField(TEXT("value"), LinearColorToJson(Value));
|
|
430
|
+
}
|
|
431
|
+
FLinearColor Default;
|
|
432
|
+
if (Parent && Parent->GetVectorParameterValue(Info, Default))
|
|
433
|
+
{
|
|
434
|
+
Obj->SetObjectField(TEXT("defaultValue"), LinearColorToJson(Default));
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
bool bOverridden = false;
|
|
438
|
+
if (Instance)
|
|
439
|
+
{
|
|
440
|
+
for (const FVectorParameterValue& Override : Instance->VectorParameterValues)
|
|
441
|
+
{
|
|
442
|
+
if (Override.ParameterInfo == Info) { bOverridden = true; break; }
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
Obj->SetBoolField(TEXT("overridden"), bOverridden);
|
|
446
|
+
if (Guids.IsValidIndex(Index))
|
|
447
|
+
{
|
|
448
|
+
Obj->SetStringField(TEXT("expressionGuid"), Guids[Index].ToString(EGuidFormats::DigitsWithHyphens));
|
|
449
|
+
}
|
|
450
|
+
Entries.Add(MakeShared<FJsonValueObject>(Obj));
|
|
451
|
+
}
|
|
452
|
+
Result->SetArrayField(TEXT("vectorParameters"), Entries);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
{
|
|
456
|
+
TArray<FMaterialParameterInfo> Infos;
|
|
457
|
+
TArray<FGuid> Guids;
|
|
458
|
+
Material->GetAllTextureParameterInfo(Infos, Guids);
|
|
459
|
+
TArray<TSharedPtr<FJsonValue>> Entries;
|
|
460
|
+
for (int32 Index = 0; Index < Infos.Num(); ++Index)
|
|
461
|
+
{
|
|
462
|
+
const FMaterialParameterInfo& Info = Infos[Index];
|
|
463
|
+
TSharedPtr<FJsonObject> Obj = MaterialParameterInfoToJson(Info);
|
|
464
|
+
|
|
465
|
+
UTexture* Value = nullptr;
|
|
466
|
+
if (Material->GetTextureParameterValue(Info, Value) && Value)
|
|
467
|
+
{
|
|
468
|
+
Obj->SetStringField(TEXT("value"), Value->GetPathName());
|
|
469
|
+
}
|
|
470
|
+
UTexture* Default = nullptr;
|
|
471
|
+
if (Parent && Parent->GetTextureParameterValue(Info, Default) && Default)
|
|
472
|
+
{
|
|
473
|
+
Obj->SetStringField(TEXT("defaultValue"), Default->GetPathName());
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
bool bOverridden = false;
|
|
477
|
+
if (Instance)
|
|
478
|
+
{
|
|
479
|
+
for (const FTextureParameterValue& Override : Instance->TextureParameterValues)
|
|
480
|
+
{
|
|
481
|
+
if (Override.ParameterInfo == Info) { bOverridden = true; break; }
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
Obj->SetBoolField(TEXT("overridden"), bOverridden);
|
|
485
|
+
if (Guids.IsValidIndex(Index))
|
|
486
|
+
{
|
|
487
|
+
Obj->SetStringField(TEXT("expressionGuid"), Guids[Index].ToString(EGuidFormats::DigitsWithHyphens));
|
|
488
|
+
}
|
|
489
|
+
Entries.Add(MakeShared<FJsonValueObject>(Obj));
|
|
490
|
+
}
|
|
491
|
+
Result->SetArrayField(TEXT("textureParameters"), Entries);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Identity + shading summary shared by `read` and `list_parameters` when the
|
|
496
|
+
// asset is not a base UMaterial.
|
|
497
|
+
void SetMaterialInterfaceIdentityFields(TSharedPtr<FJsonObject> Result, UMaterialInterface* Material)
|
|
498
|
+
{
|
|
499
|
+
if (!Result.IsValid() || !Material)
|
|
500
|
+
{
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
Result->SetStringField(TEXT("name"), Material->GetName());
|
|
505
|
+
Result->SetStringField(TEXT("path"), Material->GetPathName());
|
|
506
|
+
Result->SetStringField(TEXT("assetType"), Material->GetClass()->GetName());
|
|
507
|
+
if (UMaterialInstance* Instance = Cast<UMaterialInstance>(Material))
|
|
508
|
+
{
|
|
509
|
+
Result->SetStringField(TEXT("parentPath"), Instance->Parent ? Instance->Parent->GetPathName() : FString());
|
|
510
|
+
}
|
|
511
|
+
if (UMaterial* Base = Material->GetMaterial())
|
|
512
|
+
{
|
|
513
|
+
Result->SetStringField(TEXT("baseMaterialPath"), Base->GetPathName());
|
|
514
|
+
}
|
|
515
|
+
if (UMaterialInstanceConstant* Constant = Cast<UMaterialInstanceConstant>(Material))
|
|
516
|
+
{
|
|
517
|
+
Result->SetObjectField(TEXT("overrideCounts"), MaterialInstanceOverrideCounts(Constant));
|
|
518
|
+
Result->SetNumberField(TEXT("overrideCount"), CountTotalMaterialInstanceOverrides(Constant));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
251
521
|
}
|
|
252
522
|
|
|
253
523
|
EMaterialShadingModel FMaterialHandlers::ParseShadingModel(const FString& ShadingModelStr)
|
|
@@ -433,12 +703,34 @@ TSharedPtr<FJsonValue> FMaterialHandlers::ReadMaterial(const TSharedPtr<FJsonObj
|
|
|
433
703
|
UMaterial* Material = LoadMaterialFromPath(AssetPath);
|
|
434
704
|
if (!Material)
|
|
435
705
|
{
|
|
436
|
-
|
|
706
|
+
// #952: a MaterialInstance is a material as far as a caller is concerned.
|
|
707
|
+
// It has no expression graph of its own, so answer with what it does
|
|
708
|
+
// have: its lineage, its shading setup, and every parameter it resolves,
|
|
709
|
+
// with the ones it overrides marked.
|
|
710
|
+
UMaterialInterface* Interface = LoadAssetByPath<UMaterialInterface>(AssetPath);
|
|
711
|
+
if (!Interface)
|
|
712
|
+
{
|
|
713
|
+
return MCPError(FString::Printf(TEXT("Failed to load material or material instance at '%s'"), *AssetPath));
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
auto InstanceResult = MCPSuccess();
|
|
717
|
+
SetMaterialInterfaceIdentityFields(InstanceResult, Interface);
|
|
718
|
+
InstanceResult->SetStringField(TEXT("shadingModel"), ShadingModelToString(Interface->GetShadingModels().GetFirstShadingModel()));
|
|
719
|
+
InstanceResult->SetStringField(TEXT("blendMode"), StaticEnum<EBlendMode>()->GetNameStringByValue((int64)Interface->GetBlendMode()));
|
|
720
|
+
InstanceResult->SetBoolField(TEXT("twoSided"), Interface->IsTwoSided());
|
|
721
|
+
AddMaterialInterfaceParameters(InstanceResult, Interface);
|
|
722
|
+
InstanceResult->SetArrayField(TEXT("staticSwitches"), MaterialStaticSwitchesToJson(Interface));
|
|
723
|
+
// The graph lives on the base material; say so rather than returning an
|
|
724
|
+
// empty expression list that reads like a material with no nodes.
|
|
725
|
+
InstanceResult->SetStringField(TEXT("expressionsNote"),
|
|
726
|
+
TEXT("A MaterialInstance has no expression graph. Read baseMaterialPath for the graph."));
|
|
727
|
+
return MCPResult(InstanceResult);
|
|
437
728
|
}
|
|
438
729
|
|
|
439
730
|
auto Result = MCPSuccess();
|
|
440
731
|
Result->SetStringField(TEXT("name"), Material->GetName());
|
|
441
732
|
Result->SetStringField(TEXT("path"), Material->GetPathName());
|
|
733
|
+
Result->SetStringField(TEXT("assetType"), Material->GetClass()->GetName());
|
|
442
734
|
Result->SetStringField(TEXT("shadingModel"), ShadingModelToString(Material->GetShadingModels().GetFirstShadingModel()));
|
|
443
735
|
Result->SetStringField(TEXT("blendMode"), StaticEnum<EBlendMode>()->GetNameStringByValue((int64)Material->BlendMode));
|
|
444
736
|
Result->SetBoolField(TEXT("twoSided"), Material->IsTwoSided());
|
|
@@ -1161,7 +1453,20 @@ TSharedPtr<FJsonValue> FMaterialHandlers::ListMaterialParameters(const TSharedPt
|
|
|
1161
1453
|
UMaterial* Material = LoadMaterialFromPath(AssetPath);
|
|
1162
1454
|
if (!Material)
|
|
1163
1455
|
{
|
|
1164
|
-
|
|
1456
|
+
// #952: on a MaterialInstance report the resolved value of every
|
|
1457
|
+
// parameter, the parent's value, and which ones this instance overrides,
|
|
1458
|
+
// so a write can be verified without an execute_python round trip.
|
|
1459
|
+
UMaterialInterface* Interface = LoadAssetByPath<UMaterialInterface>(AssetPath);
|
|
1460
|
+
if (!Interface)
|
|
1461
|
+
{
|
|
1462
|
+
return MCPError(FString::Printf(TEXT("Failed to load material or material instance at '%s'"), *AssetPath));
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
auto InstanceResult = MCPSuccess();
|
|
1466
|
+
SetMaterialInterfaceIdentityFields(InstanceResult, Interface);
|
|
1467
|
+
AddMaterialInterfaceParameters(InstanceResult, Interface);
|
|
1468
|
+
InstanceResult->SetArrayField(TEXT("staticSwitches"), MaterialStaticSwitchesToJson(Interface));
|
|
1469
|
+
return MCPResult(InstanceResult);
|
|
1165
1470
|
}
|
|
1166
1471
|
|
|
1167
1472
|
TArray<TSharedPtr<FJsonValue>> ScalarParams;
|
|
@@ -1210,6 +1515,7 @@ TSharedPtr<FJsonValue> FMaterialHandlers::ListMaterialParameters(const TSharedPt
|
|
|
1210
1515
|
Result->SetArrayField(TEXT("vectorParameters"), VectorParams);
|
|
1211
1516
|
Result->SetArrayField(TEXT("textureParameters"), TextureParams);
|
|
1212
1517
|
Result->SetStringField(TEXT("path"), Material->GetPathName());
|
|
1518
|
+
Result->SetStringField(TEXT("assetType"), Material->GetClass()->GetName());
|
|
1213
1519
|
|
|
1214
1520
|
return MCPResult(Result);
|
|
1215
1521
|
}
|
|
@@ -1368,22 +1674,39 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1368
1674
|
}
|
|
1369
1675
|
}
|
|
1370
1676
|
|
|
1677
|
+
// The association decides which parameter a name refers to once material
|
|
1678
|
+
// layers are in play, and it has to be identical on the write and on the
|
|
1679
|
+
// read-back or the verify step reads a different parameter than it wrote.
|
|
1680
|
+
const EMaterialParameterAssociation Association = ParseMaterialParameterAssociation(
|
|
1681
|
+
OptionalString(Params, TEXT("association"), TEXT("Global")));
|
|
1682
|
+
const FMaterialParameterInfo ParameterInfo(FName(*ParameterName), Association);
|
|
1683
|
+
const FString AssociationName = MaterialParameterAssociationToString(Association);
|
|
1684
|
+
|
|
1371
1685
|
// Auto-detect parameter type if not provided
|
|
1372
1686
|
if (ParameterType.IsEmpty())
|
|
1373
1687
|
{
|
|
1374
1688
|
// Check which parameter collections contain this name
|
|
1375
|
-
FName ParamFName(*ParameterName);
|
|
1376
1689
|
float ScalarVal;
|
|
1377
1690
|
FLinearColor VectorVal;
|
|
1378
1691
|
UTexture* TextureVal;
|
|
1379
|
-
if (MaterialInstance->GetScalarParameterValue(
|
|
1692
|
+
if (MaterialInstance->GetScalarParameterValue(ParameterInfo, ScalarVal))
|
|
1380
1693
|
ParameterType = TEXT("scalar");
|
|
1381
|
-
else if (MaterialInstance->GetVectorParameterValue(
|
|
1694
|
+
else if (MaterialInstance->GetVectorParameterValue(ParameterInfo, VectorVal))
|
|
1382
1695
|
ParameterType = TEXT("vector");
|
|
1383
|
-
else if (MaterialInstance->GetTextureParameterValue(
|
|
1696
|
+
else if (MaterialInstance->GetTextureParameterValue(ParameterInfo, TextureVal))
|
|
1384
1697
|
ParameterType = TEXT("texture");
|
|
1385
1698
|
else
|
|
1386
|
-
|
|
1699
|
+
{
|
|
1700
|
+
// The parent does not declare the name, so nothing can be looked
|
|
1701
|
+
// up. Fall back on the shape of the payload: a colour is only ever
|
|
1702
|
+
// meant for a vector parameter, and guessing scalar there produced
|
|
1703
|
+
// the "missing value" rejection reported in #952.
|
|
1704
|
+
FLinearColor Probe;
|
|
1705
|
+
FString ProbeField;
|
|
1706
|
+
ParameterType = TryParseMaterialColorParam(Params, Probe, ProbeField)
|
|
1707
|
+
? TEXT("vector")
|
|
1708
|
+
: TEXT("scalar");
|
|
1709
|
+
}
|
|
1387
1710
|
}
|
|
1388
1711
|
|
|
1389
1712
|
FString TypeLower = ParameterType.ToLower();
|
|
@@ -1397,11 +1720,12 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1397
1720
|
}
|
|
1398
1721
|
|
|
1399
1722
|
float PrevScalar = 0.0f;
|
|
1400
|
-
const bool bHadPrev = MaterialInstance->GetScalarParameterValue(
|
|
1723
|
+
const bool bHadPrev = MaterialInstance->GetScalarParameterValue(ParameterInfo, PrevScalar);
|
|
1401
1724
|
|
|
1402
1725
|
auto Result = MCPSuccess();
|
|
1403
1726
|
Result->SetStringField(TEXT("parameterName"), ParameterName);
|
|
1404
1727
|
Result->SetStringField(TEXT("parameterType"), TEXT("scalar"));
|
|
1728
|
+
Result->SetStringField(TEXT("association"), AssociationName);
|
|
1405
1729
|
Result->SetNumberField(TEXT("value"), ScalarValue);
|
|
1406
1730
|
Result->SetStringField(TEXT("path"), MaterialInstance->GetPathName());
|
|
1407
1731
|
|
|
@@ -1409,11 +1733,31 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1409
1733
|
{
|
|
1410
1734
|
MCPSetExisted(Result);
|
|
1411
1735
|
Result->SetBoolField(TEXT("updated"), false);
|
|
1736
|
+
Result->SetNumberField(TEXT("readBack"), PrevScalar);
|
|
1412
1737
|
return MCPResult(Result);
|
|
1413
1738
|
}
|
|
1414
1739
|
|
|
1415
|
-
MaterialInstance->
|
|
1416
|
-
|
|
1740
|
+
MaterialInstance->Modify(true);
|
|
1741
|
+
const bool bApplied = UMaterialEditingLibrary::SetMaterialInstanceScalarParameterValue(
|
|
1742
|
+
MaterialInstance, FName(*ParameterName), static_cast<float>(ScalarValue), Association);
|
|
1743
|
+
if (!bApplied)
|
|
1744
|
+
{
|
|
1745
|
+
// The library declines a name the parent does not declare. Writing
|
|
1746
|
+
// the override anyway is what this handler has always done, and a
|
|
1747
|
+
// parameter added to the parent later then picks it up.
|
|
1748
|
+
MaterialInstance->SetScalarParameterValueEditorOnly(ParameterInfo, static_cast<float>(ScalarValue));
|
|
1749
|
+
UMaterialEditingLibrary::UpdateMaterialInstance(MaterialInstance);
|
|
1750
|
+
}
|
|
1751
|
+
Result->SetBoolField(TEXT("declaredByParent"), bApplied);
|
|
1752
|
+
|
|
1753
|
+
// Persist, then read the value straight back off the instance so the
|
|
1754
|
+
// caller never has to take "success" on trust (#952).
|
|
1755
|
+
Result->SetBoolField(TEXT("saved"), SaveAssetPackage(MaterialInstance));
|
|
1756
|
+
float ReadBack = 0.0f;
|
|
1757
|
+
if (MaterialInstance->GetScalarParameterValue(ParameterInfo, ReadBack))
|
|
1758
|
+
{
|
|
1759
|
+
Result->SetNumberField(TEXT("readBack"), ReadBack);
|
|
1760
|
+
}
|
|
1417
1761
|
|
|
1418
1762
|
MCPSetUpdated(Result);
|
|
1419
1763
|
if (bHadPrev)
|
|
@@ -1428,97 +1772,59 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1428
1772
|
|
|
1429
1773
|
return MCPResult(Result);
|
|
1430
1774
|
}
|
|
1431
|
-
else if (TypeLower == TEXT("vector"))
|
|
1775
|
+
else if (TypeLower == TEXT("vector") || TypeLower == TEXT("color") || TypeLower == TEXT("colour"))
|
|
1432
1776
|
{
|
|
1433
|
-
// #670:
|
|
1434
|
-
//
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
if (Params
|
|
1440
|
-
{
|
|
1441
|
-
(*ValueObj)->TryGetNumberField(TEXT("r"), R);
|
|
1442
|
-
(*ValueObj)->TryGetNumberField(TEXT("g"), G);
|
|
1443
|
-
(*ValueObj)->TryGetNumberField(TEXT("b"), B);
|
|
1444
|
-
(*ValueObj)->TryGetNumberField(TEXT("a"), A);
|
|
1445
|
-
bParsed = true;
|
|
1446
|
-
}
|
|
1447
|
-
else if (Params->TryGetArrayField(TEXT("value"), ValueArr) && ValueArr)
|
|
1777
|
+
// #670/#952: the payload may be an object {r,g,b,a}, an array [r,g,b,a],
|
|
1778
|
+
// a re-encoded JSON string or UE struct text, and it may arrive under
|
|
1779
|
+
// `value` or under `color`. Every one of those is a colour a caller
|
|
1780
|
+
// meant to set, so accept them all rather than making the caller guess.
|
|
1781
|
+
FLinearColor ColorValue = FLinearColor::White;
|
|
1782
|
+
FString SourceField;
|
|
1783
|
+
if (!TryParseMaterialColorParam(Params, ColorValue, SourceField))
|
|
1448
1784
|
{
|
|
1449
|
-
|
|
1450
|
-
if (ValueArr->Num() > 1) (*ValueArr)[1]->TryGetNumber(G);
|
|
1451
|
-
if (ValueArr->Num() > 2) (*ValueArr)[2]->TryGetNumber(B);
|
|
1452
|
-
if (ValueArr->Num() > 3) (*ValueArr)[3]->TryGetNumber(A);
|
|
1453
|
-
bParsed = true;
|
|
1454
|
-
}
|
|
1455
|
-
else
|
|
1456
|
-
{
|
|
1457
|
-
FString ValueStr;
|
|
1458
|
-
if (Params->TryGetStringField(TEXT("value"), ValueStr) && !ValueStr.IsEmpty())
|
|
1459
|
-
{
|
|
1460
|
-
// Try JSON object first.
|
|
1461
|
-
TSharedPtr<FJsonObject> Reparsed;
|
|
1462
|
-
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ValueStr);
|
|
1463
|
-
if (FJsonSerializer::Deserialize(Reader, Reparsed) && Reparsed.IsValid())
|
|
1464
|
-
{
|
|
1465
|
-
Reparsed->TryGetNumberField(TEXT("r"), R);
|
|
1466
|
-
Reparsed->TryGetNumberField(TEXT("g"), G);
|
|
1467
|
-
Reparsed->TryGetNumberField(TEXT("b"), B);
|
|
1468
|
-
Reparsed->TryGetNumberField(TEXT("a"), A);
|
|
1469
|
-
bParsed = true;
|
|
1470
|
-
}
|
|
1471
|
-
else
|
|
1472
|
-
{
|
|
1473
|
-
// Fall back to UE struct text "(R=..,G=..,B=..,A=..)".
|
|
1474
|
-
FLinearColor Parsed;
|
|
1475
|
-
if (Parsed.InitFromString(ValueStr))
|
|
1476
|
-
{
|
|
1477
|
-
R = Parsed.R; G = Parsed.G; B = Parsed.B; A = Parsed.A;
|
|
1478
|
-
bParsed = true;
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
if (!bParsed)
|
|
1484
|
-
{
|
|
1485
|
-
return MCPError(TEXT("Missing/unparseable 'value' for vector parameter - pass {r,g,b,a}, [r,g,b,a], or '(R=..,G=..,B=..,A=..)'"));
|
|
1785
|
+
return MCPError(TEXT("Missing/unparseable colour for vector parameter - pass it as 'value' or 'color', shaped {r,g,b,a}, [r,g,b,a], or '(R=..,G=..,B=..,A=..)'"));
|
|
1486
1786
|
}
|
|
1487
1787
|
|
|
1488
|
-
FLinearColor ColorValue(R, G, B, A);
|
|
1489
1788
|
FLinearColor PrevColor;
|
|
1490
|
-
const bool bHadPrev = MaterialInstance->GetVectorParameterValue(
|
|
1491
|
-
|
|
1492
|
-
TSharedPtr<FJsonObject> ValueResult = MakeShared<FJsonObject>();
|
|
1493
|
-
ValueResult->SetNumberField(TEXT("r"), R);
|
|
1494
|
-
ValueResult->SetNumberField(TEXT("g"), G);
|
|
1495
|
-
ValueResult->SetNumberField(TEXT("b"), B);
|
|
1496
|
-
ValueResult->SetNumberField(TEXT("a"), A);
|
|
1789
|
+
const bool bHadPrev = MaterialInstance->GetVectorParameterValue(ParameterInfo, PrevColor);
|
|
1497
1790
|
|
|
1498
1791
|
auto Result = MCPSuccess();
|
|
1499
1792
|
Result->SetStringField(TEXT("parameterName"), ParameterName);
|
|
1500
1793
|
Result->SetStringField(TEXT("parameterType"), TEXT("vector"));
|
|
1501
|
-
Result->
|
|
1794
|
+
Result->SetStringField(TEXT("association"), AssociationName);
|
|
1795
|
+
Result->SetStringField(TEXT("valueField"), SourceField);
|
|
1796
|
+
Result->SetObjectField(TEXT("value"), LinearColorToJson(ColorValue));
|
|
1502
1797
|
Result->SetStringField(TEXT("path"), MaterialInstance->GetPathName());
|
|
1503
1798
|
|
|
1504
1799
|
if (bHadPrev && PrevColor.Equals(ColorValue))
|
|
1505
1800
|
{
|
|
1506
1801
|
MCPSetExisted(Result);
|
|
1507
1802
|
Result->SetBoolField(TEXT("updated"), false);
|
|
1803
|
+
Result->SetObjectField(TEXT("readBack"), LinearColorToJson(PrevColor));
|
|
1508
1804
|
return MCPResult(Result);
|
|
1509
1805
|
}
|
|
1510
1806
|
|
|
1511
|
-
MaterialInstance->
|
|
1512
|
-
|
|
1807
|
+
MaterialInstance->Modify(true);
|
|
1808
|
+
const bool bApplied = UMaterialEditingLibrary::SetMaterialInstanceVectorParameterValue(
|
|
1809
|
+
MaterialInstance, FName(*ParameterName), ColorValue, Association);
|
|
1810
|
+
if (!bApplied)
|
|
1811
|
+
{
|
|
1812
|
+
MaterialInstance->SetVectorParameterValueEditorOnly(ParameterInfo, ColorValue);
|
|
1813
|
+
UMaterialEditingLibrary::UpdateMaterialInstance(MaterialInstance);
|
|
1814
|
+
}
|
|
1815
|
+
Result->SetBoolField(TEXT("declaredByParent"), bApplied);
|
|
1816
|
+
|
|
1817
|
+
Result->SetBoolField(TEXT("saved"), SaveAssetPackage(MaterialInstance));
|
|
1818
|
+
FLinearColor ReadBack;
|
|
1819
|
+
if (MaterialInstance->GetVectorParameterValue(ParameterInfo, ReadBack))
|
|
1820
|
+
{
|
|
1821
|
+
Result->SetObjectField(TEXT("readBack"), LinearColorToJson(ReadBack));
|
|
1822
|
+
}
|
|
1513
1823
|
|
|
1514
1824
|
MCPSetUpdated(Result);
|
|
1515
1825
|
if (bHadPrev)
|
|
1516
1826
|
{
|
|
1517
|
-
TSharedPtr<FJsonObject> PrevValueObj =
|
|
1518
|
-
PrevValueObj->SetNumberField(TEXT("r"), PrevColor.R);
|
|
1519
|
-
PrevValueObj->SetNumberField(TEXT("g"), PrevColor.G);
|
|
1520
|
-
PrevValueObj->SetNumberField(TEXT("b"), PrevColor.B);
|
|
1521
|
-
PrevValueObj->SetNumberField(TEXT("a"), PrevColor.A);
|
|
1827
|
+
TSharedPtr<FJsonObject> PrevValueObj = LinearColorToJson(PrevColor);
|
|
1522
1828
|
TSharedPtr<FJsonObject> Payload = MakeShared<FJsonObject>();
|
|
1523
1829
|
Payload->SetStringField(TEXT("path"), MaterialInstance->GetPathName());
|
|
1524
1830
|
Payload->SetStringField(TEXT("parameterName"), ParameterName);
|
|
@@ -1533,6 +1839,10 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1533
1839
|
{
|
|
1534
1840
|
FString TexturePath;
|
|
1535
1841
|
if (!Params->TryGetStringField(TEXT("value"), TexturePath) || TexturePath.IsEmpty())
|
|
1842
|
+
{
|
|
1843
|
+
Params->TryGetStringField(TEXT("texturePath"), TexturePath);
|
|
1844
|
+
}
|
|
1845
|
+
if (TexturePath.IsEmpty())
|
|
1536
1846
|
{
|
|
1537
1847
|
return MCPError(TEXT("Missing 'value' string field (texture asset path) for texture parameter"));
|
|
1538
1848
|
}
|
|
@@ -1549,11 +1859,12 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1549
1859
|
}
|
|
1550
1860
|
|
|
1551
1861
|
UTexture* PrevTexture = nullptr;
|
|
1552
|
-
const bool bHadPrev = MaterialInstance->GetTextureParameterValue(
|
|
1862
|
+
const bool bHadPrev = MaterialInstance->GetTextureParameterValue(ParameterInfo, PrevTexture);
|
|
1553
1863
|
|
|
1554
1864
|
auto Result = MCPSuccess();
|
|
1555
1865
|
Result->SetStringField(TEXT("parameterName"), ParameterName);
|
|
1556
1866
|
Result->SetStringField(TEXT("parameterType"), TEXT("texture"));
|
|
1867
|
+
Result->SetStringField(TEXT("association"), AssociationName);
|
|
1557
1868
|
Result->SetStringField(TEXT("value"), Texture->GetPathName());
|
|
1558
1869
|
Result->SetStringField(TEXT("path"), MaterialInstance->GetPathName());
|
|
1559
1870
|
|
|
@@ -1561,11 +1872,26 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1561
1872
|
{
|
|
1562
1873
|
MCPSetExisted(Result);
|
|
1563
1874
|
Result->SetBoolField(TEXT("updated"), false);
|
|
1875
|
+
Result->SetStringField(TEXT("readBack"), PrevTexture->GetPathName());
|
|
1564
1876
|
return MCPResult(Result);
|
|
1565
1877
|
}
|
|
1566
1878
|
|
|
1567
|
-
MaterialInstance->
|
|
1568
|
-
|
|
1879
|
+
MaterialInstance->Modify(true);
|
|
1880
|
+
const bool bApplied = UMaterialEditingLibrary::SetMaterialInstanceTextureParameterValue(
|
|
1881
|
+
MaterialInstance, FName(*ParameterName), Texture, Association);
|
|
1882
|
+
if (!bApplied)
|
|
1883
|
+
{
|
|
1884
|
+
MaterialInstance->SetTextureParameterValueEditorOnly(ParameterInfo, Texture);
|
|
1885
|
+
UMaterialEditingLibrary::UpdateMaterialInstance(MaterialInstance);
|
|
1886
|
+
}
|
|
1887
|
+
Result->SetBoolField(TEXT("declaredByParent"), bApplied);
|
|
1888
|
+
|
|
1889
|
+
Result->SetBoolField(TEXT("saved"), SaveAssetPackage(MaterialInstance));
|
|
1890
|
+
UTexture* ReadBack = nullptr;
|
|
1891
|
+
if (MaterialInstance->GetTextureParameterValue(ParameterInfo, ReadBack) && ReadBack)
|
|
1892
|
+
{
|
|
1893
|
+
Result->SetStringField(TEXT("readBack"), ReadBack->GetPathName());
|
|
1894
|
+
}
|
|
1569
1895
|
|
|
1570
1896
|
MCPSetUpdated(Result);
|
|
1571
1897
|
if (bHadPrev && PrevTexture)
|
|
@@ -1582,7 +1908,7 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetMaterialParameter(const TSharedPtr<
|
|
|
1582
1908
|
}
|
|
1583
1909
|
else
|
|
1584
1910
|
{
|
|
1585
|
-
return MCPError(FString::Printf(TEXT("Unknown parameterType '%s'. Use 'scalar', 'vector', or 'texture'."), *ParameterType));
|
|
1911
|
+
return MCPError(FString::Printf(TEXT("Unknown parameterType '%s'. Use 'scalar', 'vector' (alias 'color'), or 'texture'."), *ParameterType));
|
|
1586
1912
|
}
|
|
1587
1913
|
}
|
|
1588
1914
|
|
|
@@ -1745,15 +2071,15 @@ TSharedPtr<FJsonValue> FMaterialHandlers::BatchSetInstances(const TSharedPtr<FJs
|
|
|
1745
2071
|
double V = 0; (*PObj)->TryGetNumberField(TEXT("value"), V);
|
|
1746
2072
|
MIC->SetScalarParameterValueEditorOnly(FName(*PName), (float)V); ++ParamsSet;
|
|
1747
2073
|
}
|
|
1748
|
-
else if (PType == TEXT("vector") || PType == TEXT("color"))
|
|
2074
|
+
else if (PType == TEXT("vector") || PType == TEXT("color") || PType == TEXT("colour"))
|
|
1749
2075
|
{
|
|
1750
|
-
|
|
1751
|
-
|
|
2076
|
+
// Same colour shapes the single-instance path accepts (#952),
|
|
2077
|
+
// so a batch is never fussier about the payload than one call.
|
|
2078
|
+
FLinearColor Color;
|
|
2079
|
+
FString SourceField;
|
|
2080
|
+
if (TryParseMaterialColorParam(*PObj, Color, SourceField))
|
|
1752
2081
|
{
|
|
1753
|
-
|
|
1754
|
-
(*CObj)->TryGetNumberField(TEXT("r"), R); (*CObj)->TryGetNumberField(TEXT("g"), G);
|
|
1755
|
-
(*CObj)->TryGetNumberField(TEXT("b"), B); (*CObj)->TryGetNumberField(TEXT("a"), A);
|
|
1756
|
-
MIC->SetVectorParameterValueEditorOnly(FName(*PName), FLinearColor((float)R,(float)G,(float)B,(float)A)); ++ParamsSet;
|
|
2082
|
+
MIC->SetVectorParameterValueEditorOnly(FName(*PName), Color); ++ParamsSet;
|
|
1757
2083
|
}
|
|
1758
2084
|
}
|
|
1759
2085
|
else if (PType == TEXT("texture"))
|
|
@@ -1958,12 +2284,22 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetExpressionValue(const TSharedPtr<FJ
|
|
|
1958
2284
|
// Handle UMaterialExpressionConstant2Vector
|
|
1959
2285
|
else if (UMaterialExpressionConstant2Vector* Const2Expr = Cast<UMaterialExpressionConstant2Vector>(Expression))
|
|
1960
2286
|
{
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2287
|
+
// #979: this branch used to read only top-level lowercase `r`/`g`, which
|
|
2288
|
+
// the tool schema does not declare, so a Constant2Vector had no reachable
|
|
2289
|
+
// write path at all while its 3- and 4-component siblings took a `value`
|
|
2290
|
+
// object. All three now share one reader, and `{x,y}` works as documented.
|
|
2291
|
+
FLinearColor Components;
|
|
2292
|
+
FString SourceField;
|
|
2293
|
+
if (TryParseMaterialColorParam(Params, Components, SourceField, /*bAllowTopLevelComponents*/ true))
|
|
1966
2294
|
{
|
|
2295
|
+
Const2Expr->R = Components.R;
|
|
2296
|
+
Const2Expr->G = Components.G;
|
|
2297
|
+
bValueSet = true;
|
|
2298
|
+
|
|
2299
|
+
TSharedPtr<FJsonObject> ValueResult = MakeShared<FJsonObject>();
|
|
2300
|
+
ValueResult->SetNumberField(TEXT("r"), Const2Expr->R);
|
|
2301
|
+
ValueResult->SetNumberField(TEXT("g"), Const2Expr->G);
|
|
2302
|
+
Result->SetObjectField(TEXT("value"), ValueResult);
|
|
1967
2303
|
Result->SetNumberField(TEXT("r"), Const2Expr->R);
|
|
1968
2304
|
Result->SetNumberField(TEXT("g"), Const2Expr->G);
|
|
1969
2305
|
}
|
|
@@ -1971,81 +2307,27 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetExpressionValue(const TSharedPtr<FJ
|
|
|
1971
2307
|
// Handle UMaterialExpressionConstant3Vector - has FLinearColor Constant
|
|
1972
2308
|
else if (UMaterialExpressionConstant3Vector* Const3Expr = Cast<UMaterialExpressionConstant3Vector>(Expression))
|
|
1973
2309
|
{
|
|
1974
|
-
// #444: accept {r,g,b,a}
|
|
1975
|
-
// or top
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
auto Pick = [&](const TCHAR* L, const TCHAR* U, const TCHAR* Alt, double& Slot)
|
|
1980
|
-
{
|
|
1981
|
-
double V; if (Obj->TryGetNumberField(L, V) || Obj->TryGetNumberField(U, V) || Obj->TryGetNumberField(Alt, V)) { Slot = V; bAny = true; }
|
|
1982
|
-
};
|
|
1983
|
-
Pick(TEXT("r"), TEXT("R"), TEXT("x"), R);
|
|
1984
|
-
Pick(TEXT("g"), TEXT("G"), TEXT("y"), G);
|
|
1985
|
-
Pick(TEXT("b"), TEXT("B"), TEXT("z"), B);
|
|
1986
|
-
Pick(TEXT("a"), TEXT("A"), TEXT("w"), A);
|
|
1987
|
-
Out = FLinearColor((float)R, (float)G, (float)B, (float)A);
|
|
1988
|
-
return bAny;
|
|
1989
|
-
};
|
|
1990
|
-
const TSharedPtr<FJsonObject>* ColorObj = nullptr;
|
|
1991
|
-
FLinearColor Color = Const3Expr->Constant;
|
|
1992
|
-
if (Params->TryGetObjectField(TEXT("value"), ColorObj) && *ColorObj && ReadColor(*ColorObj, Color))
|
|
1993
|
-
{
|
|
1994
|
-
Const3Expr->Constant = Color;
|
|
1995
|
-
bValueSet = true;
|
|
1996
|
-
}
|
|
1997
|
-
else if (ReadColor(Params, Color))
|
|
2310
|
+
// #444/#979: accept {r,g,b,a}, {R,G,B,A} or {x,y,z,w} under `value` or
|
|
2311
|
+
// `color`, an array, UE struct text, or the components at the top level.
|
|
2312
|
+
FLinearColor Color;
|
|
2313
|
+
FString SourceField;
|
|
2314
|
+
if (TryParseMaterialColorParam(Params, Color, SourceField, /*bAllowTopLevelComponents*/ true))
|
|
1998
2315
|
{
|
|
1999
2316
|
Const3Expr->Constant = Color;
|
|
2000
2317
|
bValueSet = true;
|
|
2001
|
-
|
|
2002
|
-
if (bValueSet)
|
|
2003
|
-
{
|
|
2004
|
-
TSharedPtr<FJsonObject> ColorResult = MakeShared<FJsonObject>();
|
|
2005
|
-
ColorResult->SetNumberField(TEXT("r"), Const3Expr->Constant.R);
|
|
2006
|
-
ColorResult->SetNumberField(TEXT("g"), Const3Expr->Constant.G);
|
|
2007
|
-
ColorResult->SetNumberField(TEXT("b"), Const3Expr->Constant.B);
|
|
2008
|
-
ColorResult->SetNumberField(TEXT("a"), Const3Expr->Constant.A);
|
|
2009
|
-
Result->SetObjectField(TEXT("value"), ColorResult);
|
|
2318
|
+
Result->SetObjectField(TEXT("value"), LinearColorToJson(Const3Expr->Constant));
|
|
2010
2319
|
}
|
|
2011
2320
|
}
|
|
2012
2321
|
// Handle UMaterialExpressionConstant4Vector
|
|
2013
2322
|
else if (UMaterialExpressionConstant4Vector* Const4Expr = Cast<UMaterialExpressionConstant4Vector>(Expression))
|
|
2014
2323
|
{
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
auto Pick = [&](const TCHAR* L, const TCHAR* U, const TCHAR* Alt, double& Slot)
|
|
2019
|
-
{
|
|
2020
|
-
double V; if (Obj->TryGetNumberField(L, V) || Obj->TryGetNumberField(U, V) || Obj->TryGetNumberField(Alt, V)) { Slot = V; bAny = true; }
|
|
2021
|
-
};
|
|
2022
|
-
Pick(TEXT("r"), TEXT("R"), TEXT("x"), R);
|
|
2023
|
-
Pick(TEXT("g"), TEXT("G"), TEXT("y"), G);
|
|
2024
|
-
Pick(TEXT("b"), TEXT("B"), TEXT("z"), B);
|
|
2025
|
-
Pick(TEXT("a"), TEXT("A"), TEXT("w"), A);
|
|
2026
|
-
Out = FLinearColor((float)R, (float)G, (float)B, (float)A);
|
|
2027
|
-
return bAny;
|
|
2028
|
-
};
|
|
2029
|
-
const TSharedPtr<FJsonObject>* ColorObj = nullptr;
|
|
2030
|
-
FLinearColor Color = Const4Expr->Constant;
|
|
2031
|
-
if (Params->TryGetObjectField(TEXT("value"), ColorObj) && *ColorObj && ReadColor4(*ColorObj, Color))
|
|
2032
|
-
{
|
|
2033
|
-
Const4Expr->Constant = Color;
|
|
2034
|
-
bValueSet = true;
|
|
2035
|
-
}
|
|
2036
|
-
else if (ReadColor4(Params, Color))
|
|
2324
|
+
FLinearColor Color;
|
|
2325
|
+
FString SourceField;
|
|
2326
|
+
if (TryParseMaterialColorParam(Params, Color, SourceField, /*bAllowTopLevelComponents*/ true))
|
|
2037
2327
|
{
|
|
2038
2328
|
Const4Expr->Constant = Color;
|
|
2039
2329
|
bValueSet = true;
|
|
2040
|
-
|
|
2041
|
-
if (bValueSet)
|
|
2042
|
-
{
|
|
2043
|
-
TSharedPtr<FJsonObject> ColorResult = MakeShared<FJsonObject>();
|
|
2044
|
-
ColorResult->SetNumberField(TEXT("r"), Const4Expr->Constant.R);
|
|
2045
|
-
ColorResult->SetNumberField(TEXT("g"), Const4Expr->Constant.G);
|
|
2046
|
-
ColorResult->SetNumberField(TEXT("b"), Const4Expr->Constant.B);
|
|
2047
|
-
ColorResult->SetNumberField(TEXT("a"), Const4Expr->Constant.A);
|
|
2048
|
-
Result->SetObjectField(TEXT("value"), ColorResult);
|
|
2330
|
+
Result->SetObjectField(TEXT("value"), LinearColorToJson(Const4Expr->Constant));
|
|
2049
2331
|
}
|
|
2050
2332
|
}
|
|
2051
2333
|
// Handle UMaterialExpressionScalarParameter - has float DefaultValue
|
|
@@ -2070,23 +2352,13 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetExpressionValue(const TSharedPtr<FJ
|
|
|
2070
2352
|
// Handle UMaterialExpressionVectorParameter - has FLinearColor DefaultValue
|
|
2071
2353
|
else if (UMaterialExpressionVectorParameter* VectorParamExpr = Cast<UMaterialExpressionVectorParameter>(Expression))
|
|
2072
2354
|
{
|
|
2073
|
-
|
|
2074
|
-
|
|
2355
|
+
FLinearColor Color;
|
|
2356
|
+
FString SourceField;
|
|
2357
|
+
if (TryParseMaterialColorParam(Params, Color, SourceField, /*bAllowTopLevelComponents*/ false))
|
|
2075
2358
|
{
|
|
2076
|
-
|
|
2077
|
-
(*ValueObj)->TryGetNumberField(TEXT("r"), R);
|
|
2078
|
-
(*ValueObj)->TryGetNumberField(TEXT("g"), G);
|
|
2079
|
-
(*ValueObj)->TryGetNumberField(TEXT("b"), B);
|
|
2080
|
-
(*ValueObj)->TryGetNumberField(TEXT("a"), A);
|
|
2081
|
-
VectorParamExpr->DefaultValue = FLinearColor(R, G, B, A);
|
|
2359
|
+
VectorParamExpr->DefaultValue = Color;
|
|
2082
2360
|
bValueSet = true;
|
|
2083
|
-
|
|
2084
|
-
TSharedPtr<FJsonObject> ColorResult = MakeShared<FJsonObject>();
|
|
2085
|
-
ColorResult->SetNumberField(TEXT("r"), R);
|
|
2086
|
-
ColorResult->SetNumberField(TEXT("g"), G);
|
|
2087
|
-
ColorResult->SetNumberField(TEXT("b"), B);
|
|
2088
|
-
ColorResult->SetNumberField(TEXT("a"), A);
|
|
2089
|
-
Result->SetObjectField(TEXT("value"), ColorResult);
|
|
2361
|
+
Result->SetObjectField(TEXT("value"), LinearColorToJson(VectorParamExpr->DefaultValue));
|
|
2090
2362
|
}
|
|
2091
2363
|
|
|
2092
2364
|
FString ParamName;
|
|
@@ -2244,9 +2516,14 @@ TSharedPtr<FJsonValue> FMaterialHandlers::SetExpressionValue(const TSharedPtr<FJ
|
|
|
2244
2516
|
}
|
|
2245
2517
|
|
|
2246
2518
|
Material->PostEditChange();
|
|
2247
|
-
Material->MarkPackageDirty();
|
|
2248
2519
|
|
|
2520
|
+
// #979: the value was written in memory and the package only marked dirty,
|
|
2521
|
+
// so a caller who then asked something else to save it could be told the
|
|
2522
|
+
// save failed while the write had in fact landed - two answers, neither of
|
|
2523
|
+
// them the whole truth. Persist here, and report whether that worked
|
|
2524
|
+
// alongside the value that was written either way.
|
|
2249
2525
|
MCPSetUpdated(Result);
|
|
2526
|
+
Result->SetBoolField(TEXT("saved"), SaveAssetPackage(Material));
|
|
2250
2527
|
Result->SetStringField(TEXT("materialPath"), Material->GetPathName());
|
|
2251
2528
|
Result->SetNumberField(TEXT("expressionIndex"), ExpressionIndex);
|
|
2252
2529
|
Result->SetStringField(TEXT("expressionClass"), ExpressionClass);
|