// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved. #include "GeometryCollectionCommandCommon.h" #include "IMeshEditorModeEditingContract.h" #include "ScopedTransaction.h" #include "Editor.h" #include "Engine/Selection.h" #include "PackageTools.h" #include "Layers/ILayers.h" #include "GeometryCollection/GeometryCollectionActor.h" #include "EditableMeshFactory.h" #include "EditorSupportDelegates.h" #include "MeshFractureSettings.h" #include "AssetRegistryModule.h" #include "GeometryCollection/GeometryCollectionFactory.h" #include "GeometryCollection/GeometryCollectionConversion.h" #include "GeometryCollection/GeometryCollectionClusteringUtility.h" #include "GeometryCollection/GeometryCollectionAlgo.h" #include "GeometryCollection/GeometryCollectionFactory.h" #include "AssetToolsModule.h" #include "FractureToolDelegates.h" #define LOCTEXT_NAMESPACE "LogGeometryCommandCommon" DEFINE_LOG_CATEGORY(LogGeometryCommandCommon); namespace CommandCommon { static TArray GetSelectedActors() { USelection* SelectedActors = GEditor->GetSelectedActors(); TArray Actors; for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter) { AActor* Actor = Cast(*Iter); if (Actor) { Actors.Add(Actor); } } return Actors; } static ULevel* GetSelectedLevel() { USelection* SelectedActors = GEditor->GetSelectedActors(); TArray UniqueLevels; for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter) { AActor* Actor = Cast(*Iter); if (Actor) { UniqueLevels.AddUnique(Actor->GetLevel()); } } check(UniqueLevels.Num() == 1); return UniqueLevels[0]; } static AActor* AddActor(ULevel* InLevel, UClass* Class) { check(Class); UWorld* World = InLevel->OwningWorld; ULevel* DesiredLevel = InLevel; // Transactionally add the actor. AActor* Actor = NULL; { FScopedTransaction Transaction(NSLOCTEXT("UnrealEd", "AddActor", "Add Actor")); FActorSpawnParameters SpawnInfo; SpawnInfo.OverrideLevel = DesiredLevel; SpawnInfo.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; SpawnInfo.ObjectFlags = RF_Transactional; const auto Location = FVector(0); const auto Rotation = FTransform(FVector(0)).GetRotation().Rotator(); Actor = World->SpawnActor(Class, &Location, &Rotation, SpawnInfo); check(Actor); Actor->InvalidateLightingCache(); Actor->PostEditMove(true); } // If this actor is part of any layers (set in its default properties), add them into the visible layers list. GEditor->Layers->SetLayersVisibility(Actor->Layers, true); // Clean up. Actor->MarkPackageDirty(); ULevel::LevelDirtiedEvent.Broadcast(); return Actor; } } // namespace CommandCommon AGeometryCollectionActor* FGeometryCollectionCommandCommon::CreateNewGeometryActor(const FString& Name, const FTransform& Transform, UEditableMesh* SourceMesh) { // create an asset package first FString NewPackageName = FPackageName::FilenameToLongPackageName(FPaths::ProjectContentDir() + Name); FString UniquePackageName; FString UniqueAssetName; FAssetToolsModule& AssetToolsModule = FModuleManager::LoadModuleChecked("AssetTools"); AssetToolsModule.Get().CreateUniqueAssetName(NewPackageName, TEXT(""), UniquePackageName, UniqueAssetName); UPackage* Package = CreatePackage(NULL, *UniquePackageName); UGeometryCollection* GeometryCollection = static_cast( UGeometryCollectionFactory::StaticFactoryCreateNew(UGeometryCollection::StaticClass(), Package, FName(*UniqueAssetName), RF_Standalone | RF_Public, NULL, GWarn)); // Create the new Geometry Collection actor AGeometryCollectionActor* NewActor = Cast(CommandCommon::AddActor(CommandCommon::GetSelectedLevel(), AGeometryCollectionActor::StaticClass())); check(NewActor->GetGeometryCollectionComponent()); // Set the Geometry Collection asset in the new actor NewActor->GetGeometryCollectionComponent()->SetRestCollection(GeometryCollection); // copy transform of original static mesh actor to this new actor NewActor->SetActorLabel(Name); NewActor->SetActorTransform(Transform); // copy the original material(s) across TPolygonGroupAttributesConstRef MaterialSlotNames = SourceMesh->GetMeshDescription()->PolygonGroupAttributes().GetAttributesRef(MeshAttribute::PolygonGroup::MaterialAssetName); int CurrSlot = 0; if (MaterialSlotNames.GetNumElements() > 0) { for (const FPolygonGroupID PolygonGroupID : SourceMesh->GetMeshDescription()->PolygonGroups().GetElementIDs()) { FString MaterialName = MaterialSlotNames[PolygonGroupID].ToString(); UMaterialInterface* OriginalMaterial = LoadObject(nullptr, *MaterialName); if (OriginalMaterial) { // sync materials on the UObject GeometryCollection->Materials.Add(OriginalMaterial); } } // add slot for internal and selection materials // #todo(dmp): support arbitrary internal materials and a good interface GeometryCollection->AppendStandardMaterials(); // set materials on the component NewActor->GetGeometryCollectionComponent()->InitializeMaterials(GeometryCollection->Materials, GeometryCollection->GetInteriorMaterialIndex(), GeometryCollection->GetBoneSelectedMaterialIndex()); } // Mark relevant stuff dirty FAssetRegistryModule::AssetCreated(GeometryCollection); GeometryCollection->MarkPackageDirty(); Package->SetDirtyFlag(true); return NewActor; } void FGeometryCollectionCommandCommon::RemoveActor(AActor* Actor) { UWorld* World = CommandCommon::GetSelectedLevel()->OwningWorld; GEditor->SelectActor(Actor, false, true); bool ItWorked = World->DestroyActor(Actor, true, true); } void FGeometryCollectionCommandCommon::LogHierarchy(const UGeometryCollection* GeometryCollectionObject) { if (GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { UE_LOG(LogGeometryCommandCommon, Log, TEXT("Sizes: VerticesGroup %d, FacesGroup %d, GeometryGroup %d, TransformGroup %d"), GeometryCollection->NumElements(FGeometryCollection::VerticesGroup), GeometryCollection->NumElements(FGeometryCollection::FacesGroup), GeometryCollection->NumElements(FGeometryCollection::GeometryGroup), GeometryCollection->NumElements(FGeometryCollection::TransformGroup)); const TSharedRef > ExplodedVectorsArray = GeometryCollection->GetAttribute("ExplodedVector", FGeometryCollection::TransformGroup); const TSharedRef > ExplodedTransformsArray = GeometryCollection->GetAttribute("ExplodedTransform", FGeometryCollection::TransformGroup); const TSharedRef > HierarchyArray = GeometryCollection->GetAttribute("BoneHierarchy", FGeometryCollection::TransformGroup); const TSharedRef > TransformsArray = GeometryCollection->GetAttribute("Transform", FGeometryCollection::TransformGroup); const TSharedRef > BoneNamesArray = GeometryCollection->GetAttribute("BoneName", FGeometryCollection::TransformGroup); const TManagedArray& Hierarchy = *HierarchyArray; const TManagedArray& ExplodedVectors = *ExplodedVectorsArray; const TManagedArray& Transforms = *TransformsArray; const TManagedArray& ExplodedTransforms = *ExplodedTransformsArray; const TManagedArray& BoneNames = *BoneNamesArray; for (int BoneIndex = 0; BoneIndex < Hierarchy.Num(); BoneIndex++) { const FTransform& Transform = Transforms[BoneIndex]; const FVector& LocalLocation = ExplodedTransforms[BoneIndex].GetLocation(); UE_LOG(LogGeometryCommandCommon, Log, TEXT("Location %3.2f, %3.2f, %3.2f"), Transform.GetLocation().X, Transform.GetLocation().Y, Transform.GetLocation().Z); UE_LOG(LogGeometryCommandCommon, Log, TEXT("Scaling %3.2f, %3.2f, %3.2f"), Transform.GetScale3D().X, Transform.GetScale3D().Y, Transform.GetScale3D().Z); UE_LOG(LogGeometryCommandCommon, Log, TEXT("Local Location %3.2f, %3.2f, %3.2f"), LocalLocation.X, LocalLocation.Y, LocalLocation.Z); const FVector& Vector = ExplodedVectors[BoneIndex]; UE_LOG(LogGeometryCommandCommon, Log, TEXT("BoneID %d, Name %s, Level %d, IsGeometry %d, ParentBoneID %d, Offset (%3.2f, %3.2f, %3.2f), Vector (%3.2f, %3.2f, %3.2f)"), BoneIndex, BoneNames[BoneIndex].GetCharArray().GetData(), Hierarchy[BoneIndex].Level, Hierarchy[BoneIndex].IsGeometry(), Hierarchy[BoneIndex].Parent, LocalLocation.X, LocalLocation.Y, LocalLocation.Z, Vector.X, Vector.Y, Vector.Z); for (const int32 & ChildIndex : Hierarchy[BoneIndex].Children) { UE_LOG(LogGeometryCommandCommon, Log, TEXT("..ChildBoneID %d"), ChildIndex); } check((Hierarchy[BoneIndex].Children.Num() > 0) == Hierarchy[BoneIndex].IsTransform()); } } } } void FGeometryCollectionCommandCommon::UpdateExplodedView(class IMeshEditorModeEditingContract &MeshEditorMode, EViewResetType ResetType) { // Update the exploded view in the UI based on the current exploded view slider position FFractureToolDelegates::Get().OnUpdateExplodedView.Broadcast(static_cast(ResetType), static_cast(MeshEditorMode.GetFractureSettings()->CommonSettings->ViewMode)); FFractureToolDelegates::Get().OnComponentsUpdated.Broadcast(); } UGeometryCollectionComponent* FGeometryCollectionCommandCommon::GetGeometryCollectionComponent(UEditableMesh* SourceMesh) { check(SourceMesh); UGeometryCollectionComponent* GeometryCollectionComponent = nullptr; AActor* Actor = GetEditableMeshActor(SourceMesh); AGeometryCollectionActor* GeometryCollectionActor = Cast(Actor); if (GeometryCollectionActor) { GeometryCollectionComponent = GeometryCollectionActor->GetGeometryCollectionComponent(); } return GeometryCollectionComponent; } class UStaticMesh* FGeometryCollectionCommandCommon::GetStaticMesh(UEditableMesh* SourceMesh) { check(SourceMesh); const FEditableMeshSubMeshAddress& SubMeshAddress = SourceMesh->GetSubMeshAddress(); return Cast(static_cast(SubMeshAddress.MeshObjectPtr)); } AActor* FGeometryCollectionCommandCommon::GetEditableMeshActor(UEditableMesh* EditableMesh) { AActor* ReturnActor = nullptr; const TArray& SelectedActors = CommandCommon::GetSelectedActors(); for (int i = 0; i < SelectedActors.Num(); i++) { TArray PrimitiveComponents = SelectedActors[i]->GetComponentsByClass(UPrimitiveComponent::StaticClass()); for (UActorComponent* PrimitiveActorComponent : PrimitiveComponents) { UPrimitiveComponent* Component = CastChecked(PrimitiveActorComponent); FEditableMeshSubMeshAddress SubMeshAddress = UEditableMeshFactory::MakeSubmeshAddress(Component, 0); if (EditableMesh->GetSubMeshAddress() == SubMeshAddress) { ReturnActor = Component->GetOwner(); break; } } } return ReturnActor; } UEditableMesh* FGeometryCollectionCommandCommon::GetEditableMeshForActor(AActor* Actor, TArray& SelectedMeshes) { check(Actor); TArray PrimitiveComponents = Actor->GetComponentsByClass(UPrimitiveComponent::StaticClass()); for (UActorComponent* PrimitiveActorComponent : PrimitiveComponents) { UPrimitiveComponent* Component = CastChecked(PrimitiveActorComponent); FEditableMeshSubMeshAddress SubMeshAddress = UEditableMeshFactory::MakeSubmeshAddress(Component, 0); for (UEditableMesh* EditableMesh : SelectedMeshes) { if (EditableMesh->GetSubMeshAddress() == SubMeshAddress) { return EditableMesh; } } } return nullptr; } UPackage* FGeometryCollectionCommandCommon::CreateGeometryCollectionPackage(UGeometryCollection*& GeometryCollection) { UPackage* Package = CreatePackage(NULL, TEXT("/Game/GeometryCollectionAsset")); GeometryCollection = static_cast( UGeometryCollectionFactory::StaticFactoryCreateNew(UGeometryCollection::StaticClass(), Package, FName("GeometryCollectionAsset"), RF_Standalone | RF_Public, NULL, GWarn)); return Package; } void FGeometryCollectionCommandCommon::AddSingleRootNodeIfRequired(UGeometryCollection* GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { if (FGeometryCollectionClusteringUtility::ContainsMultipleRootBones(GeometryCollection)) { FGeometryCollectionClusteringUtility::ClusterAllBonesUnderNewRoot(GeometryCollection); } } } void FGeometryCollectionCommandCommon::AddAdditionalAttributesIfRequired(UGeometryCollection* GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { if (!GeometryCollection->HasAttribute("ExplodedVector", FGeometryCollection::TransformGroup)) { GeometryCollection->AddAttribute("ExplodedVector", FGeometryCollection::TransformGroup); GeometryCollection->AddAttribute("ExplodedTransform", FGeometryCollection::TransformGroup); } } } int FGeometryCollectionCommandCommon::GetRootBone(const UGeometryCollection* GeometryCollectionObject) { // Note - it is possible for their to be 2 roots briefly since FGeometryCollectionConversion::AppendStaticMesh puts new // geometry at the root, but this is very quickly fixed up in those situations, see AppendMeshesToGeometryCollection TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { TArray RootBones; FGeometryCollectionClusteringUtility::GetRootBones(GeometryCollection, RootBones); return RootBones[0]; } check(false); return -1; } void FGeometryCollectionCommandCommon::AppendMeshesToGeometryCollection(TArray& SelectedMeshes, UEditableMesh* SourceMesh, FTransform &SourceActorTransform, UGeometryCollection* GeometryCollectionObject, bool DeleteSourceMesh, TArray& OutNewNodeElements) { if (GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { AddAdditionalAttributesIfRequired(GeometryCollectionObject); for (UEditableMesh* EditableMesh : SelectedMeshes) { // don't want to add duplicate of itself if (EditableMesh == SourceMesh) continue; UStaticMesh* StaticMesh = GetStaticMesh(EditableMesh); AActor* MeshActor = GetEditableMeshActor(EditableMesh); FTransform MeshTransform = MeshActor->GetTransform(); MeshTransform.SetLocation(MeshTransform.GetLocation() - SourceActorTransform.GetLocation()); //this should be Parent relative transform FGeometryCollectionConversion::AppendStaticMesh(StaticMesh, MeshTransform, GeometryCollectionObject, false); // fix up the additional information required by fracture UI slider TSharedRef > ExplodedVectorsArray = GeometryCollection->GetAttribute("ExplodedVector", FGeometryCollection::TransformGroup); TSharedRef > ExplodedTransformsArray = GeometryCollection->GetAttribute("ExplodedTransform", FGeometryCollection::TransformGroup); TManagedArray& ExplodedTransforms = *ExplodedTransformsArray; TManagedArray& ExplodedVectors = *ExplodedVectorsArray; int LastElement = GeometryCollection->NumElements(FGeometryCollection::TransformGroup) - 1; ExplodedVectors[LastElement] = MeshTransform.GetLocation(); ExplodedTransforms[LastElement] = MeshTransform; TManagedArray& BoneName = *GeometryCollection->BoneName; BoneName[LastElement] = "Root"; OutNewNodeElements.Add(LastElement); if (DeleteSourceMesh) { RemoveActor(MeshActor); } } GeometryCollection->ReindexMaterials(); } } } void FGeometryCollectionCommandCommon::MergeSelections(const UGeometryCollectionComponent* SourceComponent, const TArray& SelectionB, TArray& MergedSelectionOut) { if (SourceComponent) { for (int32 NewElement : SourceComponent->GetSelectedBones()) { MergedSelectionOut.AddUnique(NewElement); } } for (int32 NewElement : SelectionB) { MergedSelectionOut.AddUnique(NewElement); } } void FGeometryCollectionCommandCommon::GetCenterOfBone(UGeometryCollection* GeometryCollectionObject, int Element, FVector& CentreOut) { if (GeometryCollectionObject) { TSharedPtr GeometryCollectionPtr = GeometryCollectionObject->GetGeometryCollection(); if (FGeometryCollection* GeometryCollection = GeometryCollectionPtr.Get()) { TArray Transforms; GeometryCollectionAlgo::GlobalMatrices(GeometryCollection, Transforms); check(GeometryCollection); const TManagedArray& Hierarchy = *GeometryCollection->BoneHierarchy; FVector SumCOM(0, 0, 0); int Count = 0; CombineCenterOfGeometryRecursive(Transforms, Hierarchy, Element, SumCOM, Count); if (Count > 0) { SumCOM /= Count; } CentreOut = SumCOM; } } } void FGeometryCollectionCommandCommon::CombineCenterOfGeometryRecursive(TArray& Transforms, const TManagedArray& Hierarchy, int Element, FVector& SumCOMOut, int& CountOut) { if (Hierarchy[Element].IsGeometry()) { SumCOMOut += Transforms[Element].GetLocation(); CountOut++; } for (int ChildElement : Hierarchy[Element].Children) { CombineCenterOfGeometryRecursive(Transforms, Hierarchy, ChildElement, SumCOMOut, CountOut); } } TArray FGeometryCollectionCommandCommon::GetSelectedActors() { USelection* SelectedActors = GEditor->GetSelectedActors(); TArray Actors; for (FSelectionIterator Iter(*SelectedActors); Iter; ++Iter) { AActor* Actor = Cast(*Iter); if (Actor) { Actors.Add(Actor); } } return Actors; } #undef LOCTEXT_NAMESPACE