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
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
// Refresh and inspect actions for state the editor caches (#944, #915, #914).
|
|
2
|
+
//
|
|
3
|
+
// Each of these is a case where the editor is holding a stale answer and there
|
|
4
|
+
// was no way to ask it to recompute, or no way to see the number it was
|
|
5
|
+
// actually using.
|
|
6
|
+
//
|
|
7
|
+
// #944 A placed Blueprint instance keeps the components and defaults it was
|
|
8
|
+
// constructed with. Changing the SCS or the construction script does
|
|
9
|
+
// not touch it. blueprint(run_construction_script) only runs against a
|
|
10
|
+
// TEMPORARY actor, and Python cannot help because
|
|
11
|
+
// Actor.rerun_construction_scripts() is missing from the wrapper.
|
|
12
|
+
// #915 Collision built from mesh data the mesh no longer has produced a
|
|
13
|
+
// line_trace that reported a MISS through solid geometry. A false
|
|
14
|
+
// negative is the worst answer a trace can give, because nothing about
|
|
15
|
+
// it looks wrong.
|
|
16
|
+
// #914 A UBoxComponent's authored BoxExtent was not readable anywhere, only
|
|
17
|
+
// its scaled bounds, and there was no way to ask whether two
|
|
18
|
+
// components actually overlap without a physics query that depends on
|
|
19
|
+
// the collision state #915 is about.
|
|
20
|
+
|
|
21
|
+
#include "LevelHandlers.h"
|
|
22
|
+
|
|
23
|
+
#include "Components/ActorComponent.h"
|
|
24
|
+
#include "Components/BoxComponent.h"
|
|
25
|
+
#include "Components/PrimitiveComponent.h"
|
|
26
|
+
#include "Components/SceneComponent.h"
|
|
27
|
+
#include "Dom/JsonObject.h"
|
|
28
|
+
#include "Dom/JsonValue.h"
|
|
29
|
+
#include "Editor.h"
|
|
30
|
+
#include "Engine/EngineTypes.h"
|
|
31
|
+
#include "Engine/World.h"
|
|
32
|
+
#include "EngineUtils.h"
|
|
33
|
+
#include "GameFramework/Actor.h"
|
|
34
|
+
#include "HandlerRegistry.h"
|
|
35
|
+
#include "HandlerUtils.h"
|
|
36
|
+
|
|
37
|
+
namespace
|
|
38
|
+
{
|
|
39
|
+
constexpr int32 MCPRefreshMaxActors = 5000;
|
|
40
|
+
constexpr int32 MCPRefreshMaxResultRows = 500;
|
|
41
|
+
constexpr int32 MCPRefreshDefaultMaxComponents = 2000;
|
|
42
|
+
constexpr int32 MCPRefreshHardMaxComponents = 20000;
|
|
43
|
+
constexpr int32 MCPRefreshMaxSamples = 25;
|
|
44
|
+
|
|
45
|
+
const TCHAR* MCPRefreshCollisionName(ECollisionEnabled::Type Value)
|
|
46
|
+
{
|
|
47
|
+
switch (Value)
|
|
48
|
+
{
|
|
49
|
+
case ECollisionEnabled::NoCollision: return TEXT("NoCollision");
|
|
50
|
+
case ECollisionEnabled::QueryOnly: return TEXT("QueryOnly");
|
|
51
|
+
case ECollisionEnabled::PhysicsOnly: return TEXT("PhysicsOnly");
|
|
52
|
+
case ECollisionEnabled::QueryAndPhysics: return TEXT("QueryAndPhysics");
|
|
53
|
+
case ECollisionEnabled::ProbeOnly: return TEXT("ProbeOnly");
|
|
54
|
+
case ECollisionEnabled::QueryAndProbe: return TEXT("QueryAndProbe");
|
|
55
|
+
default: return TEXT("Unknown");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** An oriented box in world space, built from a component's LOCAL bounds so
|
|
60
|
+
* it keeps the component's orientation. The world bounds are axis-aligned
|
|
61
|
+
* and would answer a different question. */
|
|
62
|
+
struct FMCPOrientedBox
|
|
63
|
+
{
|
|
64
|
+
FVector Center = FVector::ZeroVector;
|
|
65
|
+
FVector Axis[3] = { FVector::ForwardVector, FVector::RightVector, FVector::UpVector };
|
|
66
|
+
FVector Extent = FVector::ZeroVector;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
FMCPOrientedBox MCPBuildOrientedBox(USceneComponent* Component)
|
|
70
|
+
{
|
|
71
|
+
FMCPOrientedBox Box;
|
|
72
|
+
if (!Component)
|
|
73
|
+
{
|
|
74
|
+
return Box;
|
|
75
|
+
}
|
|
76
|
+
const FTransform Transform = Component->GetComponentTransform();
|
|
77
|
+
const FBoxSphereBounds LocalBounds = Component->GetLocalBounds();
|
|
78
|
+
Box.Center = Transform.TransformPosition(LocalBounds.Origin);
|
|
79
|
+
const FQuat Rotation = Transform.GetRotation();
|
|
80
|
+
Box.Axis[0] = Rotation.GetAxisX();
|
|
81
|
+
Box.Axis[1] = Rotation.GetAxisY();
|
|
82
|
+
Box.Axis[2] = Rotation.GetAxisZ();
|
|
83
|
+
const FVector Scale = Transform.GetScale3D();
|
|
84
|
+
Box.Extent = FVector(
|
|
85
|
+
LocalBounds.BoxExtent.X * FMath::Abs(Scale.X),
|
|
86
|
+
LocalBounds.BoxExtent.Y * FMath::Abs(Scale.Y),
|
|
87
|
+
LocalBounds.BoxExtent.Z * FMath::Abs(Scale.Z));
|
|
88
|
+
return Box;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Projected radius of an oriented box onto a unit axis. */
|
|
92
|
+
double MCPProjectedRadius(const FMCPOrientedBox& Box, const FVector& Axis)
|
|
93
|
+
{
|
|
94
|
+
return
|
|
95
|
+
Box.Extent.X * FMath::Abs(FVector::DotProduct(Box.Axis[0], Axis)) +
|
|
96
|
+
Box.Extent.Y * FMath::Abs(FVector::DotProduct(Box.Axis[1], Axis)) +
|
|
97
|
+
Box.Extent.Z * FMath::Abs(FVector::DotProduct(Box.Axis[2], Axis));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Separating axis test between two oriented boxes.
|
|
102
|
+
*
|
|
103
|
+
* Deliberately geometric rather than a physics overlap query: the whole
|
|
104
|
+
* reason #914 was filed alongside #915 is that a physics answer depends on
|
|
105
|
+
* collision state, and stale collision state is what produced the wrong
|
|
106
|
+
* answer in the first place. This reads the transforms and the bounds and
|
|
107
|
+
* nothing else.
|
|
108
|
+
*
|
|
109
|
+
* Returns true when the boxes intersect. OutSeparation is the largest gap
|
|
110
|
+
* found along any tested axis when they do not, and OutPenetration is the
|
|
111
|
+
* smallest overlap along any tested axis when they do.
|
|
112
|
+
*/
|
|
113
|
+
bool MCPOrientedBoxesOverlap(
|
|
114
|
+
const FMCPOrientedBox& A,
|
|
115
|
+
const FMCPOrientedBox& B,
|
|
116
|
+
double& OutSeparation,
|
|
117
|
+
double& OutPenetration,
|
|
118
|
+
FVector& OutAxis)
|
|
119
|
+
{
|
|
120
|
+
OutSeparation = 0.0;
|
|
121
|
+
OutPenetration = TNumericLimits<double>::Max();
|
|
122
|
+
OutAxis = FVector::ZeroVector;
|
|
123
|
+
|
|
124
|
+
TArray<FVector, TInlineAllocator<15>> Axes;
|
|
125
|
+
for (int32 Index = 0; Index < 3; ++Index) Axes.Add(A.Axis[Index]);
|
|
126
|
+
for (int32 Index = 0; Index < 3; ++Index) Axes.Add(B.Axis[Index]);
|
|
127
|
+
for (int32 IndexA = 0; IndexA < 3; ++IndexA)
|
|
128
|
+
{
|
|
129
|
+
for (int32 IndexB = 0; IndexB < 3; ++IndexB)
|
|
130
|
+
{
|
|
131
|
+
const FVector Cross = FVector::CrossProduct(A.Axis[IndexA], B.Axis[IndexB]);
|
|
132
|
+
// Parallel edge pairs produce a degenerate axis. The six face
|
|
133
|
+
// normals already cover that case, so skipping it is correct
|
|
134
|
+
// rather than an approximation.
|
|
135
|
+
if (Cross.SizeSquared() > UE_KINDA_SMALL_NUMBER)
|
|
136
|
+
{
|
|
137
|
+
Axes.Add(Cross.GetSafeNormal());
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const FVector Delta = B.Center - A.Center;
|
|
143
|
+
bool bOverlapping = true;
|
|
144
|
+
for (const FVector& Axis : Axes)
|
|
145
|
+
{
|
|
146
|
+
if (Axis.IsNearlyZero())
|
|
147
|
+
{
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const FVector Unit = Axis.GetSafeNormal();
|
|
151
|
+
const double Distance = FMath::Abs(FVector::DotProduct(Delta, Unit));
|
|
152
|
+
const double Reach = MCPProjectedRadius(A, Unit) + MCPProjectedRadius(B, Unit);
|
|
153
|
+
const double Gap = Distance - Reach;
|
|
154
|
+
if (Gap > 0.0)
|
|
155
|
+
{
|
|
156
|
+
bOverlapping = false;
|
|
157
|
+
if (Gap > OutSeparation)
|
|
158
|
+
{
|
|
159
|
+
OutSeparation = Gap;
|
|
160
|
+
OutAxis = Unit;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else if (bOverlapping && -Gap < OutPenetration)
|
|
164
|
+
{
|
|
165
|
+
OutPenetration = -Gap;
|
|
166
|
+
OutAxis = Unit;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!bOverlapping)
|
|
171
|
+
{
|
|
172
|
+
OutPenetration = 0.0;
|
|
173
|
+
}
|
|
174
|
+
else if (OutPenetration == TNumericLimits<double>::Max())
|
|
175
|
+
{
|
|
176
|
+
// Two zero-extent boxes overlap with every separating axis
|
|
177
|
+
// degenerate, so no axis ever narrowed the seed. Reporting the seed
|
|
178
|
+
// would hand the caller 1.79e308 as a penetration depth in
|
|
179
|
+
// centimetres, which reads as a number rather than as "there was
|
|
180
|
+
// nothing to measure".
|
|
181
|
+
OutPenetration = 0.0;
|
|
182
|
+
OutAxis = FVector::ZeroVector;
|
|
183
|
+
}
|
|
184
|
+
return bOverlapping;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
UActorComponent* MCPRefreshFindComponent(AActor* Actor, const FString& ComponentName)
|
|
188
|
+
{
|
|
189
|
+
if (!Actor) return nullptr;
|
|
190
|
+
for (UActorComponent* Component : Actor->GetComponents())
|
|
191
|
+
{
|
|
192
|
+
if (Component && Component->GetName().Equals(ComponentName, ESearchCase::IgnoreCase))
|
|
193
|
+
{
|
|
194
|
+
return Component;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return nullptr;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
TSharedPtr<FJsonObject> MCPRefreshDescribeBounds(const FBoxSphereBounds& Bounds)
|
|
201
|
+
{
|
|
202
|
+
TSharedPtr<FJsonObject> Object = MakeShared<FJsonObject>();
|
|
203
|
+
Object->SetObjectField(TEXT("origin"), MCPVec3ToJsonObject(Bounds.Origin));
|
|
204
|
+
Object->SetObjectField(TEXT("boxExtent"), MCPVec3ToJsonObject(Bounds.BoxExtent));
|
|
205
|
+
Object->SetNumberField(TEXT("sphereRadius"), Bounds.SphereRadius);
|
|
206
|
+
return Object;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Everything #914 asked to see about one side of the comparison. */
|
|
210
|
+
TSharedPtr<FJsonObject> MCPRefreshDescribeComponent(USceneComponent* Component)
|
|
211
|
+
{
|
|
212
|
+
TSharedPtr<FJsonObject> Object = MakeShared<FJsonObject>();
|
|
213
|
+
Object->SetStringField(TEXT("componentName"), Component->GetName());
|
|
214
|
+
Object->SetStringField(TEXT("componentClass"), Component->GetClass()->GetName());
|
|
215
|
+
|
|
216
|
+
const FTransform Transform = Component->GetComponentTransform();
|
|
217
|
+
TSharedPtr<FJsonObject> TransformObject = MakeShared<FJsonObject>();
|
|
218
|
+
TransformObject->SetObjectField(TEXT("location"), MCPVec3ToJsonObject(Transform.GetLocation()));
|
|
219
|
+
TransformObject->SetObjectField(TEXT("rotation"), MCPRotatorToJsonObject(Transform.Rotator()));
|
|
220
|
+
TransformObject->SetObjectField(TEXT("scale"), MCPVec3ToJsonObject(Transform.GetScale3D()));
|
|
221
|
+
Object->SetObjectField(TEXT("worldTransform"), TransformObject);
|
|
222
|
+
|
|
223
|
+
Object->SetObjectField(TEXT("localBounds"), MCPRefreshDescribeBounds(Component->GetLocalBounds()));
|
|
224
|
+
Object->SetObjectField(TEXT("worldBounds"), MCPRefreshDescribeBounds(Component->GetBounds()));
|
|
225
|
+
|
|
226
|
+
// #914's original ask: the UNSCALED authored extent, which is what the
|
|
227
|
+
// details panel shows and what no existing read returned.
|
|
228
|
+
if (const UBoxComponent* BoxComponent = Cast<UBoxComponent>(Component))
|
|
229
|
+
{
|
|
230
|
+
Object->SetObjectField(TEXT("boxExtentUnscaled"),
|
|
231
|
+
MCPVec3ToJsonObject(BoxComponent->GetUnscaledBoxExtent()));
|
|
232
|
+
Object->SetObjectField(TEXT("boxExtentScaled"),
|
|
233
|
+
MCPVec3ToJsonObject(BoxComponent->GetScaledBoxExtent()));
|
|
234
|
+
}
|
|
235
|
+
return Object;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// rerun_construction_scripts (#944)
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
TSharedPtr<FJsonValue> FLevelHandlers::RerunConstruction(const TSharedPtr<FJsonObject>& Params)
|
|
243
|
+
{
|
|
244
|
+
MCP_CHECK_GAME_THREAD();
|
|
245
|
+
|
|
246
|
+
const FString WorldScope = OptionalString(Params, TEXT("world"), TEXT("editor"));
|
|
247
|
+
UWorld* World = ResolveWorldFromParams(Params, *WorldScope);
|
|
248
|
+
if (!World)
|
|
249
|
+
{
|
|
250
|
+
return MCPError(FString::Printf(TEXT("World not available for scope '%s'"), *WorldScope));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
TArray<FString> ActorLabels;
|
|
254
|
+
{
|
|
255
|
+
const TArray<TSharedPtr<FJsonValue>>* LabelValues = nullptr;
|
|
256
|
+
if (Params->TryGetArrayField(TEXT("actorLabels"), LabelValues) && LabelValues)
|
|
257
|
+
{
|
|
258
|
+
ActorLabels = JsonArrayToStringList(LabelValues);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const FString ClassSpec = OptionalString(Params, TEXT("className"));
|
|
262
|
+
if (ActorLabels.IsEmpty() && ClassSpec.IsEmpty())
|
|
263
|
+
{
|
|
264
|
+
// Rerunning every construction script in a map destroys and rebuilds
|
|
265
|
+
// every generated component in it. That is not a default.
|
|
266
|
+
return MCPError(TEXT("Pass 'actorLabels' or 'className'. This rebuilds generated components, so it will not run against a whole level implicitly."));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
UClass* ActorClass = nullptr;
|
|
270
|
+
FString ClassFallback;
|
|
271
|
+
if (!ClassSpec.IsEmpty())
|
|
272
|
+
{
|
|
273
|
+
ActorClass = MCPResolveClassOfType(ClassSpec, AActor::StaticClass(), true);
|
|
274
|
+
if (!ActorClass)
|
|
275
|
+
{
|
|
276
|
+
ClassFallback = ClassSpec;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const bool bMatchSubclasses = OptionalBool(Params, TEXT("matchSubclasses"), true);
|
|
280
|
+
|
|
281
|
+
TArray<AActor*> Targets;
|
|
282
|
+
TSet<FString> WantedLabels(ActorLabels);
|
|
283
|
+
TSet<FString> FoundLabels;
|
|
284
|
+
for (TActorIterator<AActor> It(World); It; ++It)
|
|
285
|
+
{
|
|
286
|
+
AActor* Actor = *It;
|
|
287
|
+
if (!Actor) continue;
|
|
288
|
+
|
|
289
|
+
bool bMatches = false;
|
|
290
|
+
const FString Label = Actor->GetActorLabel();
|
|
291
|
+
if (WantedLabels.Contains(Label))
|
|
292
|
+
{
|
|
293
|
+
bMatches = true;
|
|
294
|
+
FoundLabels.Add(Label);
|
|
295
|
+
}
|
|
296
|
+
if (!bMatches && ActorClass)
|
|
297
|
+
{
|
|
298
|
+
bMatches = bMatchSubclasses ? Actor->IsA(ActorClass) : (Actor->GetClass() == ActorClass);
|
|
299
|
+
}
|
|
300
|
+
if (!bMatches && !ClassFallback.IsEmpty())
|
|
301
|
+
{
|
|
302
|
+
bMatches = Actor->GetClass()->GetName().Contains(ClassFallback, ESearchCase::IgnoreCase);
|
|
303
|
+
}
|
|
304
|
+
if (bMatches)
|
|
305
|
+
{
|
|
306
|
+
Targets.Add(Actor);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (Targets.Num() > MCPRefreshMaxActors)
|
|
310
|
+
{
|
|
311
|
+
return MCPError(FString::Printf(
|
|
312
|
+
TEXT("Matched %d actors, over the maximum of %d. Narrow the selector."),
|
|
313
|
+
Targets.Num(), MCPRefreshMaxActors));
|
|
314
|
+
}
|
|
315
|
+
Targets.Sort([](const AActor& A, const AActor& B) { return A.GetPathName() < B.GetPathName(); });
|
|
316
|
+
|
|
317
|
+
int32 Reran = 0;
|
|
318
|
+
int32 WithoutScript = 0;
|
|
319
|
+
TArray<FString> Failed;
|
|
320
|
+
TArray<FString> Samples;
|
|
321
|
+
|
|
322
|
+
#if WITH_EDITOR
|
|
323
|
+
for (AActor* Actor : Targets)
|
|
324
|
+
{
|
|
325
|
+
if (!IsValid(Actor))
|
|
326
|
+
{
|
|
327
|
+
// Do not call GetName() on the pointer the guard just rejected. A
|
|
328
|
+
// garbage-but-unfreed UObject happens to answer today, which is
|
|
329
|
+
// exactly why this would survive testing and then not survive a
|
|
330
|
+
// real collection.
|
|
331
|
+
Failed.Add(TEXT("an actor in the selection is pending destruction"));
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
// An actor with no user construction script still gets its generated
|
|
335
|
+
// components rebuilt, so this is reported rather than skipped: a
|
|
336
|
+
// caller expecting a Blueprint change to land needs to know it hit an
|
|
337
|
+
// actor whose class has no script to run.
|
|
338
|
+
if (!Actor->HasNonTrivialUserConstructionScript())
|
|
339
|
+
{
|
|
340
|
+
++WithoutScript;
|
|
341
|
+
}
|
|
342
|
+
Actor->Modify();
|
|
343
|
+
Actor->RerunConstructionScripts();
|
|
344
|
+
++Reran;
|
|
345
|
+
if (Samples.Num() < MCPRefreshMaxSamples)
|
|
346
|
+
{
|
|
347
|
+
Samples.Add(FString::Printf(TEXT("%s (%s)"), *Actor->GetActorLabel(), *Actor->GetClass()->GetName()));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
#else
|
|
351
|
+
for (AActor* Actor : Targets)
|
|
352
|
+
{
|
|
353
|
+
Failed.Add(FString::Printf(
|
|
354
|
+
TEXT("%s: RerunConstructionScripts is editor-only"), *Actor->GetActorLabel()));
|
|
355
|
+
}
|
|
356
|
+
#endif
|
|
357
|
+
|
|
358
|
+
TArray<FString> MissingLabels;
|
|
359
|
+
for (const FString& Label : ActorLabels)
|
|
360
|
+
{
|
|
361
|
+
if (!FoundLabels.Contains(Label))
|
|
362
|
+
{
|
|
363
|
+
MissingLabels.Add(Label);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
auto Result = MCPSuccess();
|
|
368
|
+
Result->SetBoolField(TEXT("success"), Failed.IsEmpty() && MissingLabels.IsEmpty());
|
|
369
|
+
if (!MissingLabels.IsEmpty())
|
|
370
|
+
{
|
|
371
|
+
Result->SetStringField(TEXT("error"), FString::Printf(
|
|
372
|
+
TEXT("%d requested actor label(s) matched nothing in this world; see missingLabels."),
|
|
373
|
+
MissingLabels.Num()));
|
|
374
|
+
}
|
|
375
|
+
Result->SetNumberField(TEXT("matched"), Targets.Num());
|
|
376
|
+
Result->SetNumberField(TEXT("reran"), Reran);
|
|
377
|
+
Result->SetNumberField(TEXT("actorsWithNoUserConstructionScript"), WithoutScript);
|
|
378
|
+
Result->SetArrayField(TEXT("failed"), MCPStringListToJson(Failed));
|
|
379
|
+
Result->SetArrayField(TEXT("missingLabels"), MCPStringListToJson(MissingLabels));
|
|
380
|
+
Result->SetArrayField(TEXT("samples"), MCPStringListToJson(Samples));
|
|
381
|
+
if (Reran > 0)
|
|
382
|
+
{
|
|
383
|
+
MCPSetUpdated(Result);
|
|
384
|
+
Result->SetStringField(TEXT("saveNote"),
|
|
385
|
+
TEXT("Reconstructed actors leave the level dirty and it is NOT saved. Call level(save) when you are done."));
|
|
386
|
+
}
|
|
387
|
+
return MCPResult(Result);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// ---------------------------------------------------------------------------
|
|
391
|
+
// recreate_physics_state (#915)
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
TSharedPtr<FJsonValue> FLevelHandlers::RecreatePhysicsState(const TSharedPtr<FJsonObject>& Params)
|
|
394
|
+
{
|
|
395
|
+
MCP_CHECK_GAME_THREAD();
|
|
396
|
+
|
|
397
|
+
const FString WorldScope = OptionalString(Params, TEXT("world"), TEXT("editor"));
|
|
398
|
+
UWorld* World = ResolveWorldFromParams(Params, *WorldScope);
|
|
399
|
+
if (!World)
|
|
400
|
+
{
|
|
401
|
+
return MCPError(FString::Printf(TEXT("World not available for scope '%s'"), *WorldScope));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
TArray<FString> ActorLabels;
|
|
405
|
+
{
|
|
406
|
+
const TArray<TSharedPtr<FJsonValue>>* LabelValues = nullptr;
|
|
407
|
+
if (Params->TryGetArrayField(TEXT("actorLabels"), LabelValues) && LabelValues)
|
|
408
|
+
{
|
|
409
|
+
ActorLabels = JsonArrayToStringList(LabelValues);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const FString LabelPrefix = OptionalString(Params, TEXT("labelPrefix"));
|
|
413
|
+
const FString Tag = OptionalString(Params, TEXT("tag"));
|
|
414
|
+
const FString ActorClassSpec = OptionalString(Params, TEXT("classFilter"));
|
|
415
|
+
const FString ComponentClassSpec = OptionalString(Params, TEXT("componentClass"));
|
|
416
|
+
const FString ComponentNameContains = OptionalString(Params, TEXT("componentNameContains"));
|
|
417
|
+
|
|
418
|
+
if (ActorLabels.IsEmpty() && LabelPrefix.IsEmpty() && Tag.IsEmpty() &&
|
|
419
|
+
ActorClassSpec.IsEmpty() && ComponentClassSpec.IsEmpty() && ComponentNameContains.IsEmpty())
|
|
420
|
+
{
|
|
421
|
+
// A world-wide physics rebuild is a long, disruptive operation, and the
|
|
422
|
+
// bug this action exists for is always localised to the meshes that
|
|
423
|
+
// changed. Refusing an unbounded call is the point.
|
|
424
|
+
return MCPError(TEXT("Pass at least one bound: actorLabels, labelPrefix, tag, classFilter, componentClass or componentNameContains. This never refreshes a whole world implicitly."));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
UClass* ActorClass = nullptr;
|
|
428
|
+
if (!ActorClassSpec.IsEmpty())
|
|
429
|
+
{
|
|
430
|
+
ActorClass = MCPResolveClassOfType(ActorClassSpec, AActor::StaticClass(), true);
|
|
431
|
+
if (!ActorClass)
|
|
432
|
+
{
|
|
433
|
+
return MCPClassNotFoundError(ActorClassSpec, TEXT("classFilter"));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
UClass* ComponentClass = nullptr;
|
|
437
|
+
if (!ComponentClassSpec.IsEmpty())
|
|
438
|
+
{
|
|
439
|
+
ComponentClass = MCPResolveClassOfType(ComponentClassSpec, UActorComponent::StaticClass(), true);
|
|
440
|
+
if (!ComponentClass)
|
|
441
|
+
{
|
|
442
|
+
return MCPClassNotFoundError(ComponentClassSpec, TEXT("componentClass"));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// dryRun defaults to TRUE: this is the preflight #915 asked for. Seeing
|
|
447
|
+
// which components are about to be rebuilt, and what their collision state
|
|
448
|
+
// is now, is most of the value.
|
|
449
|
+
const bool bDryRun = OptionalBool(Params, TEXT("dryRun"), true);
|
|
450
|
+
const int32 MaxComponents = FMath::Clamp(
|
|
451
|
+
OptionalInt(Params, TEXT("maxComponents"), MCPRefreshDefaultMaxComponents),
|
|
452
|
+
1, MCPRefreshHardMaxComponents);
|
|
453
|
+
|
|
454
|
+
TSet<FString> WantedLabels(ActorLabels);
|
|
455
|
+
TSet<FString> FoundLabels;
|
|
456
|
+
TArray<UPrimitiveComponent*> Targets;
|
|
457
|
+
for (TActorIterator<AActor> It(World); It; ++It)
|
|
458
|
+
{
|
|
459
|
+
AActor* Actor = *It;
|
|
460
|
+
if (!Actor) continue;
|
|
461
|
+
const FString Label = Actor->GetActorLabel();
|
|
462
|
+
|
|
463
|
+
if (!WantedLabels.IsEmpty())
|
|
464
|
+
{
|
|
465
|
+
if (!WantedLabels.Contains(Label)) continue;
|
|
466
|
+
FoundLabels.Add(Label);
|
|
467
|
+
}
|
|
468
|
+
if (!LabelPrefix.IsEmpty() && !Label.StartsWith(LabelPrefix, ESearchCase::CaseSensitive)) continue;
|
|
469
|
+
if (!Tag.IsEmpty() && !Actor->ActorHasTag(FName(*Tag))) continue;
|
|
470
|
+
if (ActorClass && !Actor->IsA(ActorClass)) continue;
|
|
471
|
+
|
|
472
|
+
for (UActorComponent* Component : Actor->GetComponents())
|
|
473
|
+
{
|
|
474
|
+
UPrimitiveComponent* Primitive = Cast<UPrimitiveComponent>(Component);
|
|
475
|
+
if (!Primitive) continue;
|
|
476
|
+
if (ComponentClass && !Primitive->IsA(ComponentClass)) continue;
|
|
477
|
+
if (!ComponentNameContains.IsEmpty() &&
|
|
478
|
+
!Primitive->GetName().Contains(ComponentNameContains, ESearchCase::IgnoreCase))
|
|
479
|
+
{
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
Targets.Add(Primitive);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
Targets.Sort([](const UPrimitiveComponent& A, const UPrimitiveComponent& B)
|
|
487
|
+
{
|
|
488
|
+
return A.GetPathName() < B.GetPathName();
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
auto Result = MCPSuccess();
|
|
492
|
+
Result->SetBoolField(TEXT("dryRun"), bDryRun);
|
|
493
|
+
Result->SetNumberField(TEXT("matchedComponents"), Targets.Num());
|
|
494
|
+
|
|
495
|
+
TArray<FString> MissingLabels;
|
|
496
|
+
for (const FString& Label : ActorLabels)
|
|
497
|
+
{
|
|
498
|
+
if (!FoundLabels.Contains(Label)) MissingLabels.Add(Label);
|
|
499
|
+
}
|
|
500
|
+
if (!MissingLabels.IsEmpty())
|
|
501
|
+
{
|
|
502
|
+
Result->SetArrayField(TEXT("missingLabels"), MCPStringListToJson(MissingLabels));
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (Targets.Num() > MaxComponents)
|
|
506
|
+
{
|
|
507
|
+
Result->SetBoolField(TEXT("success"), false);
|
|
508
|
+
Result->SetStringField(TEXT("error"), FString::Printf(
|
|
509
|
+
TEXT("Matched %d components, over maxComponents (%d). Narrow the bounds or raise it deliberately."),
|
|
510
|
+
Targets.Num(), MaxComponents));
|
|
511
|
+
return MCPResult(Result);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
TArray<TSharedPtr<FJsonValue>> Rows;
|
|
515
|
+
int32 Recreated = 0;
|
|
516
|
+
int32 PhysicsStateAfter = 0;
|
|
517
|
+
int32 NoCollisionCount = 0;
|
|
518
|
+
TMap<FString, int32> CollisionSummary;
|
|
519
|
+
|
|
520
|
+
for (UPrimitiveComponent* Primitive : Targets)
|
|
521
|
+
{
|
|
522
|
+
const ECollisionEnabled::Type Before = Primitive->GetCollisionEnabled();
|
|
523
|
+
const bool bStateBefore = Primitive->IsPhysicsStateCreated();
|
|
524
|
+
|
|
525
|
+
if (!bDryRun)
|
|
526
|
+
{
|
|
527
|
+
Primitive->RecreatePhysicsState();
|
|
528
|
+
++Recreated;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
const ECollisionEnabled::Type After = Primitive->GetCollisionEnabled();
|
|
532
|
+
const bool bStateAfter = Primitive->IsPhysicsStateCreated();
|
|
533
|
+
if (bStateAfter) ++PhysicsStateAfter;
|
|
534
|
+
if (After == ECollisionEnabled::NoCollision) ++NoCollisionCount;
|
|
535
|
+
++CollisionSummary.FindOrAdd(MCPRefreshCollisionName(After));
|
|
536
|
+
|
|
537
|
+
if (Rows.Num() < MCPRefreshMaxResultRows)
|
|
538
|
+
{
|
|
539
|
+
TSharedPtr<FJsonObject> Row = MakeShared<FJsonObject>();
|
|
540
|
+
Row->SetStringField(TEXT("actorLabel"), Primitive->GetOwner() ? Primitive->GetOwner()->GetActorLabel() : FString());
|
|
541
|
+
Row->SetStringField(TEXT("componentName"), Primitive->GetName());
|
|
542
|
+
Row->SetStringField(TEXT("componentClass"), Primitive->GetClass()->GetName());
|
|
543
|
+
Row->SetStringField(TEXT("status"), bDryRun ? TEXT("would_recreate") : TEXT("recreated"));
|
|
544
|
+
Row->SetStringField(TEXT("collisionEnabledBefore"), MCPRefreshCollisionName(Before));
|
|
545
|
+
Row->SetStringField(TEXT("collisionEnabledAfter"), MCPRefreshCollisionName(After));
|
|
546
|
+
Row->SetStringField(TEXT("collisionProfile"), Primitive->GetCollisionProfileName().ToString());
|
|
547
|
+
Row->SetBoolField(TEXT("physicsStateCreatedBefore"), bStateBefore);
|
|
548
|
+
Row->SetBoolField(TEXT("physicsStateCreatedAfter"), bStateAfter);
|
|
549
|
+
Row->SetBoolField(TEXT("registered"), Primitive->IsRegistered());
|
|
550
|
+
Rows.Add(MakeShared<FJsonValueObject>(Row));
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
TSharedPtr<FJsonObject> Summary = MakeShared<FJsonObject>();
|
|
555
|
+
for (const TPair<FString, int32>& Pair : CollisionSummary)
|
|
556
|
+
{
|
|
557
|
+
Summary->SetNumberField(Pair.Key, Pair.Value);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
Result->SetNumberField(bDryRun ? TEXT("wouldRecreate") : TEXT("recreated"),
|
|
561
|
+
bDryRun ? Targets.Num() : Recreated);
|
|
562
|
+
Result->SetNumberField(TEXT("physicsStateCreated"), PhysicsStateAfter);
|
|
563
|
+
Result->SetNumberField(TEXT("componentsWithNoCollision"), NoCollisionCount);
|
|
564
|
+
Result->SetObjectField(TEXT("collisionSummary"), Summary);
|
|
565
|
+
Result->SetNumberField(TEXT("returnedResults"), Rows.Num());
|
|
566
|
+
Result->SetBoolField(TEXT("resultsTruncated"), Rows.Num() < Targets.Num());
|
|
567
|
+
Result->SetArrayField(TEXT("results"), Rows);
|
|
568
|
+
if (Targets.IsEmpty())
|
|
569
|
+
{
|
|
570
|
+
Result->SetStringField(TEXT("zeroMatchNote"),
|
|
571
|
+
TEXT("Nothing matched. The bounds are AND-ed: an actor selector and a componentClass must both hold. Check with level(query_components, countOnly:true) using the same filters."));
|
|
572
|
+
}
|
|
573
|
+
if (bDryRun)
|
|
574
|
+
{
|
|
575
|
+
Result->SetStringField(TEXT("dryRunNote"),
|
|
576
|
+
TEXT("dryRun defaults to TRUE for this action: the preflight shows which components would be rebuilt and their current collision state. Pass dryRun=false to rebuild them."));
|
|
577
|
+
}
|
|
578
|
+
else
|
|
579
|
+
{
|
|
580
|
+
Result->SetStringField(TEXT("traceNote"),
|
|
581
|
+
TEXT("Re-run the trace that gave the wrong answer. A false-negative line_trace caused by stale collision looks exactly like a real miss, so the only proof this worked is the trace."));
|
|
582
|
+
}
|
|
583
|
+
return MCPResult(Result);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// ---------------------------------------------------------------------------
|
|
587
|
+
// test_component_overlap (#914)
|
|
588
|
+
// ---------------------------------------------------------------------------
|
|
589
|
+
TSharedPtr<FJsonValue> FLevelHandlers::TestComponentOverlap(const TSharedPtr<FJsonObject>& Params)
|
|
590
|
+
{
|
|
591
|
+
MCP_CHECK_GAME_THREAD();
|
|
592
|
+
|
|
593
|
+
const FString WorldScope = OptionalString(Params, TEXT("world"), TEXT("editor"));
|
|
594
|
+
UWorld* World = ResolveWorldFromParams(Params, *WorldScope);
|
|
595
|
+
if (!World)
|
|
596
|
+
{
|
|
597
|
+
return MCPError(FString::Printf(TEXT("World not available for scope '%s'"), *WorldScope));
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
FString ActorLabelA;
|
|
601
|
+
if (auto Err = RequireStringAlt(Params, TEXT("actorLabelA"), TEXT("actorPathA"), ActorLabelA)) return Err;
|
|
602
|
+
FString ActorLabelB;
|
|
603
|
+
if (auto Err = RequireStringAlt(Params, TEXT("actorLabelB"), TEXT("actorPathB"), ActorLabelB)) return Err;
|
|
604
|
+
const FString ComponentNameA = OptionalString(Params, TEXT("componentNameA"));
|
|
605
|
+
const FString ComponentNameB = OptionalString(Params, TEXT("componentNameB"));
|
|
606
|
+
|
|
607
|
+
FString Method = OptionalString(Params, TEXT("method"), TEXT("OBB")).ToUpper();
|
|
608
|
+
if (Method != TEXT("OBB") && Method != TEXT("AABB"))
|
|
609
|
+
{
|
|
610
|
+
return MCPError(TEXT("'method' must be either 'OBB' (oriented, default) or 'AABB' (axis-aligned world bounds)"));
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// #983: each side is selected by its own label / path pair, so an overlap
|
|
614
|
+
// test between two copy-pasted props refuses rather than answering about
|
|
615
|
+
// two actors the caller did not name.
|
|
616
|
+
auto ResolveSide = [&](const TCHAR* LabelKey, const TCHAR* PathKey, const FString& ComponentName,
|
|
617
|
+
USceneComponent*& OutComponent, AActor*& OutActor, TSharedPtr<FJsonValue>& OutError) -> bool
|
|
618
|
+
{
|
|
619
|
+
FMCPActorSelector Selector;
|
|
620
|
+
Selector.LabelKey = LabelKey;
|
|
621
|
+
Selector.PathKey = PathKey;
|
|
622
|
+
AActor* Actor = MCPResolveActor(World, Params, OutError, Selector);
|
|
623
|
+
if (!Actor) return false;
|
|
624
|
+
OutActor = Actor;
|
|
625
|
+
const FString ActorLabel = Actor->GetActorLabel();
|
|
626
|
+
if (ComponentName.IsEmpty())
|
|
627
|
+
{
|
|
628
|
+
OutComponent = Actor->GetRootComponent();
|
|
629
|
+
if (!OutComponent)
|
|
630
|
+
{
|
|
631
|
+
OutError = MCPError(FString::Printf(TEXT("Actor '%s' has no root component"), *ActorLabel));
|
|
632
|
+
return false;
|
|
633
|
+
}
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
UActorComponent* Component = MCPRefreshFindComponent(Actor, ComponentName);
|
|
637
|
+
OutComponent = Cast<USceneComponent>(Component);
|
|
638
|
+
if (!OutComponent)
|
|
639
|
+
{
|
|
640
|
+
OutError = MCPError(FString::Printf(
|
|
641
|
+
TEXT("No SceneComponent named '%s' on actor '%s'"), *ComponentName, *ActorLabel));
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
return true;
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
USceneComponent* ComponentA = nullptr;
|
|
648
|
+
USceneComponent* ComponentB = nullptr;
|
|
649
|
+
AActor* ActorA = nullptr;
|
|
650
|
+
AActor* ActorB = nullptr;
|
|
651
|
+
TSharedPtr<FJsonValue> ResolveError;
|
|
652
|
+
if (!ResolveSide(TEXT("actorLabelA"), TEXT("actorPathA"), ComponentNameA, ComponentA, ActorA, ResolveError)) return ResolveError;
|
|
653
|
+
if (!ResolveSide(TEXT("actorLabelB"), TEXT("actorPathB"), ComponentNameB, ComponentB, ActorB, ResolveError)) return ResolveError;
|
|
654
|
+
if (ComponentA == ComponentB)
|
|
655
|
+
{
|
|
656
|
+
return MCPError(TEXT("Both sides resolved to the same component; an overlap test between a component and itself has no answer"));
|
|
657
|
+
}
|
|
658
|
+
ActorLabelA = ActorA->GetActorLabel();
|
|
659
|
+
ActorLabelB = ActorB->GetActorLabel();
|
|
660
|
+
|
|
661
|
+
auto Result = MCPSuccess();
|
|
662
|
+
Result->SetStringField(TEXT("method"), Method);
|
|
663
|
+
Result->SetStringField(TEXT("actorLabelA"), ActorLabelA);
|
|
664
|
+
Result->SetStringField(TEXT("actorPathA"), ActorA->GetPathName());
|
|
665
|
+
Result->SetStringField(TEXT("actorLabelB"), ActorLabelB);
|
|
666
|
+
Result->SetStringField(TEXT("actorPathB"), ActorB->GetPathName());
|
|
667
|
+
|
|
668
|
+
TSharedPtr<FJsonObject> SideA = MCPRefreshDescribeComponent(ComponentA);
|
|
669
|
+
SideA->SetStringField(TEXT("actorLabel"), ActorLabelA);
|
|
670
|
+
SideA->SetStringField(TEXT("actorPath"), ActorA->GetPathName());
|
|
671
|
+
TSharedPtr<FJsonObject> SideB = MCPRefreshDescribeComponent(ComponentB);
|
|
672
|
+
SideB->SetStringField(TEXT("actorLabel"), ActorLabelB);
|
|
673
|
+
SideB->SetStringField(TEXT("actorPath"), ActorB->GetPathName());
|
|
674
|
+
Result->SetObjectField(TEXT("a"), SideA);
|
|
675
|
+
Result->SetObjectField(TEXT("b"), SideB);
|
|
676
|
+
|
|
677
|
+
bool bOverlap = false;
|
|
678
|
+
double Separation = 0.0;
|
|
679
|
+
double Penetration = 0.0;
|
|
680
|
+
FVector Axis = FVector::ZeroVector;
|
|
681
|
+
|
|
682
|
+
if (Method == TEXT("AABB"))
|
|
683
|
+
{
|
|
684
|
+
const FBox BoxA = ComponentA->GetBounds().GetBox();
|
|
685
|
+
const FBox BoxB = ComponentB->GetBounds().GetBox();
|
|
686
|
+
bOverlap = BoxA.Intersect(BoxB);
|
|
687
|
+
// Per-axis gap or overlap depth, which is what a caller tuning a
|
|
688
|
+
// placement actually wants back.
|
|
689
|
+
FVector PerAxis = FVector::ZeroVector;
|
|
690
|
+
for (int32 Index = 0; Index < 3; ++Index)
|
|
691
|
+
{
|
|
692
|
+
const double Gap = FMath::Max(
|
|
693
|
+
BoxA.Min[Index] - BoxB.Max[Index],
|
|
694
|
+
BoxB.Min[Index] - BoxA.Max[Index]);
|
|
695
|
+
PerAxis[Index] = Gap;
|
|
696
|
+
}
|
|
697
|
+
if (bOverlap)
|
|
698
|
+
{
|
|
699
|
+
// Every axis gap is negative when they intersect; the shallowest
|
|
700
|
+
// is the penetration depth.
|
|
701
|
+
Penetration = -FMath::Max3(PerAxis.X, PerAxis.Y, PerAxis.Z);
|
|
702
|
+
}
|
|
703
|
+
else
|
|
704
|
+
{
|
|
705
|
+
Separation = FMath::Max3(PerAxis.X, PerAxis.Y, PerAxis.Z);
|
|
706
|
+
}
|
|
707
|
+
Result->SetObjectField(TEXT("perAxisGap"), MCPVec3ToJsonObject(PerAxis));
|
|
708
|
+
}
|
|
709
|
+
else
|
|
710
|
+
{
|
|
711
|
+
const FMCPOrientedBox BoxA = MCPBuildOrientedBox(ComponentA);
|
|
712
|
+
const FMCPOrientedBox BoxB = MCPBuildOrientedBox(ComponentB);
|
|
713
|
+
bOverlap = MCPOrientedBoxesOverlap(BoxA, BoxB, Separation, Penetration, Axis);
|
|
714
|
+
Result->SetObjectField(TEXT("separatingAxis"), MCPVec3ToJsonObject(Axis));
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
Result->SetBoolField(TEXT("overlap"), bOverlap);
|
|
718
|
+
Result->SetNumberField(TEXT("separation"), bOverlap ? 0.0 : Separation);
|
|
719
|
+
Result->SetNumberField(TEXT("penetration"), bOverlap ? Penetration : 0.0);
|
|
720
|
+
Result->SetStringField(TEXT("note"),
|
|
721
|
+
TEXT("This is a geometric test over the components' bounds and transforms. It deliberately does not run a physics overlap query, because a physics answer depends on collision state and stale collision state is what makes a trace lie. Use level(recreate_physics_state) when you need the physics answer to agree."));
|
|
722
|
+
return MCPResult(Result);
|
|
723
|
+
}
|