5 #include "DlgPickAssetPath.h" 6 #include "IAssetTools.h" 7 #include "AssetToolsModule.h" 8 #include "AssetRegistryModule.h" 9 #include "PhysicsEngine/PhysicsSettings.h" 10 #include "PhysicsEngine/BodySetup.h" 11 #include "IDetailCustomization.h" 12 #include "DetailCategoryBuilder.h" 13 #include "DetailWidgetRow.h" 16 #include "Input/SCheckBox.h" 17 #include "Input/SComboBox.h" 19 #define LOCTEXT_NAMESPACE "RuntimeMeshComponentDetails" 28 IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory(
"RuntimeMesh");
30 const FText ConvertToStaticMeshText = LOCTEXT(
"ConvertToStaticMesh",
"Create StaticMesh");
32 CookingModes = TArray<TSharedPtr<ERuntimeMeshCollisionCookingMode>>
34 MakeShared<ERuntimeMeshCollisionCookingMode>(ERuntimeMeshCollisionCookingMode::CollisionPerformance),
35 MakeShared<ERuntimeMeshCollisionCookingMode>(ERuntimeMeshCollisionCookingMode::CookingPerformance)
39 #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 18 46 RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText,
false)
49 SNullWidget::NullWidget
52 .VAlign(VAlign_Center)
56 .VAlign(VAlign_Center)
57 .ToolTipText(LOCTEXT(
"ConvertToStaticMeshTooltip",
"Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance."))
63 .Text(ConvertToStaticMeshText)
69 TArray<TSharedRef<IPropertyHandle>> AllProperties;
70 bool bSimpleProperties =
true;
71 bool bAdvancedProperties =
false;
73 RuntimeMeshCategory.GetDefaultProperties(AllProperties, bSimpleProperties, bAdvancedProperties);
74 for (
auto& Property : AllProperties)
76 RuntimeMeshCategory.AddProperty(Property);
80 TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
81 DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
84 for (TWeakObjectPtr<UObject>& Object : ObjectsBeingCustomized)
86 URuntimeMeshComponent* Component = Cast<URuntimeMeshComponent>(Object.Get());
87 if (ensure(Component))
89 if (Component->GetRuntimeMesh())
96 FText UseComplexAsSimpleName = LOCTEXT(
"RuntimeMeshProperty_UseComplexAsSimpleCollision",
"Use Complex as Simple Collision");
97 RuntimeMeshCategory.AddCustomRow(UseComplexAsSimpleName,
false)
101 .Text(UseComplexAsSimpleName)
102 .Font(IDetailLayoutBuilder::GetDetailFont())
105 .VAlign(VAlign_Center)
112 FText UseAsyncCollisionName = LOCTEXT(
"RuntimeMeshProperty_AsyncCollision",
"Use Async Cooking");
113 RuntimeMeshCategory.AddCustomRow(UseAsyncCollisionName,
false)
117 .Text(UseAsyncCollisionName)
118 .Font(IDetailLayoutBuilder::GetDetailFont())
121 .VAlign(VAlign_Center)
128 FText ShouldSerializeMeshDataName = LOCTEXT(
"RuntimeMeshProperty_ShouldSerialize",
"Should Serialize Mesh Data");
129 RuntimeMeshCategory.AddCustomRow(ShouldSerializeMeshDataName,
false)
133 .Text(ShouldSerializeMeshDataName)
134 .Font(IDetailLayoutBuilder::GetDetailFont())
137 .VAlign(VAlign_Center)
144 FText CollisionCookingModeName = LOCTEXT(
"RuntimeMeshProperty_CollisionCookingModeName",
"Collision Cooking Mode");
145 RuntimeMeshCategory.AddCustomRow(CollisionCookingModeName,
false)
149 .Text(CollisionCookingModeName)
150 .Font(IDetailLayoutBuilder::GetDetailFont())
153 .VAlign(VAlign_Center)
155 SNew(SComboBox<TSharedPtr<ERuntimeMeshCollisionCookingMode>>)
171 URuntimeMeshComponent* RuntimeMeshComp =
nullptr;
174 URuntimeMeshComponent* TestRuntimeComp = Cast<URuntimeMeshComponent>(Object.Get());
176 if (TestRuntimeComp !=
nullptr && !TestRuntimeComp->IsTemplate())
178 RuntimeMeshComp = TestRuntimeComp;
183 return RuntimeMeshComp;
189 ECheckBoxState State = ECheckBoxState::Undetermined;
193 if (Mesh && Mesh->IsValidLowLevel())
195 ECheckBoxState NewState = Mesh->IsCollisionUsingComplexAsSimple() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
197 if (State == ECheckBoxState::Undetermined || State == NewState)
203 State = ECheckBoxState::Undetermined;
216 if (Mesh && Mesh->IsValidLowLevel())
218 Mesh->SetCollisionUseComplexAsSimple(InCheckboxState == ECheckBoxState::Checked);
225 ECheckBoxState State = ECheckBoxState::Undetermined;
229 if (Mesh && Mesh->IsValidLowLevel())
231 ECheckBoxState NewState = Mesh->IsCollisionUsingAsyncCooking() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
233 if (State == ECheckBoxState::Undetermined || State == NewState)
239 State = ECheckBoxState::Undetermined;
252 if (Mesh && Mesh->IsValidLowLevel())
254 Mesh->SetCollisionUseAsyncCooking(InCheckboxState == ECheckBoxState::Checked);
261 ECheckBoxState State = ECheckBoxState::Undetermined;
265 if (Mesh && Mesh->IsValidLowLevel())
267 ECheckBoxState NewState = Mesh->ShouldSerializeMeshData() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
269 if (State == ECheckBoxState::Undetermined || State == NewState)
275 State = ECheckBoxState::Undetermined;
288 if (Mesh && Mesh->IsValidLowLevel())
290 Mesh->SetShouldSerializeMeshData(InCheckboxState == ECheckBoxState::Checked);
301 case ERuntimeMeshCollisionCookingMode::CollisionPerformance:
302 return LOCTEXT(
"ERuntimeMeshCollisionCookingMode_CollisionPerformance",
"Collision Performance");
303 case ERuntimeMeshCollisionCookingMode::CookingPerformance:
304 return LOCTEXT(
"ERuntimeMeshCollisionCookingMode_CookingPerformance",
"Cooking Performance");
307 return LOCTEXT(
"ERuntimeMeshCollisionCookingMode_Unknown",
"Unknown/Multiple Values");
318 bool bIsFirst =
true;
319 bool bIsAllSame =
true;
323 if (Mesh && Mesh->IsValidLowLevel())
326 if (bIsFirst || CurrentMode == NewMode)
328 CurrentMode = NewMode;
339 return bIsAllSame ? *
CookingModes.FindByPredicate([CurrentMode](
const TSharedPtr<ERuntimeMeshCollisionCookingMode>&
Mode) ->
bool 341 return *Mode == CurrentMode;
347 if (NewMode.IsValid())
351 if (Mesh && Mesh->IsValidLowLevel())
353 Mesh->SetCollisionMode(*NewMode);
369 if (RuntimeMeshComp !=
nullptr)
371 FString NewNameSuggestion = FString(TEXT(
"RuntimeMeshComp"));
372 FString PackageName = FString(TEXT(
"/Game/Meshes/")) + NewNameSuggestion;
374 FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>(
"AssetTools");
375 AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(
""), PackageName, Name);
377 TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
378 SNew(SDlgPickAssetPath)
379 .Title(LOCTEXT(
"ConvertToStaticMeshPickName",
"Choose New StaticMesh Location"))
380 .DefaultAssetPath(FText::FromString(PackageName));
382 if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
385 FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
386 FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
389 if (MeshName == NAME_None)
392 UserPackageName = PackageName;
400 TArray<FStaticMaterial> Materials;
402 bool bUseHighPrecisionTangents =
false;
403 bool bUseFullPrecisionUVs =
false;
405 const int32 NumSections = RuntimeMeshComp->GetNumSections();
406 int32 VertexBase = 0;
408 for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
410 auto MeshData = RuntimeMeshComp->GetSectionReadonly(SectionIdx);
411 check(MeshData.IsValid());
413 int32 NumUVs = MeshData->NumUVChannels();
419 RawMesh.WedgeTexCoords[
Index].SetNumZeroed(RawMesh.WedgeIndices.Num());
423 int32 NumVertices = MeshData->NumVertices();
426 RawMesh.VertexPositions.Add(MeshData->GetPosition(
Index));
430 int32 NumTris = MeshData->NumIndices();
433 int32 VertexIndex = MeshData->GetIndex(
Index);
434 RawMesh.WedgeIndices.Add(VertexIndex + VertexBase);
436 FVector TangentX = MeshData->GetTangent(VertexIndex);
437 FVector TangentZ = MeshData->GetNormal(VertexIndex);
439 FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * MeshData->GetNormal(VertexIndex).W;
440 RawMesh.WedgeTangentX.Add(TangentX);
441 RawMesh.WedgeTangentY.Add(TangentY);
442 RawMesh.WedgeTangentZ.Add(TangentZ);
445 for (int32 UVIndex = 0; UVIndex < NumUVs; UVIndex++)
447 RawMesh.WedgeTexCoords[UVIndex].Add(MeshData->GetUV(VertexIndex, UVIndex));
450 RawMesh.WedgeColors.Add(MeshData->GetColor(VertexIndex));
454 UMaterialInterface* Material = RuntimeMeshComp->GetSectionMaterial(SectionIdx);
455 int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
458 for (int32 TriIdx = 0; TriIdx < NumTris / 3; TriIdx++)
461 RawMesh.FaceMaterialIndices.Add(MaterialIndex);
463 RawMesh.FaceSmoothingMasks.Add(0);
467 VertexBase += NumVertices;
473 RawMesh.WedgeTexCoords[
Index].SetNumZeroed(RawMesh.WedgeIndices.Num());
477 if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
480 UPackage* Package = CreatePackage(*UserPackageName);
484 UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
485 StaticMesh->InitResources();
487 StaticMesh->LightingGuid = FGuid::NewGuid();
490 FStaticMeshSourceModel* SrcModel =
new (StaticMesh->GetSourceModels()) FStaticMeshSourceModel();
491 SrcModel->BuildSettings.bRecomputeNormals =
false;
492 SrcModel->BuildSettings.bRecomputeTangents =
false;
493 SrcModel->BuildSettings.bRemoveDegenerates =
false;
494 SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = bUseHighPrecisionTangents;
495 SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
496 SrcModel->BuildSettings.bGenerateLightmapUVs =
true;
497 SrcModel->BuildSettings.SrcLightmapIndex = 0;
498 SrcModel->BuildSettings.DstLightmapIndex = 1;
499 SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
502 StaticMesh->StaticMaterials = Materials;
503 int32 NumMaterials = StaticMesh->StaticMaterials.Num();
506 for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
508 FMeshSectionInfo Info = StaticMesh->GetSectionInfoMap().Get(0, SectionIdx);
509 Info.MaterialIndex = SectionIdx;
510 Info.bEnableCollision =
true;
511 StaticMesh->GetSectionInfoMap().Set(0, SectionIdx, Info);
515 StaticMesh->CreateBodySetup();
516 StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
519 StaticMesh->Build(
false);
522 StaticMesh->MarkPackageDirty();
524 StaticMesh->PostEditChange();
527 FAssetRegistryModule::AssetCreated(StaticMesh);
532 return FReply::Handled();
536 #undef LOCTEXT_NAMESPACE static TSharedRef< IDetailCustomization > MakeInstance()
class URuntimeMeshComponent * GetFirstSelectedRuntimeMeshComp() const
FText GetSelectedModeText() const
FText GetModeText(const TSharedPtr< ERuntimeMeshCollisionCookingMode > &Mode) const
FReply ClickedOnConvertToStaticMesh()
void ShouldSerializeMeshDataCheckedStateChanged(ECheckBoxState InCheckboxState)
void AsyncCollisionCheckedStateChanged(ECheckBoxState InCheckboxState)
TArray< TSharedPtr< ERuntimeMeshCollisionCookingMode > > CookingModes
virtual void CustomizeDetails(IDetailLayoutBuilder &DetailBuilder) override
bool ConvertToStaticMeshEnabled() const
ECheckBoxState UseComplexAsSimple() const
ECheckBoxState IsAsyncCollisionEnabled() const
static const textual_icon check
ECheckBoxState ShouldSerializeMeshData() const
void UseComplexAsSimpleCheckedStateChanged(ECheckBoxState InCheckboxState)
void CollisionCookingModeSelectionChanged(TSharedPtr< ERuntimeMeshCollisionCookingMode > NewMode, ESelectInfo::Type SelectionType)
TSharedRef< SWidget > MakeCollisionModeComboItemWidget(TSharedPtr< ERuntimeMeshCollisionCookingMode > Mode)
TSharedPtr< ERuntimeMeshCollisionCookingMode > GetCurrentCollisionCookingMode() const
TArray< URuntimeMesh * > RuntimeMeshesReferenced
ERuntimeMeshCollisionCookingMode
TArray< TWeakObjectPtr< UObject > > SelectedObjectsList