RuntimeMeshComponentDetails.cpp
Go to the documentation of this file.
1 // Copyright 2016-2018 Chris Conway (Koderz). All Rights Reserved.
2 
4 #include "RuntimeMeshComponent.h"
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"
14 #include "RawMesh.h"
15 
16 #include "Input/SCheckBox.h"
17 #include "Input/SComboBox.h"
18 
19 #define LOCTEXT_NAMESPACE "RuntimeMeshComponentDetails"
20 
21 TSharedRef<IDetailCustomization> FRuntimeMeshComponentDetails::MakeInstance()
22 {
23  return MakeShareable(new FRuntimeMeshComponentDetails);
24 }
25 
26 void FRuntimeMeshComponentDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
27 {
28  IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory("RuntimeMesh");
29 
30  const FText ConvertToStaticMeshText = LOCTEXT("ConvertToStaticMesh", "Create StaticMesh");
31 
32  CookingModes = TArray<TSharedPtr<ERuntimeMeshCollisionCookingMode>>
33  {
34  MakeShared<ERuntimeMeshCollisionCookingMode>(ERuntimeMeshCollisionCookingMode::CollisionPerformance),
35  MakeShared<ERuntimeMeshCollisionCookingMode>(ERuntimeMeshCollisionCookingMode::CookingPerformance)
36  };
37 
38  // Cache set of selected things
39 #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 18
40  SelectedObjectsList = DetailBuilder.GetDetailsView()->GetSelectedObjects();
41 #else
42  SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects();
43 #endif
44 
45  // Add the Create Static Mesh button
46  RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText, false)
47  .NameContent()
48  [
49  SNullWidget::NullWidget
50  ]
51  .ValueContent()
52  .VAlign(VAlign_Center)
53  .MaxDesiredWidth(250)
54  [
55  SNew(SButton)
56  .VAlign(VAlign_Center)
57  .ToolTipText(LOCTEXT("ConvertToStaticMeshTooltip", "Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance."))
60  .Content()
61  [
62  SNew(STextBlock)
63  .Text(ConvertToStaticMeshText)
64  ]
65  ];
66 
67  {
68  // Add all the default properties
69  TArray<TSharedRef<IPropertyHandle>> AllProperties;
70  bool bSimpleProperties = true;
71  bool bAdvancedProperties = false;
72  // Add all properties in the category in order
73  RuntimeMeshCategory.GetDefaultProperties(AllProperties, bSimpleProperties, bAdvancedProperties);
74  for (auto& Property : AllProperties)
75  {
76  RuntimeMeshCategory.AddProperty(Property);
77  }
78  }
79 
80  TArray<TWeakObjectPtr<UObject>> ObjectsBeingCustomized;
81  DetailBuilder.GetObjectsBeingCustomized(ObjectsBeingCustomized);
82 
84  for (TWeakObjectPtr<UObject>& Object : ObjectsBeingCustomized)
85  {
86  URuntimeMeshComponent* Component = Cast<URuntimeMeshComponent>(Object.Get());
87  if (ensure(Component))
88  {
89  if (Component->GetRuntimeMesh())
90  {
91  RuntimeMeshesReferenced.AddUnique(Component->GetRuntimeMesh());
92  }
93  }
94  }
95 
96  FText UseComplexAsSimpleName = LOCTEXT("RuntimeMeshProperty_UseComplexAsSimpleCollision", "Use Complex as Simple Collision");
97  RuntimeMeshCategory.AddCustomRow(UseComplexAsSimpleName, false)
98  .NameContent()
99  [
100  SNew(STextBlock)
101  .Text(UseComplexAsSimpleName)
102  .Font(IDetailLayoutBuilder::GetDetailFont())
103  ]
104  .ValueContent()
105  .VAlign(VAlign_Center)
106  [
107  SNew(SCheckBox)
110  ];
111 
112  FText UseAsyncCollisionName = LOCTEXT("RuntimeMeshProperty_AsyncCollision", "Use Async Cooking");
113  RuntimeMeshCategory.AddCustomRow(UseAsyncCollisionName, false)
114  .NameContent()
115  [
116  SNew(STextBlock)
117  .Text(UseAsyncCollisionName)
118  .Font(IDetailLayoutBuilder::GetDetailFont())
119  ]
120  .ValueContent()
121  .VAlign(VAlign_Center)
122  [
123  SNew(SCheckBox)
126  ];
127 
128  FText ShouldSerializeMeshDataName = LOCTEXT("RuntimeMeshProperty_ShouldSerialize", "Should Serialize Mesh Data");
129  RuntimeMeshCategory.AddCustomRow(ShouldSerializeMeshDataName, false)
130  .NameContent()
131  [
132  SNew(STextBlock)
133  .Text(ShouldSerializeMeshDataName)
134  .Font(IDetailLayoutBuilder::GetDetailFont())
135  ]
136  .ValueContent()
137  .VAlign(VAlign_Center)
138  [
139  SNew(SCheckBox)
142  ];
143 
144  FText CollisionCookingModeName = LOCTEXT("RuntimeMeshProperty_CollisionCookingModeName", "Collision Cooking Mode");
145  RuntimeMeshCategory.AddCustomRow(CollisionCookingModeName, false)
146  .NameContent()
147  [
148  SNew(STextBlock)
149  .Text(CollisionCookingModeName)
150  .Font(IDetailLayoutBuilder::GetDetailFont())
151  ]
152  .ValueContent()
153  .VAlign(VAlign_Center)
154  [
155  SNew(SComboBox<TSharedPtr<ERuntimeMeshCollisionCookingMode>>)
156  .OptionsSource(&CookingModes)
159  .InitiallySelectedItem(GetCurrentCollisionCookingMode())
160  [
161  SNew(STextBlock).Text(this, &FRuntimeMeshComponentDetails::GetSelectedModeText)
162  ]
163  ];
164 
165 
166 }
167 
169 {
170  // Find first selected valid RuntimeMeshComp
171  URuntimeMeshComponent* RuntimeMeshComp = nullptr;
172  for (const TWeakObjectPtr<UObject>& Object : SelectedObjectsList)
173  {
174  URuntimeMeshComponent* TestRuntimeComp = Cast<URuntimeMeshComponent>(Object.Get());
175  // See if this one is good
176  if (TestRuntimeComp != nullptr && !TestRuntimeComp->IsTemplate())
177  {
178  RuntimeMeshComp = TestRuntimeComp;
179  break;
180  }
181  }
182 
183  return RuntimeMeshComp;
184 }
185 
186 
188 {
189  ECheckBoxState State = ECheckBoxState::Undetermined;
190 
192  {
193  if (Mesh && Mesh->IsValidLowLevel())
194  {
195  ECheckBoxState NewState = Mesh->IsCollisionUsingComplexAsSimple() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
196 
197  if (State == ECheckBoxState::Undetermined || State == NewState)
198  {
199  State = NewState;
200  }
201  else
202  {
203  State = ECheckBoxState::Undetermined;
204  break;
205  }
206  }
207  }
208 
209  return State;
210 }
211 
213 {
215  {
216  if (Mesh && Mesh->IsValidLowLevel())
217  {
218  Mesh->SetCollisionUseComplexAsSimple(InCheckboxState == ECheckBoxState::Checked);
219  }
220  }
221 }
222 
224 {
225  ECheckBoxState State = ECheckBoxState::Undetermined;
226 
228  {
229  if (Mesh && Mesh->IsValidLowLevel())
230  {
231  ECheckBoxState NewState = Mesh->IsCollisionUsingAsyncCooking() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
232 
233  if (State == ECheckBoxState::Undetermined || State == NewState)
234  {
235  State = NewState;
236  }
237  else
238  {
239  State = ECheckBoxState::Undetermined;
240  break;
241  }
242  }
243  }
244 
245  return State;
246 }
247 
249 {
251  {
252  if (Mesh && Mesh->IsValidLowLevel())
253  {
254  Mesh->SetCollisionUseAsyncCooking(InCheckboxState == ECheckBoxState::Checked);
255  }
256  }
257 }
258 
260 {
261  ECheckBoxState State = ECheckBoxState::Undetermined;
262 
264  {
265  if (Mesh && Mesh->IsValidLowLevel())
266  {
267  ECheckBoxState NewState = Mesh->ShouldSerializeMeshData() ? ECheckBoxState::Checked : ECheckBoxState::Unchecked;
268 
269  if (State == ECheckBoxState::Undetermined || State == NewState)
270  {
271  State = NewState;
272  }
273  else
274  {
275  State = ECheckBoxState::Undetermined;
276  break;
277  }
278  }
279  }
280 
281  return State;
282 }
283 
285 {
287  {
288  if (Mesh && Mesh->IsValidLowLevel())
289  {
290  Mesh->SetShouldSerializeMeshData(InCheckboxState == ECheckBoxState::Checked);
291  }
292  }
293 }
294 
295 FText FRuntimeMeshComponentDetails::GetModeText(const TSharedPtr<ERuntimeMeshCollisionCookingMode>& Mode) const
296 {
297  if (Mode.IsValid())
298  {
299  switch (*Mode)
300  {
301  case ERuntimeMeshCollisionCookingMode::CollisionPerformance:
302  return LOCTEXT("ERuntimeMeshCollisionCookingMode_CollisionPerformance", "Collision Performance");
303  case ERuntimeMeshCollisionCookingMode::CookingPerformance:
304  return LOCTEXT("ERuntimeMeshCollisionCookingMode_CookingPerformance", "Cooking Performance");
305  }
306  }
307  return LOCTEXT("ERuntimeMeshCollisionCookingMode_Unknown", "Unknown/Multiple Values");
308 }
309 
310 TSharedRef<SWidget> FRuntimeMeshComponentDetails::MakeCollisionModeComboItemWidget(TSharedPtr<ERuntimeMeshCollisionCookingMode> Mode)
311 {
312  return SNew(STextBlock).Text(GetModeText(Mode)).ToolTipText(GetModeText(Mode));
313 }
314 
315 TSharedPtr<ERuntimeMeshCollisionCookingMode> FRuntimeMeshComponentDetails::GetCurrentCollisionCookingMode() const
316 {
317  ERuntimeMeshCollisionCookingMode CurrentMode = ERuntimeMeshCollisionCookingMode::CollisionPerformance;
318  bool bIsFirst = true;
319  bool bIsAllSame = true;
320 
322  {
323  if (Mesh && Mesh->IsValidLowLevel())
324  {
325  ERuntimeMeshCollisionCookingMode NewMode = Mesh->GetCollisionMode();
326  if (bIsFirst || CurrentMode == NewMode)
327  {
328  CurrentMode = NewMode;
329  bIsFirst = false;
330  }
331  else
332  {
333  bIsAllSame = false;
334  break;
335  }
336  }
337  }
338 
339  return bIsAllSame ? *CookingModes.FindByPredicate([CurrentMode](const TSharedPtr<ERuntimeMeshCollisionCookingMode>& Mode) -> bool
340  {
341  return *Mode == CurrentMode;
342  }) : nullptr;
343 }
344 
345 void FRuntimeMeshComponentDetails::CollisionCookingModeSelectionChanged(TSharedPtr<ERuntimeMeshCollisionCookingMode> NewMode, ESelectInfo::Type SelectionType)
346 {
347  if (NewMode.IsValid())
348  {
350  {
351  if (Mesh && Mesh->IsValidLowLevel())
352  {
353  Mesh->SetCollisionMode(*NewMode);
354  }
355  }
356  }
357 }
358 
360 {
361  return GetFirstSelectedRuntimeMeshComp() != nullptr;
362 }
363 
364 
366 {
367  // Find first selected RuntimeMeshComp
368  URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
369  if (RuntimeMeshComp != nullptr)
370  {
371  FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
372  FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
373  FString Name;
374  FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
375  AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
376 
377  TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
378  SNew(SDlgPickAssetPath)
379  .Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
380  .DefaultAssetPath(FText::FromString(PackageName));
381 
382  if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
383  {
384  // Get the full name of where we want to create the physics asset.
385  FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
386  FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
387 
388  // Check if the user inputed a valid asset name, if they did not, give it the generated default name
389  if (MeshName == NAME_None)
390  {
391  // Use the defaults that were already generated.
392  UserPackageName = PackageName;
393  MeshName = *Name;
394  }
395 
396  // Raw mesh data we are filling in
397  FRawMesh RawMesh;
398 
399  // Unique Materials to apply to new mesh
400  TArray<FStaticMaterial> Materials;
401 
402  bool bUseHighPrecisionTangents = false;
403  bool bUseFullPrecisionUVs = false;
404 
405  const int32 NumSections = RuntimeMeshComp->GetNumSections();
406  int32 VertexBase = 0;
407  int32 MaxUVs = 0;
408  for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
409  {
410  auto MeshData = RuntimeMeshComp->GetSectionReadonly(SectionIdx);
411  check(MeshData.IsValid());
412 
413  int32 NumUVs = MeshData->NumUVChannels();
414  MaxUVs = FMath::Max(NumUVs, MaxUVs);
415 
416  // Fill out existing UV channels to start of this one
417  for (int32 Index = 0; Index < MaxUVs; Index++)
418  {
419  RawMesh.WedgeTexCoords[Index].SetNumZeroed(RawMesh.WedgeIndices.Num());
420  }
421 
422  // Copy the vertex positions
423  int32 NumVertices = MeshData->NumVertices();
424  for (int32 Index = 0; Index < NumVertices; Index++)
425  {
426  RawMesh.VertexPositions.Add(MeshData->GetPosition(Index));
427  }
428 
429  // Copy wedges
430  int32 NumTris = MeshData->NumIndices();
431  for (int32 Index = 0; Index < NumTris; Index++)
432  {
433  int32 VertexIndex = MeshData->GetIndex(Index);
434  RawMesh.WedgeIndices.Add(VertexIndex + VertexBase);
435 
436  FVector TangentX = MeshData->GetTangent(VertexIndex);
437  FVector TangentZ = MeshData->GetNormal(VertexIndex);
438 
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);
443 
444 
445  for (int32 UVIndex = 0; UVIndex < NumUVs; UVIndex++)
446  {
447  RawMesh.WedgeTexCoords[UVIndex].Add(MeshData->GetUV(VertexIndex, UVIndex));
448  }
449 
450  RawMesh.WedgeColors.Add(MeshData->GetColor(VertexIndex));
451  }
452 
453  // Find a material index for this section.
454  UMaterialInterface* Material = RuntimeMeshComp->GetSectionMaterial(SectionIdx);
455  int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
456 
457  // copy face info
458  for (int32 TriIdx = 0; TriIdx < NumTris / 3; TriIdx++)
459  {
460  // Set the face material
461  RawMesh.FaceMaterialIndices.Add(MaterialIndex);
462 
463  RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
464  }
465 
466  // Update offset for creating one big index/vertex buffer
467  VertexBase += NumVertices;
468  }
469 
470  // Fill out existing UV channels to start of this one
471  for (int32 Index = 0; Index < MaxUVs; Index++)
472  {
473  RawMesh.WedgeTexCoords[Index].SetNumZeroed(RawMesh.WedgeIndices.Num());
474  }
475 
476  // If we got some valid data.
477  if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
478  {
479  // Then find/create it.
480  UPackage* Package = CreatePackage(*UserPackageName);
481  check(Package);
482 
483  // Create StaticMesh object
484  UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
485  StaticMesh->InitResources();
486 
487  StaticMesh->LightingGuid = FGuid::NewGuid();
488 
489  // Add source to new StaticMesh
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);
500 
501  // Set the materials used for this static mesh
502  StaticMesh->StaticMaterials = Materials;
503  int32 NumMaterials = StaticMesh->StaticMaterials.Num();
504 
505  // Set up the SectionInfoMap to enable collision
506  for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
507  {
508  FMeshSectionInfo Info = StaticMesh->GetSectionInfoMap().Get(0, SectionIdx);
509  Info.MaterialIndex = SectionIdx;
510  Info.bEnableCollision = true;
511  StaticMesh->GetSectionInfoMap().Set(0, SectionIdx, Info);
512  }
513 
514  // Configure body setup for working collision.
515  StaticMesh->CreateBodySetup();
516  StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
517 
518  // Build mesh from source
519  StaticMesh->Build(false);
520 
521  // Make package dirty.
522  StaticMesh->MarkPackageDirty();
523 
524  StaticMesh->PostEditChange();
525 
526  // Notify asset registry of new asset
527  FAssetRegistryModule::AssetCreated(StaticMesh);
528  }
529  }
530  }
531 
532  return FReply::Handled();
533 }
534 
535 
536 #undef LOCTEXT_NAMESPACE
static TSharedRef< IDetailCustomization > MakeInstance()
class URuntimeMeshComponent * GetFirstSelectedRuntimeMeshComp() const
FText GetModeText(const TSharedPtr< ERuntimeMeshCollisionCookingMode > &Mode) const
void ShouldSerializeMeshDataCheckedStateChanged(ECheckBoxState InCheckboxState)
void AsyncCollisionCheckedStateChanged(ECheckBoxState InCheckboxState)
TArray< TSharedPtr< ERuntimeMeshCollisionCookingMode > > CookingModes
virtual void CustomizeDetails(IDetailLayoutBuilder &DetailBuilder) override
Mode
Definition: monitors.c:40
ECheckBoxState IsAsyncCollisionEnabled() const
static const textual_icon check
Definition: model-views.h:260
struct Index Index
Definition: sqlite3.c:11789
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


librealsense2
Author(s): Sergey Dorodnicov , Doron Hirshberg , Mark Horn , Reagan Lopez , Itay Carpis
autogenerated on Mon May 3 2021 02:47:41