void DecomposeUCXMesh( const TArray<FVector>& CollisionVertices, const TArray<int32>& CollisionFaceIdx, UBodySetup* BodySetup ) { // We keep no ref to this Model, so it will be GC'd at some point after the import. UModel* TempModel = new UModel(FPostConstructInitializeProperties(),NULL,1); FMeshConnectivityBuilder ConnectivityBuilder; // Send triangles to connectivity builder for(int32 x = 0;x < CollisionFaceIdx.Num();x += 3) { const FVector &VertexA = CollisionVertices[ CollisionFaceIdx[x + 2] ]; const FVector &VertexB = CollisionVertices[ CollisionFaceIdx[x + 1] ]; const FVector &VertexC = CollisionVertices[ CollisionFaceIdx[x + 0] ]; ConnectivityBuilder.AddTriangle( VertexA, VertexB, VertexC ); } ConnectivityBuilder.CreateConnectivityGroups(); // For each valid group build BSP and extract convex hulls for ( int32 i=0; i<ConnectivityBuilder.Groups.Num(); i++ ) { const FMeshConnectivityGroup &Group = ConnectivityBuilder.Groups[ i ]; // TODO: add some BSP friendly checks here // e.g. if group triangles form a closed mesh // Generate polygons from group triangles TempModel->Polys->Element.Empty(); for ( int32 j=0; j<Group.Triangles.Num(); j++ ) { const FMeshConnectivityTriangle &Triangle = ConnectivityBuilder.Triangles[ Group.Triangles[j] ]; FPoly* Poly = new( TempModel->Polys->Element ) FPoly(); Poly->Init(); Poly->iLink = j / 3; // Add vertices new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[0] ].Position ); new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[1] ].Position ); new( Poly->Vertices ) FVector( ConnectivityBuilder.Vertices[ Triangle.Vertices[2] ].Position ); // Update polygon normal Poly->CalcNormal(1); } // Build bounding box. TempModel->BuildBound(); // Build BSP for the brush. FBSPOps::bspBuild( TempModel,FBSPOps::BSP_Good,15,70,1,0 ); FBSPOps::bspRefresh( TempModel, 1 ); FBSPOps::bspBuildBounds( TempModel ); // Convert collision model into a collection of convex hulls. // Generated convex hulls will be added to existing ones BodySetup->CreateFromModel( TempModel, false ); } }
void DeselectAllSurfacesInLevel(ULevel* InLevel) { if(InLevel) { UModel* Model = InLevel->Model; for( int32 SurfaceIndex = 0 ; SurfaceIndex < Model->Surfs.Num() ; ++SurfaceIndex ) { FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if( Surf.PolyFlags & PF_Selected ) { Model->ModifySurf( SurfaceIndex, false ); Surf.PolyFlags &= ~PF_Selected; } } } }
/** * Deselects all BSP surfaces in the specified level. * * @param Level The level for which to deselect all levels. * @return The number of surfaces that were deselected */ static uint32 DeselectAllSurfacesForLevel(ULevel* Level) { uint32 NumSurfacesDeselected = 0; if ( Level ) { UModel* Model = Level->Model; for( int32 SurfaceIndex = 0 ; SurfaceIndex < Model->Surfs.Num() ; ++SurfaceIndex ) { FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if( Surf.PolyFlags & PF_Selected ) { Model->ModifySurf( SurfaceIndex, false ); Surf.PolyFlags &= ~PF_Selected; ++NumSurfacesDeselected; } } } return NumSurfacesDeselected; }
void FLevelModel::DeselectAllSurfaces() { ULevel* Level = GetLevelObject(); if (Level == NULL) { return; } UModel* Model = Level->Model; for (int32 SurfaceIndex = 0; SurfaceIndex < Model->Surfs.Num(); ++SurfaceIndex) { FBspSurf& Surf = Model->Surfs[SurfaceIndex]; if (Surf.PolyFlags & PF_Selected) { Model->ModifySurf(SurfaceIndex, false); Surf.PolyFlags&= ~PF_Selected; } } }
bool FEdModeTexture::StartTracking(FEditorViewportClient* InViewportClient, FViewport* InViewport) { // call base version because it calls the StartModify() virtual method needed to track drag events bool BaseRtn = FEdMode::StartTracking(InViewportClient, InViewport); // Complete the previous transaction if one exists if( ScopedTransaction ) { EndTracking(InViewportClient, InViewport); } // Start a new transaction ScopedTransaction = new FScopedTransaction( NSLOCTEXT("UnrealEd", "TextureManipulation", "Texture Manipulation") ); for( FConstLevelIterator Iterator = GetWorld()->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; Model->ModifySelectedSurfs( true ); } return BaseRtn; }
void UEditorEngine::polySelectMatchingResolution(UWorld* InWorld, bool bCurrentLevelOnly) { // true if at least one surface was selected. bool bSurfaceWasSelected = false; TArray<float> SelectedResolutions; if (bCurrentLevelOnly == true) { for (TSelectedSurfaceIterator<FCurrentLevelSurfaceLevelFilter> It(InWorld); It; ++It) { SelectedResolutions.AddUnique(It->LightMapScale); } if (SelectedResolutions.Num() > 0) { if (SelectedResolutions.Num() > 1) { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "BSPSelect_DifferentResolutionsSelected", "Different selected resolutions.\nCan only select matching for a single resolution.")); } else { // Select all surfaces with matching materials. for (TSurfaceIterator<FCurrentLevelSurfaceLevelFilter> It(InWorld); It; ++It) { if (SelectedResolutions.Contains(It->LightMapScale)) { UModel* Model = It.GetModel(); const int32 SurfaceIndex = It.GetSurfaceIndex(); Model->ModifySurf( SurfaceIndex, 0 ); GEditor->SelectBSPSurf( Model, SurfaceIndex, true, false ); bSurfaceWasSelected = true; } } } } } else { for (TSelectedSurfaceIterator<> It(InWorld); It; ++It) { SelectedResolutions.AddUnique(It->LightMapScale); } if (SelectedResolutions.Num() > 0) { if (SelectedResolutions.Num() > 1) { FMessageDialog::Open( EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "BSPSelect_DifferentResolutionsSelected", "Different selected resolutions.\nCan only select matching for a single resolution.")); } else { // Select all surfaces with matching materials. for (TSurfaceIterator<> It(InWorld); It; ++It) { if (SelectedResolutions.Contains(It->LightMapScale)) { UModel* Model = It.GetModel(); const int32 SurfaceIndex = It.GetSurfaceIndex(); Model->ModifySurf( SurfaceIndex, 0 ); GEditor->SelectBSPSurf( Model, SurfaceIndex, true, false ); bSurfaceWasSelected = true; } } } } } if ( bSurfaceWasSelected ) { NoteSelectionChange(); } }
void UEditorEngine::polySelectMatchingMaterial(UWorld* InWorld, bool bCurrentLevelOnly) { // true if at least one surface was selected. bool bSurfaceWasSelected = false; // true if default material representations have already been added to the materials list. bool bDefaultMaterialAdded = false; TArray<UMaterialInterface*> Materials; if ( bCurrentLevelOnly ) { // Get list of unique materials that are on selected faces. for ( TSelectedSurfaceIterator<FCurrentLevelSurfaceLevelFilter> It(InWorld) ; It ; ++It ) { if ( It->Material && It->Material != UMaterial::GetDefaultMaterial(MD_Surface) ) { Materials.AddUnique( It->Material ); } else if ( !bDefaultMaterialAdded ) { bDefaultMaterialAdded = true; // Add both representations of the default material. Materials.AddUnique( NULL ); Materials.AddUnique( UMaterial::GetDefaultMaterial(MD_Surface) ); } } // Select all surfaces with matching materials. for ( TSurfaceIterator<FCurrentLevelSurfaceLevelFilter> It(InWorld) ; It ; ++It ) { // Map the default material to NULL, so that NULL assignments match manual default material assignments. if( Materials.Contains( It->Material ) ) { UModel* Model = It.GetModel(); const int32 SurfaceIndex = It.GetSurfaceIndex(); Model->ModifySurf( SurfaceIndex, 0 ); GEditor->SelectBSPSurf( Model, SurfaceIndex, true, false ); bSurfaceWasSelected = true; } } } else { // Get list of unique materials that are on selected faces. for ( TSelectedSurfaceIterator<> It(InWorld) ; It ; ++It ) { if ( It->Material && It->Material != UMaterial::GetDefaultMaterial(MD_Surface) ) { Materials.AddUnique( It->Material ); } else if ( !bDefaultMaterialAdded ) { bDefaultMaterialAdded = true; // Add both representations of the default material. Materials.AddUnique( NULL ); Materials.AddUnique( UMaterial::GetDefaultMaterial(MD_Surface) ); } } // Select all surfaces with matching materials. for ( TSurfaceIterator<> It(InWorld) ; It ; ++It ) { // Map the default material to NULL, so that NULL assignments match manual default material assignments. if( Materials.Contains( It->Material ) ) { UModel* Model = It.GetModel(); const int32 SurfaceIndex = It.GetSurfaceIndex(); Model->ModifySurf( SurfaceIndex, 0 ); GEditor->SelectBSPSurf( Model, SurfaceIndex, true, false ); bSurfaceWasSelected = true; } } } if ( bSurfaceWasSelected ) { NoteSelectionChange(); } }
/** * Selects all adjacent polygons (only coplanars if Coplanars==1) * @return Number of polygons newly selected. */ static int32 TagAdjacentsType(UWorld* InWorld, EAdjacentsType AdjacentType) { // Allocate GFlags1 check( GFlags1.Num() == 0 ); for( FConstLevelIterator Iterator = InWorld->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; uint8* Ptr = new uint8[MAX_uint16+1]; FMemory::Memzero( Ptr, MAX_uint16+1 ); GFlags1.Add( Ptr ); } FVert *VertPool; FVector *Base,*Normal; uint8 b; int32 i; int Selected,Found; Selected = 0; // Find all points corresponding to selected vertices: int32 ModelIndex1 = 0; for( FConstLevelIterator Iterator = InWorld->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; uint8* Flags1 = GFlags1[ModelIndex1++]; for (i=0; i<Model->Nodes.Num(); i++) { FBspNode &Node = Model->Nodes[i]; FBspSurf &Poly = Model->Surfs[Node.iSurf]; if (Poly.PolyFlags & PF_Selected) { VertPool = &Model->Verts[Node.iVertPool]; for (b=0; b<Node.NumVertices; b++) { Flags1[(VertPool++)->pVertex] = 1; } } } } // Select all unselected nodes for which two or more vertices are selected: ModelIndex1 = 0; int32 ModelIndex2 = -1; for( FConstLevelIterator Iterator = InWorld->GetLevelIterator(); Iterator; ++Iterator ) { UModel* Model = (*Iterator)->Model; uint8* Flags1 = GFlags1[ModelIndex1]; ModelIndex1++; ModelIndex2++; for( i = 0 ; i < Model->Nodes.Num() ; i++ ) { FBspNode &Node = Model->Nodes[i]; FBspSurf &Poly = Model->Surfs[Node.iSurf]; if (!(Poly.PolyFlags & PF_Selected)) { Found = 0; VertPool = &Model->Verts[Node.iVertPool]; // Base = &Model->Points [Poly.pBase]; Normal = &Model->Vectors[Poly.vNormal]; // for (b=0; b<Node.NumVertices; b++) Found += Flags1[(VertPool++)->pVertex]; // if (AdjacentType == ADJACENT_COPLANARS) { if (!GFlags2[ModelIndex2][Node.iSurf]) Found=0; } else if (AdjacentType == ADJACENT_FLOORS) { if (FMath::Abs(Normal->Z) <= 0.85) Found = 0; } else if (AdjacentType == ADJACENT_WALLS) { if (FMath::Abs(Normal->Z) >= 0.10) Found = 0; } else if (AdjacentType == ADJACENT_SLANTS) { if (FMath::Abs(Normal->Z) > 0.85) Found = 0; if (FMath::Abs(Normal->Z) < 0.10) Found = 0; } if (Found > 0) { Model->ModifySurf( Node.iSurf, 0 ); GEditor->SelectBSPSurf( Model, Node.iSurf, true, false ); Selected++; } } } } // Free GFlags1. for ( i = 0 ; i < GFlags1.Num() ; ++i ) { delete[] GFlags1[i]; } GFlags1.Empty(); GEditor->NoteSelectionChange(); return Selected; }
/** * Parse and import text as property values for the object specified. This function should never be called directly - use ImportObjectProperties instead. * * @param ObjectStruct the struct for the data we're importing * @param DestData the location to import the property values to * @param SourceText pointer to a buffer containing the values that should be parsed and imported * @param SubobjectRoot when dealing with nested subobjects, corresponds to the top-most outer that * is not a subobject/template * @param SubobjectOuter the outer to use for creating subobjects/components. NULL when importing structdefaultproperties * @param Warn output device to use for log messages * @param Depth current nesting level * @param InstanceGraph contains the mappings of instanced objects and components to their templates * * @return NULL if the default values couldn't be imported */ static const TCHAR* ImportProperties( uint8* DestData, const TCHAR* SourceText, UStruct* ObjectStruct, UObject* SubobjectRoot, UObject* SubobjectOuter, FFeedbackContext* Warn, int32 Depth, FObjectInstancingGraph& InstanceGraph, const TMap<FName, AActor*>* ActorRemapper ) { check(!GIsUCCMakeStandaloneHeaderGenerator); check(ObjectStruct!=NULL); check(DestData!=NULL); if ( SourceText == NULL ) return NULL; // Cannot create subobjects when importing struct defaults, or if SubobjectOuter (used as the Outer for any subobject declarations encountered) is NULL bool bSubObjectsAllowed = !ObjectStruct->IsA(UScriptStruct::StaticClass()) && SubobjectOuter != NULL; // true when DestData corresponds to a subobject in a class default object bool bSubObject = false; UClass* ComponentOwnerClass = NULL; if ( bSubObjectsAllowed ) { bSubObject = SubobjectRoot != NULL && SubobjectRoot->HasAnyFlags(RF_ClassDefaultObject); if ( SubobjectRoot == NULL ) { SubobjectRoot = SubobjectOuter; } ComponentOwnerClass = SubobjectOuter != NULL ? SubobjectOuter->IsA(UClass::StaticClass()) ? CastChecked<UClass>(SubobjectOuter) : SubobjectOuter->GetClass() : NULL; } // The PortFlags to use for all ImportText calls uint32 PortFlags = PPF_Delimited | PPF_CheckReferences; if (GIsImportingT3D) { PortFlags |= PPF_AttemptNonQualifiedSearch; } FString StrLine; TArray<FDefinedProperty> DefinedProperties; // Parse all objects stored in the actor. // Build list of all text properties. bool ImportedBrush = 0; int32 LinesConsumed = 0; while (FParse::LineExtended(&SourceText, StrLine, LinesConsumed, true)) { // remove extra whitespace and optional semicolon from the end of the line { int32 Length = StrLine.Len(); while ( Length > 0 && (StrLine[Length - 1] == TCHAR(';') || StrLine[Length - 1] == TCHAR(' ') || StrLine[Length - 1] == 9) ) { Length--; } if (Length != StrLine.Len()) { StrLine = StrLine.Left(Length); } } if ( ContextSupplier != NULL ) { ContextSupplier->CurrentLine += LinesConsumed; } if (StrLine.Len() == 0) { continue; } const TCHAR* Str = *StrLine; int32 NewLineNumber; if( FParse::Value( Str, TEXT("linenumber="), NewLineNumber ) ) { if ( ContextSupplier != NULL ) { ContextSupplier->CurrentLine = NewLineNumber; } } else if( GetBEGIN(&Str,TEXT("Brush")) && ObjectStruct->IsChildOf(ABrush::StaticClass()) ) { // If SubobjectOuter is NULL, we are importing defaults for a UScriptStruct's defaultproperties block if ( !bSubObjectsAllowed ) { Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN BRUSH: Subobjects are not allowed in this context")); return NULL; } // Parse brush on this line. TCHAR BrushName[NAME_SIZE]; if( FParse::Value( Str, TEXT("Name="), BrushName, NAME_SIZE ) ) { // If an initialized brush with this name already exists in the level, rename the existing one. // It is deemed to be initialized if it has a non-zero poly count. // If it is uninitialized, the existing object will have been created by a forward reference in the import text, // and it will now be redefined. This relies on the behavior that NewObject<> will return an existing pointer // if an object with the same name and outer is passed. UModel* ExistingBrush = FindObject<UModel>( SubobjectRoot, BrushName ); if (ExistingBrush && ExistingBrush->Polys && ExistingBrush->Polys->Element.Num() > 0) { ExistingBrush->Rename(); } // Create model. UModelFactory* ModelFactory = NewObject<UModelFactory>(); ModelFactory->FactoryCreateText( UModel::StaticClass(), SubobjectRoot, FName(BrushName, FNAME_Add, true), RF_NoFlags, NULL, TEXT("t3d"), SourceText, SourceText+FCString::Strlen(SourceText), Warn ); ImportedBrush = 1; } } else if (GetBEGIN(&Str, TEXT("Foliage"))) { UFoliageType* SourceFoliageType; FName ComponentName; if (SubobjectRoot && ParseObject<UFoliageType>(Str, TEXT("FoliageType="), SourceFoliageType, ANY_PACKAGE) && FParse::Value(Str, TEXT("Component="), ComponentName) ) { UPrimitiveComponent* ActorComponent = FindObjectFast<UPrimitiveComponent>(SubobjectRoot, ComponentName); if (ActorComponent && ActorComponent->GetComponentLevel()) { AInstancedFoliageActor* IFA = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(ActorComponent->GetComponentLevel(), true); FFoliageMeshInfo* MeshInfo = nullptr; UFoliageType* FoliageType = IFA->AddFoliageType(SourceFoliageType, &MeshInfo); const TCHAR* StrPtr; FString TextLine; while (MeshInfo && FParse::Line(&SourceText, TextLine)) { StrPtr = *TextLine; if (GetEND(&StrPtr, TEXT("Foliage"))) { break; } // Parse the instance properties FFoliageInstance Instance; FString Temp; if (FParse::Value(StrPtr, TEXT("Location="), Temp, false)) { GetFVECTOR(*Temp, Instance.Location); } if (FParse::Value(StrPtr, TEXT("Rotation="), Temp, false)) { GetFROTATOR(*Temp, Instance.Rotation, 1); } if (FParse::Value(StrPtr, TEXT("PreAlignRotation="), Temp, false)) { GetFROTATOR(*Temp, Instance.PreAlignRotation, 1); } if (FParse::Value(StrPtr, TEXT("DrawScale3D="), Temp, false)) { GetFVECTOR(*Temp, Instance.DrawScale3D); } FParse::Value(StrPtr, TEXT("Flags="), Instance.Flags); // Add the instance MeshInfo->AddInstance(IFA, FoliageType, Instance, ActorComponent); } } } } else if( GetBEGIN(&Str,TEXT("Object"))) { // If SubobjectOuter is NULL, we are importing defaults for a UScriptStruct's defaultproperties block if ( !bSubObjectsAllowed ) { Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: Subobjects are not allowed in this context")); return NULL; } // Parse subobject default properties. // Note: default properties subobjects have compiled class as their Outer (used for localization). UClass* TemplateClass = NULL; bool bInvalidClass = false; ParseObject<UClass>(Str, TEXT("Class="), TemplateClass, ANY_PACKAGE, &bInvalidClass); if (bInvalidClass) { Warn->Logf(ELogVerbosity::Error,TEXT("BEGIN OBJECT: Invalid class specified: %s"), *StrLine); return NULL; } // parse the name of the template FName TemplateName = NAME_None; FParse::Value(Str,TEXT("Name="),TemplateName); if(TemplateName == NAME_None) { Warn->Logf(ELogVerbosity::Error,TEXT("BEGIN OBJECT: Must specify valid name for subobject/component: %s"), *StrLine); return NULL; } // points to the parent class's template subobject/component, if we are overriding a subobject/component declared in our parent class UObject* BaseTemplate = NULL; bool bRedefiningSubobject = false; if( TemplateClass ) { } else { // next, verify that a template actually exists in the parent class UClass* ParentClass = ComponentOwnerClass->GetSuperClass(); check(ParentClass); UObject* ParentCDO = ParentClass->GetDefaultObject(); check(ParentCDO); BaseTemplate = StaticFindObjectFast(UObject::StaticClass(), SubobjectOuter, TemplateName); bRedefiningSubobject = (BaseTemplate != NULL); if (BaseTemplate == NULL) { BaseTemplate = StaticFindObjectFast(UObject::StaticClass(), ParentCDO, TemplateName); } if ( BaseTemplate == NULL ) { // wasn't found Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: No base template named %s found in parent class %s: %s"), *TemplateName.ToString(), *ParentClass->GetName(), *StrLine); return NULL; } TemplateClass = BaseTemplate->GetClass(); } // because the outer won't be a default object checkSlow(TemplateClass != NULL); if (bRedefiningSubobject) { // since we're redefining an object in the same text block, only need to import properties again SourceText = ImportObjectProperties( (uint8*)BaseTemplate, SourceText, TemplateClass, SubobjectRoot, BaseTemplate, Warn, Depth + 1, ContextSupplier ? ContextSupplier->CurrentLine : 0, &InstanceGraph, ActorRemapper ); } else { UObject* Archetype = NULL; UObject* ComponentTemplate = NULL; // Since we are changing the class we can't use the Archetype, // however that is fine since we will have been editing the CDO anyways if (!FBlueprintEditorUtils::IsAnonymousBlueprintClass(SubobjectOuter->GetClass())) { // if an archetype was specified in the Begin Object block, use that as the template for the ConstructObject call. FString ArchetypeName; if (FParse::Value(Str, TEXT("Archetype="), ArchetypeName)) { // if given a name, break it up along the ' so separate the class from the name FString ObjectClass; FString ObjectPath; if ( FPackageName::ParseExportTextPath(ArchetypeName, &ObjectClass, &ObjectPath) ) { // find the class UClass* ArchetypeClass = (UClass*)StaticFindObject(UClass::StaticClass(), ANY_PACKAGE, *ObjectClass); if (ArchetypeClass) { // if we had the class, find the archetype Archetype = StaticFindObject(ArchetypeClass, ANY_PACKAGE, *ObjectPath); } } } } if (SubobjectOuter->HasAnyFlags(RF_ClassDefaultObject)) { if (!Archetype) // if an archetype was specified explicitly, we will stick with that { Archetype = ComponentOwnerClass->GetDefaultSubobjectByName(TemplateName); if(Archetype) { if ( BaseTemplate == NULL ) { // BaseTemplate should only be NULL if the Begin Object line specified a class Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: The component name %s is already used (if you want to override the component, don't specify a class): %s"), *TemplateName.ToString(), *StrLine); return NULL; } // the component currently in the component template map and the base template should be the same checkf(Archetype==BaseTemplate,TEXT("OverrideComponent: '%s' BaseTemplate: '%s'"), *Archetype->GetFullName(), *BaseTemplate->GetFullName()); } } } else // handle the non-template case (subobjects and non-template components) { // don't allow Actor-derived subobjects if ( TemplateClass->IsChildOf(AActor::StaticClass()) ) { Warn->Logf(ELogVerbosity::Error,TEXT("Cannot create subobjects from Actor-derived classes: %s"), *StrLine); return NULL; } ComponentTemplate = FindObject<UObject>(SubobjectOuter, *TemplateName.ToString()); if (ComponentTemplate != NULL) { // if we're overriding a subobject declared in a parent class, we should already have an object with that name that // was instanced when ComponentOwnerClass's CDO was initialized; if so, it's archetype should be the BaseTemplate. If it // isn't, then there are two unrelated subobject definitions using the same name. if ( ComponentTemplate->GetArchetype() != BaseTemplate ) { } else if ( BaseTemplate == NULL ) { // BaseTemplate should only be NULL if the Begin Object line specified a class Warn->Logf(ELogVerbosity::Error, TEXT("BEGIN OBJECT: A subobject named %s is already declared in a parent class. If you intended to override that subobject, don't specify a class in the derived subobject definition: %s"), *TemplateName.ToString(), *StrLine); return NULL; } } } // Propagate object flags to the sub object. EObjectFlags NewFlags = SubobjectOuter->GetMaskedFlags( RF_PropagateToSubObjects ); if (!Archetype) // no override and we didn't find one from the class table, so go with the base { Archetype = BaseTemplate; } UObject* OldComponent = NULL; if (ComponentTemplate) { bool bIsOkToReuse = ComponentTemplate->GetClass() == TemplateClass && ComponentTemplate->GetOuter() == SubobjectOuter && ComponentTemplate->GetFName() == TemplateName && (ComponentTemplate->GetArchetype() == Archetype || !Archetype); if (!bIsOkToReuse) { UE_LOG(LogEditorObject, Log, TEXT("Could not reuse component instance %s, name clash?"), *ComponentTemplate->GetFullName()); ComponentTemplate->Rename(); // just abandon the existing component, we are going to create OldComponent = ComponentTemplate; ComponentTemplate = NULL; } } if (!ComponentTemplate) { ComponentTemplate = NewObject<UObject>( SubobjectOuter, TemplateClass, TemplateName, NewFlags, Archetype, !!SubobjectOuter, &InstanceGraph ); } else { // We do not want to set RF_Transactional for construction script created components, so we have to monkey with things here if (NewFlags & RF_Transactional) { UActorComponent* Component = Cast<UActorComponent>(ComponentTemplate); if (Component && Component->IsCreatedByConstructionScript()) { NewFlags &= ~RF_Transactional; } } // Make sure desired flags are set - existing object could be pending kill ComponentTemplate->ClearFlags(RF_AllFlags); ComponentTemplate->SetFlags(NewFlags); } // replace all properties in this subobject outer' class that point to the original subobject with the new subobject TMap<UObject*, UObject*> ReplacementMap; if (Archetype) { checkSlow(ComponentTemplate->GetArchetype() == Archetype); ReplacementMap.Add(Archetype, ComponentTemplate); InstanceGraph.AddNewInstance(ComponentTemplate); } if (OldComponent) { ReplacementMap.Add(OldComponent, ComponentTemplate); } FArchiveReplaceObjectRef<UObject> ReplaceAr(SubobjectOuter, ReplacementMap, false, false, true); // import the properties for the subobject SourceText = ImportObjectProperties( (uint8*)ComponentTemplate, SourceText, TemplateClass, SubobjectRoot, ComponentTemplate, Warn, Depth+1, ContextSupplier ? ContextSupplier->CurrentLine : 0, &InstanceGraph, ActorRemapper ); } } else if( FParse::Command(&Str,TEXT("CustomProperties"))) { check(SubobjectOuter); SubobjectOuter->ImportCustomProperties(Str, Warn); } else if( GetEND(&Str,TEXT("Actor")) || GetEND(&Str,TEXT("DefaultProperties")) || GetEND(&Str,TEXT("structdefaultproperties")) || (GetEND(&Str,TEXT("Object")) && Depth) ) { // End of properties. break; } else if( GetREMOVE(&Str,TEXT("Component")) ) { checkf(false, TEXT("Remove component is illegal in pasted text")); } else { // Property. UProperty::ImportSingleProperty(Str, DestData, ObjectStruct, SubobjectOuter, PortFlags, Warn, DefinedProperties); } } if (ActorRemapper) { for (const auto& DefinedProperty : DefinedProperties) { RemapProperty(DefinedProperty.Property, DefinedProperty.Index, *ActorRemapper, DestData); } } // Prepare brush. if( ImportedBrush && ObjectStruct->IsChildOf<ABrush>() && !ObjectStruct->IsChildOf<AVolume>() ) { check(GIsEditor); ABrush* Actor = (ABrush*)DestData; check(Actor->GetBrushComponent()); if( Actor->GetBrushComponent()->Mobility == EComponentMobility::Static ) { // Prepare static brush. Actor->SetNotForClientOrServer(); } else { // Prepare moving brush. FBSPOps::csgPrepMovingBrush( Actor ); } } return SourceText; }
void SetLevelVisibility(ULevel* Level, bool bShouldBeVisible, bool bForceLayersVisible) { // Nothing to do if ( Level == NULL ) { return; } // Handle the case of the p-level // The p-level can't be unloaded, so its actors/BSP should just be temporarily hidden/unhidden // Also, intentionally do not force layers visible for the p-level if ( Level->IsPersistentLevel() ) { //create a transaction so we can undo the visibilty toggle const FScopedTransaction Transaction( LOCTEXT( "ToggleLevelVisibility", "Toggle Level Visibility" ) ); if ( Level->bIsVisible != bShouldBeVisible ) { Level->Modify(); } // Set the visibility of each actor in the p-level for ( TArray<AActor*>::TIterator PLevelActorIter( Level->Actors ); PLevelActorIter; ++PLevelActorIter ) { AActor* CurActor = *PLevelActorIter; if ( CurActor && !FActorEditorUtils::IsABuilderBrush(CurActor) && CurActor->bHiddenEdLevel == bShouldBeVisible ) { CurActor->Modify(); CurActor->bHiddenEdLevel = !bShouldBeVisible; CurActor->RegisterAllComponents(); CurActor->MarkComponentsRenderStateDirty(); } } // Set the visibility of each BSP surface in the p-level UModel* CurLevelModel = Level->Model; if ( CurLevelModel ) { CurLevelModel->Modify(); for ( TArray<FBspSurf>::TIterator SurfaceIterator( CurLevelModel->Surfs ); SurfaceIterator; ++SurfaceIterator ) { FBspSurf& CurSurf = *SurfaceIterator; CurSurf.bHiddenEdLevel = !bShouldBeVisible; } } // Add/remove model components from the scene for(int32 ComponentIndex = 0; ComponentIndex < Level->ModelComponents.Num(); ComponentIndex++) { UModelComponent* CurLevelModelCmp = Level->ModelComponents[ComponentIndex]; if(CurLevelModelCmp) { if (bShouldBeVisible && CurLevelModelCmp) { CurLevelModelCmp->RegisterComponentWithWorld(Level->OwningWorld); } else if (!bShouldBeVisible && CurLevelModelCmp->IsRegistered()) { CurLevelModelCmp->UnregisterComponent(); } } } FEditorSupportDelegates::RedrawAllViewports.Broadcast(); } else { ULevelStreaming* StreamingLevel = NULL; if (Level->OwningWorld == NULL || Level->OwningWorld->PersistentLevel != Level ) { StreamingLevel = FLevelUtils::FindStreamingLevel( Level ); } // If were hiding a level, lets make sure to close the level transform mode if its the same level currently selected for edit FEdModeLevel* LevelMode = static_cast<FEdModeLevel*>(GEditorModeTools().GetActiveMode( FBuiltinEditorModes::EM_Level )); if( LevelMode && LevelMode->IsEditing( StreamingLevel ) ) { GEditorModeTools().DeactivateMode( FBuiltinEditorModes::EM_Level ); } //create a transaction so we can undo the visibilty toggle const FScopedTransaction Transaction( LOCTEXT( "ToggleLevelVisibility", "Toggle Level Visibility" ) ); // Handle the case of a streaming level if ( StreamingLevel ) { // We need to set the RF_Transactional to make a streaming level serialize itself. so store the original ones, set the flag, and put the original flags back when done EObjectFlags cachedFlags = StreamingLevel->GetFlags(); StreamingLevel->SetFlags( RF_Transactional ); StreamingLevel->Modify(); StreamingLevel->SetFlags( cachedFlags ); // Set the visibility state for this streaming level. StreamingLevel->bShouldBeVisibleInEditor = bShouldBeVisible; } if( !bShouldBeVisible ) { GEditor->Layers->RemoveLevelLayerInformation( Level ); } // UpdateLevelStreaming sets Level->bIsVisible directly, so we need to make sure it gets saved to the transaction buffer. if ( Level->bIsVisible != bShouldBeVisible ) { Level->Modify(); } if ( StreamingLevel ) { Level->OwningWorld->FlushLevelStreaming(); // In the Editor we expect this operation will complete in a single call check(Level->bIsVisible == bShouldBeVisible); } else if (Level->OwningWorld != NULL) { // In case we level has no associated StreamingLevel, remove or add to world directly if (bShouldBeVisible) { if (!Level->bIsVisible) { Level->OwningWorld->AddToWorld(Level); } } else { Level->OwningWorld->RemoveFromWorld(Level); } // In the Editor we expect this operation will complete in a single call check(Level->bIsVisible == bShouldBeVisible); } if( bShouldBeVisible ) { GEditor->Layers->AddLevelLayerInformation( Level ); } // Force the level's layers to be visible, if desired FEditorSupportDelegates::RedrawAllViewports.Broadcast(); // Iterate over the level's actors, making a list of their layers and unhiding the layers. TTransArray<AActor*>& Actors = Level->Actors; for ( int32 ActorIndex = 0 ; ActorIndex < Actors.Num() ; ++ActorIndex ) { AActor* Actor = Actors[ ActorIndex ]; if ( Actor ) { bool bModified = false; if ( bShouldBeVisible && bForceLayersVisible && GEditor->Layers->IsActorValidForLayer( Actor ) ) { // Make the actor layer visible, if it's not already. if ( Actor->bHiddenEdLayer ) { bModified = Actor->Modify(); Actor->bHiddenEdLayer = false; } const bool bIsVisible = true; GEditor->Layers->SetLayersVisibility( Actor->Layers, bIsVisible ); } // Set the visibility of each actor in the streaming level if ( !FActorEditorUtils::IsABuilderBrush(Actor) && Actor->bHiddenEdLevel == bShouldBeVisible ) { if ( !bModified ) { bModified = Actor->Modify(); } Actor->bHiddenEdLevel = !bShouldBeVisible; if (bShouldBeVisible) { Actor->ReregisterAllComponents(); } else { Actor->UnregisterAllComponents(); } } } } } FEditorDelegates::RefreshLayerBrowser.Broadcast(); // Notify the Scene Outliner, as new Actors may be present in the world. GEngine->BroadcastLevelActorsChanged(); // If the level is being hidden, deselect actors and surfaces that belong to this level. if ( !bShouldBeVisible ) { USelection* SelectedActors = GEditor->GetSelectedActors(); SelectedActors->Modify(); TTransArray<AActor*>& Actors = Level->Actors; for ( int32 ActorIndex = 0 ; ActorIndex < Actors.Num() ; ++ActorIndex ) { AActor* Actor = Actors[ ActorIndex ]; if ( Actor ) { SelectedActors->Deselect( Actor ); } } DeselectAllSurfacesInLevel(Level); // Tell the editor selection status was changed. GEditor->NoteSelectionChange(); } Level->bIsVisible = bShouldBeVisible; }