2019-12-27 09:26:59 -05:00
// Copyright Epic Games, Inc. All Rights Reserved.
2019-10-01 20:41:42 -04:00
# include "DisplaceMeshTool.h"
2020-12-09 18:33:03 -04:00
# include "AssetUtils/Texture2DUtil.h"
2020-12-14 19:16:22 -04:00
# include "Curves/CurveFloat.h"
# include "Curves/RichCurve.h"
2019-10-01 20:41:42 -04:00
# include "InteractiveToolManager.h"
# include "ToolBuilderUtil.h"
2019-10-25 10:19:13 -04:00
# include "ToolSetupUtil.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/DynamicMesh3.h"
2021-06-12 14:30:22 -04:00
# include "Components/DynamicMeshComponent.h"
2021-06-13 00:36:02 -04:00
# include "DynamicMesh/MeshNormals.h"
2019-10-25 10:19:13 -04:00
# include "ModelingOperators.h"
2019-10-01 20:41:42 -04:00
# include "Async/ParallelFor.h"
# include "ProfilingDebugging/ScopedTimers.h"
2020-04-18 18:42:59 -04:00
# include "MeshDescription.h"
2021-06-11 22:42:32 -04:00
# include "ModelingToolTargetUtil.h"
2021-08-31 11:04:39 -04:00
# include "Operations/PNTriangles.h"
2022-01-28 14:07:23 -05:00
# include "Operations/UniformTessellate.h"
2021-12-13 13:34:29 -05:00
2021-01-19 16:10:15 -04:00
// needed to disable normals recalculation on the underlying asset
# include "AssetUtils/MeshDescriptionUtil.h"
# include "Engine/Classes/Engine/StaticMesh.h"
# include "Engine/Classes/Components/StaticMeshComponent.h"
2021-03-09 19:33:56 -04:00
using namespace UE : : Geometry ;
2021-01-19 16:10:15 -04:00
2019-10-01 20:41:42 -04:00
# define LOCTEXT_NAMESPACE "UDisplaceMeshTool"
2020-12-09 18:33:03 -04:00
namespace DisplaceMeshToolLocals {
2020-04-18 18:42:59 -04:00
2020-01-27 20:11:15 -05:00
namespace ComputeDisplacement
2020-01-07 15:54:23 -05:00
{
2020-04-18 18:42:59 -04:00
/// Directional Filter: Scale displacement for a given vertex based on how well
/// the vertex normal agrees with the specified direction.
struct FDirectionalFilter
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
bool bEnableFilter = false ;
FVector3d FilterDirection = { 1 , 0 , 0 } ;
double FilterWidth = 0.1 ;
const double RampSlope = 5.0 ;
double FilterValue ( const FVector3d & EvalNormal ) const
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
if ( ! bEnableFilter ) { return 1.0 ; }
double DotWithFilterDirection = EvalNormal . Dot ( FilterDirection ) ;
double Offset = 1.0 / RampSlope ;
double MinX = 1.0 - ( 2.0 + Offset ) * FilterWidth ; // Start increasing here
double MaxX = FMathd : : Min ( 1.0 , MinX + Offset ) ; // Stop increasing here
if ( FMathd : : Abs ( MaxX - MinX ) < FMathd : : ZeroTolerance ) { return 0.0 ; }
double Y = ( DotWithFilterDirection - MinX ) / ( MaxX - MinX ) ; // Clamped linear interpolation for the ramp region
return FMathd : : Clamp ( Y , 0.0 , 1.0 ) ;
2019-10-25 10:19:13 -04:00
}
2020-04-18 18:42:59 -04:00
} ;
template < typename DisplaceFunc >
void ParallelDisplace ( const FDynamicMesh3 & Mesh ,
const TArray < FVector3d > & Positions ,
const FMeshNormals & Normals ,
TArray < FVector3d > & DisplacedPositions ,
DisplaceFunc Displace )
{
ensure ( Positions . Num ( ) = = Normals . GetNormals ( ) . Num ( ) ) ;
ensure ( Positions . Num ( ) = = DisplacedPositions . Num ( ) ) ;
ensure ( Mesh . VertexCount ( ) = = Positions . Num ( ) ) ;
int32 NumVertices = Mesh . MaxVertexID ( ) ;
ParallelFor ( NumVertices , [ & ] ( int32 vid )
{
if ( Mesh . IsVertex ( vid ) )
{
DisplacedPositions [ vid ] = Displace ( vid , Positions [ vid ] , Normals [ vid ] ) ;
}
} ) ;
2019-10-25 10:19:13 -04:00
}
2020-04-18 18:42:59 -04:00
void Constant ( const FDynamicMesh3 & Mesh ,
const TArray < FVector3d > & Positions ,
const FMeshNormals & Normals ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TFunctionRef < float ( int32 , const FVector3d & , const FVector3d & ) > IntensityFunc ,
2020-04-18 18:42:59 -04:00
TArray < FVector3d > & DisplacedPositions )
{
ParallelDisplace ( Mesh , Positions , Normals , DisplacedPositions ,
[ & ] ( int32 vid , const FVector3d & Position , const FVector3d & Normal )
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
double Intensity = IntensityFunc ( vid , Position , Normal ) ;
2020-04-18 18:42:59 -04:00
return Position + ( Intensity * Normal ) ;
} ) ;
}
2019-10-25 10:19:13 -04:00
void RandomNoise ( const FDynamicMesh3 & Mesh ,
2020-04-18 18:42:59 -04:00
const TArray < FVector3d > & Positions ,
const FMeshNormals & Normals ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TFunctionRef < float ( int32 , const FVector3d & , const FVector3d & ) > IntensityFunc ,
2020-04-18 18:42:59 -04:00
int RandomSeed ,
TArray < FVector3d > & DisplacedPositions )
2019-10-25 10:19:13 -04:00
{
FMath : : SRandInit ( RandomSeed ) ;
for ( int vid : Mesh . VertexIndicesItr ( ) )
{
double RandVal = 2.0 * ( FMath : : SRand ( ) - 0.5 ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
double Intensity = IntensityFunc ( vid , Positions [ vid ] , Normals [ vid ] ) ;
2019-10-25 10:19:13 -04:00
DisplacedPositions [ vid ] = Positions [ vid ] + ( Normals [ vid ] * RandVal * Intensity ) ;
}
}
2020-01-27 20:11:15 -05:00
void PerlinNoise ( const FDynamicMesh3 & Mesh ,
const TArray < FVector3d > & Positions ,
const FMeshNormals & Normals ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TFunctionRef < float ( int32 , const FVector3d & , const FVector3d & ) > IntensityFunc ,
2020-04-18 18:42:59 -04:00
const TArray < FPerlinLayerProperties > & PerlinLayerProperties ,
2020-01-27 20:11:15 -05:00
int RandomSeed ,
TArray < FVector3d > & DisplacedPositions )
{
FMath : : SRandInit ( RandomSeed ) ;
2020-04-18 18:42:59 -04:00
const float RandomOffset = 10000.0f * FMath : : SRand ( ) ;
ParallelDisplace ( Mesh , Positions , Normals , DisplacedPositions ,
[ & ] ( int32 vid , const FVector3d & Position , const FVector3d & Normal )
2020-01-27 20:11:15 -05:00
{
2020-04-18 18:42:59 -04:00
// Compute the sum of Perlin noise evaluations for this point
FVector EvalLocation ( Position + RandomOffset ) ;
double TotalNoiseValue = 0.0 ;
for ( int32 Layer = 0 ; Layer < PerlinLayerProperties . Num ( ) ; + + Layer )
{
TotalNoiseValue + = PerlinLayerProperties [ Layer ] . Intensity * FMath : : PerlinNoise3D ( PerlinLayerProperties [ Layer ] . Frequency * EvalLocation ) ;
}
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
double Intensity = IntensityFunc ( vid , Position , Normal ) ;
2020-04-18 18:42:59 -04:00
return Position + ( TotalNoiseValue * Intensity * Normal ) ;
} ) ;
2020-01-27 20:11:15 -05:00
}
2019-10-25 10:19:13 -04:00
void Map ( const FDynamicMesh3 & Mesh ,
2020-04-18 18:42:59 -04:00
const TArray < FVector3d > & Positions ,
const FMeshNormals & Normals ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TFunctionRef < float ( int32 , const FVector3d & , const FVector3d & ) > IntensityFunc ,
2020-04-18 18:42:59 -04:00
const FSampledScalarField2f & DisplaceField ,
2020-12-09 18:33:03 -04:00
TArray < FVector3d > & DisplacedPositions ,
float DisplaceFieldBaseValue = 128.0 / 255 , // value that corresponds to zero displacement
FVector2f UVScale = FVector2f ( 1 , 1 ) ,
2020-12-14 19:16:22 -04:00
FVector2f UVOffset = FVector2f ( 0 , 0 ) ,
FRichCurve * AdjustmentCurve = nullptr )
2019-10-25 10:19:13 -04:00
{
const FDynamicMeshUVOverlay * UVOverlay = Mesh . Attributes ( ) - > GetUVLayer ( 0 ) ;
2020-12-09 18:33:03 -04:00
// We set things up such that DisplaceField goes from 0 to 1 in the U direction,
// but the V direction may be shorter or longer if the texture is not square
// (it will be 1/AspectRatio)
float VHeight = DisplaceField . Height ( ) * DisplaceField . CellDimensions . Y ;
2019-10-25 10:19:13 -04:00
for ( int tid : Mesh . TriangleIndicesItr ( ) )
{
FIndex3i Tri = Mesh . GetTriangle ( tid ) ;
FIndex3i UVTri = UVOverlay - > GetTriangle ( tid ) ;
for ( int j = 0 ; j < 3 ; + + j )
{
int vid = Tri [ j ] ;
FVector2f UV = UVOverlay - > GetElement ( UVTri [ j ] ) ;
2020-12-09 18:33:03 -04:00
// Adjust UV value and tile it.
// Note that we're effectively stretching the texture to be square before tiling, since this
// seems to be what non square textures do by default in UE. If we decide to tile without
// stretching by default someday, we'd do UV - FVector2f(FMath::Floor(UV.X), FMath:Floor(UV.Y/VHeight)*VHeight)
// without multiplying by VHeight afterward.
UV = UV * UVScale + UVOffset ;
UV = UV - FVector2f ( FMath : : Floor ( UV . X ) , FMath : : Floor ( UV . Y ) ) ;
UV . Y * = VHeight ;
2020-12-14 19:16:22 -04:00
double Offset = DisplaceField . BilinearSampleClamped ( UV ) ;
if ( AdjustmentCurve )
{
Offset = AdjustmentCurve - > Eval ( Offset ) ;
}
Offset - = DisplaceFieldBaseValue ;
2020-12-09 18:33:03 -04:00
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
double Intensity = IntensityFunc ( vid , Positions [ vid ] , Normals [ vid ] ) ;
2019-10-25 10:19:13 -04:00
DisplacedPositions [ vid ] = Positions [ vid ] + ( Offset * Intensity * Normals [ vid ] ) ;
}
}
}
2020-01-27 20:11:15 -05:00
void Sine ( const FDynamicMesh3 & Mesh ,
const TArray < FVector3d > & Positions ,
2020-04-18 18:42:59 -04:00
const FMeshNormals & Normals ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TFunctionRef < float ( int32 , const FVector3d & , const FVector3d & ) > IntensityFunc ,
2020-01-27 20:11:15 -05:00
double Frequency ,
double PhaseShift ,
2020-04-18 18:42:59 -04:00
const FVector3d & Direction ,
2020-01-27 20:11:15 -05:00
TArray < FVector3d > & DisplacedPositions )
{
2020-04-18 18:42:59 -04:00
FQuaterniond RotateToDirection ( Direction , { 0.0 , 0.0 , 1.0 } ) ;
ParallelDisplace ( Mesh , Positions , Normals , DisplacedPositions ,
[ & ] ( int32 vid , const FVector3d & Position , const FVector3d & Normal )
2020-01-27 20:11:15 -05:00
{
2020-04-18 18:42:59 -04:00
FVector3d RotatedPosition = RotateToDirection * Position ;
double DistXY = FMath : : Sqrt ( RotatedPosition . X * RotatedPosition . X + RotatedPosition . Y * RotatedPosition . Y ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
double Intensity = IntensityFunc ( vid , Position , Normal ) ;
2020-04-18 18:42:59 -04:00
FVector3d Offset = Intensity * FMath : : Sin ( Frequency * DistXY + PhaseShift ) * Direction ;
return Position + Offset ;
} ) ;
2020-01-27 20:11:15 -05:00
}
2020-04-18 18:42:59 -04:00
}
2019-10-25 10:19:13 -04:00
class FSubdivideMeshOp : public FDynamicMeshOperator
{
public :
2021-12-15 10:49:00 -05:00
FSubdivideMeshOp ( const FDynamicMesh3 & SourceMesh , EDisplaceMeshToolSubdivisionType SubdivisionTypeIn , int SubdivisionsCountIn , TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap ) ;
2019-10-25 10:19:13 -04:00
void CalculateResult ( FProgressCancel * Progress ) final ;
private :
2021-12-15 10:49:00 -05:00
EDisplaceMeshToolSubdivisionType SubdivisionType ;
2019-10-25 10:19:13 -04:00
int SubdivisionsCount ;
} ;
2021-12-15 10:49:00 -05:00
FSubdivideMeshOp : : FSubdivideMeshOp ( const FDynamicMesh3 & SourceMesh , EDisplaceMeshToolSubdivisionType SubdivisionTypeIn , int SubdivisionsCountIn , TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap )
2021-08-31 11:04:39 -04:00
: SubdivisionType ( SubdivisionTypeIn ) , SubdivisionsCount ( SubdivisionsCountIn )
2019-10-25 10:19:13 -04:00
{
ResultMesh - > Copy ( SourceMesh ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
// If we have a WeightMap, initialize VertexUV.X with weightmap value. Note that we are going to process .Y anyway,
// we could (for exmaple) speculatively compute another weightmap, or store previous weightmap values there, to support
// fast switching between two...
ResultMesh - > EnableVertexUVs ( FVector2f : : Zero ( ) ) ;
if ( WeightMap ! = nullptr )
{
for ( int32 vid : ResultMesh - > VertexIndicesItr ( ) )
{
ResultMesh - > SetVertexUV ( vid , FVector2f ( WeightMap - > GetValue ( vid ) , 0 ) ) ;
}
}
else
{
for ( int32 vid : ResultMesh - > VertexIndicesItr ( ) )
{
ResultMesh - > SetVertexUV ( vid , FVector2f : : One ( ) ) ;
}
}
2019-10-25 10:19:13 -04:00
}
void FSubdivideMeshOp : : CalculateResult ( FProgressCancel * ProgressCancel )
{
2021-12-15 10:49:00 -05:00
if ( SubdivisionType = = EDisplaceMeshToolSubdivisionType : : Flat )
2019-10-25 10:19:13 -04:00
{
2022-01-28 14:07:23 -05:00
FUniformTessellate Tessellator ( ResultMesh . Get ( ) ) ;
Tessellator . Progress = ProgressCancel ;
Tessellator . TessellationNum = SubdivisionsCount ;
2021-12-13 13:34:29 -05:00
2022-01-28 14:07:23 -05:00
if ( Tessellator . Validate ( ) = = EOperationValidationResult : : Ok )
2021-08-31 11:04:39 -04:00
{
2022-01-28 14:07:23 -05:00
Tessellator . Compute ( ) ;
2021-12-13 13:34:29 -05:00
}
2021-08-31 11:04:39 -04:00
}
2021-12-15 10:49:00 -05:00
else if ( SubdivisionType = = EDisplaceMeshToolSubdivisionType : : PNTriangles )
2021-08-31 11:04:39 -04:00
{
FPNTriangles PNTriangles ( ResultMesh . Get ( ) ) ;
PNTriangles . Progress = ProgressCancel ;
2022-01-28 14:07:23 -05:00
PNTriangles . TessellationLevel = SubdivisionsCount ;
2021-08-31 11:04:39 -04:00
if ( PNTriangles . Validate ( ) = = EOperationValidationResult : : Ok )
{
PNTriangles . Compute ( ) ;
}
}
else
{
// Unsupported subdivision type
checkNoEntry ( ) ;
2019-10-25 10:19:13 -04:00
}
}
class FSubdivideMeshOpFactory : public IDynamicMeshOperatorFactory
{
public :
FSubdivideMeshOpFactory ( FDynamicMesh3 & SourceMeshIn ,
2021-12-15 10:49:00 -05:00
EDisplaceMeshToolSubdivisionType SubdivisionTypeIn ,
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
int SubdivisionsCountIn ,
2021-02-17 11:50:23 -04:00
TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMapIn )
2021-08-31 11:04:39 -04:00
: SourceMesh ( SourceMeshIn ) , SubdivisionType ( SubdivisionTypeIn ) , SubdivisionsCount ( SubdivisionsCountIn ) , WeightMap ( WeightMapIn )
2019-10-25 10:19:13 -04:00
{
}
2021-08-31 11:04:39 -04:00
2021-12-15 10:49:00 -05:00
void SetSubdivisionType ( EDisplaceMeshToolSubdivisionType SubdivisionTypeIn ) ;
EDisplaceMeshToolSubdivisionType GetSubdivisionType ( ) const ;
2021-08-31 11:04:39 -04:00
2019-10-25 10:19:13 -04:00
void SetSubdivisionsCount ( int SubdivisionsCountIn ) ;
2021-08-31 11:04:39 -04:00
int GetSubdivisionsCount ( ) const ;
2019-10-25 10:19:13 -04:00
2021-02-17 11:50:23 -04:00
void SetWeightMap ( TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMapIn ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
2019-11-01 17:39:56 -04:00
TUniquePtr < FDynamicMeshOperator > MakeNewOperator ( ) final
2019-10-25 10:19:13 -04:00
{
2021-08-31 11:04:39 -04:00
return MakeUnique < FSubdivideMeshOp > ( SourceMesh , SubdivisionType , SubdivisionsCount , WeightMap ) ;
2019-10-25 10:19:13 -04:00
}
private :
const FDynamicMesh3 & SourceMesh ;
2021-12-15 10:49:00 -05:00
EDisplaceMeshToolSubdivisionType SubdivisionType ;
2019-10-25 10:19:13 -04:00
int SubdivisionsCount ;
2021-02-17 11:50:23 -04:00
TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap ;
2019-10-25 10:19:13 -04:00
} ;
2021-12-15 10:49:00 -05:00
void FSubdivideMeshOpFactory : : SetSubdivisionType ( EDisplaceMeshToolSubdivisionType SubdivisionTypeIn )
2021-08-31 11:04:39 -04:00
{
SubdivisionType = SubdivisionTypeIn ;
}
2021-12-15 10:49:00 -05:00
EDisplaceMeshToolSubdivisionType FSubdivideMeshOpFactory : : GetSubdivisionType ( ) const
2021-08-31 11:04:39 -04:00
{
return SubdivisionType ;
}
2019-10-25 10:19:13 -04:00
void FSubdivideMeshOpFactory : : SetSubdivisionsCount ( int SubdivisionsCountIn )
{
SubdivisionsCount = SubdivisionsCountIn ;
}
2021-08-31 11:04:39 -04:00
int FSubdivideMeshOpFactory : : GetSubdivisionsCount ( ) const
2019-10-25 10:19:13 -04:00
{
return SubdivisionsCount ;
}
2021-02-17 11:50:23 -04:00
void FSubdivideMeshOpFactory : : SetWeightMap ( TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMapIn )
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
{
WeightMap = WeightMapIn ;
}
2020-04-18 18:42:59 -04:00
// A collection of parameters to avoid having excess function parameters
struct DisplaceMeshParameters
{
float DisplaceIntensity = 0.0f ;
int RandomSeed = 0 ;
UTexture2D * DisplacementMap = nullptr ;
float SineWaveFrequency = 0.0f ;
float SineWavePhaseShift = 0.0f ;
FVector SineWaveDirection = { 0.0f , 0.0f , 0.0f } ;
bool bEnableFilter = false ;
FVector FilterDirection = { 0.0f , 0.0f , 0.0f } ;
float FilterWidth = 0.0f ;
FSampledScalarField2f DisplaceField ;
TArray < FPerlinLayerProperties > PerlinLayerProperties ;
2021-01-19 16:10:15 -04:00
bool bRecalculateNormals = true ;
2020-04-18 18:42:59 -04:00
2020-12-09 18:33:03 -04:00
// Used in texture map displacement
2021-05-19 11:21:56 -04:00
int32 DisplacementMapChannel = 0 ;
2020-12-09 18:33:03 -04:00
float DisplacementMapBaseValue = 128.0 / 255 ; // i.e., what constitutes no displacement
FVector2f UVScale = FVector2f ( 1 , 1 ) ;
FVector2f UVOffset = FVector2f ( 0 , 0 ) ;
2020-12-14 19:16:22 -04:00
// This gets used by worker threads, so do not try to change an existing curve- make
// a new one each time.
2021-02-17 11:50:23 -04:00
TSharedPtr < FRichCurve , ESPMode : : ThreadSafe > AdjustmentCurve ;
2020-12-09 18:33:03 -04:00
2021-02-17 11:50:23 -04:00
TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap ;
2020-04-18 18:42:59 -04:00
TFunction < float ( const FVector3d & , const FIndexedWeightMap ) > WeightMapQueryFunc ;
} ;
2019-10-25 10:19:13 -04:00
class FDisplaceMeshOp : public FDynamicMeshOperator
{
public :
2021-02-17 11:50:23 -04:00
FDisplaceMeshOp ( TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > SourceMeshIn ,
2020-04-18 18:42:59 -04:00
const DisplaceMeshParameters & DisplaceParametersIn ,
EDisplaceMeshToolDisplaceType DisplacementTypeIn ) ;
2019-10-25 10:19:13 -04:00
void CalculateResult ( FProgressCancel * Progress ) final ;
private :
2021-02-17 11:50:23 -04:00
TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > SourceMesh ;
2020-04-18 18:42:59 -04:00
DisplaceMeshParameters Parameters ;
2019-10-25 10:19:13 -04:00
EDisplaceMeshToolDisplaceType DisplacementType ;
TArray < FVector3d > SourcePositions ;
FMeshNormals SourceNormals ;
TArray < FVector3d > DisplacedPositions ;
} ;
2021-02-17 11:50:23 -04:00
FDisplaceMeshOp : : FDisplaceMeshOp ( TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > SourceMeshIn ,
2020-04-18 18:42:59 -04:00
const DisplaceMeshParameters & DisplaceParametersIn ,
EDisplaceMeshToolDisplaceType DisplacementTypeIn )
: SourceMesh ( MoveTemp ( SourceMeshIn ) ) ,
Parameters ( DisplaceParametersIn ) ,
DisplacementType ( DisplacementTypeIn )
2019-10-25 10:19:13 -04:00
{
}
void FDisplaceMeshOp : : CalculateResult ( FProgressCancel * Progress )
{
2021-01-13 12:32:53 -04:00
if ( Progress & & Progress - > Cancelled ( ) ) return ;
2019-10-25 10:19:13 -04:00
ResultMesh - > Copy ( * SourceMesh ) ;
2021-01-13 12:32:53 -04:00
if ( Progress & & Progress - > Cancelled ( ) ) return ;
2020-12-09 18:33:03 -04:00
if ( DisplacementType = = EDisplaceMeshToolDisplaceType : : DisplacementMap & & ! Parameters . DisplacementMap )
{
return ;
}
2019-10-25 10:19:13 -04:00
SourceNormals = FMeshNormals ( SourceMesh . Get ( ) ) ;
SourceNormals . ComputeVertexNormals ( ) ;
2021-01-13 12:32:53 -04:00
if ( Progress & & Progress - > Cancelled ( ) ) return ;
2019-10-25 10:19:13 -04:00
// cache initial positions
SourcePositions . SetNum ( SourceMesh - > MaxVertexID ( ) ) ;
for ( int vid : SourceMesh - > VertexIndicesItr ( ) )
{
SourcePositions [ vid ] = SourceMesh - > GetVertex ( vid ) ;
}
2021-01-13 12:32:53 -04:00
if ( Progress & & Progress - > Cancelled ( ) ) return ;
2019-10-25 10:19:13 -04:00
DisplacedPositions . SetNum ( SourceMesh - > MaxVertexID ( ) ) ;
2021-01-13 12:32:53 -04:00
if ( Progress & & Progress - > Cancelled ( ) ) return ;
2020-04-18 18:42:59 -04:00
ComputeDisplacement : : FDirectionalFilter DirectionalFilter { Parameters . bEnableFilter ,
FVector3d ( Parameters . FilterDirection ) ,
Parameters . FilterWidth } ;
double Intensity = Parameters . DisplaceIntensity ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
TUniqueFunction < float ( int32 vid , const FVector3d & ) > WeightMapQueryFunc = [ & ] ( int32 , const FVector3d & ) { return 1.0f ; } ;
2020-04-18 18:42:59 -04:00
if ( Parameters . WeightMap . IsValid ( ) )
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
if ( SourceMesh - > IsCompactV ( ) & & SourceMesh - > VertexCount ( ) = = Parameters . WeightMap - > Num ( ) )
{
WeightMapQueryFunc = [ & ] ( int32 vid , const FVector3d & Pos ) { return Parameters . WeightMap - > GetValue ( vid ) ; } ;
}
else
{
// disable input query function as it uses expensive AABBTree lookup
//WeightMapQueryFunc = [&](int32 vid, const FVector3d& Pos) { return Parameters.WeightMapQueryFunc(Pos, *Parameters.WeightMap); };
WeightMapQueryFunc = [ & ] ( int32 vid , const FVector3d & Pos ) { return SourceMesh - > GetVertexUV ( vid ) . X ; } ;
}
2020-04-18 18:42:59 -04:00
}
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
auto IntensityFunc = [ & ] ( int32 vid , const FVector3d & Position , const FVector3d & Normal )
2020-04-18 18:42:59 -04:00
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
return Intensity * DirectionalFilter . FilterValue ( Normal ) * WeightMapQueryFunc ( vid , Position ) ;
2020-04-18 18:42:59 -04:00
} ;
2021-02-03 14:57:28 -04:00
2019-10-25 10:19:13 -04:00
// compute Displaced positions in PositionBuffer
switch ( DisplacementType )
{
default :
case EDisplaceMeshToolDisplaceType : : Constant :
2020-04-18 18:42:59 -04:00
ComputeDisplacement : : Constant ( * SourceMesh ,
SourcePositions ,
SourceNormals ,
IntensityFunc ,
2019-10-25 10:19:13 -04:00
DisplacedPositions ) ;
break ;
case EDisplaceMeshToolDisplaceType : : RandomNoise :
2020-04-18 18:42:59 -04:00
ComputeDisplacement : : RandomNoise ( * SourceMesh ,
SourcePositions ,
SourceNormals ,
IntensityFunc ,
Parameters . RandomSeed ,
2019-10-25 10:19:13 -04:00
DisplacedPositions ) ;
break ;
2020-01-27 20:11:15 -05:00
case EDisplaceMeshToolDisplaceType : : PerlinNoise :
ComputeDisplacement : : PerlinNoise ( * SourceMesh ,
SourcePositions ,
SourceNormals ,
2020-04-18 18:42:59 -04:00
IntensityFunc ,
Parameters . PerlinLayerProperties ,
Parameters . RandomSeed ,
2020-01-27 20:11:15 -05:00
DisplacedPositions ) ;
break ;
2019-10-25 10:19:13 -04:00
case EDisplaceMeshToolDisplaceType : : DisplacementMap :
2020-04-18 18:42:59 -04:00
ComputeDisplacement : : Map ( * SourceMesh ,
SourcePositions ,
SourceNormals ,
IntensityFunc ,
Parameters . DisplaceField ,
2020-12-09 18:33:03 -04:00
DisplacedPositions ,
Parameters . DisplacementMapBaseValue ,
Parameters . UVScale ,
2020-12-14 19:16:22 -04:00
Parameters . UVOffset ,
Parameters . AdjustmentCurve . Get ( ) ) ;
2019-10-25 10:19:13 -04:00
break ;
2020-01-27 20:11:15 -05:00
case EDisplaceMeshToolDisplaceType : : SineWave :
2020-04-18 18:42:59 -04:00
ComputeDisplacement : : Sine ( * SourceMesh ,
SourcePositions ,
SourceNormals ,
IntensityFunc ,
Parameters . SineWaveFrequency ,
Parameters . SineWavePhaseShift ,
2021-03-30 21:25:22 -04:00
( FVector3d ) Parameters . SineWaveDirection ,
2020-01-27 20:11:15 -05:00
DisplacedPositions ) ;
break ;
2019-10-25 10:19:13 -04:00
}
// update preview vertex positions
for ( int vid : ResultMesh - > VertexIndicesItr ( ) )
{
ResultMesh - > SetVertex ( vid , DisplacedPositions [ vid ] ) ;
}
// recalculate normals
2021-01-19 16:10:15 -04:00
if ( Parameters . bRecalculateNormals )
2019-10-25 10:19:13 -04:00
{
2021-01-19 16:10:15 -04:00
if ( ResultMesh - > HasAttributes ( ) )
{
FMeshNormals Normals ( ResultMesh . Get ( ) ) ;
FDynamicMeshNormalOverlay * NormalOverlay = ResultMesh - > Attributes ( ) - > PrimaryNormals ( ) ;
Normals . RecomputeOverlayNormals ( NormalOverlay ) ;
Normals . CopyToOverlay ( NormalOverlay ) ;
}
else
{
FMeshNormals : : QuickComputeVertexNormals ( * ResultMesh ) ;
}
2019-10-25 10:19:13 -04:00
}
}
class FDisplaceMeshOpFactory : public IDynamicMeshOperatorFactory
{
public :
2021-02-17 11:50:23 -04:00
FDisplaceMeshOpFactory ( TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > & SourceMeshIn ,
2020-04-18 18:42:59 -04:00
const DisplaceMeshParameters & DisplaceParametersIn ,
2020-01-27 20:11:15 -05:00
EDisplaceMeshToolDisplaceType DisplacementTypeIn )
2019-10-25 10:19:13 -04:00
: SourceMesh ( SourceMeshIn )
{
2020-04-18 18:42:59 -04:00
SetIntensity ( DisplaceParametersIn . DisplaceIntensity ) ;
SetRandomSeed ( DisplaceParametersIn . RandomSeed ) ;
2021-05-19 11:21:56 -04:00
SetDisplacementMap ( DisplaceParametersIn . DisplacementMap , DisplaceParametersIn . DisplacementMapChannel ) ; // Calls UpdateMap
2020-04-18 18:42:59 -04:00
SetFrequency ( DisplaceParametersIn . SineWaveFrequency ) ;
SetPhaseShift ( DisplaceParametersIn . SineWavePhaseShift ) ;
SetSineWaveDirection ( DisplaceParametersIn . SineWaveDirection ) ;
SetEnableDirectionalFilter ( DisplaceParametersIn . bEnableFilter ) ;
SetFilterDirection ( DisplaceParametersIn . FilterDirection ) ;
SetFilterFalloffWidth ( DisplaceParametersIn . FilterWidth ) ;
SetPerlinNoiseLayerProperties ( DisplaceParametersIn . PerlinLayerProperties ) ;
2019-10-25 10:19:13 -04:00
SetDisplacementType ( DisplacementTypeIn ) ;
2020-04-18 18:42:59 -04:00
Parameters . WeightMap = DisplaceParametersIn . WeightMap ;
Parameters . WeightMapQueryFunc = DisplaceParametersIn . WeightMapQueryFunc ;
2020-12-09 18:33:03 -04:00
Parameters . DisplacementMapBaseValue = DisplaceParametersIn . DisplacementMapBaseValue ;
Parameters . UVScale = DisplaceParametersIn . UVScale ;
Parameters . UVOffset = DisplaceParametersIn . UVOffset ;
2020-12-14 19:16:22 -04:00
Parameters . AdjustmentCurve = DisplaceParametersIn . AdjustmentCurve ;
2019-10-25 10:19:13 -04:00
}
void SetIntensity ( float IntensityIn ) ;
void SetRandomSeed ( int RandomSeedIn ) ;
2021-05-19 11:21:56 -04:00
void SetDisplacementMap ( UTexture2D * DisplacementMapIn , int32 ChannelIn ) ;
2020-12-09 18:33:03 -04:00
void SetDisplacementMapUVAdjustment ( const FVector2f & UVScale , const FVector2f & UVOffset ) ;
void SetDisplacementMapBaseValue ( float DisplacementMapBaseValue ) ;
2020-12-14 19:16:22 -04:00
void SetAdjustmentCurve ( UCurveFloat * CurveFloat ) ;
2020-01-27 20:11:15 -05:00
void SetFrequency ( float FrequencyIn ) ;
void SetPhaseShift ( float PhaseShiftIn ) ;
2020-04-18 18:42:59 -04:00
void SetSineWaveDirection ( const FVector & Direction ) ;
2019-10-25 10:19:13 -04:00
void SetDisplacementType ( EDisplaceMeshToolDisplaceType TypeIn ) ;
2020-04-18 18:42:59 -04:00
void SetEnableDirectionalFilter ( bool EnableDirectionalFilter ) ;
void SetFilterDirection ( const FVector & Direction ) ;
void SetFilterFalloffWidth ( float FalloffWidth ) ;
void SetPerlinNoiseLayerProperties ( const TArray < FPerlinLayerProperties > & PerlinLayerProperties ) ;
2021-02-17 11:50:23 -04:00
void SetWeightMap ( TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap ) ;
2021-01-19 16:10:15 -04:00
void SetRecalculateNormals ( bool bRecalculateNormals ) ;
2019-10-25 10:19:13 -04:00
2019-11-01 17:39:56 -04:00
TUniquePtr < FDynamicMeshOperator > MakeNewOperator ( ) final
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
return MakeUnique < FDisplaceMeshOp > ( SourceMesh , Parameters , DisplacementType ) ;
2019-10-25 10:19:13 -04:00
}
private :
void UpdateMap ( ) ;
2020-04-18 18:42:59 -04:00
DisplaceMeshParameters Parameters ;
2019-10-25 10:19:13 -04:00
EDisplaceMeshToolDisplaceType DisplacementType ;
2021-02-17 11:50:23 -04:00
TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > & SourceMesh ;
2019-10-25 10:19:13 -04:00
} ;
void FDisplaceMeshOpFactory : : SetIntensity ( float IntensityIn )
{
2020-04-18 18:42:59 -04:00
Parameters . DisplaceIntensity = IntensityIn ;
2019-10-25 10:19:13 -04:00
}
void FDisplaceMeshOpFactory : : SetRandomSeed ( int RandomSeedIn )
{
2020-04-18 18:42:59 -04:00
Parameters . RandomSeed = RandomSeedIn ;
2019-10-25 10:19:13 -04:00
}
2021-05-19 11:21:56 -04:00
void FDisplaceMeshOpFactory : : SetDisplacementMap ( UTexture2D * DisplacementMapIn , int32 ChannelIn )
2019-10-25 10:19:13 -04:00
{
2020-12-14 19:16:22 -04:00
Parameters . DisplacementMap = DisplacementMapIn ;
2021-05-19 11:21:56 -04:00
Parameters . DisplacementMapChannel = ChannelIn ;
2020-12-14 19:16:22 -04:00
// Note that we do the update even if we got the same pointer, because the texture
// may have been changed in the editor.
UpdateMap ( ) ;
2019-10-25 10:19:13 -04:00
}
2020-12-09 18:33:03 -04:00
void FDisplaceMeshOpFactory : : SetDisplacementMapUVAdjustment ( const FVector2f & UVScale , const FVector2f & UVOffset )
{
Parameters . UVScale = UVScale ;
Parameters . UVOffset = UVOffset ;
}
void FDisplaceMeshOpFactory : : SetDisplacementMapBaseValue ( float DisplacementMapBaseValue )
{
// We could bake this into the displacement field, but that would require calling UpdateMap with
// every slider change, which is slow. So we'll just pass this down to the calculation.
Parameters . DisplacementMapBaseValue = DisplacementMapBaseValue ;
}
2020-12-14 19:16:22 -04:00
void FDisplaceMeshOpFactory : : SetAdjustmentCurve ( UCurveFloat * CurveFloat )
{
2021-02-17 11:50:23 -04:00
Parameters . AdjustmentCurve = CurveFloat ? TSharedPtr < FRichCurve , ESPMode : : ThreadSafe > (
2020-12-14 19:16:22 -04:00
static_cast < FRichCurve * > ( CurveFloat - > FloatCurve . Duplicate ( ) ) )
: nullptr ;
}
2020-01-27 20:11:15 -05:00
void FDisplaceMeshOpFactory : : SetFrequency ( float FrequencyIn )
{
2020-04-18 18:42:59 -04:00
Parameters . SineWaveFrequency = FrequencyIn ;
2020-01-27 20:11:15 -05:00
}
void FDisplaceMeshOpFactory : : SetPhaseShift ( float PhaseShiftIn )
{
2020-04-18 18:42:59 -04:00
Parameters . SineWavePhaseShift = PhaseShiftIn ;
}
void FDisplaceMeshOpFactory : : SetSineWaveDirection ( const FVector & Direction )
{
2020-12-09 18:33:03 -04:00
Parameters . SineWaveDirection = Direction . GetSafeNormal ( ) ;
2020-01-27 20:11:15 -05:00
}
2019-10-25 10:19:13 -04:00
void FDisplaceMeshOpFactory : : SetDisplacementType ( EDisplaceMeshToolDisplaceType TypeIn )
{
DisplacementType = TypeIn ;
}
void FDisplaceMeshOpFactory : : UpdateMap ( )
{
2020-04-18 18:42:59 -04:00
if ( Parameters . DisplacementMap = = nullptr | |
2021-11-18 14:37:34 -05:00
Parameters . DisplacementMap - > GetPlatformData ( ) = = nullptr | |
Parameters . DisplacementMap - > GetPlatformData ( ) - > Mips . Num ( ) < 1 )
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
Parameters . DisplaceField = FSampledScalarField2f ( ) ;
2020-12-09 18:33:03 -04:00
Parameters . DisplaceField . GridValues . AssignAll ( 0 ) ;
2019-10-25 10:19:13 -04:00
return ;
}
2020-12-09 18:33:03 -04:00
TImageBuilder < FVector4f > DisplacementMapValues ;
2021-07-20 15:48:13 -04:00
if ( ! UE : : AssetUtils : : ReadTexture ( Parameters . DisplacementMap , DisplacementMapValues ,
2020-12-14 19:16:22 -04:00
// need bPreferPlatformData to be true to respond to non-destructive changes to the texture in the editor
true ) )
2020-01-07 15:54:23 -05:00
{
2020-04-18 18:42:59 -04:00
Parameters . DisplaceField = FSampledScalarField2f ( ) ;
2020-12-09 18:33:03 -04:00
Parameters . DisplaceField . GridValues . AssignAll ( 0 ) ;
2019-10-25 10:19:13 -04:00
}
else
{
2021-07-20 15:48:13 -04:00
const FImageDimensions DisplacementMapDimensions = DisplacementMapValues . GetDimensions ( ) ;
2020-12-09 18:33:03 -04:00
int64 TextureWidth = DisplacementMapDimensions . GetWidth ( ) ;
int64 TextureHeight = DisplacementMapDimensions . GetHeight ( ) ;
2020-04-18 18:42:59 -04:00
Parameters . DisplaceField . Resize ( TextureWidth , TextureHeight , 0.0f ) ;
2020-12-09 18:33:03 -04:00
// Note that the height of the texture will not be 1.0 if it was not square. This should be kept in mind when sampling it later.
2020-04-18 18:42:59 -04:00
Parameters . DisplaceField . SetCellSize ( 1.0f / ( float ) TextureWidth ) ;
2019-10-25 10:19:13 -04:00
2020-11-17 22:32:38 -04:00
for ( int64 y = 0 ; y < TextureHeight ; + + y )
2019-10-25 10:19:13 -04:00
{
2020-11-17 22:32:38 -04:00
for ( int64 x = 0 ; x < TextureWidth ; + + x )
2019-10-25 10:19:13 -04:00
{
2021-05-19 11:21:56 -04:00
Parameters . DisplaceField . GridValues [ y * TextureWidth + x ] =
DisplacementMapValues . GetPixel ( y * TextureWidth + x ) [ Parameters . DisplacementMapChannel ] ;
2019-10-25 10:19:13 -04:00
}
}
}
}
2020-04-18 18:42:59 -04:00
void FDisplaceMeshOpFactory : : SetEnableDirectionalFilter ( bool EnableDirectionalFilter )
{
Parameters . bEnableFilter = EnableDirectionalFilter ;
}
void FDisplaceMeshOpFactory : : SetFilterDirection ( const FVector & Direction )
{
2020-12-09 18:33:03 -04:00
Parameters . FilterDirection = Direction . GetSafeNormal ( ) ;
2020-04-18 18:42:59 -04:00
}
void FDisplaceMeshOpFactory : : SetFilterFalloffWidth ( float FalloffWidth )
{
Parameters . FilterWidth = FalloffWidth ;
}
void FDisplaceMeshOpFactory : : SetPerlinNoiseLayerProperties ( const TArray < FPerlinLayerProperties > & LayerProperties )
{
Parameters . PerlinLayerProperties = LayerProperties ;
}
2021-02-17 11:50:23 -04:00
void FDisplaceMeshOpFactory : : SetWeightMap ( TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > WeightMap )
2020-04-18 18:42:59 -04:00
{
Parameters . WeightMap = WeightMap ;
}
2021-01-19 16:10:15 -04:00
void FDisplaceMeshOpFactory : : SetRecalculateNormals ( bool RecalcNormalsIn )
{
Parameters . bRecalculateNormals = RecalcNormalsIn ;
}
2019-10-25 10:19:13 -04:00
} // namespace
/*
* ToolBuilder
*/
2021-03-18 18:26:33 -04:00
USingleSelectionMeshEditingTool * UDisplaceMeshToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2019-10-25 10:19:13 -04:00
{
2021-03-18 18:26:33 -04:00
return NewObject < UDisplaceMeshTool > ( SceneState . ToolManager ) ;
2019-10-25 10:19:13 -04:00
}
/*
* Tool
*/
2020-04-18 18:42:59 -04:00
TArray < FString > UDisplaceMeshCommonProperties : : GetWeightMapsFunc ( )
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
return WeightMapsList ;
2019-10-25 10:19:13 -04:00
}
void UDisplaceMeshTool : : Setup ( )
{
2020-12-09 18:33:03 -04:00
using namespace DisplaceMeshToolLocals ;
2019-10-25 10:19:13 -04:00
UInteractiveTool : : Setup ( ) ;
2020-04-18 18:42:59 -04:00
// UInteractiveToolPropertySets
NoiseProperties = NewObject < UDisplaceMeshPerlinNoiseProperties > ( ) ;
NoiseProperties - > RestoreProperties ( this ) ;
CommonProperties = NewObject < UDisplaceMeshCommonProperties > ( ) ;
CommonProperties - > RestoreProperties ( this ) ;
DirectionalFilterProperties = NewObject < UDisplaceMeshDirectionalFilterProperties > ( ) ;
DirectionalFilterProperties - > RestoreProperties ( this ) ;
TextureMapProperties = NewObject < UDisplaceMeshTextureMapProperties > ( ) ;
TextureMapProperties - > RestoreProperties ( this ) ;
SineWaveProperties = NewObject < UDisplaceMeshSineWaveProperties > ( ) ;
SineWaveProperties - > RestoreProperties ( this ) ;
if ( TextureMapProperties - > DisplacementMap ! = nullptr & & TextureMapProperties - > DisplacementMap - > IsValidLowLevel ( ) = = false )
{
TextureMapProperties - > DisplacementMap = nullptr ;
}
2021-04-06 17:59:10 -04:00
if ( TextureMapProperties - > AdjustmentCurve = = nullptr )
2020-12-14 19:16:22 -04:00
{
2021-04-06 17:59:10 -04:00
// if curve is null, create from default
TextureMapProperties - > AdjustmentCurve = ToolSetupUtil : : GetContrastAdjustmentCurve ( GetToolManager ( ) ) ;
2020-12-14 19:16:22 -04:00
}
2020-04-18 18:42:59 -04:00
// populate weight maps list
2021-06-11 22:42:32 -04:00
const FMeshDescription * MeshDescription = UE : : ToolTarget : : GetMeshDescription ( Target ) ;
2020-04-18 18:42:59 -04:00
TArray < FName > WeightMaps ;
2021-06-11 22:42:32 -04:00
UE : : WeightMaps : : FindVertexWeightMaps ( MeshDescription , WeightMaps ) ;
2020-04-18 18:42:59 -04:00
CommonProperties - > WeightMapsList . Add ( TEXT ( " None " ) ) ;
for ( FName Name : WeightMaps )
{
CommonProperties - > WeightMapsList . Add ( Name . ToString ( ) ) ;
}
if ( WeightMaps . Contains ( CommonProperties - > WeightMap ) = = false ) // discard restored value if it doesn't apply
{
CommonProperties - > WeightMap = FName ( CommonProperties - > WeightMapsList [ 0 ] ) ;
}
UpdateActiveWeightMap ( ) ;
2019-10-25 10:19:13 -04:00
// create dynamic mesh component to use for live preview
2021-11-18 14:37:34 -05:00
FActorSpawnParameters SpawnInfo ;
PreviewMeshActor = TargetWorld - > SpawnActor < AInternalToolFrameworkActor > ( FVector : : ZeroVector , FRotator : : ZeroRotator , SpawnInfo ) ;
DynamicMeshComponent = NewObject < UDynamicMeshComponent > ( PreviewMeshActor ) ;
DynamicMeshComponent - > SetupAttachment ( PreviewMeshActor - > GetRootComponent ( ) ) ;
2019-10-25 10:19:13 -04:00
DynamicMeshComponent - > RegisterComponent ( ) ;
2021-06-11 22:42:32 -04:00
DynamicMeshComponent - > SetWorldTransform ( ( FTransform ) UE : : ToolTarget : : GetLocalToWorldTransform ( Target ) ) ;
2020-12-14 19:16:22 -04:00
DynamicMeshComponent - > bExplicitShowWireframe = CommonProperties - > bShowWireframe ;
2021-10-07 22:25:54 -04:00
ToolSetupUtil : : ApplyRenderingConfigurationToPreview ( DynamicMeshComponent , Target ) ;
2019-10-25 10:19:13 -04:00
2019-12-19 18:07:47 -05:00
// transfer materials
2021-06-11 22:42:32 -04:00
FComponentMaterialSet MaterialSet = UE : : ToolTarget : : GetMaterialSet ( Target ) ;
2019-12-19 18:07:47 -05:00
for ( int k = 0 ; k < MaterialSet . Materials . Num ( ) ; + + k )
2019-10-01 20:41:42 -04:00
{
2019-12-19 18:07:47 -05:00
DynamicMeshComponent - > SetMaterial ( k , MaterialSet . Materials [ k ] ) ;
2019-10-01 20:41:42 -04:00
}
2021-06-11 22:42:32 -04:00
DynamicMeshComponent - > SetTangentsType ( EDynamicMeshComponentTangentsMode : : AutoCalculated ) ;
DynamicMeshComponent - > SetMesh ( UE : : ToolTarget : : GetDynamicMeshCopy ( Target ) ) ;
2019-10-25 10:19:13 -04:00
OriginalMesh . Copy ( * DynamicMeshComponent - > GetMesh ( ) ) ;
2020-04-18 18:42:59 -04:00
OriginalMeshSpatial . SetMesh ( & OriginalMesh , true ) ;
2019-10-25 10:19:13 -04:00
2020-04-18 18:42:59 -04:00
DisplaceMeshParameters Parameters ;
Parameters . DisplaceIntensity = CommonProperties - > DisplaceIntensity ;
Parameters . RandomSeed = CommonProperties - > RandomSeed ;
Parameters . DisplacementMap = TextureMapProperties - > DisplacementMap ;
2021-01-20 18:11:09 -04:00
Parameters . bRecalculateNormals = TextureMapProperties - > bRecalcNormals ;
2020-04-18 18:42:59 -04:00
Parameters . SineWaveFrequency = SineWaveProperties - > SineWaveFrequency ;
Parameters . SineWavePhaseShift = SineWaveProperties - > SineWavePhaseShift ;
2020-12-09 18:33:03 -04:00
Parameters . SineWaveDirection = SineWaveProperties - > SineWaveDirection . GetSafeNormal ( ) ;
2020-04-18 18:42:59 -04:00
Parameters . bEnableFilter = DirectionalFilterProperties - > bEnableFilter ;
2020-12-09 18:33:03 -04:00
Parameters . FilterDirection = DirectionalFilterProperties - > FilterDirection . GetSafeNormal ( ) ;
2020-04-18 18:42:59 -04:00
Parameters . FilterWidth = DirectionalFilterProperties - > FilterWidth ;
Parameters . PerlinLayerProperties = NoiseProperties - > PerlinLayerProperties ;
Parameters . WeightMap = ActiveWeightMap ;
Parameters . WeightMapQueryFunc = [ this ] ( const FVector3d & Position , const FIndexedWeightMap & WeightMap ) { return WeightMapQuery ( Position , WeightMap ) ; } ;
Displacer = MakeUnique < FDisplaceMeshOpFactory > ( SubdividedMesh , Parameters , CommonProperties - > DisplacementType ) ;
2019-10-25 10:19:13 -04:00
// hide input StaticMeshComponent
2021-06-11 22:42:32 -04:00
UE : : ToolTarget : : HideSourceObject ( Target ) ;
2019-10-25 10:19:13 -04:00
// initialize our properties
ToolPropertyObjects . Add ( this ) ;
2020-04-18 18:42:59 -04:00
AddToolPropertySource ( CommonProperties ) ;
SetToolPropertySourceEnabled ( CommonProperties , true ) ;
AddToolPropertySource ( DirectionalFilterProperties ) ;
SetToolPropertySourceEnabled ( DirectionalFilterProperties , true ) ;
AddToolPropertySource ( TextureMapProperties ) ;
SetToolPropertySourceEnabled ( TextureMapProperties , CommonProperties - > DisplacementType = = EDisplaceMeshToolDisplaceType : : DisplacementMap ) ;
AddToolPropertySource ( SineWaveProperties ) ;
SetToolPropertySourceEnabled ( SineWaveProperties , CommonProperties - > DisplacementType = = EDisplaceMeshToolDisplaceType : : SineWave ) ;
AddToolPropertySource ( NoiseProperties ) ;
SetToolPropertySourceEnabled ( NoiseProperties , CommonProperties - > DisplacementType = = EDisplaceMeshToolDisplaceType : : PerlinNoise ) ;
// Set up a callback for when the type of displacement changes
CommonProperties - > WatchProperty ( CommonProperties - > DisplacementType ,
[ this ] ( EDisplaceMeshToolDisplaceType NewType )
{
SetToolPropertySourceEnabled ( NoiseProperties , ( NewType = = EDisplaceMeshToolDisplaceType : : PerlinNoise ) ) ;
SetToolPropertySourceEnabled ( SineWaveProperties , ( NewType = = EDisplaceMeshToolDisplaceType : : SineWave ) ) ;
SetToolPropertySourceEnabled ( TextureMapProperties , ( NewType = = EDisplaceMeshToolDisplaceType : : DisplacementMap ) ) ;
} ) ;
2019-10-25 10:19:13 -04:00
ValidateSubdivisions ( ) ;
2021-08-31 11:04:39 -04:00
Subdivider = MakeUnique < FSubdivideMeshOpFactory > ( OriginalMesh , CommonProperties - > SubdivisionType , CommonProperties - > Subdivisions , ActiveWeightMap ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
2019-10-25 10:19:13 -04:00
StartComputation ( ) ;
2020-04-18 18:42:59 -04:00
2021-02-08 17:02:09 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Displace " ) ) ;
2020-04-18 18:42:59 -04:00
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartDisplaceMesh " , " Subdivide and Displace the input mesh using different noise functions and maps " ) ,
EToolMessageLevel : : UserNotification ) ;
2019-10-25 10:19:13 -04:00
}
2022-01-28 18:40:54 -05:00
void UDisplaceMeshTool : : OnShutdown ( EToolShutdownType ShutdownType )
2019-10-25 10:19:13 -04:00
{
2021-04-06 17:59:10 -04:00
// unhook any active listener for contrast curve
DisconnectActiveContrastCurveTarget ( ) ;
2020-12-14 19:16:22 -04:00
2020-04-18 18:42:59 -04:00
CommonProperties - > SaveProperties ( this ) ;
NoiseProperties - > SaveProperties ( this ) ;
DirectionalFilterProperties - > SaveProperties ( this ) ;
SineWaveProperties - > SaveProperties ( this ) ;
TextureMapProperties - > SaveProperties ( this ) ;
2019-10-25 10:19:13 -04:00
if ( DynamicMeshComponent ! = nullptr )
2019-10-01 20:41:42 -04:00
{
2021-06-11 22:42:32 -04:00
UE : : ToolTarget : : ShowSourceObject ( Target ) ;
2019-10-25 10:19:13 -04:00
if ( ShutdownType = = EToolShutdownType : : Accept )
{
// this block bakes the modified DynamicMeshComponent back into the StaticMeshComponent inside an undo transaction
GetToolManager ( ) - > BeginUndoTransaction ( LOCTEXT ( " DisplaceMeshToolTransactionName " , " Displace Mesh " ) ) ;
2021-01-19 16:10:15 -04:00
// if we are applying a map and not recalculating normals, we need to make sure normals recalculation is disabled
// on the underlying StaticMesh Asset, or it will run on the Bake() below and the output result will not be the same as the preview
if ( CommonProperties - > DisplacementType = = EDisplaceMeshToolDisplaceType : : DisplacementMap & & TextureMapProperties - > bRecalcNormals = = false )
{
2021-06-11 22:42:32 -04:00
UStaticMeshComponent * StaticMeshComponent = Cast < UStaticMeshComponent > ( UE : : ToolTarget : : GetTargetComponent ( Target ) ) ;
2021-01-19 16:10:15 -04:00
if ( StaticMeshComponent )
{
if ( UStaticMesh * StaticMesh = StaticMeshComponent - > GetStaticMesh ( ) )
{
StaticMesh - > Modify ( ) ;
// disable auto-generated normals and tangents build settings
UE : : MeshDescription : : FStaticMeshBuildSettingChange SettingsChange ;
SettingsChange . AutoGeneratedNormals = UE : : MeshDescription : : EBuildSettingBoolChange : : Disable ;
UE : : MeshDescription : : ConfigureBuildSettings ( StaticMesh , 0 , SettingsChange ) ;
}
}
}
2021-06-11 22:42:32 -04:00
DynamicMeshComponent - > ProcessMesh ( [ & ] ( const FDynamicMesh3 & ReadMesh )
2019-10-25 10:19:13 -04:00
{
2021-06-11 22:42:32 -04:00
UE : : ToolTarget : : CommitDynamicMeshUpdate ( Target , ReadMesh , CommonProperties - > Subdivisions > 0 ) ;
2019-10-25 10:19:13 -04:00
} ) ;
GetToolManager ( ) - > EndUndoTransaction ( ) ;
}
DynamicMeshComponent - > UnregisterComponent ( ) ;
DynamicMeshComponent - > DestroyComponent ( ) ;
DynamicMeshComponent = nullptr ;
}
2021-11-18 14:37:34 -05:00
if ( PreviewMeshActor ! = nullptr )
{
PreviewMeshActor - > Destroy ( ) ;
PreviewMeshActor = nullptr ;
}
2019-10-25 10:19:13 -04:00
}
2021-04-06 17:59:10 -04:00
2019-10-25 10:19:13 -04:00
void UDisplaceMeshTool : : ValidateSubdivisions ( )
{
2020-04-18 18:42:59 -04:00
if ( CommonProperties - > bDisableSizeWarning )
{
GetToolManager ( ) - > DisplayMessage ( FText : : GetEmpty ( ) , EToolMessageLevel : : UserWarning ) ;
return ;
}
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
bool bIsInitialized = ( Subdivider ! = nullptr ) ;
2019-10-25 10:19:13 -04:00
constexpr int MaxTriangles = 3000000 ;
double NumTriangles = OriginalMesh . MaxTriangleID ( ) ;
2021-12-13 13:34:29 -05:00
int MaxSubdivisions = ( int ) ( FMath : : Sqrt ( MaxTriangles / NumTriangles ) - 1 ) ;
2020-04-18 18:42:59 -04:00
if ( CommonProperties - > Subdivisions > MaxSubdivisions )
2019-10-25 10:19:13 -04:00
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
if ( bIsInitialized ) // only show warning after initial tool startup
{
FText WarningText = FText : : Format ( LOCTEXT ( " SubdivisionsTooHigh " , " Desired number of Subdivisions ({0}) exceeds maximum number ({1}) for a mesh of this number of triangles. " ) ,
FText : : AsNumber ( CommonProperties - > Subdivisions ) ,
FText : : AsNumber ( MaxSubdivisions ) ) ;
GetToolManager ( ) - > DisplayMessage ( WarningText , EToolMessageLevel : : UserWarning ) ;
}
2020-04-18 18:42:59 -04:00
CommonProperties - > Subdivisions = MaxSubdivisions ;
2019-10-01 20:41:42 -04:00
}
else
2019-09-12 13:55:17 -04:00
{
2019-10-25 10:19:13 -04:00
FText ClearWarningText ;
GetToolManager ( ) - > DisplayMessage ( ClearWarningText , EToolMessageLevel : : UserWarning ) ;
}
2020-04-18 18:42:59 -04:00
if ( CommonProperties - > Subdivisions < 0 )
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
CommonProperties - > Subdivisions = 0 ;
2019-10-25 10:19:13 -04:00
}
}
2019-10-01 20:41:42 -04:00
2019-10-25 10:19:13 -04:00
# if WITH_EDITOR
2020-04-18 18:42:59 -04:00
void UDisplaceMeshTool : : OnPropertyModified ( UObject * PropertySet , FProperty * Property )
2019-10-25 10:19:13 -04:00
{
2020-12-09 18:33:03 -04:00
using namespace DisplaceMeshToolLocals ;
2020-04-18 18:42:59 -04:00
if ( PropertySet & & Property )
2019-10-25 10:19:13 -04:00
{
2020-04-18 18:42:59 -04:00
FDisplaceMeshOpFactory * DisplacerDownCast = static_cast < FDisplaceMeshOpFactory * > ( Displacer . Get ( ) ) ;
FSubdivideMeshOpFactory * SubdividerDownCast = static_cast < FSubdivideMeshOpFactory * > ( Subdivider . Get ( ) ) ;
const FString PropertySetName = PropertySet - > GetFName ( ) . GetPlainNameString ( ) ;
const FName PropName = Property - > GetFName ( ) ;
2019-10-25 10:19:13 -04:00
bNeedsDisplaced = true ;
2020-04-18 18:42:59 -04:00
2021-08-31 11:04:39 -04:00
if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , SubdivisionType ) )
{
if ( CommonProperties - > SubdivisionType ! = SubdividerDownCast - > GetSubdivisionType ( ) )
{
SubdividerDownCast - > SetSubdivisionType ( CommonProperties - > SubdivisionType ) ;
bNeedsSubdivided = true ;
}
else
{
return ;
}
}
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , Subdivisions ) )
2019-09-12 13:55:17 -04:00
{
2019-10-25 10:19:13 -04:00
ValidateSubdivisions ( ) ;
2020-04-18 18:42:59 -04:00
if ( CommonProperties - > Subdivisions ! = SubdividerDownCast - > GetSubdivisionsCount ( ) )
2019-09-24 11:07:45 -04:00
{
2020-04-18 18:42:59 -04:00
SubdividerDownCast - > SetSubdivisionsCount ( CommonProperties - > Subdivisions ) ;
2019-10-25 10:19:13 -04:00
bNeedsSubdivided = true ;
}
else
{
return ;
2019-10-01 20:41:42 -04:00
}
2019-09-12 13:55:17 -04:00
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , RandomSeed ) )
2019-10-01 20:41:42 -04:00
{
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetRandomSeed ( CommonProperties - > RandomSeed ) ;
2019-10-01 20:41:42 -04:00
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , DisplacementType ) )
2019-10-01 20:41:42 -04:00
{
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetDisplacementType ( CommonProperties - > DisplacementType ) ;
2019-10-01 20:41:42 -04:00
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , DisplaceIntensity ) )
2019-09-12 13:55:17 -04:00
{
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetIntensity ( CommonProperties - > DisplaceIntensity ) ;
2019-09-12 13:55:17 -04:00
}
2020-12-14 19:16:22 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , bShowWireframe ) )
{
DynamicMeshComponent - > bExplicitShowWireframe = CommonProperties - > bShowWireframe ;
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshSineWaveProperties , SineWaveFrequency ) )
2020-01-27 20:11:15 -05:00
{
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetFrequency ( SineWaveProperties - > SineWaveFrequency ) ;
2020-01-27 20:11:15 -05:00
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshSineWaveProperties , SineWavePhaseShift ) )
2020-01-27 20:11:15 -05:00
{
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetPhaseShift ( SineWaveProperties - > SineWavePhaseShift ) ;
2020-01-27 20:11:15 -05:00
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , DisplacementMap ) )
2019-10-25 10:19:13 -04:00
{
2021-01-19 16:10:15 -04:00
if ( TextureMapProperties - > DisplacementMap ! = nullptr
& & TextureMapProperties - > DisplacementMap - > VirtualTextureStreaming )
{
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " DisplaceToolVirtualTextureMessage " , " Virtual Texture must be disabled on the selected Texture2D to use it as a Displacement Map in this Tool " ) ,
EToolMessageLevel : : UserWarning ) ;
}
else
{
GetToolManager ( ) - > DisplayMessage ( FText : : GetEmpty ( ) , EToolMessageLevel : : UserWarning ) ;
}
2021-12-15 10:49:00 -05:00
DisplacerDownCast - > SetDisplacementMap ( TextureMapProperties - > DisplacementMap , static_cast < int32 > ( TextureMapProperties - > Channel ) ) ;
2021-05-19 11:21:56 -04:00
}
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , Channel ) )
{
2021-12-15 10:49:00 -05:00
DisplacerDownCast - > SetDisplacementMap ( TextureMapProperties - > DisplacementMap , static_cast < int32 > ( TextureMapProperties - > Channel ) ) ;
2019-10-25 10:19:13 -04:00
}
2020-12-09 18:33:03 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , DisplacementMapBaseValue ) )
{
DisplacerDownCast - > SetDisplacementMapBaseValue ( TextureMapProperties - > DisplacementMapBaseValue ) ;
}
2021-01-19 16:10:15 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , bRecalcNormals ) )
{
DisplacerDownCast - > SetRecalculateNormals ( TextureMapProperties - > bRecalcNormals ) ;
}
2020-12-14 19:16:22 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , bApplyAdjustmentCurve )
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , AdjustmentCurve ) )
{
DisplacerDownCast - > SetAdjustmentCurve ( TextureMapProperties - > bApplyAdjustmentCurve ? TextureMapProperties - > AdjustmentCurve : nullptr ) ;
}
2020-04-18 18:42:59 -04:00
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , WeightMap )
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshCommonProperties , bInvertWeightMap ) )
{
UpdateActiveWeightMap ( ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
SubdividerDownCast - > SetWeightMap ( ActiveWeightMap ) ;
2020-04-18 18:42:59 -04:00
DisplacerDownCast - > SetWeightMap ( ActiveWeightMap ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
bNeedsSubdivided = true ;
2020-04-18 18:42:59 -04:00
}
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshDirectionalFilterProperties , bEnableFilter ) )
{
DisplacerDownCast - > SetEnableDirectionalFilter ( DirectionalFilterProperties - > bEnableFilter ) ;
}
else if ( PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshDirectionalFilterProperties , FilterWidth ) )
{
DisplacerDownCast - > SetFilterFalloffWidth ( DirectionalFilterProperties - > FilterWidth ) ;
}
else if ( ( PropName = = GET_MEMBER_NAME_CHECKED ( FPerlinLayerProperties , Frequency ) ) | | ( PropName = = GET_MEMBER_NAME_CHECKED ( FPerlinLayerProperties , Intensity ) ) )
{
DisplacerDownCast - > SetPerlinNoiseLayerProperties ( NoiseProperties - > PerlinLayerProperties ) ;
}
2020-12-09 18:33:03 -04:00
// The FName we get for the individual vector elements are all the same, whereas resetting with the "revert
// to default" arrow gets us the name of the vector itself. We'll just update all of them if any of them
// change.
else if ( PropName = = " X " | | PropName = = " Y " | | PropName = = " Z "
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshDirectionalFilterProperties , FilterDirection )
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshSineWaveProperties , SineWaveDirection )
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , UVScale )
| | PropName = = GET_MEMBER_NAME_CHECKED ( UDisplaceMeshTextureMapProperties , UVOffset ) )
2020-04-18 18:42:59 -04:00
{
2020-12-09 18:33:03 -04:00
DisplacerDownCast - > SetFilterDirection ( DirectionalFilterProperties - > FilterDirection ) ;
DisplacerDownCast - > SetSineWaveDirection ( SineWaveProperties - > SineWaveDirection ) ;
2022-02-01 12:08:54 -05:00
DisplacerDownCast - > SetDisplacementMapUVAdjustment ( FVector2f ( TextureMapProperties - > UVScale ) , FVector2f ( TextureMapProperties - > UVOffset ) ) ; // LWC_TODO: Precision loss
2020-04-18 18:42:59 -04:00
}
2019-10-25 10:19:13 -04:00
StartComputation ( ) ;
2019-10-01 20:41:42 -04:00
}
2019-10-25 10:19:13 -04:00
}
# endif
2019-10-01 20:41:42 -04:00
2021-04-06 17:59:10 -04:00
void UDisplaceMeshTool : : UpdateActiveContrastCurveTarget ( )
{
using namespace DisplaceMeshToolLocals ;
// if user resets the AdjustmentCurve field, it will go to nullptr, in this case we will force it
// back to a new default curve
if ( TextureMapProperties - > AdjustmentCurve = = nullptr )
{
using namespace DisplaceMeshToolLocals ;
TextureMapProperties - > AdjustmentCurve = ToolSetupUtil : : GetContrastAdjustmentCurve ( GetToolManager ( ) ) ;
bNeedsDisplaced = true ;
}
# if WITH_EDITORONLY_DATA
// if the AdjustmentCurve changes, then we need to change which one we are listening to for CurveUpdate events
if ( TextureMapProperties - > AdjustmentCurve ! = ActiveContrastCurveTarget )
{
DisconnectActiveContrastCurveTarget ( ) ;
if ( TextureMapProperties - > AdjustmentCurve ! = nullptr )
{
ActiveContrastCurveTarget = TextureMapProperties - > AdjustmentCurve ;
ActiveContrastCurveListenerHandle = ActiveContrastCurveTarget - > OnUpdateCurve . AddWeakLambda ( this ,
[ this ] ( UCurveBase * Curve , EPropertyChangeType : : Type ChangeType ) {
if ( TextureMapProperties - > bApplyAdjustmentCurve )
{
FDisplaceMeshOpFactory * DisplacerDownCast = static_cast < FDisplaceMeshOpFactory * > ( Displacer . Get ( ) ) ;
DisplacerDownCast - > SetAdjustmentCurve ( TextureMapProperties - > AdjustmentCurve ) ;
bNeedsDisplaced = true ;
StartComputation ( ) ;
}
} ) ;
}
}
# endif
}
void UDisplaceMeshTool : : DisconnectActiveContrastCurveTarget ( )
{
using namespace DisplaceMeshToolLocals ;
# if WITH_EDITORONLY_DATA
if ( ActiveContrastCurveTarget ! = nullptr )
{
ActiveContrastCurveTarget - > OnUpdateCurve . Remove ( ActiveContrastCurveListenerHandle ) ;
ActiveContrastCurveListenerHandle = FDelegateHandle ( ) ;
ActiveContrastCurveTarget = nullptr ;
FDisplaceMeshOpFactory * DisplacerDownCast = static_cast < FDisplaceMeshOpFactory * > ( Displacer . Get ( ) ) ;
DisplacerDownCast - > SetAdjustmentCurve ( nullptr ) ;
}
# endif
}
2020-04-18 18:42:59 -04:00
void UDisplaceMeshTool : : OnTick ( float DeltaTime )
2019-10-25 10:19:13 -04:00
{
2021-04-06 17:59:10 -04:00
UpdateActiveContrastCurveTarget ( ) ;
2019-10-25 10:19:13 -04:00
AdvanceComputation ( ) ;
}
2019-10-01 20:41:42 -04:00
2019-10-25 10:19:13 -04:00
void UDisplaceMeshTool : : StartComputation ( )
{
if ( bNeedsSubdivided )
2019-10-01 20:41:42 -04:00
{
2019-10-25 10:19:13 -04:00
if ( SubdivideTask )
{
SubdivideTask - > CancelAndDelete ( ) ;
}
2020-03-06 15:43:31 -05:00
SubdividedMesh = nullptr ;
2019-10-25 10:19:13 -04:00
SubdivideTask = new FAsyncTaskExecuterWithAbort < TModelingOpTask < FDynamicMeshOperator > > ( Subdivider - > MakeNewOperator ( ) ) ;
SubdivideTask - > StartBackgroundTask ( ) ;
bNeedsSubdivided = false ;
2019-12-19 18:07:47 -05:00
DynamicMeshComponent - > SetOverrideRenderMaterial ( ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
2019-10-01 20:41:42 -04:00
}
2019-10-25 10:19:13 -04:00
if ( bNeedsDisplaced & & DisplaceTask )
{
DisplaceTask - > CancelAndDelete ( ) ;
DisplaceTask = nullptr ;
2019-12-19 18:07:47 -05:00
DynamicMeshComponent - > SetOverrideRenderMaterial ( ToolSetupUtil : : GetDefaultWorkingMaterial ( GetToolManager ( ) ) ) ;
2019-10-25 10:19:13 -04:00
}
AdvanceComputation ( ) ;
2019-10-01 20:41:42 -04:00
}
2019-10-25 10:19:13 -04:00
void UDisplaceMeshTool : : AdvanceComputation ( )
2019-10-01 20:41:42 -04:00
{
2021-04-06 17:59:10 -04:00
using namespace DisplaceMeshToolLocals ;
2019-10-25 10:19:13 -04:00
if ( SubdivideTask & & SubdivideTask - > IsDone ( ) )
{
2021-02-17 11:50:23 -04:00
SubdividedMesh = TSharedPtr < FDynamicMesh3 , ESPMode : : ThreadSafe > ( SubdivideTask - > GetTask ( ) . ExtractOperator ( ) - > ExtractResult ( ) . Release ( ) ) ;
2019-10-25 10:19:13 -04:00
delete SubdivideTask ;
SubdivideTask = nullptr ;
}
if ( SubdividedMesh & & bNeedsDisplaced )
{
2021-04-06 17:59:10 -04:00
// force update of contrast curve
FDisplaceMeshOpFactory * DisplacerDownCast = static_cast < FDisplaceMeshOpFactory * > ( Displacer . Get ( ) ) ;
DisplacerDownCast - > SetAdjustmentCurve ( TextureMapProperties - > bApplyAdjustmentCurve ? TextureMapProperties - > AdjustmentCurve : nullptr ) ;
2019-10-25 10:19:13 -04:00
DisplaceTask = new FAsyncTaskExecuterWithAbort < TModelingOpTask < FDynamicMeshOperator > > ( Displacer - > MakeNewOperator ( ) ) ;
DisplaceTask - > StartBackgroundTask ( ) ;
bNeedsDisplaced = false ;
}
if ( DisplaceTask & & DisplaceTask - > IsDone ( ) )
{
TUniquePtr < FDynamicMesh3 > DisplacedMesh = DisplaceTask - > GetTask ( ) . ExtractOperator ( ) - > ExtractResult ( ) ;
delete DisplaceTask ;
DisplaceTask = nullptr ;
2019-12-19 18:07:47 -05:00
DynamicMeshComponent - > ClearOverrideRenderMaterial ( ) ;
2019-10-25 10:19:13 -04:00
DynamicMeshComponent - > GetMesh ( ) - > Copy ( * DisplacedMesh ) ;
DynamicMeshComponent - > NotifyMeshUpdated ( ) ;
GetToolManager ( ) - > PostInvalidation ( ) ;
}
2019-10-01 20:41:42 -04:00
}
2020-04-18 18:42:59 -04:00
void UDisplaceMeshTool : : UpdateActiveWeightMap ( )
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
if ( CommonProperties - > WeightMap = = FName ( TEXT ( " None " ) ) )
2020-04-18 18:42:59 -04:00
{
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
ActiveWeightMap = nullptr ;
}
else
{
2021-02-17 11:50:23 -04:00
TSharedPtr < FIndexedWeightMap , ESPMode : : ThreadSafe > NewWeightMap = MakeShared < FIndexedWeightMap , ESPMode : : ThreadSafe > ( ) ;
2021-06-11 22:42:32 -04:00
const FMeshDescription * MeshDescription = UE : : ToolTarget : : GetMeshDescription ( Target ) ;
UE : : WeightMaps : : GetVertexWeightMap ( MeshDescription , CommonProperties - > WeightMap , * NewWeightMap , 1.0f ) ;
ModelingTools: improve performance in DisplaceMeshTool.
- Avoid launching level-4 subdivision initially, which currently happens before subdivision level is clamped, and will compute at least one subdivision before cancelling (which can take a long time on a mesh with millions of triangles)
- support ProgressCancel inside SubdivideMesh() function, which can take significant compute/memory resources for a single call
- Pass VertexID through various weightmap functions, so we can directly sample displacement at vertex when it is known
- store weightmap values in per-vertex UVs before subdivision. These are linearly interpolated during subdivision, so output mesh has weightmap value stored in VertexUV.X, and can be directly sampled, instead of using AABBTree nearest-point query
- due to above, need to invalidate subdivision if weightmap changes.
- If weight map is "None", set null ActiveWeightMap, this is cleanly handled and avoids potentially-costly weightmap sampling if it is not needed
- fix issue with subdivision-steps clamping message being shown at Tool startup, only show after the user has explicitly change the value
#rb tyson.brochu
#rnx
#jira none
[CL 15133805 by Ryan Schmidt in ue5-main branch]
2021-01-18 22:33:08 -04:00
if ( CommonProperties - > bInvertWeightMap )
{
NewWeightMap - > InvertWeightMap ( ) ;
}
ActiveWeightMap = NewWeightMap ;
2020-04-18 18:42:59 -04:00
}
}
float UDisplaceMeshTool : : WeightMapQuery ( const FVector3d & Position , const FIndexedWeightMap & WeightMap ) const
{
double NearDistSqr ;
int32 NearTID = OriginalMeshSpatial . FindNearestTriangle ( Position , NearDistSqr ) ;
if ( NearTID < 0 )
{
return 1.0f ;
}
FDistPoint3Triangle3d Distance = TMeshQueries < FDynamicMesh3 > : : TriangleDistance ( OriginalMesh , NearTID , Position ) ;
FIndex3i Tri = OriginalMesh . GetTriangle ( NearTID ) ;
return WeightMap . GetInterpValue ( Tri , Distance . TriangleBaryCoords ) ;
}
# include "Tests/DisplaceMeshTool_Tests.inl"
2019-10-01 20:41:42 -04:00
# undef LOCTEXT_NAMESPACE