// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Math/Axis.h" #include "PreviewScene.h" #include "PhysicsAssetEditorSelection.h" #include "PhysicsAssetUtils.h" #include "PhysicsEngine/ConstraintInstance.h" #include "PhysicsEngine/ShapeElem.h" #include "Preferences/PhysicsAssetEditorOptions.h" #include "PhysicsAssetEditorSharedData.generated.h" struct FBoneVertInfo; class FPhysicsAssetEditorSharedData; class IPersonaPreviewScene; class UBodySetup; class UPhysicsAsset; class UPhysicsAssetEditorPhysicsHandleComponent; class UPhysicsAssetEditorSkeletalMeshComponent; class UPhysicsConstraintTemplate; class USkeletalMesh; class UStaticMeshComponent; /** Scoped object that blocks selection broadcasts until it leaves scope */ struct FScopedBulkSelection { FScopedBulkSelection(TSharedPtr InSharedData); ~FScopedBulkSelection(); TSharedPtr SharedData; }; /** * class UPhysicsAssetCollisionPair * * Used to simplify Copy + Paste of collision relationships between physics bodies. */ UCLASS(MinimalAPI) class UPhysicsAssetCollisionPair : public UObject { GENERATED_BODY() public: void Set(const FName InBoneNameA, const FName InBoneNameB) { BoneNameA = InBoneNameA; BoneNameB = InBoneNameB; } UPROPERTY() FName BoneNameA; UPROPERTY() FName BoneNameB; }; /*----------------------------------------------------------------------------- FPhysicsAssetEditorSharedData -----------------------------------------------------------------------------*/ class FPhysicsAssetEditorSharedData { public: using SelectionFilterRange = UPhysicsAssetEditorSelection::FilterRange; using SelectionUniqueRange = UPhysicsAssetEditorSelection::UniqueRange; /** Constructor/Destructor */ FPhysicsAssetEditorSharedData(); virtual ~FPhysicsAssetEditorSharedData(); enum EPhysicsAssetEditorConstraintType { PCT_Swing1, PCT_Swing2, PCT_Twist, }; /** Encapsulates a selected set of bodies or constraints */ using FSelection = FPhysicsAssetEditorSelectedElement; /** Initializes members */ void Initialize(const TSharedRef& InPreviewScene); /** Caches a preview mesh. Sets us to a default mesh if none is set yet (or if an older one got deleted) */ void CachePreviewMesh(); /** Accessor for mesh view mode, allows access for simulation and non-simulation modes */ EPhysicsAssetEditorMeshViewMode GetCurrentMeshViewMode(bool bSimulation); /** Accessor for Center of Mass view mode, allows access for simulation and non-simulation modes */ EPhysicsAssetEditorCenterOfMassViewMode GetCurrentCenterOfMassViewMode(const bool bSimulation) const; /** Accessor for collision view mode, allows access for simulation and non-simulation modes */ EPhysicsAssetEditorCollisionViewMode GetCurrentCollisionViewMode(bool bSimulation); /** Accessor for constraint view mode, allows access for simulation and non-simulation modes */ EPhysicsAssetEditorConstraintViewMode GetCurrentConstraintViewMode(bool bSimulation); /** Clear all of the selected constraints */ void ClearSelectedConstraints(); /** Add or remove a constraint from the current selection */ void ModifySelectedConstraints(const int32 ConstraintIndex, const bool bSelected); /** Add or remove a collection of constraints from the current selection */ void ModifySelectedConstraints(const TArray& ConstraintsIndices, const bool bSelected); /** Set the current selection */ void SetSelectedConstraints(const TArray& ConstraintsIndices); /** Check whether the constraint at the specified index is selected */ bool IsConstraintSelected(int32 ConstraintIndex) const; /** Get the world transform of the specified selected constraint */ FTransform GetConstraintWorldTM(const FSelection* Constraint, EConstraintFrame::Type Frame) const; /** Get the world transform of the specified constraint */ FTransform GetConstraintWorldTM(const UPhysicsConstraintTemplate* const ConstraintSetup, const EConstraintFrame::Type Frame, const float Scale = 1.0f) const; /** Get the world transform of the specified constraint */ FTransform GetConstraintMatrix(int32 ConstraintIndex, EConstraintFrame::Type Frame, float Scale) const; /** Get the body transform of the specified constraint */ FTransform GetConstraintBodyTM(const UPhysicsConstraintTemplate* ConstraintSetup, EConstraintFrame::Type Frame) const; /** Set the constraint relative transform */ void SetConstraintRelTM(const FSelection* Constraint, const FTransform& RelTM); /** Set the constraint relative transform for a single selected constraint */ inline void SetSelectedConstraintRelTM(const FTransform& RelTM) { SetConstraintRelTM(GetSelectedConstraint(), RelTM); } /** Snaps a constraint at the specified index to it's bone */ void SnapConstraintToBone(const int32 ConstraintIndex, const EConstraintTransformComponentFlags ComponentFlags = EConstraintTransformComponentFlags::All); /** Snaps the specified constraint to it's bone */ void SnapConstraintToBone(FConstraintInstance& ConstraintInstance, const EConstraintTransformComponentFlags ComponentFlags = EConstraintTransformComponentFlags::All); /** Paste the currently-copied constraint properties onto the single selected constraint */ void PasteConstraintProperties(); /** Cycles the rows of the transform matrix for the selected constraint. Assumes the selected constraint * is valid and that we are in constraint editing mode*/ void CycleCurrentConstraintOrientation(); /** Cycles the active constraint*/ void CycleCurrentConstraintActive(); /** Cycles the active constraint between locked and limited */ void ToggleConstraint(EPhysicsAssetEditorConstraintType Constraint); /** Gets whether the active constraint is locked */ bool IsAngularConstraintLocked(EPhysicsAssetEditorConstraintType Constraint) const; void DeleteBody(int32 DelBodyIndex, bool bRefreshComponent = true); /** Deletes all currently selected objects */ void DeleteCurrentSelection(); /** Deletes the currently selected bodies and all their primitives */ void DeleteCurrentBody(); /** Deletes the currently selected primitives */ void DeleteCurrentPrim(); /** Deletes the currently selected constraints */ void DeleteCurrentConstraint(); void SetGroupSelectionActive(const bool bIsActive); bool IsGroupSelectionActive() const; void ModifySelected(const TArray& InSelectedElements, const bool bSelected); void SetSelected(const TArray& InSelectedElements); bool IsSelected(const FSelection& InSelection) const; /** Center of Mass Selection */ void ClearSelectedCoMs(); void ModifySelectedCoMs(const FSelection& InSelectedCoM, const bool bSelected); void ModifySelectedCoMs(const TArray& InSelectedElements, const bool bSelected); void SetSelectedCoMs(const FSelection& InSelectedElement); void SetSelectedCoMs(const TArray& InSelectedElements); bool IsCoMSelected(const int32 BodyIndex) const; /** Body Selection */ void ClearSelectedBody(); void ModifySelectedBodies(const FSelection& Body, bool bSelected); void ModifySelectedBodies(const TArray& Bodies, bool bSelected); void SetSelectedBodies(const FSelection& InSelectedElement); void SetSelectedBodies(const TArray& Bodies); bool IsBodySelected(const FSelection& Body) const; bool IsBodySelected(const int32 BodyIndex) const; void ModifySelectedBodies(const int32 BodyIndex, const bool bSelected); void ModifySelectedBodies(const TArray& BodiesIndices, const bool bSelected); void SetSelectedBodies(const int32 BodyIndex); void SetSelectedBodies(const TArray& BodiesIndices); void SetSelectedBodiesAllPrimitive(const TArray& BodiesIndices, bool bSelected); void SetSelectedBodiesPrimitivesWithCollisionType(const TArray& BodiesIndices, const ECollisionEnabled::Type CollisionType, bool bSelected); void SetSelectedBodiesPrimitives(const TArray& BodiesIndices, bool bSelected, const TFunction&, const int32 BodyIndex, const FKShapeElem&)>& Predicate); /** Primitives Selection */ void ClearSelectedPrimitives(); void ModifySelectedPrimitives(const FSelection& InSelectedPrimitive, const bool bSelected); void ModifySelectedPrimitives(const TArray& InSelectedElements, const bool bSelected); void SetSelectedPrimitives(const FSelection& InSelectedElement); void SetSelectedPrimitives(const TArray& InSelectedElements); void ToggleSelectionType(bool bIgnoreUserConstraints = true); void ToggleShowSelected(); bool IsBodyHidden(const int32 BodyIndex) const; bool IsConstraintHidden(const int32 ConstraintIndex) const; void HideBody(const int32 BodyIndex); void ShowBody(const int32 BodyIndex); void HideConstraint(const int32 ConstraintIndex); void ShowConstraint(const int32 ConstraintIndex); void ShowAll(); void HideAll(); void HideAllBodies(); void HideAllConstraints(); void ToggleShowOnlyColliding(); void ToggleShowOnlyConstrained(); void ToggleShowOnlySelected(); void ShowSelected(); void HideSelected(); void RefreshPhysicsAssetChange(const UPhysicsAsset* InPhysAsset, bool bFullClothRefresh = true); void MakeNewBody(int32 NewBoneIndex, bool bAutoSelect = true); void MakeNewBody(const FPhysAssetCreateParams& NewBodyData, const int32 NewBoneIndex, const bool bAutoSelect = true); void RecreateBody(const int32 BoneIndex, const bool bAutoSelect = true); // Create a new physics body to replace an existing one. void RecreateBody(const FPhysAssetCreateParams& BodyData, const int32 BoneIndex, const bool bAutoSelect = true); // Create a new physics body to replace an existing one. void MakeOrRecreateBody(const int32 NewBoneIndex, const bool bAutoSelect = true); // Create a new physics body, replacing an existing one at the same index if it exists. void MakeNewConstraints(int32 ParentBodyIndex, const TArray& ChildBodyIndices); void MakeNewConstraint(int32 ParentBodyIndex, int32 ChildBodyIndex); void CopySelectedBodiesAndConstraintsToClipboard(int32& OutNumCopiedBodies, int32& OutNumCopiedConstraints, int32& OutNumCopiedDisabledCollisionPairs); bool CanPasteBodiesAndConstraintsFromClipboard() const; void PasteBodiesAndConstraintsFromClipboard(int32& OutNumPastedBodies, int32& OutNumPastedConstraints, int32& OutNumPastedDisabledCollisionPairs); void CopySelectedShapesToClipboard(int32& OutNumCopiedShapes, int32& OutNumBodiesCopiedFrom); bool CanPasteShapesFromClipboard() const; void PasteShapesFromClipboard(int32& OutNumPastedShapes, int32& OutNumBodiesPastedInto); void CopyBodyProperties(); void CopyConstraintProperties(); void PasteBodyProperties(); void CopyBodyName(); bool WeldSelectedBodies(bool bWeld = true); void Mirror(); /** auto name a primitive, if PrimitiveIndex is INDEX_NONE, then the last primitive of specified typed is renamed */ void AutoNamePrimitive(int32 BodyIndex, EAggCollisionShape::Type PrimitiveType, int32 PrimitiveIndex = INDEX_NONE); void AutoNameAllPrimitives(int32 BodyIndex, EAggCollisionShape::Type PrimitiveType); void AutoNameAllPrimitives(int32 BodyIndex, EPhysAssetFitGeomType PrimitiveType); /** Toggle simulation on and off */ void ToggleSimulation(); /** Open a new body dialog */ void OpenNewBodyDlg(); /** Open a new body dialog, filling in NewBodyResponse when the dialog is closed */ static void OpenNewBodyDlg(EAppReturnType::Type* NewBodyResponse); /** Helper function for creating the details panel widget and other controls that form the New body dialog (used by OpenNewBodyDlg and the tools tab) */ static TSharedRef CreateGenerateBodiesWidget(const FSimpleDelegate& InOnCreate, const FSimpleDelegate& InOnCancel = FSimpleDelegate(), const TAttribute& InIsEnabled = TAttribute(), const TAttribute& InCreateButtonText = TAttribute(), bool bForNewAsset = false); /** Handle clicking on a body */ void HitBone(int32 BodyIndex, EAggCollisionShape::Type PrimType, int32 PrimIndex, bool bGroupSelect); /** Handle clicking on a Center of Mass marker */ void HitCoM(const int32 BodyIndex, const bool bGroupSelect); /** Handle clikcing on a constraint */ void HitConstraint(int32 ConstraintIndex, bool bGroupSelect); /** Undo/Redo */ void PostUndo(); void Redo(); /** Helpers to enable/disable collision on selected bodies */ void SetCollisionBetweenSelected(bool bEnableCollision); bool CanSetCollisionBetweenSelected(bool bEnableCollision) const; void SetCollisionBetweenSelectedAndAll(bool bEnableCollision); bool CanSetCollisionBetweenSelectedAndAll(bool bEnableCollision) const; /** Helpers to set primitive-level collision filtering on selected bodies */ void SetPrimitiveCollision(ECollisionEnabled::Type CollisionEnabled); bool CanSetPrimitiveCollision(ECollisionEnabled::Type CollisionEnabled) const; bool GetIsPrimitiveCollisionEnabled(ECollisionEnabled::Type CollisionEnabled) const; void SetPrimitiveContributeToMass(bool bContributeToMass); bool CanSetPrimitiveContributeToMass() const; bool GetPrimitiveContributeToMass() const; /** Prevents GC from collecting our objects */ void AddReferencedObjects(FReferenceCollector& Collector); /** Enables and disables simulation. Used by ToggleSimulation */ void EnableSimulation(bool bEnableSimulation); /** Force simulation off for all bodies, regardless of physics type */ void ForceDisableSimulation(); /** Update the clothing simulation's (if any) collision */ void UpdateClothPhysics(); /** broadcast a selection change ( if bSuspendSelectionBroadcast is false ) */ void BroadcastSelectionChanged(); /** broadcast a change in the hierarchy */ void BroadcastHierarchyChanged(); /** broadcast a change in the preview*/ void BroadcastPreviewChanged(); /** Returns true if the clipboard contains data this class can process */ static bool ClipboardHasCompatibleData(); /** Control whether we draw a CoM marker in the viewport */ void ToggleShowCom(); void SetShowCom(bool InValue); bool GetShowCom() const; /** Returns the correct location to draw a CoM marker in the viewport */ FVector GetCOMRenderPosition(const int32 BodyIndex) const; bool IsCoMAxisFixedInComponentSpace(const int32 BodyIndex, const EAxis::Type InAxis) const; void SetCoMAxisFixedInComponentSpace(const int32 BodyIndex, const EAxis::Type InAxis, const bool bValue); // Calculate a Center of Mass nudge (offset) for a given body that will locate that body's CoM at the supplied position in world space. FVector CalculateCoMNudgeForWorldSpacePosition(const int32 BodyIndex, const FVector& CoMPositionWorldSpace) const; // Make a copy of the current component space CoM position from each selected physics body - called before a change in physics body transform void RecordSelectedCoM(); const FVector* FindManipulatedBodyCoMPosition(const int32 BodyIndex) const { return ManipulatedBodyCoMPositionMap.Find(BodyIndex); } FVector* FindManipulatedBodyCoMPosition(const int32 BodyIndex) { return ManipulatedBodyCoMPositionMap.Find(BodyIndex); } void PostManipulationUpdateCoM(); void UpdateCoM(); const UPhysicsAssetEditorSelection* GetSelectedObjects() const; SelectionFilterRange SelectedBodies() const; SelectionFilterRange SelectedCoMs() const; SelectionFilterRange SelectedConstraints() const; SelectionFilterRange SelectedPrimitives() const; SelectionFilterRange SelectedBodiesAndPrimitives() const; SelectionUniqueRange UniqueSelectionReferencingBodies() const; // Returns the most recently selected body or primitive - this is useful as selecting a primitive often acts in the same way as selecting its owning body. const FSelection* GetSelectedBodyOrPrimitive() const; const FSelection* GetSelectedBody() const; const FSelection* GetSelectedCoM() const; const FSelection* GetSelectedConstraint() const; const FSelection* GetSelectedPrimitive() const; FVector GetSelectedCoMPosition(); /** Clears all the selected objects */ void ClearSelected(); private: bool ModifySelectionInternal(TFunctionRef SelectionOperation); /** Initializes a constraint setup */ void InitConstraintSetup(UPhysicsConstraintTemplate* ConstraintSetup, int32 ChildBodyIndex, int32 ParentBodyIndex); /** Collision editing helper methods */ void SetCollisionBetween(int32 Body1Index, int32 Body2Index, bool bEnableCollision); /** Update the cached array of bodies that do not collide with the current body selection */ void UpdateNoCollisionBodies(); /** Copy the properties of the one and only selected constraint */ void CopyConstraintProperties(const UPhysicsConstraintTemplate * FromConstraintSetup, UPhysicsConstraintTemplate * ToConstraintSetup, bool bKeepOldRotation = false); /** Copies a reference to a given element to the clipboard */ void CopyToClipboard(const FString& ObjectType, UObject* Object); /** Pastes data from the clipboard on a given type */ bool PasteFromClipboard(const FString& InObjectType, UPhysicsAsset*& OutAsset, UObject*& OutObject); /** Clears data in clipboard if it was pointing to the given type/data */ void ConditionalClearClipboard(const FString& ObjectType, UObject* Object); /** Checks and parses clipboard data */ static bool ParseClipboard(UPhysicsAsset*& OutAsset, FString& OutObjectType, UObject*& OutObject); /** Generate a new unique name for a constraint */ FString MakeUniqueNewConstraintName(); public: /** Callback for handling selection changes */ DECLARE_EVENT_OneParam(FPhysicsAssetEditorSharedData, FSelectionChanged, const TArray&); FSelectionChanged SelectionChangedEvent; /** Callback for handling changes to the bone/body/constraint hierarchy */ DECLARE_EVENT(FPhysicsAssetEditorSharedData, FHierarchyChanged); FHierarchyChanged HierarchyChangedEvent; /** Callback for handling changes to the current selection in the tree */ DECLARE_EVENT(FPhysicsAssetEditorSharedData, FHierarchySelectionChangedEvent); FHierarchySelectionChangedEvent HierarchySelectionChangedEvent; /** Callback for triggering a refresh of the preview viewport */ DECLARE_EVENT(FPhysicsAssetEditorSharedData, FPreviewChanged); FPreviewChanged PreviewChangedEvent; /** The PhysicsAsset asset being inspected */ TObjectPtr PhysicsAsset; /** PhysicsAssetEditor specific skeletal mesh component */ TObjectPtr EditorSkelComp; /** PhysicsAssetEditor specific physical animation component */ TObjectPtr PhysicalAnimationComponent; /** Preview scene */ TWeakPtr PreviewScene; /** Editor options */ TObjectPtr EditorOptions; /** Results from the new body dialog */ EAppReturnType::Type NewBodyResponse; /** Helps define how the asset behaves given user interaction in simulation mode*/ TObjectPtr MouseHandle; /** Draw color for center of mass debug strings */ const FColor COMRenderColor; /** List of bodies that don't collide with the currently selected collision body */ TArray NoCollisionBodies; /** Bone info */ TArray DominantWeightBoneInfos; TArray AnyWeightBoneInfos; TObjectPtr SelectedObjects; void BeginManipulation(); void EndManipulation(); bool IsManipulating() const { return bManipulating; } TMap ManipulatedBodyCoMPositionMap; struct FPhysicsAssetRenderSettings* GetRenderSettings() const; /** Physics Body Overlap Detection **/ void FindOverlappingBodyPairs(const int32 InBodyIndex, TArray>& OutCollidingBodyPairs); void RemoveOverlappingBodyPairs(const int32 InBodyIndex, TArray>& OutCollidingBodyPairs); void InitializeOverlappingBodyPairs(); void UpdateOverlappingBodyPairs(const int32 InBodyIndex); bool IsBodyOverlapping(const int32 InBodyIndex) const; bool ShouldShowBodyOverlappingHighlight(const int32 InBodyIndex) const; void ToggleHighlightOverlapingBodies(); bool IsHighlightingOverlapingBodies() const; TArray> OverlappingCollidingBodyPairs; // A record of all the pairs of physics bodies that are overlapping and not flagged as non-colliding in the physics asset. /** Misc toggles */ bool bRunningSimulation; bool bNoGravitySimulation; /** Manipulation (rotate, translate, scale) */ bool bManipulating; bool bIsGroupSelectionActive = false; bool bShouldUpdatedSelectedCoMs = false; /** when true, we dont broadcast every selection change - allows for bulk changes without so much overhead */ bool bSuspendSelectionBroadcast; /** Used to prevent recursion with tree hierarchy ... needs to be rewritten! */ int32 InsideSelChange; FTransform ResetTM; FIntPoint LastClickPos; FVector LastClickOrigin; FVector LastClickDirection; FVector LastClickHitPos; FVector LastClickHitNormal; bool bLastClickHit; }; EAggCollisionShape::Type ConvertPhysicsAssetGeomTypeToAggCollisionShapeType(EPhysAssetFitGeomType PhysicsAssetGeomType); EPhysAssetFitGeomType ConvertAggCollisionShapeTypeToPhysicsAssetGeomType(const EAggCollisionShape::Type AggCollisionShapeType);