Example #1
2
/**
 * Creates a static mesh object from raw triangle data.
 */
UStaticMesh* CreateStaticMesh(struct FRawMesh& RawMesh,TArray<UMaterialInterface*>& Materials,UObject* InOuter,FName InName)
{
	// Create the UStaticMesh object.
	FStaticMeshComponentRecreateRenderStateContext RecreateRenderStateContext(FindObject<UStaticMesh>(InOuter,*InName.ToString()));
	UStaticMesh* StaticMesh = new(InOuter,InName,RF_Public|RF_Standalone) UStaticMesh(FPostConstructInitializeProperties());

	// Add one LOD for the base mesh
	FStaticMeshSourceModel* SrcModel = new(StaticMesh->SourceModels) FStaticMeshSourceModel();
	SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
	StaticMesh->Materials = Materials;

	int32 NumSections = StaticMesh->Materials.Num();

	// Set up the SectionInfoMap to enable collision
	for (int32 SectionIdx = 0; SectionIdx < NumSections; ++SectionIdx)
	{
		FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
		Info.MaterialIndex = SectionIdx;
		Info.bEnableCollision = true;
		StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
	}

	StaticMesh->Build();
	StaticMesh->MarkPackageDirty();
	return StaticMesh;
}
EReimportResult::Type UReimportSpeedTreeFactory::Reimport(UObject* Obj)
{
#if WITH_SPEEDTREE
	UStaticMesh* Mesh = Cast<UStaticMesh>(Obj);
	if (!Mesh)
	{
		return EReimportResult::Failed;
	}

	const FString Filename = Mesh->AssetImportData->GetFirstFilename();
	const FString FileExtension = FPaths::GetExtension(Filename);
	const bool bIsSRT = FCString::Stricmp(*FileExtension, TEXT("SRT")) == 0;

	if (!bIsSRT)
	{
		return EReimportResult::Failed;
	}

	if (!(Filename.Len()))
	{
		// Since this is a new system most static meshes don't have paths, so logging has been commented out
		//UE_LOG(LogEditorFactories, Warning, TEXT("-- cannot reimport: static mesh resource does not have path stored."));
		return EReimportResult::Failed;
	}

	UE_LOG(LogEditorFactories, Log, TEXT("Performing atomic reimport of [%s]"), *Filename);

	bool OutCanceled = false;

	if (ImportObject(Mesh->GetClass(), Mesh->GetOuter(), *Mesh->GetName(), RF_Public | RF_Standalone, Filename, nullptr, OutCanceled) != nullptr)
	{
		UE_LOG(LogEditorFactories, Log, TEXT("-- imported successfully"));

		Mesh->AssetImportData->Update(Filename);
		Mesh->MarkPackageDirty();

		return EReimportResult::Succeeded;
	}
	else if (OutCanceled)
	{
		UE_LOG(LogEditorFactories, Warning, TEXT("-- import canceled"));
	}
	else
	{
		UE_LOG(LogEditorFactories, Warning, TEXT("-- import failed"));
	}
#endif // #if WITH_SPEEDTREE

	return EReimportResult::Failed;
}
EReimportResult::Type UReimportFbxSceneFactory::ReimportStaticMesh(void* VoidFbxImporter, TSharedPtr<FFbxMeshInfo> MeshInfo)
{
	UnFbx::FFbxImporter* FbxImporter = (UnFbx::FFbxImporter*)VoidFbxImporter;
	//Find the UObject associate with this MeshInfo
	UPackage* PkgExist = LoadPackage(nullptr, *(MeshInfo->GetImportPath()), LOAD_Verify | LOAD_NoWarn);
	if (PkgExist != nullptr)
	{
		PkgExist->FullyLoad();
	}

	FString AssetName = MeshInfo->GetFullImportName();
	UStaticMesh* Mesh = FindObjectSafe<UStaticMesh>(ANY_PACKAGE, *AssetName);
	if (Mesh == nullptr)
	{
		//We reimport only static mesh here
		FbxImporter->AddTokenizedErrorMessage(FTokenizedMessage::Create(EMessageSeverity::Error, FText::Format(FText::FromString("Reimport Mesh {0} fail, the original staicmesh in the content browser cannot be load."), FText::FromString(MeshInfo->GetImportPath()))), FName(TEXT("Reimport Fbx Scene")));
		return EReimportResult::Failed;
	}
	
	//Copy default options to StaticMeshImportData
	SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh);
	SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);

	UnFbx::FBXImportOptions* OverrideImportSettings = GetOptionsFromName(MeshInfo->OptionName);
	if (OverrideImportSettings != nullptr)
	{
		SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(OverrideImportSettings, GlobalImportSettings);
		SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(OverrideImportSettings, SceneImportOptionsStaticMesh);
	}
	else
	{
		SFbxSceneOptionWindow::CopyFbxOptionsToFbxOptions(GlobalImportSettingsReference, GlobalImportSettings);
		SFbxSceneOptionWindow::CopyFbxOptionsToStaticMeshOptions(GlobalImportSettingsReference, SceneImportOptionsStaticMesh);
	}
	SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);

	FbxImporter->ApplyTransformSettingsToFbxNode(FbxImporter->Scene->GetRootNode(), StaticMeshImportData);
	const TArray<UAssetUserData*>* UserData = Mesh->GetAssetUserDataArray();
	TArray<UAssetUserData*> UserDataCopy;
	if (UserData)
	{
		for (int32 Idx = 0; Idx < UserData->Num(); Idx++)
		{
			UserDataCopy.Add((UAssetUserData*)StaticDuplicateObject((*UserData)[Idx], GetTransientPackage()));
		}
	}

	// preserve settings in navcollision subobject
	UNavCollision* NavCollision = Mesh->NavCollision ?
		(UNavCollision*)StaticDuplicateObject(Mesh->NavCollision, GetTransientPackage()) :
		nullptr;

	// preserve extended bound settings
	const FVector PositiveBoundsExtension = Mesh->PositiveBoundsExtension;
	const FVector NegativeBoundsExtension = Mesh->NegativeBoundsExtension;
	Mesh = FbxImporter->ReimportSceneStaticMesh(MeshInfo->UniqueId, Mesh, StaticMeshImportData);
	if (Mesh != nullptr)
	{
		//Put back the new mesh data since the reimport is putting back the original import data
		SceneImportOptionsStaticMesh->FillStaticMeshInmportData(StaticMeshImportData, SceneImportOptions);
		Mesh->AssetImportData = StaticMeshImportData;

		// Copy user data to newly created mesh
		for (int32 Idx = 0; Idx < UserDataCopy.Num(); Idx++)
		{
			UserDataCopy[Idx]->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty);
			Mesh->AddAssetUserData(UserDataCopy[Idx]);
		}

		if (NavCollision)
		{
			Mesh->NavCollision = NavCollision;
			NavCollision->Rename(nullptr, Mesh, REN_DontCreateRedirectors | REN_DoNotDirty);
		}

		// Restore bounds extension settings
		Mesh->PositiveBoundsExtension = PositiveBoundsExtension;
		Mesh->NegativeBoundsExtension = NegativeBoundsExtension;

		Mesh->AssetImportData->Update(FbxImportFileName);

		// Try to find the outer package so we can dirty it up
		if (Mesh->GetOutermost())
		{
			Mesh->GetOutermost()->MarkPackageDirty();
		}
		else
		{
			Mesh->MarkPackageDirty();
		}
		AllNewAssets.Add(MeshInfo, Mesh);
		AssetToSyncContentBrowser.Add(Mesh);
	}
	else
	{
		return EReimportResult::Failed;
	}
	return EReimportResult::Succeeded;
}
FReply FRuntimeMeshComponentDetails::ClickedOnConvertToStaticMesh()
{
 	// Find first selected RuntimeMeshComp
 	URuntimeMeshComponent* RuntimeMeshComp = GetFirstSelectedRuntimeMeshComp();
 	if (RuntimeMeshComp != nullptr)
 	{
 		FString NewNameSuggestion = FString(TEXT("RuntimeMeshComp"));
 		FString PackageName = FString(TEXT("/Game/Meshes/")) + NewNameSuggestion;
 		FString Name;
 		FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools");
 		AssetToolsModule.Get().CreateUniqueAssetName(PackageName, TEXT(""), PackageName, Name);
 
 		TSharedPtr<SDlgPickAssetPath> PickAssetPathWidget =
 			SNew(SDlgPickAssetPath)
 			.Title(LOCTEXT("ConvertToStaticMeshPickName", "Choose New StaticMesh Location"))
 			.DefaultAssetPath(FText::FromString(PackageName));
 
 		if (PickAssetPathWidget->ShowModal() == EAppReturnType::Ok)
 		{
 			// Get the full name of where we want to create the physics asset.
 			FString UserPackageName = PickAssetPathWidget->GetFullAssetPath().ToString();
 			FName MeshName(*FPackageName::GetLongPackageAssetName(UserPackageName));
 
 			// Check if the user inputed a valid asset name, if they did not, give it the generated default name
 			if (MeshName == NAME_None)
 			{
 				// Use the defaults that were already generated.
 				UserPackageName = PackageName;
 				MeshName = *Name;
 			}
 
 			// Raw mesh data we are filling in
 			FRawMesh RawMesh;
 					
			// Unique Materials to apply to new mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
			TArray<FStaticMaterial> Materials;
#else
			TArray<UMaterialInterface*> Materials;
#endif
 
			bool bUseHighPrecisionTangents = false;
			bool bUseFullPrecisionUVs = false;

 			const int32 NumSections = RuntimeMeshComp->GetNumSections();
 			int32 VertexBase = 0;
 			for (int32 SectionIdx = 0; SectionIdx < NumSections; SectionIdx++)
 			{
				const IRuntimeMeshVerticesBuilder* Vertices;
				const FRuntimeMeshIndicesBuilder* Indices;
				RuntimeMeshComp->GetSectionMesh(SectionIdx, Vertices, Indices);

				if (Vertices->HasHighPrecisionNormals())
				{
					bUseHighPrecisionTangents = true;
				}
				if (Vertices->HasHighPrecisionUVs())
				{
					bUseFullPrecisionUVs = true;
				}
 
 				// Copy verts
				Vertices->Seek(-1);
				while (Vertices->MoveNext() < Vertices->Length())
				{
					RawMesh.VertexPositions.Add(Vertices->GetPosition());
				}
 
 				// Copy 'wedge' info
				Indices->Seek(0);
				while (Indices->HasRemaining())
 				{
					int32 Index = Indices->ReadOne();
 
 					RawMesh.WedgeIndices.Add(Index + VertexBase);
 

					Vertices->Seek(Index);
 
					FVector TangentX = Vertices->GetTangent();
					FVector TangentZ = Vertices->GetNormal();
 					FVector TangentY = (TangentX ^ TangentZ).GetSafeNormal() * Vertices->GetNormal().W;
 
 					RawMesh.WedgeTangentX.Add(TangentX);
 					RawMesh.WedgeTangentY.Add(TangentY);
 					RawMesh.WedgeTangentZ.Add(TangentZ);
 
					for (int UVIndex = 0; UVIndex < 8; UVIndex++)
					{
						if (!Vertices->HasUVComponent(UVIndex))
						{
							break;
						}
						RawMesh.WedgeTexCoords[UVIndex].Add(Vertices->GetUV(UVIndex));
					}

 					RawMesh.WedgeColors.Add(Vertices->GetColor());
 				}
 
				// Find a material index for this section.
				UMaterialInterface* Material = RuntimeMeshComp->GetMaterial(SectionIdx);

#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
				int32 MaterialIndex = Materials.AddUnique(FStaticMaterial(Material));
#else
				int32 MaterialIndex = Materials.AddUnique(Material);
#endif
				

 				// copy face info
 				int32 NumTris = Indices->Length() / 3;
 				for (int32 TriIdx=0; TriIdx < NumTris; TriIdx++)
 				{
					// Set the face material
					RawMesh.FaceMaterialIndices.Add(MaterialIndex);

 					RawMesh.FaceSmoothingMasks.Add(0); // Assume this is ignored as bRecomputeNormals is false
 				}
 
 				// Update offset for creating one big index/vertex buffer
				VertexBase += Vertices->Length();
 			}
 
 			// If we got some valid data.
 			if (RawMesh.VertexPositions.Num() >= 3 && RawMesh.WedgeIndices.Num() >= 3)
 			{
 				// Then find/create it.
 				UPackage* Package = CreatePackage(NULL, *UserPackageName);
 				check(Package);
 
 				// Create StaticMesh object
 				UStaticMesh* StaticMesh = NewObject<UStaticMesh>(Package, MeshName, RF_Public | RF_Standalone);
 				StaticMesh->InitResources();
 
 				StaticMesh->LightingGuid = FGuid::NewGuid();
 
 				// Add source to new StaticMesh
 				FStaticMeshSourceModel* SrcModel = new (StaticMesh->SourceModels) FStaticMeshSourceModel();
 				SrcModel->BuildSettings.bRecomputeNormals = false;
 				SrcModel->BuildSettings.bRecomputeTangents = false;
 				SrcModel->BuildSettings.bRemoveDegenerates = false;
 				SrcModel->BuildSettings.bUseHighPrecisionTangentBasis = bUseHighPrecisionTangents;
				SrcModel->BuildSettings.bUseFullPrecisionUVs = bUseFullPrecisionUVs;
 				SrcModel->BuildSettings.bGenerateLightmapUVs = true;
 				SrcModel->BuildSettings.SrcLightmapIndex = 0;
 				SrcModel->BuildSettings.DstLightmapIndex = 1;
 				SrcModel->RawMeshBulkData->SaveRawMesh(RawMesh);
 
				// Set the materials used for this static mesh
#if ENGINE_MAJOR_VERSION == 4 && ENGINE_MINOR_VERSION >= 14
				StaticMesh->StaticMaterials = Materials;
				int32 NumMaterials = StaticMesh->StaticMaterials.Num();
#else
				StaticMesh->Materials = Materials;
				int32 NumMaterials = StaticMesh->Materials.Num();
#endif

				// Set up the SectionInfoMap to enable collision
				for (int32 SectionIdx = 0; SectionIdx < NumMaterials; SectionIdx++)
				{
					FMeshSectionInfo Info = StaticMesh->SectionInfoMap.Get(0, SectionIdx);
					Info.MaterialIndex = SectionIdx;
					Info.bEnableCollision = true;
					StaticMesh->SectionInfoMap.Set(0, SectionIdx, Info);
				}

				// Configure body setup for working collision.
				StaticMesh->CreateBodySetup();
				StaticMesh->BodySetup->CollisionTraceFlag = CTF_UseComplexAsSimple;

 				// Build mesh from source
 				StaticMesh->Build(false);

				// Make package dirty.
				StaticMesh->MarkPackageDirty();

 				StaticMesh->PostEditChange();
 
 				// Notify asset registry of new asset
 				FAssetRegistryModule::AssetCreated(StaticMesh);
 			}
 		}
 	}

	return FReply::Handled();
}