RuntimeMeshComponentDetails.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. // Copyright 2016 Chris Conway (Koderz). All Rights Reserved.
  2. #include "RuntimeMeshComponentEditorPrivatePCH.h"
  3. #include "RuntimeMeshComponentDetails.h"
  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. #define LOCTEXT_NAMESPACE "RuntimeMeshComponentDetails"
  12. TSharedRef<IDetailCustomization> FRuntimeMeshComponentDetails::MakeInstance()
  13. {
  14. return MakeShareable(new FRuntimeMeshComponentDetails);
  15. }
  16. void FRuntimeMeshComponentDetails::CustomizeDetails( IDetailLayoutBuilder& DetailBuilder )
  17. {
  18. IDetailCategoryBuilder& RuntimeMeshCategory = DetailBuilder.EditCategory("RuntimeMesh");
  19. const FText ConvertToStaticMeshText = LOCTEXT("ConvertToStaticMesh", "Create StaticMesh");
  20. // Cache set of selected things
  21. SelectedObjectsList = DetailBuilder.GetDetailsView().GetSelectedObjects();
  22. RuntimeMeshCategory.AddCustomRow(ConvertToStaticMeshText, false)
  23. .NameContent()
  24. [
  25. SNullWidget::NullWidget
  26. ]
  27. .ValueContent()
  28. .VAlign(VAlign_Center)
  29. .MaxDesiredWidth(250)
  30. [
  31. SNew(SButton)
  32. .VAlign(VAlign_Center)
  33. .ToolTipText(LOCTEXT("ConvertToStaticMeshTooltip", "Create a new StaticMesh asset using current geometry from this RuntimeMeshComponent. Does not modify instance."))
  34. .OnClicked(this, &FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh)
  35. .IsEnabled(this, &FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled)
  36. .Content()
  37. [
  38. SNew(STextBlock)
  39. .Text(ConvertToStaticMeshText)
  40. ]
  41. ];
  42. }
  43. URuntimeMeshComponent* FRuntimeMeshComponentDetails::GetFirstSelectedRuntimeMeshComp() const
  44. {
  45. // Find first selected valid RuntimeMeshComp
  46. URuntimeMeshComponent* RuntimeMeshComp = nullptr;
  47. for (const TWeakObjectPtr<UObject>& Object : SelectedObjectsList)
  48. {
  49. URuntimeMeshComponent* TestRuntimeComp = Cast<URuntimeMeshComponent>(Object.Get());
  50. // See if this one is good
  51. if (TestRuntimeComp != nullptr && !TestRuntimeComp->IsTemplate())
  52. {
  53. RuntimeMeshComp = TestRuntimeComp;
  54. break;
  55. }
  56. }
  57. return RuntimeMeshComp;
  58. }
  59. bool FRuntimeMeshComponentDetails::ConvertToStaticMeshEnabled() const
  60. {
  61. return GetFirstSelectedRuntimeMeshComp() != nullptr;
  62. }
  63. FReply FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh()
  64. {
  65. // Find first selected RuntimeMeshComp
  66. URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
  67. if (RuntimeMeshComp != nullptr)
  68. {
  69. FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
  70. FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
  71. FString Name;
  72. FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
  73. AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
  74. TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
  75. SNew(SDlgPickAssetPath)
  76. .Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
  77. .DefaultAssetPath(FText::FromString(PackageName));
  78. if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
  79. {
  80. // Get the full name of where we want to create the physics asset.
  81. FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
  82. FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
  83. // Check if the user inputed a valid asset name, if they did not, give it the generated default name
  84. if (MeshName == NAME_None)
  85. {
  86. // Use the defaults that were already generated.
  87. UserPackageName = PackageName;
  88. MeshName = *Name;
  89. }
  90. // Raw mesh data we are filling in
  91. FRawMesh RawMesh;
  92. // Unique Materials to apply to new mesh
  93. #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
  94. TArray<FStaticMaterial> Materials;
  95. #else
  96. TArray<UMaterialInterface*> Materials;
  97. #endif
  98. bool bUseHighPrecisionTangents = false;
  99. bool bUseFullPrecisionUVs = false;
  100. const int32 NumSections = RuntimeMeshComp->GetNumSections();
  101. int32 VertexBase = 0;
  102. for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
  103. {
  104. const IRuntimeMeshVerticesBuilder* Vertices;
  105. const FRuntimeMeshIndicesBuilder* Indices;
  106. RuntimeMeshComp->GetSectionMesh(SectionIdx, Vertices, Indices);
  107. if (Vertices->HasHighPrecisionNormals())
  108. {
  109. bUseHighPrecisionTangents = true;
  110. }
  111. if (Vertices->HasHighPrecisionUVs())
  112. {
  113. bUseFullPrecisionUVs = true;
  114. }
  115. // Copy verts
  116. Vertices->Seek(-1);
  117. while (Vertices->MoveNext() < Vertices->Length())
  118. {
  119. RawMesh.VertexPositions.Add(Vertices->GetPosition());
  120. }
  121. // Copy 'wedge' info
  122. Indices->Seek(0);
  123. while (Indices->HasRemaining())
  124. {
  125. int32 Index = Indices->ReadOne();
  126. RawMesh.WedgeIndices.Add(Index + VertexBase);
  127. Vertices->Seek(Index);
  128. FVector TangentX = Vertices->GetTangent();
  129. FVector TangentZ = Vertices->GetNormal();
  130. FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * Vertices->GetNormal().W;
  131. RawMesh.WedgeTangentX.Add(TangentX);
  132. RawMesh.WedgeTangentY.Add(TangentY);
  133. RawMesh.WedgeTangentZ.Add(TangentZ);
  134. for (int UVIndex = 0; UVIndex < 8; UVIndex++)
  135. {
  136. if (!Vertices->HasUVComponent(UVIndex))
  137. {
  138. break;
  139. }
  140. RawMesh.WedgeTexCoords[UVIndex].Add(Vertices->GetUV(UVIndex));
  141. }
  142. RawMesh.WedgeColors.Add(Vertices->GetColor());
  143. }
  144. // Find a material index for this section.
  145. UMaterialInterface* Material = RuntimeMeshComp->GetMaterial(SectionIdx);
  146. #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
  147. int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
  148. #else
  149. int32 MaterialIndex = Materials.AddUnique(Material);
  150. #endif
  151. // copy face info
  152. int32 NumTris = Indices->Length() / 3;
  153. for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
  154. {
  155. // Set the face material
  156. RawMesh.FaceMaterialIndices.Add(MaterialIndex);
  157. RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
  158. }
  159. // Update offset for creating one big index/vertex buffer
  160. VertexBase += Vertices->Length();
  161. }
  162. // If we got some valid data.
  163. if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
  164. {
  165. // Then find/create it.
  166. UPackage* Package = CreatePackage(NULL, *UserPackageName);
  167. check(Package);
  168. // Create StaticMesh object
  169. UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
  170. StaticMesh->InitResources();
  171. StaticMesh->LightingGuid = FGuid::NewGuid();
  172. // Add source to new StaticMesh
  173. FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
  174. SrcModel->BuildSettings.bRecomputeNormals = false;
  175. SrcModel->BuildSettings.bRecomputeTangents = false;
  176. SrcModel->BuildSettings.bRemoveDegenerates = false;
  177. SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = bUseHighPrecisionTangents;
  178. SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
  179. SrcModel->BuildSettings.bGenerateLightmapUVs = true;
  180. SrcModel->BuildSettings.SrcLightmapIndex = 0;
  181. SrcModel->BuildSettings.DstLightmapIndex = 1;
  182. SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
  183. // Set the materials used for this static mesh
  184. #if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
  185. StaticMesh->StaticMaterials = Materials;
  186. int32 NumMaterials = StaticMesh->StaticMaterials.Num();
  187. #else
  188. StaticMesh->Materials = Materials;
  189. int32 NumMaterials = StaticMesh->Materials.Num();
  190. #endif
  191. // Set up the SectionInfoMap to enable collision
  192. for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
  193. {
  194. FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
  195. Info.MaterialIndex = SectionIdx;
  196. Info.bEnableCollision = true;
  197. StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
  198. }
  199. // Configure body setup for working collision.
  200. StaticMesh->CreateBodySetup();
  201. StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;
  202. // Build mesh from source
  203. StaticMesh->Build(false);
  204. // Make package dirty.
  205. StaticMesh->MarkPackageDirty();
  206. StaticMesh->PostEditChange();
  207. // Notify asset registry of new asset
  208. FAssetRegistryModule::AssetCreated(StaticMesh);
  209. }
  210. }
  211. }
  212. return FReply::Handled();
  213. }
  214. #undef LOCTEXT_NAMESPACE