// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/ObjectMacros.h" #include "Engine/SkeletalMesh.h" #include "EngineDefines.h" #include "Components/SkeletalMeshComponent.h" #include "Delegates/DelegateCombinations.h" #include "SkeletalMeshSceneProxy.h" #include "DebugSkelMeshComponent.generated.h" class Error; DECLARE_DELEGATE_RetVal(FText, FGetExtendedViewportText); DECLARE_DELEGATE(FOnDebugForceLODChanged); USTRUCT() struct FSelectedSocketInfo { GENERATED_USTRUCT_BODY() /** Default constructor */ FSelectedSocketInfo() : Socket(nullptr) , bSocketIsOnSkeleton( false ) { } /** Constructor */ FSelectedSocketInfo( class USkeletalMeshSocket* InSocket, bool bInSocketIsOnSkeleton ) : Socket( InSocket ) , bSocketIsOnSkeleton( bInSocketIsOnSkeleton ) { } bool IsValid() const { return Socket != nullptr; } void Reset() { Socket = nullptr; } /** The socket we have selected */ class USkeletalMeshSocket* Socket; /** true if on skeleton, false if on mesh */ bool bSocketIsOnSkeleton; }; /** Different modes for Persona's Turn Table. */ namespace EPersonaTurnTableMode { enum Type { Stopped, Playing, Paused }; }; /** Different modes for when processing root motion */ UENUM() enum class EProcessRootMotionMode : uint8 { /** Preview mesh will not consume root motion */ Ignore, /** Preview mesh will consume root motion continually */ Loop, /** Preview mesh will consume root motion resetting the position back to the origin every time the animation loops */ LoopAndReset }; /** Different modes for visualizing root motion */ UENUM() enum class EVisualizeRootMotionMode : uint8 { /** Preview will not show root motion */ None, /** Preview will show root motion path */ Trajectory, /** Preview will show root motion path and orientation. */ TrajectoryAndOrientation }; ////////////////////////////////////////////////////////////////////////// // FDebugSkelMeshSceneProxy class UDebugSkelMeshComponent; class FSkeletalMeshRenderData; class FDebugSkelMeshDynamicData { public: FDebugSkelMeshDynamicData(UDebugSkelMeshComponent* InComponent); bool bDrawMesh; bool bDrawNormals; bool bDrawTangents; bool bDrawBinormals; bool bDrawClothPaintPreview; bool bFlipNormal; bool bDoubleSidedOpaqueCloth; int32 ClothingSimDataIndexWhenPainting; TArray ClothingSimIndices; TArray ClothingVisiblePropertyValues; float PropertyViewMin; float PropertyViewMax; TArray SkinnedPositions; TArray SkinnedNormals; }; /** * A skeletal mesh component scene proxy with additional debugging options. */ class FDebugSkelMeshSceneProxy : public FSkeletalMeshSceneProxy { public: SIZE_T GetTypeHash() const override; /** * Constructor. * @param Component - skeletal mesh primitive being added */ FDebugSkelMeshSceneProxy(const UDebugSkelMeshComponent* InComponent, FSkeletalMeshRenderData* InSkelMeshRenderData, FLinearColor InWireframeOverlayColor = FLinearColor::White); virtual ~FDebugSkelMeshSceneProxy() {} virtual void GetDynamicMeshElements(const TArray& Views, const FSceneViewFamily& ViewFamily, uint32 VisibilityMap, FMeshElementCollector& Collector) const override; FDebugSkelMeshDynamicData* DynamicData; SIZE_T GetAllocatedSize() const { return FSkeletalMeshSceneProxy::GetAllocatedSize(); } virtual uint32 GetMemoryFootprint() const override { return sizeof(*this) + GetAllocatedSize(); } private: bool bSelectable; }; /** Generic modes used to render debug skeletons depending on editor-specific context */ UENUM() enum class ESkeletonDrawMode : uint8 { /** Bones are visible and selectable */ Default, /** Bones are completely hidden */ Hidden, /** Bones are visible but non-selectable */ GreyedOut }; UCLASS(transient, MinimalAPI) class UDebugSkelMeshComponent : public USkeletalMeshComponent { GENERATED_UCLASS_BODY() /** Global drawing mode for this skeleton. Depends on context of specific editor using the component. */ UPROPERTY() ESkeletonDrawMode SkeletonDrawMode = ESkeletonDrawMode::Default; /** If true then the skeletal mesh associated with the component is drawn. */ UPROPERTY() uint32 bDrawMesh:1; /** If true then the bone names associated with the skeletal mesh are displayed */ UPROPERTY() uint32 bShowBoneNames:1; /** Bone influences viewing */ UPROPERTY(transient) uint32 bDrawBoneInfluences:1; /** Morphtarget viewing */ UPROPERTY(transient) uint32 bDrawMorphTargetVerts : 1; /** Vertex normal viewing */ UPROPERTY(transient) uint32 bDrawNormals:1; /** Vertex tangent viewing */ UPROPERTY(transient) uint32 bDrawTangents:1; /** Vertex binormal viewing */ UPROPERTY(transient) uint32 bDrawBinormals:1; /** Socket hit points viewing */ UPROPERTY(transient) uint32 bDrawSockets:1; /** Attribute visualization */ UPROPERTY(transient) uint32 bDrawAttributes : 1; /** Skeleton sockets visible? */ UPROPERTY(transient) uint32 bSkeletonSocketsVisible:1; /** Mesh sockets visible? */ UPROPERTY(transient) uint32 bMeshSocketsVisible:1; /** Display raw animation bone transform */ UPROPERTY(transient) uint32 bDisplayRawAnimation:1; /** Display non retargeted animation pose */ UPROPERTY(Transient) uint32 bDisplayNonRetargetedPose:1; /** Display additive base bone transform */ UPROPERTY(transient) uint32 bDisplayAdditiveBasePose:1; /** Display baked animation pose */ UPROPERTY(Transient) uint32 bDisplayBakedAnimation:1; /** Display source animation pose */ UPROPERTY(Transient) uint32 bDisplaySourceAnimation:1; /** Display Notification visualizations in viewport */ UPROPERTY(Transient) uint32 bShowNotificationVisualizations:1; /** Display Metadata visualizations in viewport */ UPROPERTY(Transient) uint32 bShowAssetUserDataVisualizations:1; /** Display Bound **/ UPROPERTY(transient) bool bDisplayBound; UPROPERTY(transient) bool bDisplayVertexColors; UPROPERTY(transient) FLinearColor WireframeMeshOverlayColor; UE_DEPRECATED(5.0, "This variable is no longer used. Use ProcessRootMotionMode instead.") UPROPERTY() uint32 bPreviewRootMotion_DEPRECATED : 1; /** Requested Process root motion mode, ProcessRootMotionMode gets set based on requested mode and what is supported. */ UPROPERTY(transient) EProcessRootMotionMode RequestedProcessRootMotionMode; /** Process root motion mode */ UPROPERTY(transient) EProcessRootMotionMode ProcessRootMotionMode; UPROPERTY(transient) EVisualizeRootMotionMode VisualizeRootMotionMode; /** Playback time last time ConsumeRootmotion was called */ UPROPERTY(transient) float ConsumeRootMotionPreviousPlaybackTime; UPROPERTY(transient) TEnumAsByte ConsumeRootMotionPreviousAdditiveAnimType = AAT_MAX; UPROPERTY(transient) uint32 bShowClothData : 1; UPROPERTY(transient) float MinClothPropertyView; UPROPERTY(transient) float MaxClothPropertyView; UPROPERTY(transient) float ClothMeshOpacity; UPROPERTY(transient) bool bClothFlipNormal; UPROPERTY(transient) bool bClothCullBackface; UPROPERTY(transient) uint32 bRequiredBonesUpToDateDuringTick : 1; /* Bounds computed from cloth. */ FBoxSphereBounds CachedClothBounds; /** Non Compressed SpaceBases for when bDisplayRawAnimation == true **/ TArray UncompressedSpaceBases; /** Storage of Additive Base Pose for when bDisplayAdditiveBasePose == true, as they have to be calculated */ TArray AdditiveBasePoses; /** Storage for non retargeted pose. */ TArray NonRetargetedSpaceBases; /** Storage of Baked Animation Pose for when bDisplayBakedAnimation == true, as they have to be calculated */ TArray BakedAnimationPoses; /** Storage of Source Animation Pose for when bDisplaySourceAnimation == true, as they have to be calculated */ TArray SourceAnimationPoses; /** Transform representing the actor transform at the beginning of the animation sequence. */ FTransform RootMotionReferenceTransform; /** Array of bones to render bone weights for */ UPROPERTY(transient) TArray BonesOfInterest; /** Array of morphtargets to render verts for */ UPROPERTY(transient) TArray> MorphTargetOfInterests; /** Array of materials to restore when not rendering blend weights */ UPROPERTY(transient) TArray> SkelMaterials; UPROPERTY(transient, NonTransactional) TObjectPtr PreviewInstance; UPROPERTY(transient) TObjectPtr SavedAnimScriptInstance; /** Does this component use in game bounds or does it use bounds calculated from bones */ UPROPERTY(transient) bool bIsUsingInGameBounds; /** Does this component use pre-skinned bounds? This overrides other bounds settings */ UPROPERTY(transient) bool bIsUsingPreSkinnedBounds; /** Base skel mesh has support for suspending clothing, but single ticks are more of a debug feature when stepping through an animation * So we control that using this flag */ UPROPERTY(transient) bool bPerformSingleClothingTick; UPROPERTY(transient) bool bPauseClothingSimulationWithAnim; /** Should the LOD of the debug mesh component track the LOD of the instance being debugged */ UPROPERTY(transient) bool bTrackAttachedInstanceLOD; /** Palettized semi randomized colors for multi-color bone display */ UNREALED_API FLinearColor GetBoneColor(int32 BoneIndex) const; // Helper method that sets the forced lod UNREALED_API void SetDebugForcedLOD(int32 InNewForcedLOD); //~ Begin USceneComponent Interface. UNREALED_API virtual FBoxSphereBounds CalcBounds(const FTransform& LocalToWorld) const override; //~ End USceneComponent Interface. //~ Begin UPrimitiveComponent Interface. UNREALED_API virtual FPrimitiveSceneProxy* CreateSceneProxy() override; // engine only draw bounds IF selected // @todo fix this properly // this isn't really the best way to do this, but for now // we'll just mark as selected UNREALED_API virtual bool ShouldRenderSelected() const override; //~ End UPrimitiveComponent Interface. //~ Begin SkinnedMeshComponent Interface UNREALED_API virtual bool ShouldCPUSkin() const override; UNREALED_API virtual bool ShouldNaniteSkin() override; UNREALED_API virtual void PostInitMeshObject(class FSkeletalMeshObject* MeshObject) override; UNREALED_API virtual void RefreshBoneTransforms(FActorComponentTickFunction* TickFunction = NULL) override; virtual int32 GetLODBias() const override { return 0; } //~ End SkinnedMeshComponent Interface //~ Begin SkeletalMeshComponent Interface UNREALED_API virtual void InitAnim(bool bForceReinit) override; virtual bool IsWindEnabled() const override { return true; } UNREALED_API virtual void OnClearAnimScriptInstance() override; UNREALED_API virtual void SetSkeletalMesh(USkeletalMesh* InSkelMesh, bool bReinitPose = true) override; //~ End SkeletalMeshComponent Interface //~ Begin UObject interface UNREALED_API virtual void PostInitProperties() override; //~ End UObject interface // return true if currently preview animation asset is on UNREALED_API virtual bool IsPreviewOn() const; // @todo document UNREALED_API FString GetPreviewText() const; // @todo anim : you still need to give asset, so that we know which one to disable // we can disable per asset, so that if some other window disabled before me, I don't accidently turn it off UNREALED_API virtual void EnablePreview(bool bEnable, class UAnimationAsset * PreviewAsset); // Create the preview instance to use (default UAnimPreviewInstance) UNREALED_API virtual TObjectPtr CreatePreviewInstance(); // reference pose for this component // we don't want to use default refpose because you still want to move joint when this mode is on UNREALED_API virtual void ShowReferencePose(bool bRefPose); UNREALED_API virtual bool IsReferencePoseShown() const; /** Called when mirror data table changes on anim instance. */ UNREALED_API void OnMirrorDataTableChanged(); /** * Update material information depending on color render mode * Refresh/replace materials */ UNREALED_API void SetShowBoneWeight(bool bNewShowBoneWeight); /** * Update material information depending on color render mode * Refresh/replace materials */ UNREALED_API void SetShowMorphTargetVerts(bool bNewShowMorphTargetVerts); /** * Does it use in-game bounds or bounds calculated from bones */ UNREALED_API bool IsUsingInGameBounds() const; /** * Set to use in-game bounds or bounds calculated from bones */ UNREALED_API void UseInGameBounds(bool bUseInGameBounds); /** * Does it use pre-skinned bounds */ UNREALED_API bool IsUsingPreSkinnedBounds() const; /** * Set to use pre-skinned bounds */ UNREALED_API void UsePreSkinnedBounds(bool bUsePreSkinnedBounds); /** * Test if in-game bounds are as big as preview bounds */ UNREALED_API bool CheckIfBoundsAreCorrrect(); /** Get the in-game bounds of the skeleton mesh */ UNREALED_API FBoxSphereBounds CalcGameBounds(const FTransform& LocalToWorld) const; /** * Update components position based on animation root motion */ UNREALED_API void ConsumeRootMotion(const FVector& FloorMin, const FVector& FloorMax); /** Sets the flag used to determine whether or not the current active cloth sim mesh should be rendered */ UNREALED_API void SetShowClothProperty(bool bState); /** Get whether we should be previewing root motion */ UE_DEPRECATED(5.0, "Please use IsProcessingRootMotion or GetProcessRootMotionMode") bool GetPreviewRootMotion() const { return IsProcessingRootMotion(); } /** Set whether we should be previewing root motion. Note: disabling root motion preview resets transform. */ UE_DEPRECATED(5.0, "Please use SetProcessRootMotionMode") void SetPreviewRootMotion(bool bInPreviewRootMotion) { SetProcessRootMotionMode(bInPreviewRootMotion ? EProcessRootMotionMode::Loop : EProcessRootMotionMode::Ignore); } /** Whether we are processing root motion or not */ UNREALED_API bool IsProcessingRootMotion() const; /** Gets requested process root motion mode, can differ from GetProcessRootMotionMode() if the current asset does not support root motion. */ UNREALED_API EProcessRootMotionMode GetRequestedProcessRootMotionMode() const; /** Gets process root motion mode */ UNREALED_API EProcessRootMotionMode GetProcessRootMotionMode() const; /** Sets process root motion mode, the request may be ignored if current asset does not support the mode. Note: disabling root motion preview resets transform. */ UNREALED_API void SetProcessRootMotionMode(EProcessRootMotionMode Mode); /** Sets how we visualize root motion in the viewport. See EVisualizeRootMotionMode for details. */ UNREALED_API void SetVisualizeRootMotionMode(EVisualizeRootMotionMode Mode) { VisualizeRootMotionMode = Mode; }; UNREALED_API bool IsVisualizeRootMotionMode(EVisualizeRootMotionMode Mode) const { return VisualizeRootMotionMode == Mode; }; UNREALED_API EVisualizeRootMotionMode GetVisualizeRootMotionMode() const { return VisualizeRootMotionMode; }; /** Whether the supplied root motion mode can be used for the current asset */ UNREALED_API bool CanUseProcessRootMotionMode(EProcessRootMotionMode Mode) const; /** Whether the current asset or animation blueprint is using root motion */ UNREALED_API bool DoesCurrentAssetHaveRootMotion() const; /** Sets flags whether we notification visualizations should be drawn in the viewport. */ UNREALED_API void SetShowNotificationVisualizations(const bool bShow) { bShowNotificationVisualizations = bShow; } UNREALED_API bool IsNotificationVisualizationsEnabled() const { return bShowNotificationVisualizations; } UE_DEPRECATED(5.5, "Use VisualizeRootMotionMode functions instead.") UNREALED_API void SetShowRootMotionVisualizations(const bool bShow) { VisualizeRootMotionMode = bShow ? EVisualizeRootMotionMode::Trajectory : EVisualizeRootMotionMode::None; } UNREALED_API bool IsRootMotionVisualizationsEnabled() const { return VisualizeRootMotionMode != EVisualizeRootMotionMode::None; } /** Sets flags whether we AssetUserData visualizations should be drawn in the viewport. */ UNREALED_API void SetShowAssetUserDataVisualizations(const bool bShow) { bShowAssetUserDataVisualizations = bShow; } UNREALED_API bool IsAssetUserDataVisualizationsEnabled() const { return bShowAssetUserDataVisualizations; } /** Whether the current LOD of the debug mesh is being synced with the attached (preview) mesh instance. */ UNREALED_API bool IsTrackingAttachedLOD() const; /** Set the wireframe mesh overlay color, which basically controls the color of the wireframe. */ void SetWireframeMeshOverlayColor(FLinearColor Color) { WireframeMeshOverlayColor = Color; } /** Get the wireframe mesh overlay color, which basically controls the color of the wireframe. */ FLinearColor GetWireframeMeshOverlayColor() const { return WireframeMeshOverlayColor; } #if WITH_EDITOR //TODO - This is a really poor way to post errors to the user. Work out a better way. struct FAnimNotifyErrors { FAnimNotifyErrors(UObject* InSourceNotify) : SourceNotify(InSourceNotify) {} UObject* SourceNotify; TArray Errors; }; TArray AnimNotifyErrors; UNREALED_API virtual void ReportAnimNotifyError(const FText& Error, UObject* InSourceNotify) override; UNREALED_API virtual void ClearAnimNotifyErrors(UObject* InSourceNotify) override; /** * Extended viewport text delegate handling. Registering a delegate allows external * objects to place custom text in the anim tools viewports. */ UNREALED_API FDelegateHandle RegisterExtendedViewportTextDelegate(const FGetExtendedViewportText& InDelegate); UNREALED_API void UnregisterExtendedViewportTextDelegate(const FDelegateHandle& InDelegateHandle); const TArray& GetExtendedViewportTextDelegates() const { return ExtendedViewportTextDelegates; } UNREALED_API FDelegateHandle RegisterOnDebugForceLODChangedDelegate(const FOnDebugForceLODChanged& InDelegate); UNREALED_API void UnregisterOnDebugForceLODChangedDelegate(); private: TArray ExtendedViewportTextDelegates; FOnDebugForceLODChanged OnDebugForceLODChangedDelegate; public: #endif /** * Force all body instance to not simulate physics regardless of their physic type * To get back in a default state call : ResetAllBodiesSimulatePhysics */ void DisableAllBodiesSimulatePhysics() { for (int32 BodyIdx = 0; BodyIdx < Bodies.Num(); ++BodyIdx) { if (FBodyInstance* BodyInst = Bodies[BodyIdx]) { BodyInst->SetInstanceSimulatePhysics(false); } } } /** * toggle visibility between cloth sections and non-cloth sections for all LODs * if bShowOnlyClothSections is true, shows only cloth sections. On the other hand, * if bShowOnlyClothSections is false, hides only cloth sections. */ UNREALED_API void ToggleClothSectionsVisibility(bool bShowOnlyClothSections); /** Restore all section visibilities to original states for all LODs */ UNREALED_API void RestoreClothSectionsVisibility(); /** * To normal game/runtime code we don't want to expose a non-const pointer to the simulation, so we can only get * one from this editor-only component. Intended for debug options/visualisations/editor-only code to poke the sim */ UNREALED_API IClothingSimulation* GetMutableClothingSimulation(); /** to avoid clothing reset while modifying properties in Persona */ UNREALED_API virtual void CheckClothTeleport() override; /** The currently selected asset guid if we're painting, used to build dynamic mesh to paint sim parameters */ FGuid SelectedClothingGuidForPainting; /** The currently selected LOD for painting */ int32 SelectedClothingLodForPainting; /** The currently selected mask inside the above LOD to be painted */ int32 SelectedClothingLodMaskForPainting; /** Find a section using a clothing asset with the given GUID and set its visiblity */ UNREALED_API void SetMeshSectionVisibilityForCloth(FGuid InClothGuid, bool bVisibility); // fixes up the disabled flags so clothing is enabled and originals are disabled as // ToggleMeshSectionForCloth will make these get out of sync UNREALED_API void ResetMeshSectionVisibility(); // Rebuilds the fixed parameter on the mesh to mesh data, to be used if the editor has // changed a vert to be fixed or unfixed otherwise the simulation will not work // bInvalidateDerivedDataCache can only be false during previewing as otherwise the changes won't be correctly saved UE_DEPRECATED(5.0, "This function is redundant, since it is always called after ApplyParameterMasks and therefore will be removed.") UNREALED_API void RebuildClothingSectionsFixedVerts(bool bInvalidateDerivedDataCache = true); TArray SkinnedSelectedClothingPositions; TArray SkinnedSelectedClothingNormals; private: // Rebuilds the fixed vertex attribute on any cloth deformer mappings, // including LOD bias mappings, that reference the specified LOD section. UE_DEPRECATED(5.0, "This function is redundant, since it is always called after ApplyParameterMasks and therefore will be removed.") UNREALED_API void RebuildClothingSectionFixedVerts(int32 LODIndex, int32 SectionIndex); // Helper function to generate space bases for current frame UNREALED_API void GenSpaceBases(TArray& OutSpaceBases); // Helper function to enable overlay material UNREALED_API void EnableOverlayMaterial(bool bEnable); // Rebuilds the cloth bounds for the asset. UNREALED_API void RebuildCachedClothBounds(); UNREALED_API void SetProcessRootMotionModeInternal(EProcessRootMotionMode Mode); void ResetRootTransform(); protected: // Overridden to support single clothing ticks UNREALED_API virtual bool ShouldRunClothTick() const override; UNREALED_API virtual void SendRenderDynamicData_Concurrent() override; public: /** Set TurnTableMode and reset the turntable rotation if needed. */ UNREALED_API void SetTurnTableMode(EPersonaTurnTableMode::Type NewMode); /** Current turn table mode */ EPersonaTurnTableMode::Type TurnTableMode; /** Current turn table speed scaling */ float TurnTableSpeedScaling; UNREALED_API virtual void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction) override; UNREALED_API void RefreshSelectedClothingSkinnedPositions(); virtual bool CanOverrideCollisionProfile() const { return true; } UNREALED_API virtual void GetUsedMaterials(TArray& OutMaterials, bool bGetDebugMaterials = false) const override; /** * Define Custom Default pose for this component for preview */ virtual void SetCustomDefaultPose() {}; /* * return RefSkeleton for drawing; */ virtual const FReferenceSkeleton& GetReferenceSkeleton() const { if (GetSkeletalMeshAsset()) { return GetSkeletalMeshAsset()->GetRefSkeleton(); } static FReferenceSkeleton EmptySkeleton; return EmptySkeleton; } /* * return bone indices to draw */ virtual const TArray& GetDrawBoneIndices() const { return RequiredBones; } virtual int32 GetNumDrawTransform() const { return GetNumComponentSpaceTransforms(); } /* * returns the transform of the joint */ virtual FTransform GetDrawTransform(int32 BoneIndex) const { const TArray& SpaceTransforms = GetComponentSpaceTransforms(); if (SpaceTransforms.IsValidIndex(BoneIndex)) { return SpaceTransforms[BoneIndex]; } return FTransform::Identity; } }; /* * This class is use to remove the alternate skinning preview from the multiple editor that can show it. * Important it should be destroy after the PostEditChange of the skeletalmesh is done and the renderdata have been recreate * i.e. FScopedSkeletalMeshPostEditChange should be create after FScopedSuspendAlternateSkinWeightPreview and delete before FScopedSuspendAlternateSkinWeightPreview */ class FScopedSuspendAlternateSkinWeightPreview { public: /* * This constructor suspend the alternate skinning preview for all editor component that use the specified skeletalmesh * Parameters: * @param InSkeletalMesh - SkeletalMesh use to know which preview component we have to suspend the alternate skinning preview. */ UNREALED_API FScopedSuspendAlternateSkinWeightPreview(class USkeletalMesh* InSkeletalMesh); /* * This destructor put back the preview alternate skinning */ UNREALED_API ~FScopedSuspendAlternateSkinWeightPreview(); private: TArray< TTuple > SuspendedComponentArray; };