2021-05-28 02:09:37 -04:00
|
|
|
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
|
|
#include "ModelingToolTargetUtil.h"
|
|
|
|
|
|
|
|
|
|
#include "ToolTargets/ToolTarget.h"
|
2021-07-29 15:20:40 -04:00
|
|
|
#include "TargetInterfaces/DynamicMeshCommitter.h"
|
|
|
|
|
#include "TargetInterfaces/DynamicMeshProvider.h"
|
2021-05-28 02:09:37 -04:00
|
|
|
#include "TargetInterfaces/MaterialProvider.h"
|
|
|
|
|
#include "TargetInterfaces/MeshDescriptionCommitter.h"
|
|
|
|
|
#include "TargetInterfaces/MeshDescriptionProvider.h"
|
|
|
|
|
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
|
2021-09-17 16:47:10 -04:00
|
|
|
#include "TargetInterfaces/AssetBackedTarget.h"
|
2023-01-24 05:18:59 -05:00
|
|
|
#include "TargetInterfaces/SkeletalMeshBackedTarget.h"
|
|
|
|
|
#include "TargetInterfaces/StaticMeshBackedTarget.h"
|
2021-06-21 18:09:10 -04:00
|
|
|
#include "TargetInterfaces/DynamicMeshSource.h"
|
2021-11-26 16:37:25 -05:00
|
|
|
#include "TargetInterfaces/PhysicsDataSource.h"
|
2021-05-28 02:09:37 -04:00
|
|
|
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
#include "ModelingObjectsCreationAPI.h"
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
#include "Components/PrimitiveComponent.h"
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
#include "Components/StaticMeshComponent.h"
|
|
|
|
|
#include "GameFramework/Volume.h"
|
2021-06-12 14:30:22 -04:00
|
|
|
#include "Components/DynamicMeshComponent.h"
|
2021-05-28 02:09:37 -04:00
|
|
|
|
2021-06-11 22:42:32 -04:00
|
|
|
#include "MeshConversionOptions.h"
|
2021-05-28 02:09:37 -04:00
|
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
|
|
|
#include "DynamicMeshToMeshDescription.h"
|
2021-12-08 18:46:29 -05:00
|
|
|
#include "StaticMeshAttributes.h"
|
2021-05-28 02:09:37 -04:00
|
|
|
|
2022-11-30 10:38:46 -05:00
|
|
|
#include "DynamicMesh/NonManifoldMappingSupport.h"
|
|
|
|
|
|
2021-06-21 18:09:10 -04:00
|
|
|
#define LOCTEXT_NAMESPACE "ModelingToolTargetUtil"
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
using namespace UE::Geometry;
|
|
|
|
|
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
AActor* UE::ToolTarget::GetTargetActor(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
|
|
|
|
return TargetComponent->GetOwnerActor();
|
|
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
UPrimitiveComponent* UE::ToolTarget::GetTargetComponent(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
|
|
|
|
return TargetComponent->GetOwnerComponent();
|
|
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-17 16:47:10 -04:00
|
|
|
FString UE::ToolTarget::GetHumanReadableName(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IAssetBackedTarget* TargetAsset = Cast<IAssetBackedTarget>(Target);
|
|
|
|
|
if (TargetAsset)
|
|
|
|
|
{
|
|
|
|
|
return TargetAsset->GetSourceData()->GetName();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
|
|
|
|
return TargetComponent->GetOwnerComponent()->GetFullGroupName(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FString("");
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
bool UE::ToolTarget::HideSourceObject(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
|
|
|
|
TargetComponent->SetOwnerVisibility(false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UE::ToolTarget::ShowSourceObject(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
|
|
|
|
TargetComponent->SetOwnerVisibility(true);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-21 18:09:10 -04:00
|
|
|
bool UE::ToolTarget::SetSourceObjectVisible(UToolTarget* Target, bool bVisible)
|
|
|
|
|
{
|
|
|
|
|
if (bVisible)
|
|
|
|
|
{
|
|
|
|
|
return ShowSourceObject(Target);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return HideSourceObject(Target);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2022-01-29 14:37:53 -05:00
|
|
|
FTransform3d UE::ToolTarget::GetLocalToWorldTransform(UToolTarget* Target)
|
2021-05-28 02:09:37 -04:00
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* TargetComponent = Cast<IPrimitiveComponentBackedTarget>(Target);
|
|
|
|
|
if (TargetComponent)
|
|
|
|
|
{
|
2022-01-29 14:37:53 -05:00
|
|
|
return (FTransform3d)TargetComponent->GetWorldTransform();
|
2021-05-28 02:09:37 -04:00
|
|
|
}
|
|
|
|
|
ensure(false);
|
2022-01-29 14:37:53 -05:00
|
|
|
return FTransform3d();
|
2021-05-28 02:09:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FComponentMaterialSet UE::ToolTarget::GetMaterialSet(UToolTarget* Target, bool bPreferAssetMaterials)
|
|
|
|
|
{
|
|
|
|
|
FComponentMaterialSet MaterialSet;
|
|
|
|
|
IMaterialProvider* MaterialProvider = Cast<IMaterialProvider>(Target);
|
|
|
|
|
if (ensure(MaterialProvider))
|
|
|
|
|
{
|
|
|
|
|
MaterialProvider->GetMaterialSet(MaterialSet, bPreferAssetMaterials);
|
|
|
|
|
}
|
|
|
|
|
return MaterialSet;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-11 22:42:32 -04:00
|
|
|
bool UE::ToolTarget::CommitMaterialSetUpdate(
|
|
|
|
|
UToolTarget* Target,
|
|
|
|
|
const FComponentMaterialSet& UpdatedMaterials,
|
|
|
|
|
bool bApplyToAsset)
|
|
|
|
|
{
|
|
|
|
|
IMaterialProvider* MaterialProvider = Cast<IMaterialProvider>(Target);
|
|
|
|
|
if (MaterialProvider)
|
|
|
|
|
{
|
|
|
|
|
return MaterialProvider->CommitMaterialSetUpdate(UpdatedMaterials, bApplyToAsset);
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-17 11:09:56 -04:00
|
|
|
TArray<EMeshLODIdentifier> UE::ToolTarget::GetMeshDescriptionLODs(UToolTarget* Target, bool& bOutTargetSupportsLODs, bool bOnlyReturnDefaultLOD, bool bExcludeAutoGeneratedLODs)
|
|
|
|
|
{
|
|
|
|
|
if (!bOnlyReturnDefaultLOD)
|
|
|
|
|
{
|
|
|
|
|
if (IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target))
|
|
|
|
|
{
|
|
|
|
|
bOutTargetSupportsLODs = MeshDescriptionProvider->SupportsLODs();
|
|
|
|
|
return MeshDescriptionProvider->GetAvailableLODs(bExcludeAutoGeneratedLODs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
bOutTargetSupportsLODs = false;
|
|
|
|
|
TArray<EMeshLODIdentifier> DefaultArray{ EMeshLODIdentifier::Default };
|
|
|
|
|
return DefaultArray;
|
|
|
|
|
}
|
2021-06-11 22:42:32 -04:00
|
|
|
|
2023-05-18 13:52:35 -04:00
|
|
|
|
|
|
|
|
EMeshLODIdentifier UE::ToolTarget::GetTargetMeshDescriptionLOD(
|
|
|
|
|
UToolTarget* Target,
|
|
|
|
|
bool& bOutTargetSupportsLODs)
|
|
|
|
|
{
|
|
|
|
|
if (IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target))
|
|
|
|
|
{
|
|
|
|
|
bOutTargetSupportsLODs = MeshDescriptionProvider->SupportsLODs();
|
|
|
|
|
return MeshDescriptionProvider->GetMeshDescriptionLOD();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bOutTargetSupportsLODs = false;
|
|
|
|
|
return EMeshLODIdentifier::Default;
|
|
|
|
|
}
|
|
|
|
|
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
const FMeshDescription* UE::ToolTarget::GetMeshDescription(UToolTarget* Target, const FGetMeshParameters& GetMeshParams)
|
2021-05-28 02:09:37 -04:00
|
|
|
{
|
|
|
|
|
static FMeshDescription EmptyMeshDescription;
|
|
|
|
|
|
|
|
|
|
IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target);
|
|
|
|
|
if (MeshDescriptionProvider)
|
|
|
|
|
{
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
return MeshDescriptionProvider->GetMeshDescription(GetMeshParams);
|
2021-05-28 02:09:37 -04:00
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return &EmptyMeshDescription;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 14:53:07 -05:00
|
|
|
FMeshDescription UE::ToolTarget::GetEmptyMeshDescription(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
static FMeshDescription EmptyMeshDescription;
|
|
|
|
|
|
|
|
|
|
IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target);
|
|
|
|
|
if (MeshDescriptionProvider)
|
|
|
|
|
{
|
|
|
|
|
return MeshDescriptionProvider->GetEmptyMeshDescription();
|
|
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return EmptyMeshDescription;
|
|
|
|
|
}
|
2021-05-28 02:09:37 -04:00
|
|
|
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
FMeshDescription UE::ToolTarget::GetMeshDescriptionCopy(UToolTarget* Target, const FGetMeshParameters& GetMeshParams)
|
2021-06-15 17:05:40 -04:00
|
|
|
{
|
|
|
|
|
IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target);
|
|
|
|
|
if (MeshDescriptionProvider)
|
|
|
|
|
{
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
return MeshDescriptionProvider->GetMeshDescriptionCopy(GetMeshParams);
|
2021-06-15 17:05:40 -04:00
|
|
|
}
|
|
|
|
|
ensure(false);
|
|
|
|
|
return FMeshDescription();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-06-11 22:42:32 -04:00
|
|
|
FDynamicMesh3 UE::ToolTarget::GetDynamicMeshCopy(UToolTarget* Target, bool bWantMeshTangents)
|
2021-05-28 02:09:37 -04:00
|
|
|
{
|
2021-06-21 18:09:10 -04:00
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource)
|
|
|
|
|
{
|
|
|
|
|
UDynamicMesh* DynamicMesh = DynamicMeshSource->GetDynamicMeshContainer();
|
|
|
|
|
FDynamicMesh3 Mesh;
|
|
|
|
|
DynamicMesh->ProcessMesh([&](const FDynamicMesh3& ReadMesh) { Mesh = ReadMesh; });
|
|
|
|
|
return Mesh;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-02 12:00:30 -04:00
|
|
|
// TODO: Handle tangent computation. For now skip if tangents requested.
|
2021-07-29 15:20:40 -04:00
|
|
|
IDynamicMeshProvider* DynamicMeshProvider = Cast<IDynamicMeshProvider>(Target);
|
2021-08-02 12:00:30 -04:00
|
|
|
if (DynamicMeshProvider && !bWantMeshTangents)
|
2021-07-29 15:20:40 -04:00
|
|
|
{
|
2021-11-24 23:55:31 -05:00
|
|
|
return DynamicMeshProvider->GetDynamicMesh();
|
2021-07-29 15:20:40 -04:00
|
|
|
}
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
IMeshDescriptionProvider* MeshDescriptionProvider = Cast<IMeshDescriptionProvider>(Target);
|
|
|
|
|
FDynamicMesh3 Mesh(EMeshComponents::FaceGroups);
|
|
|
|
|
Mesh.EnableAttributes();
|
|
|
|
|
if (MeshDescriptionProvider)
|
|
|
|
|
{
|
|
|
|
|
FMeshDescriptionToDynamicMesh Converter;
|
2022-11-30 10:38:46 -05:00
|
|
|
Converter.bVIDsFromNonManifoldMeshDescriptionAttr= true;
|
2021-06-11 22:42:32 -04:00
|
|
|
if (bWantMeshTangents)
|
|
|
|
|
{
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
FGetMeshParameters GetMeshParams;
|
|
|
|
|
GetMeshParams.bWantMeshTangents = true;
|
|
|
|
|
FMeshDescription MeshDescriptionCopy = MeshDescriptionProvider->GetMeshDescriptionCopy(GetMeshParams);
|
2021-06-11 22:42:32 -04:00
|
|
|
Converter.Convert(&MeshDescriptionCopy, Mesh, bWantMeshTangents);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Converter.Convert(MeshDescriptionProvider->GetMeshDescription(), Mesh, bWantMeshTangents);
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
return Mesh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ensure(false);
|
|
|
|
|
return Mesh;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-17 11:09:56 -04:00
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdate(UToolTarget* Target, const FMeshDescription* UpdatedMesh, const FComponentMaterialSet* UpdatedMaterials, const FCommitMeshParameters& CommitParams)
|
2021-06-08 03:51:23 -04:00
|
|
|
{
|
2021-11-23 11:37:51 -05:00
|
|
|
if (!ensure(UpdatedMesh != nullptr))
|
|
|
|
|
{
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-08 03:51:23 -04:00
|
|
|
IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target);
|
|
|
|
|
if (!ensure(MeshDescriptionCommitter))
|
|
|
|
|
{
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (UpdatedMaterials != nullptr)
|
|
|
|
|
{
|
2021-06-11 22:42:32 -04:00
|
|
|
CommitMaterialSetUpdate(Target, *UpdatedMaterials, true);
|
2021-06-08 03:51:23 -04:00
|
|
|
}
|
|
|
|
|
|
2023-04-17 11:09:56 -04:00
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(*UpdatedMesh, CommitParams);
|
2021-12-06 12:42:19 -05:00
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
2021-06-08 03:51:23 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-17 11:09:56 -04:00
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdate(UToolTarget* Target, FMeshDescription&& UpdatedMesh, const FCommitMeshParameters& CommitParams)
|
2021-12-06 12:42:19 -05:00
|
|
|
{
|
|
|
|
|
if (IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target))
|
|
|
|
|
{
|
2023-04-17 11:09:56 -04:00
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(UpdatedMesh), CommitParams);
|
2021-12-06 12:42:19 -05:00
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitMeshDescriptionUpdateViaDynamicMesh(
|
|
|
|
|
UToolTarget* Target,
|
|
|
|
|
const UE::Geometry::FDynamicMesh3& UpdatedMesh,
|
2023-04-17 11:09:56 -04:00
|
|
|
bool bHaveModifiedTopology,
|
|
|
|
|
const FCommitMeshParameters& CommitParams)
|
2021-12-06 12:42:19 -05:00
|
|
|
{
|
|
|
|
|
IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target);
|
|
|
|
|
if (!ensure(MeshDescriptionCommitter))
|
|
|
|
|
{
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-12 14:53:07 -05:00
|
|
|
FDynamicMeshToMeshDescription Converter;
|
2021-12-06 12:42:19 -05:00
|
|
|
FMeshDescription ConvertedMesh;
|
|
|
|
|
if (bHaveModifiedTopology)
|
|
|
|
|
{
|
2022-01-12 14:53:07 -05:00
|
|
|
ConvertedMesh = UE::ToolTarget::GetEmptyMeshDescription(Target);
|
2021-12-06 12:42:19 -05:00
|
|
|
Converter.Convert(&UpdatedMesh, ConvertedMesh);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2022-01-12 14:53:07 -05:00
|
|
|
// FGetMeshParameters should never need to be initialized to anything other than the
|
|
|
|
|
// default because we are either (1) ignoring the tangents, in which case, we don't
|
|
|
|
|
// want to update them anyway (2) replacing them, in which case, we don't need to
|
|
|
|
|
// compute them
|
|
|
|
|
|
ModelingTools: Add support for reading and writing to a specific LOD via IMeshDescriptionProvider/Committer. Refactor various other aspects of MeshDescription access. Update MeshTransferTool to be able to transfer from and to specific LODs.
Add new MeshTargetInterfaceTypes.h file, move EStaticMeshEditingLOD there and rename to EMeshLODIdentifier. Add FGetMeshParameters and FCommitMeshParameters types.
IMeshDescriptionProvider::GetMeshDescription() now takes FGetMeshParameters to optionally specify LOD.
Added IMeshDescriptionProvider::GetMeshDescriptionCopy() function, to handle copy-case.
removed IMeshDescriptionProvider::CalculateAutoGeneratedAttributes(). This was only being used to force Tangents computation, which can now be done via GetMeshDescriptionCopy() and FGetMeshParameters.bWantMeshTangents option
IMeshDescriptionCommitter commit functions now take a FCommitMeshParameters to optionally specify target LOD.
StaticMeshComponentToolTarget::GetMeshDescriptionCopy() added, optionally computes auto-generated MeshDescription attributes on the copy
StaticMesh(Component)ToolTarget updated to support FCommitMeshParameters target-LOD.
SkeletalMesh, Volume, and DynamicMesh ToolTargets updated for new APIs but do not currently support any of the new LOD parameters. These should never be called w/ LOD parameters in the current codebase, ensures added to catch any errors (they would be non-fatal).
UE::ToolTarget::GetMeshDescription() and GetMeshDescriptionCopy() in now take FGetMeshParameters argument.
Removed bWantMeshTangents param to GetMeshDescriptionCopy(), now done via FGetMeshParameters. Updated call sites.
TransferMeshesTool now supports specifying read and write LOD (numbered or HiRes) for StaticMeshComponent source/target, via above functions.
TransferMeshesTool can now also target a new LOD in a StaticMeshComponent, index is 1 larger than current maximum source LOD.
#rb lonnie.li, rinat.abdrashitov
#rnx
#jira none
#preflight 61b8d56b2e65a1df046aa5e1
#ROBOMERGE-AUTHOR: ryan.schmidt
#ROBOMERGE-SOURCE: CL 18461686 in //UE5/Release-5.0/... via CL 18461725
#ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v899-18417669)
[CL 18461778 by ryan schmidt in ue5-release-engine-test branch]
2021-12-14 18:40:01 -05:00
|
|
|
ConvertedMesh = UE::ToolTarget::GetMeshDescriptionCopy(Target, FGetMeshParameters());
|
2021-12-06 12:42:19 -05:00
|
|
|
Converter.Update(&UpdatedMesh, ConvertedMesh);
|
|
|
|
|
}
|
2022-01-12 14:53:07 -05:00
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
|
2023-04-17 11:09:56 -04:00
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(ConvertedMesh), CommitParams);
|
2021-12-06 12:42:19 -05:00
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-24 23:55:31 -05:00
|
|
|
void UE::ToolTarget::Internal::CommitDynamicMeshViaIPersistentDynamicMeshSource(
|
|
|
|
|
IPersistentDynamicMeshSource& DynamicMeshSource,
|
|
|
|
|
const FDynamicMesh3& UpdatedMesh, bool bHaveModifiedTopology)
|
|
|
|
|
{
|
|
|
|
|
UDynamicMesh* DynamicMesh = DynamicMeshSource.GetDynamicMeshContainer();
|
|
|
|
|
TUniquePtr<FDynamicMesh3> CurrentMesh = DynamicMesh->ExtractMesh();
|
|
|
|
|
TSharedPtr<FDynamicMesh3> CurrentMeshShared(CurrentMesh.Release());
|
2021-05-28 02:09:37 -04:00
|
|
|
|
2021-11-24 23:55:31 -05:00
|
|
|
DynamicMesh->EditMesh([&](FDynamicMesh3& EditMesh)
|
|
|
|
|
{
|
|
|
|
|
EditMesh.CompactCopy(UpdatedMesh);
|
2022-11-30 10:38:46 -05:00
|
|
|
FNonManifoldMappingSupport::RemoveAllNonManifoldMappingData(EditMesh);
|
2021-11-24 23:55:31 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
TSharedPtr<FDynamicMesh3> NewMeshShared = MakeShared<FDynamicMesh3>();
|
|
|
|
|
DynamicMesh->ProcessMesh([&](const FDynamicMesh3& ReadMesh) { *NewMeshShared = ReadMesh; });
|
|
|
|
|
|
|
|
|
|
TUniquePtr<FMeshReplacementChange> ReplaceChange = MakeUnique<FMeshReplacementChange>(CurrentMeshShared, NewMeshShared);
|
|
|
|
|
|
|
|
|
|
DynamicMeshSource.CommitDynamicMeshChange(MoveTemp(ReplaceChange), LOCTEXT("CommitDynamicMeshUpdate_MeshSource", "Update Mesh"));
|
|
|
|
|
|
|
|
|
|
// todo support bModifiedTopology flag?
|
|
|
|
|
}
|
2021-06-11 22:42:32 -04:00
|
|
|
|
|
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshUpdate(
|
|
|
|
|
UToolTarget* Target, const FDynamicMesh3& UpdatedMesh,
|
|
|
|
|
bool bHaveModifiedTopology,
|
|
|
|
|
const FConversionToMeshDescriptionOptions& ConversionOptions,
|
|
|
|
|
const FComponentMaterialSet* UpdatedMaterials)
|
|
|
|
|
{
|
2021-06-23 22:14:55 -04:00
|
|
|
if (UpdatedMaterials != nullptr)
|
|
|
|
|
{
|
|
|
|
|
CommitMaterialSetUpdate(Target, *UpdatedMaterials, true);
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-21 18:09:10 -04:00
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource)
|
|
|
|
|
{
|
2021-11-24 23:55:31 -05:00
|
|
|
Internal::CommitDynamicMeshViaIPersistentDynamicMeshSource(
|
|
|
|
|
*DynamicMeshSource, UpdatedMesh, bHaveModifiedTopology);
|
2021-06-21 18:09:10 -04:00
|
|
|
|
|
|
|
|
return EDynamicMeshUpdateResult::Ok;
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-29 15:20:40 -04:00
|
|
|
IDynamicMeshCommitter* DynamicMeshCommitter = Cast<IDynamicMeshCommitter>(Target);
|
|
|
|
|
if (DynamicMeshCommitter)
|
|
|
|
|
{
|
|
|
|
|
IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo;
|
|
|
|
|
CommitInfo.bTopologyChanged = bHaveModifiedTopology;
|
|
|
|
|
CommitInfo.bPolygroupsChanged = ConversionOptions.bSetPolyGroups;
|
|
|
|
|
CommitInfo.bPositionsChanged = ConversionOptions.bUpdatePositions;
|
|
|
|
|
CommitInfo.bNormalsChanged = ConversionOptions.bUpdateNormals;
|
|
|
|
|
CommitInfo.bTangentsChanged = ConversionOptions.bUpdateTangents;
|
|
|
|
|
CommitInfo.bUVsChanged = ConversionOptions.bUpdateUVs;
|
|
|
|
|
CommitInfo.bVertexColorsChanged = ConversionOptions.bUpdateVtxColors;
|
2021-08-11 20:29:24 -04:00
|
|
|
CommitInfo.bTransformVertexColorsSRGBToLinear = ConversionOptions.bTransformVtxColorsSRGBToLinear;
|
2021-07-29 15:20:40 -04:00
|
|
|
|
|
|
|
|
DynamicMeshCommitter->CommitDynamicMesh(UpdatedMesh, CommitInfo);
|
|
|
|
|
|
|
|
|
|
return EDynamicMeshUpdateResult::Ok;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-11 22:42:32 -04:00
|
|
|
IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target);
|
|
|
|
|
if (MeshDescriptionCommitter)
|
|
|
|
|
{
|
2021-12-06 12:42:19 -05:00
|
|
|
FMeshDescription ConvertedMesh;
|
|
|
|
|
FDynamicMeshToMeshDescription Converter(ConversionOptions);
|
|
|
|
|
if (!bHaveModifiedTopology)
|
2021-06-11 22:42:32 -04:00
|
|
|
{
|
2021-12-06 12:42:19 -05:00
|
|
|
Converter.UpdateUsingConversionOptions(&UpdatedMesh, ConvertedMesh);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Converter.Convert(&UpdatedMesh, ConvertedMesh);
|
|
|
|
|
}
|
2021-06-11 22:42:32 -04:00
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(ConvertedMesh));
|
|
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
2021-06-11 22:42:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ensure(false);
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshUVUpdate(UToolTarget* Target, const UE::Geometry::FDynamicMesh3* UpdatedMesh)
|
|
|
|
|
{
|
2021-06-21 18:09:10 -04:00
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource)
|
|
|
|
|
{
|
2021-10-13 12:32:12 -04:00
|
|
|
// just do a full mesh update for now
|
2021-06-21 18:09:10 -04:00
|
|
|
// todo actually only update UVs?
|
2021-10-13 12:32:12 -04:00
|
|
|
return CommitDynamicMeshUpdate(Target, *UpdatedMesh, true);
|
2021-06-21 18:09:10 -04:00
|
|
|
}
|
|
|
|
|
|
2021-09-17 16:47:10 -04:00
|
|
|
IDynamicMeshCommitter* DynamicMeshCommitter = Cast<IDynamicMeshCommitter>(Target);
|
|
|
|
|
if (DynamicMeshCommitter)
|
|
|
|
|
{
|
|
|
|
|
IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo(false);
|
|
|
|
|
CommitInfo.bUVsChanged = true;
|
|
|
|
|
|
|
|
|
|
DynamicMeshCommitter->CommitDynamicMesh(*UpdatedMesh, CommitInfo);
|
|
|
|
|
|
|
|
|
|
return EDynamicMeshUpdateResult::Ok;
|
|
|
|
|
}
|
2021-06-21 18:09:10 -04:00
|
|
|
|
2021-05-28 02:09:37 -04:00
|
|
|
IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target);
|
2021-06-08 03:51:23 -04:00
|
|
|
if (!ensure(MeshDescriptionCommitter))
|
|
|
|
|
{
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
2021-05-28 02:09:37 -04:00
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
FMeshDescription NewMeshDescription = UE::ToolTarget::GetMeshDescriptionCopy(Target);
|
|
|
|
|
bool bVerticesOnly = false;
|
|
|
|
|
bool bAttributesOnly = true;
|
|
|
|
|
if (FDynamicMeshToMeshDescription::HaveMatchingElementCounts(UpdatedMesh, &NewMeshDescription, bVerticesOnly, bAttributesOnly))
|
2021-05-28 02:09:37 -04:00
|
|
|
{
|
2021-12-06 12:42:19 -05:00
|
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
|
Converter.UpdateAttributes(UpdatedMesh, NewMeshDescription, false, false, true/*update uvs*/);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// must have been duplicate tris in the mesh description; we can't count on 1-to-1 mapping of TriangleIDs. Just convert
|
|
|
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
|
Converter.Convert(UpdatedMesh, NewMeshDescription);
|
|
|
|
|
}
|
2021-05-28 02:09:37 -04:00
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(NewMeshDescription));
|
|
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-07-13 13:40:52 -04:00
|
|
|
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
|
2021-12-06 12:42:19 -05:00
|
|
|
|
|
|
|
|
UE::ToolTarget::EDynamicMeshUpdateResult UE::ToolTarget::CommitDynamicMeshNormalsUpdate(
|
|
|
|
|
UToolTarget* Target,
|
|
|
|
|
const UE::Geometry::FDynamicMesh3* UpdatedMesh,
|
|
|
|
|
bool bUpdateTangents)
|
|
|
|
|
{
|
|
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource)
|
|
|
|
|
{
|
|
|
|
|
// just do a full mesh update for now
|
|
|
|
|
return CommitDynamicMeshUpdate(Target, *UpdatedMesh, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDynamicMeshCommitter* DynamicMeshCommitter = Cast<IDynamicMeshCommitter>(Target);
|
|
|
|
|
if (DynamicMeshCommitter)
|
|
|
|
|
{
|
|
|
|
|
IDynamicMeshCommitter::FDynamicMeshCommitInfo CommitInfo(false);
|
|
|
|
|
CommitInfo.bNormalsChanged = true;
|
|
|
|
|
CommitInfo.bTangentsChanged = bUpdateTangents;
|
|
|
|
|
DynamicMeshCommitter->CommitDynamicMesh(*UpdatedMesh, CommitInfo);
|
|
|
|
|
return EDynamicMeshUpdateResult::Ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMeshDescriptionCommitter* MeshDescriptionCommitter = Cast<IMeshDescriptionCommitter>(Target);
|
|
|
|
|
if (!ensure(MeshDescriptionCommitter))
|
|
|
|
|
{
|
|
|
|
|
return EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FMeshDescription NewMeshDescription = UE::ToolTarget::GetMeshDescriptionCopy(Target);
|
|
|
|
|
bool bVerticesOnly = false;
|
|
|
|
|
bool bAttributesOnly = true;
|
|
|
|
|
if (FDynamicMeshToMeshDescription::HaveMatchingElementCounts(UpdatedMesh, &NewMeshDescription, bVerticesOnly, bAttributesOnly))
|
|
|
|
|
{
|
|
|
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
|
Converter.UpdateAttributes(UpdatedMesh, NewMeshDescription, true, bUpdateTangents, false);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// must have been duplicate tris in the mesh description; we can't count on 1-to-1 mapping of TriangleIDs. Just convert
|
|
|
|
|
FDynamicMeshToMeshDescription Converter;
|
|
|
|
|
Converter.Convert(UpdatedMesh, NewMeshDescription);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool bOK = MeshDescriptionCommitter->CommitMeshDescription(MoveTemp(NewMeshDescription));
|
|
|
|
|
return (bOK) ? EDynamicMeshUpdateResult::Ok : EDynamicMeshUpdateResult::Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2022-12-14 17:43:16 -05:00
|
|
|
bool UE::ToolTarget::SupportsIncrementalMeshChanges(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource)
|
|
|
|
|
{
|
|
|
|
|
return DynamicMeshSource->HasDynamicMeshComponent();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool UE::ToolTarget::ApplyIncrementalMeshEditChange(
|
|
|
|
|
UToolTarget* Target,
|
|
|
|
|
TFunctionRef<bool(FDynamicMesh3& EditMesh, UObject* TransactionTarget)> MeshEditChangeFunc )
|
|
|
|
|
{
|
|
|
|
|
IPersistentDynamicMeshSource* DynamicMeshSource = Cast<IPersistentDynamicMeshSource>(Target);
|
|
|
|
|
if (DynamicMeshSource && DynamicMeshSource->HasDynamicMeshComponent())
|
|
|
|
|
{
|
|
|
|
|
bool bOK;
|
|
|
|
|
UDynamicMeshComponent* Component = DynamicMeshSource->GetDynamicMeshComponent();
|
|
|
|
|
Component->EditMesh([&](FDynamicMesh3& EditMesh)
|
|
|
|
|
{
|
|
|
|
|
bOK = MeshEditChangeFunc(EditMesh, Component);
|
|
|
|
|
});
|
|
|
|
|
return bOK;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-12-06 12:42:19 -05:00
|
|
|
|
|
|
|
|
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
bool UE::ToolTarget::ConfigureCreateMeshObjectParams(UToolTarget* SourceTarget, FCreateMeshObjectParams& DerivedParamsOut)
|
|
|
|
|
{
|
|
|
|
|
IPrimitiveComponentBackedTarget* ComponentTarget = Cast<IPrimitiveComponentBackedTarget>(SourceTarget);
|
|
|
|
|
if (ComponentTarget)
|
|
|
|
|
{
|
|
|
|
|
if (Cast<UStaticMeshComponent>(ComponentTarget->GetOwnerComponent()) != nullptr)
|
|
|
|
|
{
|
|
|
|
|
DerivedParamsOut.TypeHint = ECreateObjectTypeHint::StaticMesh;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-12 14:30:22 -04:00
|
|
|
if (Cast<UDynamicMeshComponent>(ComponentTarget->GetOwnerComponent()) != nullptr)
|
2021-06-08 03:51:23 -04:00
|
|
|
{
|
|
|
|
|
DerivedParamsOut.TypeHint = ECreateObjectTypeHint::DynamicMeshActor;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
ModelingTools: add support for creating Volumes directly from DrawPolygon, DrawRevolve, DrawPolyPath, and AddPrimitive, CombineMeshes, CutMeshWithMesh, PlaneCut, BaseCreateFromSelected Tools. Improve support for Editing volumes, eg handling mesh/volume interactions, and add configurable auto-simplification for volumes to avoid painful Editor hangs.
- Move ToolTarget implementations, DynamicMeshToVolume to ModelingComponentsEditorOnly module
- move VolumeToDynamicMesh, DynamicMeshProvider/Commiter interfaces to ModelingComponents module
- Add UCreateMeshObjectTypeProperties property set to expose mesh/volume options
- Add FCreateMeshObjectParams::TypeHintClass to allow AVolume type (or other UClass hints) to be passed to creation APIs
- Add UE::ToolTarget::ConfigureCreateMeshObjectParams() util function in ModelingToolTargetUtil, tries to determine output type in a FCreateMeshObjectParams based on input ToolTarget
- Add UEditorModelingObjectsCreationAPI::CreateVolume() implementation
- Add UEditorModelingObjectsCreationAPI::FilterMaterials() that strips out any internal materials and replaces with WorldGridMaterial. This occurs when (eg) subtracting a Volume from a StaticMesh, because the temporary volume mesh gets assigned internal materials, but the Tools don't know this. Use in EditorModelingObjectsCreationAPI when creating new objects. UStaticMeshComponentToolTarget also does this filtering in ::CommitMaterialSetUpdate().
- Add ::ComponentTypeSupportsCollision() function to ComponentCollisionUtil, use to avoid checks/ensures for Volume targets
- Add support for automatic mesh simplification in DynamicMeshToVolume. Add CVar to VolumeDynamicMeshToolTarget.h to control max triangle count (default 500). Apply auto-simplify when creating or updating an AVolume. This prevents the Editor from blocking for long periods on meshes that are too high-res for volumes (even 500 is quite high).
- DynamicMeshToVolume now emits polygroup-faces that contain holes (ie multiple boundary loops) as a set of triangles, rather than emitting separate overlapping faces for each boundary loop
#rb none
#rnx
#jira none
#preflight 60ba50632c42ea0001cb54c5
[CL 16561742 by Ryan Schmidt in ue5-main branch]
2021-06-04 16:04:03 -04:00
|
|
|
AVolume* VolumeActor = Cast<AVolume>(ComponentTarget->GetOwnerActor());
|
|
|
|
|
if (VolumeActor != nullptr)
|
|
|
|
|
{
|
|
|
|
|
DerivedParamsOut.TypeHint = ECreateObjectTypeHint::Volume;
|
|
|
|
|
DerivedParamsOut.TypeHintClass = VolumeActor->GetClass();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2021-06-21 18:09:10 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2021-11-26 16:37:25 -05:00
|
|
|
UBodySetup* UE::ToolTarget::GetPhysicsBodySetup(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
if (IPhysicsDataSource* PhysicsSource = Cast<IPhysicsDataSource>(Target))
|
|
|
|
|
{
|
|
|
|
|
return PhysicsSource->GetBodySetup();
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IInterface_CollisionDataProvider* UE::ToolTarget::GetPhysicsCollisionDataProvider(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
if (IPhysicsDataSource* PhysicsSource = Cast<IPhysicsDataSource>(Target))
|
|
|
|
|
{
|
|
|
|
|
return PhysicsSource->GetComplexCollisionProvider();
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-24 05:18:59 -05:00
|
|
|
UStaticMesh* UE::ToolTarget::GetStaticMeshFromTargetIfAvailable(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
IStaticMeshBackedTarget* TargetStaticMeshTarget = Cast<IStaticMeshBackedTarget>(Target);
|
|
|
|
|
UStaticMesh* TargetStaticMesh = TargetStaticMeshTarget ? TargetStaticMeshTarget->GetStaticMesh() : nullptr;
|
|
|
|
|
return TargetStaticMesh;
|
|
|
|
|
}
|
2021-11-26 16:37:25 -05:00
|
|
|
|
2023-01-24 05:18:59 -05:00
|
|
|
USkeletalMesh* UE::ToolTarget::GetSkeletalMeshFromTargetIfAvailable(UToolTarget* Target)
|
|
|
|
|
{
|
|
|
|
|
ISkeletalMeshBackedTarget* TargetSkeletalMeshTarget = Cast<ISkeletalMeshBackedTarget>(Target);
|
|
|
|
|
USkeletalMesh* TargetSkeletalMesh = TargetSkeletalMeshTarget ? TargetSkeletalMeshTarget->GetSkeletalMesh() : nullptr;
|
|
|
|
|
return TargetSkeletalMesh;
|
|
|
|
|
}
|
2021-11-26 16:37:25 -05:00
|
|
|
|
|
|
|
|
|
2021-06-21 18:09:10 -04:00
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|