// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "Components/DynamicMeshComponent.h" #include "DynamicMesh/DynamicMeshAABBTree3.h" #include "InteractiveToolObjects.h" #include "Changes/MeshVertexChange.h" #include "Changes/MeshChange.h" #include "PreviewMesh.generated.h" #define UE_API MODELINGCOMPONENTS_API PREDECLARE_USE_GEOMETRY_CLASS(FDynamicMesh3); struct FMeshDescription; // predeclare tangents template PREDECLARE_GEOMETRY(template class TMeshTangents); /** * UPreviewMesh internally spawns a APreviewMeshActor to hold the preview mesh object. * We use this AInternalToolFrameworkActor subclass so that we can identify such objects * at higher levels (for example to prevent them from being deleted in the Editor) */ UCLASS(MinimalAPI, Transient, NotPlaceable, Hidden, NotBlueprintable, NotBlueprintType) class APreviewMeshActor : public AInternalToolFrameworkActor { GENERATED_BODY() private: APreviewMeshActor(); public: }; /** * UPreviewMesh is a utility object that spawns and owns a transient mesh object in the World. * This can be used to show live preview geometry during modeling operations. * Call CreateInWorld() to set it up, and Disconnect() to shut it down. * * Currently implemented via an internal Actor that has a UDynamicMeshComponent root component, * with an AABBTree created/updated if FProperty bBuildSpatialDataStructure=true. * The Actor is destroyed on Disconnect(). * * The intention with UPreviewMesh is to provide a higher-level interface than the Component. * In future the internal Component may be replaced with another class (eg OctreeDynamicMeshComponent), * or automatically swap between the two, etc. * * As a result direct access to the Actor/Component, or a non-const FDynamicMesh3, is intentionally not provided. * Wrapper functions are provided (or should be added) for necessary Actor/Component parameters. * To edit the mesh either a copy is done, or EditMesh()/ApplyChange() must be used. * These functions automatically update necessary internal data structures. * */ UCLASS(MinimalAPI, Transient) class UPreviewMesh : public UObject, public IMeshVertexCommandChangeTarget, public IMeshCommandChangeTarget, public IMeshReplacementCommandChangeTarget { GENERATED_BODY() public: UE_API UPreviewMesh(); UE_API virtual ~UPreviewMesh(); // // construction / destruction // /** * Create preview mesh in the World with the given transform */ UE_API void CreateInWorld(UWorld* World, const FTransform& WithTransform); /** * Remove and destroy preview mesh */ UE_API void Disconnect(); /** * @return internal Actor created by this UPreviewMesh */ AActor* GetActor() const { return TemporaryParentActor; } /** * @return internal Root Component of internal Actor */ UPrimitiveComponent* GetRootComponent() { return DynamicMeshComponent; } // // visualization parameters // /** * Enable/disable wireframe overlay rendering */ UE_API void EnableWireframe(bool bEnable); /** * Enable/disable shadow rendering */ UE_API void SetShadowsEnabled(bool bEnable); /** * Set material on the preview mesh */ UE_API void SetMaterial(UMaterialInterface* Material); /** * Set material on the preview mesh */ UE_API void SetMaterial(int MaterialIndex, UMaterialInterface* Material); /** * Set the entire material set on the preview mesh */ UE_API void SetMaterials(const TArray& Materials); /** * Get number of materials in the preview mesh (base materials, i.e., not including override material) */ UE_API int32 GetNumMaterials() const; /** * Get material from the preview mesh */ UE_API UMaterialInterface* GetMaterial(int MaterialIndex = 0) const; /** * Get the entire materials array from the preview mesh. Appends to OutMaterials without clearing it. */ UE_API void GetMaterials(TArray& OutMaterials) const; /** * Set an override material for the preview mesh. This material will override all the given materials. */ UE_API void SetOverrideRenderMaterial(UMaterialInterface* Material); /** * Clear the override material for the preview mesh. */ UE_API void ClearOverrideRenderMaterial(); /** * @return the actual material that will be used for rendering for the given MaterialIndex. Will return override material if set. * */ UE_API UMaterialInterface* GetActiveMaterial(int MaterialIndex = 0) const; /** * Set an secondary material for the preview mesh. This material will be applied to secondary triangle buffer if enabled. */ UE_API void SetSecondaryRenderMaterial(UMaterialInterface* Material); /** * Clear the secondary material for the preview mesh. */ UE_API void ClearSecondaryRenderMaterial(); /** * Set a override wireframe material for the preview mesh. This material will be applied when wireframe is enabled. */ UE_API void SetOverrideWireframeRenderMaterial(UMaterialInterface* Material); /** * Clear the wireframe override material for the preview mesh. */ UE_API void ClearOverrideWireframeRenderMaterial(); /** * Set an override secondary wireframe material for the preview mesh. This material will be applied to seccondary triangle buffer when wireframe is enabled. */ UE_API void SetOverrideSecondaryWireframeRenderMaterial(UMaterialInterface* Material); /** * Clear the wireframe override material for the preview mesh. */ UE_API void ClearOverrideSecondaryWireframeRenderMaterial(); /** * Enable secondary triangle buffers. The Secondary material will be applied to any triangles that pass TriangleFilterFunc. * @param TriangleFilterFunc predicate used to identify secondary triangles */ UE_API void EnableSecondaryTriangleBuffers(TUniqueFunction TriangleFilterFunc); /** * Disable secondary triangle buffers */ UE_API void DisableSecondaryTriangleBuffers(); /** * Show/Hide the secondary triangle buffers. */ UE_API void SetSecondaryBuffersVisibility(bool bSecondaryVisibility); /** * Call this after updating the secondary triangle sorting. * This function will update the existing buffers if possible, without rebuilding entire RenderProxy. */ UE_API void FastNotifySecondaryTrianglesChanged(); /** * Set the tangents mode for the underlying component, if available. * Note that this function may need to be called before the mesh is initialized. */ UE_API void SetTangentsMode(EDynamicMeshComponentTangentsMode TangentsType); /** * Calculate tangents for the underlying component. * This will calculate and assign tangents for the preview mesh independent of the tangents mode. * But if the tangents mode is set to AutoCalculated then it will try to use the auto calculated tangents. * @return true if tangents were successfully calculated and assigned to the underlying mesh */ UE_API bool CalculateTangents(); /** * Get the current transform on the preview mesh */ UE_API FTransform GetTransform() const; /** * Set the transform on the preview mesh */ UE_API void SetTransform(const FTransform& UseTransform); /** * @return true if the preview mesh is visible */ UE_API bool IsVisible() const; /** * Set visibility state of the preview mesh */ UE_API void SetVisible(bool bVisible); /** Render data update hint (values mirror EDynamicMeshComponentRenderUpdateMode) */ enum class ERenderUpdateMode { /** Do not update render data */ NoUpdate = 0, /** Invalidate overlay of internal component, rebuilding all render data */ FullUpdate = 1, /** Attempt to do partial update of render data if possible */ FastUpdate = 2 }; /** * Set the triangle color function for rendering / render data construction */ UE_API void SetTriangleColorFunction(TFunction TriangleColorFunc, ERenderUpdateMode UpdateMode = ERenderUpdateMode::FullUpdate); /** * Clear the triangle color function for rendering / render data construction */ UE_API void ClearTriangleColorFunction(ERenderUpdateMode UpdateMode = ERenderUpdateMode::FullUpdate); // // Queries // /** * Test for ray intersection with the preview mesh. * Requires that bBuildSpatialDataStructure = true * @param TriangleFilterF optional filter function for triangles * @return true if ray intersections mesh * @warning returns false if preview is not visible */ UE_API bool TestRayIntersection(const FRay3d& WorldRay, TFunction TriangleFilterF = nullptr); /** * Find ray intersection with the preview mesh. * Requires that bBuildSpatialDataStructure = true * @param WorldRay ray in world space * @param HitOut output hit data (only certain members are initialized, see implementation) * @param TriangleFilterF optional filter function for triangles * @return true if ray intersections mesh * @warning returns false if preview is not visible */ UE_API bool FindRayIntersection(const FRay3d& WorldRay, FHitResult& HitOut, TFunction TriangleFilterF = nullptr); /** * Find nearest point on current mesh to given WorldPoint * Requires that bBuildSpatialDataStructure = true unless bLinearSearch = true * @param WorldPoint point in world space * @param bLinearSearch test every triangle. On a mesh where only a few queries will be run, this is faster than building the spatial data structure. * @return nearest point in world space */ UE_API FVector3d FindNearestPoint(const FVector3d& WorldPoint, bool bLinearSearch = false); // // Read access to internal mesh // /** * Clear the preview mesh */ UE_API void ClearPreview(); /** * Update the internal mesh by copying the given Mesh * @param Mesh to copy. * @param UpdateMode Type of rendering update required. Should be FullUpdate if topology changes, otherwise can be FastUpdate. * @param ModifiedAttribs Only relevant in case of FastUpdate- determines which attributes actually changed. */ UE_API void UpdatePreview(const FDynamicMesh3* Mesh, ERenderUpdateMode UpdateMode = ERenderUpdateMode::FullUpdate, EMeshRenderAttributeFlags ModifiedAttribs = EMeshRenderAttributeFlags::AllVertexAttribs); /** * Update the internal mesh by moving in the given Mesh * @param Mesh to move. * @param UpdateMode Type of rendering update required. Should be FullUpdate if topology changes, otherwise can be FastUpdate. * @param ModifiedAttribs Only relevant in case of FastUpdate- determines which attributes actually changed. */ UE_API void UpdatePreview(FDynamicMesh3&& Mesh, ERenderUpdateMode UpdateMode = ERenderUpdateMode::FullUpdate, EMeshRenderAttributeFlags ModifiedAttribs = EMeshRenderAttributeFlags::AllVertexAttribs); /** * @return pointer to the current FDynamicMesh used for preview @todo deprecate this function, use GetMesh() instead */ const FDynamicMesh3* GetPreviewDynamicMesh() const { return GetMesh(); } /** * Read access to the internal mesh. This function will be deprecated/removed, use ProcessMesh() instead. * @return pointer to the current FDynamicMesh used for preview */ UE_API const FDynamicMesh3* GetMesh() const; /** * Give external code direct read access to the internal FDynamicMesh3. * This should be used preferentially over GetMesh() / GetPreviewDynamicMesh() */ UE_API virtual void ProcessMesh(TFunctionRef ProcessFunc) const; /** * @return point to the current AABBTree used for preview spatial mesh, or nullptr if not available * @warning this has to return non-const because of current FDynamicMeshAABBTree3 API, but you should not modify this! */ UE_API UE::Geometry::FDynamicMeshAABBTree3* GetSpatial(); /** * @return the current preview FDynamicMesh, and replace with a new empty mesh */ UE_API TUniquePtr ExtractPreviewMesh() const; // // Edit access to internal mesh, and change-tracking/notification // /** * Replace mesh with new mesh */ UE_API void ReplaceMesh(const FDynamicMesh3& NewMesh); /** * Replace mesh with new mesh */ UE_API void ReplaceMesh(FDynamicMesh3&& NewMesh); /** * Apply EditFunc to the internal mesh and update internal data structures as necessary */ UE_API void EditMesh(TFunctionRef EditFunc); /** * Apply EditFunc to the internal mesh, and update spatial data structure if requested, * but do not update/rebuild rendering data structures. NotifyDeferredEditOcurred() must be * called to complete a deferred edit, this will update the rendering mesh. * DeferredEditMesh can be called multiple times before NotifyDeferredEditCompleted() is called. * @param EditFunc function that is applied to the internal mesh * @param bRebuildSpatial if true, and internal spatial data structure is enabled, rebuild it for updated mesh */ UE_API void DeferredEditMesh(TFunctionRef EditFunc, bool bRebuildSpatial); /** * Notify that a DeferredEditMesh sequence is complete and cause update of rendering data structures. * @param UpdateMode type of rendering update required for the applied mesh edits * @param ModifiedAttribs which mesh attributes have been modified and need to be updated * @param bRebuildSpatial if true, and internal spatial data structure is enabled, rebuild it for updated mesh */ UE_API void NotifyDeferredEditCompleted(ERenderUpdateMode UpdateMode, EMeshRenderAttributeFlags ModifiedAttribs, bool bRebuildSpatial); /** * Notify that a deferred edit is completed and cause update of rendering data structures for modified Triangles. * This can reduce the cost of mesh updates, but only if SetEnableRenderMeshDecomposition(true) has been called * @param ModifiedAttribs which mesh attributes have been modified and need to be updated */ UE_API void NotifyRegionDeferredEditCompleted(const TArray& Triangles, EMeshRenderAttributeFlags ModifiedAttribs); /** * Notify that a deferred edit is completed and cause update of rendering data structures for modified Triangles. * This can reduce the cost of mesh updates, but only if SetEnableRenderMeshDecomposition(true) has been called * @param ModifiedAttribs which mesh attributes have been modified and need to be updated */ UE_API void NotifyRegionDeferredEditCompleted(const TSet& Triangles, EMeshRenderAttributeFlags ModifiedAttribs); /** * Apply EditFunc to the internal mesh and update internal data structures as necessary. * EditFunc is required to notify the given FDynamicMeshChangeTracker about all mesh changes * @return FMeshChange extracted from FDynamicMeshChangeTracker that represents mesh edit */ UE_API TUniquePtr TrackedEditMesh(TFunctionRef EditFunc); /** * Apply/Revert a vertex deformation change to the internal mesh (implements IMeshVertexCommandChangeTarget) */ UE_API virtual void ApplyChange(const FMeshVertexChange* Change, bool bRevert) override; /** * Apply/Revert a general mesh change to the internal mesh (implements IMeshCommandChangeTarget) */ UE_API virtual void ApplyChange(const FMeshChange* Change, bool bRevert) override; /** * Apply/Revert a general mesh change to the internal mesh (implements IMeshReplacementCommandChangeTarget) */ UE_API virtual void ApplyChange(const FMeshReplacementChange* Change, bool bRevert) override; /** @return delegate that is broadcast whenever the internal mesh component is changed */ UE_API FSimpleMulticastDelegate& GetOnMeshChanged(); /** * Force rebuild of internal spatial data structure. Can be used in context of DeferredEditMesh to rebuild spatial data * structure w/o rebuilding render data */ UE_API void ForceRebuildSpatial(); /** * Enable automatically-computed decomposition of internal mesh into subregions when rendering (ie inside the Component). * This allows for faster local updates via NotifyRegionDeferredEditCompleted() functions above. * Decomposition will be automatically recomputed as necessary when internal mesh is modified via changes, edits, etc */ UE_API virtual void SetEnableRenderMeshDecomposition(bool bEnable); /** @return true if SetEnableRenderMeshDecomposition(true) has been called. */ bool GetIsRenderMeshDecompositionEnabled() const { return bDecompositionEnabled; } public: /** If true, we build a spatial data structure internally for the preview mesh, which allows for hit-testing */ UPROPERTY() bool bBuildSpatialDataStructure; protected: /** The temporary actor we create internally to own the preview mesh component */ APreviewMeshActor* TemporaryParentActor = nullptr; /** This component is set as the root component of TemporaryParentActor */ UPROPERTY() TObjectPtr DynamicMeshComponent = nullptr; /** Spatial data structure that is initialized if bBuildSpatialDataStructure = true when UpdatePreview() is called */ UE::Geometry::FDynamicMeshAABBTree3 MeshAABBTree; /** If true, mesh will be chunked into multiple render buffers inside the DynamicMeshComponent */ bool bDecompositionEnabled = false; /** Update chunk decomposition */ UE_API void UpdateRenderMeshDecomposition(); // This function is called internally on some changes, to let the path tracer know that this mesha/actor // has been modified in a way that will require invalidating the current path tracing result UE_API void NotifyWorldPathTracedOutputInvalidated(); }; #undef UE_API