void FEdModeGeometry::PostUndo() { // Rebuild the geometry data from the current brush state GetFromSource(); // Restore selection information. for( int32 o = 0 ; o < GeomObjects.Num() ; ++o ) { int32 Idx = 0; FGeomObject* go = GeomObjects[o]; ABrush* Actor = go->GetActualBrush(); // First, clear the current selection go->SelectNone(); // Next, restore the cached selection int32 res = go->SetPivotFromSelectionArray( Actor->SavedSelections ); //use the centre of the actor if we didnt find a suitable selection if( res == INDEX_NONE ) { FEditorModeTools& Tools = GEditorModeTools(); Tools.SetPivotLocation( Actor->GetActorLocation() , false ); } go->ForceLastSelectionIndex( res ); } }
void UTexAlignerDefault::AlignSurf( ETexAlign InTexAlignType, UModel* InModel, FBspSurfIdx* InSurfIdx, FPoly* InPoly, FVector* InNormal ) { InPoly->Base = InPoly->Vertices[0]; InPoly->TextureU = FVector::ZeroVector; InPoly->TextureV = FVector::ZeroVector; InPoly->Finalize( NULL, 0 ); InPoly->TextureU *= UTile; InPoly->TextureV *= VTile; ABrush* Actor = InSurfIdx->Surf->Actor; const FVector PrePivot = Actor->GetPivotOffset(); const FVector Location = Actor->GetActorLocation(); const FRotator Rotation = Actor->GetActorRotation(); const FVector Scale = Actor->GetActorScale(); const FRotationMatrix RotMatrix(Rotation); FVector Base = RotMatrix.TransformVector((InPoly->Base - PrePivot) * Scale) + Location; FVector TextureU = RotMatrix.TransformVector(InPoly->TextureU / Scale); FVector TextureV = RotMatrix.TransformVector(InPoly->TextureV / Scale); InSurfIdx->Surf->pBase = FBSPOps::bspAddPoint(InModel, &Base, 0); InSurfIdx->Surf->vTextureU = FBSPOps::bspAddVector( InModel, &TextureU, 0); InSurfIdx->Surf->vTextureV = FBSPOps::bspAddVector( InModel, &TextureV, 0); }
void UEditorEngine::polyUpdateMaster ( UModel* Model, int32 iSurf, int32 UpdateTexCoords ) { FBspSurf &Surf = Model->Surfs[iSurf]; ABrush* Actor = Surf.Actor; if( !Actor ) return; UModel* Brush = Actor->Brush; check(Brush); FVector ActorLocation; FVector ActorPrePivot; FVector ActorScale; FRotator ActorRotation; if (Brush->bCachedOwnerTransformValid) { // Use transform cached when the geometry was last built, in case the current Actor transform has changed since then // (e.g. because Auto Update BSP is disabled) ActorLocation = Brush->OwnerLocationWhenLastBuilt; ActorPrePivot = Brush->OwnerPrepivotWhenLastBuilt; ActorScale = Brush->OwnerScaleWhenLastBuilt; ActorRotation = -Brush->OwnerRotationWhenLastBuilt; } else { // No cached owner transform, so use the current one ActorLocation = Actor->GetActorLocation(); ActorPrePivot = Actor->GetPrePivot(); ActorScale = Actor->GetActorScale(); ActorRotation = -Actor->GetActorRotation(); } for( int32 iEdPoly = Surf.iBrushPoly; iEdPoly < Brush->Polys->Element.Num(); iEdPoly++ ) { FPoly& MasterEdPoly = Brush->Polys->Element[iEdPoly]; if( iEdPoly==Surf.iBrushPoly || MasterEdPoly.iLink==Surf.iBrushPoly ) { MasterEdPoly.Material = Surf.Material; MasterEdPoly.PolyFlags = Surf.PolyFlags & ~(PF_NoEdit); if( UpdateTexCoords ) { MasterEdPoly.Base = ActorRotation.RotateVector(Model->Points[Surf.pBase] - ActorLocation) / ActorScale + ActorPrePivot; MasterEdPoly.TextureU = ActorRotation.RotateVector(Model->Vectors[Surf.vTextureU]) * ActorScale; MasterEdPoly.TextureV = ActorRotation.RotateVector(Model->Vectors[Surf.vTextureV]) * ActorScale; } } } Model->InvalidSurfaces = true; }
void UEditorEngine::mapBrushPut() { TArray<FEdMode*> ActiveModes; GLevelEditorModeTools().GetActiveModes( ActiveModes ); for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = static_cast<AActor*>( *It ); checkSlow( Actor->IsA(AActor::StaticClass()) ); ABrush* BrushActor = Cast< ABrush >( Actor ); if( BrushActor && !FActorEditorUtils::IsABuilderBrush(Actor) ) { check( BrushActor->GetWorld() ); ABrush* WorldBrush = BrushActor->GetWorld()->GetDefaultBrush(); check( WorldBrush ); BrushActor->Modify(); BrushActor->Brush->Polys->Element = WorldBrush->Brush->Polys->Element; BrushActor->CopyPosRotScaleFrom( WorldBrush ); BrushActor->SetNeedRebuild(BrushActor->GetLevel()); WorldBrush->ReregisterAllComponents(); for( int32 ModeIndex = 0; ModeIndex < ActiveModes.Num(); ++ModeIndex ) { ActiveModes[ModeIndex]->UpdateInternalData(); } } } }
/** * Adds a brush to the list of CSG brushes in the level, using a CSG operation. * * @return A newly-created copy of the brush. */ ABrush* FBSPOps::csgAddOperation( ABrush* Actor, uint32 PolyFlags, EBrushType BrushType) { check(Actor); check(Actor->BrushComponent); check(Actor->Brush); check(Actor->Brush->Polys); check(Actor->GetWorld()); // Can't do this if brush has no polys. if( !Actor->Brush->Polys->Element.Num() ) return NULL; // Spawn a new actor for the brush. ABrush* Result = Actor->GetWorld()->SpawnBrush(); Result->SetNotForClientOrServer(); // Duplicate the brush. csgCopyBrush ( Result, Actor, PolyFlags, RF_Transactional, 0, true ); check(Result->Brush); if( Result->GetBrushBuilder() ) { Result->SetActorLabel( FText::Format( NSLOCTEXT("BSPBrushOps", "BrushName", "{0} Brush"), FText::FromString( Result->GetBrushBuilder()->GetClass()->GetDescription() ) ).ToString() ); } // Assign the default material to the brush's polys. for( int32 i=0; i<Result->Brush->Polys->Element.Num(); i++ ) { FPoly& CurrentPoly = Result->Brush->Polys->Element[i]; if ( !CurrentPoly.Material ) { CurrentPoly.Material = UMaterial::GetDefaultMaterial(MD_Surface); } } // Set add-info. Result->BrushType = BrushType; Result->ReregisterAllComponents(); return Result; }
void UEditorEngine::MapSetBrush( UWorld* InWorld, EMapSetBrushFlags PropertiesMask, uint16 BrushColor, FName GroupName, uint32 SetPolyFlags, uint32 ClearPolyFlags, uint32 BrushType, int32 DrawType ) { // Fire ULevel::LevelDirtiedEvent when falling out of scope. FScopedLevelDirtied LevelDirtyCallback; for( FStaticBrushIterator It(InWorld); It; ++It ) { ABrush* Brush = CastChecked<ABrush>(*It); if( !FActorEditorUtils::IsABuilderBrush(Brush) && Brush->IsSelected() ) { if( PropertiesMask & MSB_PolyFlags ) { Brush->Modify(); Brush->PolyFlags = (Brush->PolyFlags & ~ClearPolyFlags) | SetPolyFlags; Brush->UpdateComponentTransforms(); Brush->MarkPackageDirty(); LevelDirtyCallback.Request(); } if( PropertiesMask & MSB_BrushType ) { Brush->Modify(); Brush->BrushType = EBrushType(BrushType); Brush->UpdateComponentTransforms(); Brush->MarkPackageDirty(); LevelDirtyCallback.Request(); } } } }
FVector FEdModeTexture::GetWidgetLocation() const { for ( TSelectedSurfaceIterator<> It(GetWorld()) ; It ; ++It ) { FBspSurf* Surf = *It; ABrush* BrushActor = ( ABrush* )Surf->Actor; if( BrushActor ) { FPoly* poly = &BrushActor->Brush->Polys->Element[ Surf->iBrushPoly ]; return BrushActor->ActorToWorld().TransformPosition( poly->GetMidPoint() ); } } return FEdMode::GetWidgetLocation(); }
void ACanvasSkia::_SetPaint_Fill() { ABrush* br = GetBrush(); m_Paint.setStyle(SkPaint::kFill_Style); if( br->GetType() == BrushTypeSolidColor ) { ASolidBrush* sb = (ASolidBrush *)br; m_Paint.setColor(toSkColor(sb->GetColor())); } else if( br->GetType() == BrushTypeLinearGradient ) { SkPoint pts[2]; ALinearGradientBrush* lgb = (ALinearGradientBrush*)br; SkRect rx = ToSkRect( lgb->GetRect() ); pts[0].set( rx.fLeft,rx.fTop ); if( lgb->IsVert() ) pts[1].set( rx.fLeft,rx.fBottom ); else pts[1].set( rx.fRight,rx.fTop ); SkColor colors[2]; colors[0] = toSkColor( lgb->GetStartColor() ); colors[1] = toSkColor( lgb->GetEndColor() ); SkScalar pos[2]; pos[0] = SkScalar(0.0); pos[1] = SkScalar(1.0); SkShader* pShader = SkGradientShader::CreateLinear(pts,colors,pos,2,SkShader::kClamp_TileMode); m_Paint.setShader(pShader)->unref(); } else if( br->GetType() == BrushTypeRadialGradient ) { ARadialBrush* rb = (ARadialBrush*)br; SkPoint ptCenter; APoint ptCenter0 = rb->GetCenter(); ptCenter.set( SkIntToScalar(ptCenter0.x),SkIntToScalar(ptCenter0.y) ); SkScalar nRadius = SkIntToScalar( rb->GetRadius() ); SkColor colors[2]; colors[0] = toSkColor( rb->GetStartColor() ); colors[1] = toSkColor( rb->GetEndColor() ); SkScalar pos[2]; pos[0] = SkScalar(0.0); pos[1] = SkScalar(1.0); SkShader* pShader = SkGradientShader::CreateRadial(ptCenter,nRadius,colors,pos,2,SkShader::kClamp_TileMode); m_Paint.setShader(pShader)->unref(); } }
/** * Iterates to next suitable actor. */ void operator++() { bool FoundSuitableActor = false; while( !ReachedEnd && !FoundSuitableActor ) { if( ++ActorIndex >= World->GetCurrentLevel()->Actors.Num() ) { ReachedEnd = true; } else { //@todo locked levels - should we skip brushes contained by locked levels? ABrush* Brush = Cast<ABrush>(World->GetCurrentLevel()->Actors[ActorIndex]); FoundSuitableActor = Brush && Brush->IsStaticBrush(); } } }
void UEditorEngine::MapBrushGet(UWorld* InWorld) { for ( FSelectionIterator It( GEditor->GetSelectedActorIterator() ) ; It ; ++It ) { AActor* Actor = static_cast<AActor*>( *It ); checkSlow( Actor->IsA(AActor::StaticClass()) ); ABrush* BrushActor = Cast< ABrush >( Actor ); if( BrushActor && !FActorEditorUtils::IsABuilderBrush(Actor) ) { check( BrushActor->GetWorld() ); ABrush* WorldBrush = BrushActor->GetWorld()->GetDefaultBrush(); check( WorldBrush ); WorldBrush->Modify(); WorldBrush->Brush->Polys->Element = BrushActor->Brush->Polys->Element; WorldBrush->CopyPosRotScaleFrom( BrushActor ); WorldBrush->ReregisterAllComponents(); break; } } GEditor->SelectNone( false, true ); GEditor->SelectActor(InWorld->GetDefaultBrush(), true, true); }
bool FModeTool_GeometryModify::EndModify() { // Let the modifier finish up. if( CurrentModifier != NULL ) { FEdModeGeometry* mode = ((FEdModeGeometry*)GEditorModeTools().GetActiveMode(FBuiltinEditorModes::EM_Geometry)); // Update the source data to match the current geometry data. mode->SendToSource(); // Make sure the source data has remained viable. if( mode->FinalizeSourceData() ) { // If the source data was modified, reconstruct the geometry data to reflect that. mode->GetFromSource(); } CurrentModifier->EndModify(); // Update internals. for( FEdModeGeometry::TGeomObjectIterator Itor( mode->GeomObjectItor() ) ; Itor ; ++Itor ) { FGeomObject* go = *Itor; go->ComputeData(); FBSPOps::bspUnlinkPolys( go->GetActualBrush()->Brush ); // If geometry was actually changed, call PostEditBrush if(bGeomModified) { ABrush* Brush = go->GetActualBrush(); if(Brush) { if(!Brush->IsStaticBrush()) { FBSPOps::csgPrepMovingBrush(Brush); } } bGeomModified = false; } } } return 1; }
bool IsABuilderBrush( const AActor* InActor ) { bool bIsBuilder = false; #if WITH_EDITOR if ( InActor && InActor->GetWorld() && !InActor->HasAnyFlags(RF_ClassDefaultObject) ) { ULevel* ActorLevel = InActor->GetLevel(); if ((ActorLevel != nullptr) && (ActorLevel->Actors.Num() >= 2)) { // If the builder brush exists then it will be the 2nd actor in the actors array. ABrush* BuilderBrush = Cast<ABrush>(ActorLevel->Actors[1]); // If the second actor is not a brush then it certainly cannot be the builder brush. if ((BuilderBrush != nullptr) && (BuilderBrush->GetBrushComponent() != nullptr) && (BuilderBrush->Brush != nullptr)) { bIsBuilder = (BuilderBrush == InActor); } } } #endif return bIsBuilder; }
bool FEdModeGeometry::GetCustomDrawingCoordinateSystem( FMatrix& InMatrix, void* InData ) { if( GetSelectionState() == GSS_None ) { return 0; } if( InData ) { FGeomBase* GeomBase = static_cast<FGeomBase*>(InData); FGeomObjectPtr GeomObject = GeomBase->GetParentObject(); check(GeomObject.IsValid()); ABrush* Brush = GeomObject->GetActualBrush(); InMatrix = FRotationMatrix(GeomBase->GetNormal().Rotation()) * FQuatRotationMatrix(Brush->GetActorQuat()); } else { // If we don't have a specific geometry object to get the normal from // use the one that was last selected. for( int32 o = 0 ; o < GeomObjects.Num() ; ++o ) { FGeomObjectPtr go = GeomObjects[o]; go->CompileSelectionOrder(); if( go->SelectionOrder.Num() ) { FGeomBase* GeomBase = go->SelectionOrder[go->SelectionOrder.Num() - 1]; check(GeomBase != nullptr); FGeomObjectPtr GeomObject = GeomBase->GetParentObject(); check(GeomObject.IsValid()); ABrush* Brush = GeomObject->GetActualBrush(); InMatrix = FRotationMatrix( go->SelectionOrder[ go->SelectionOrder.Num()-1 ]->GetWidgetRotation() ) * FQuatRotationMatrix(Brush->GetActorQuat()); return 1; } } } return 0; }
FSelectedActorInfo BuildSelectedActorInfo( const TArray<AActor*>& SelectedActors) { FSelectedActorInfo ActorInfo; if( SelectedActors.Num() > 0 ) { // Get the class type of the first actor. AActor* FirstActor = SelectedActors[0]; if( FirstActor && !FirstActor->HasAnyFlags( RF_ClassDefaultObject ) ) { UClass* FirstClass = FirstActor->GetClass(); UObject* FirstArchetype = FirstActor->GetArchetype(); ActorInfo.bAllSelectedAreBrushes = Cast< ABrush >( FirstActor ) != NULL; ActorInfo.SelectionClass = FirstClass; // Compare all actor types with the baseline. for ( int32 ActorIndex = 0; ActorIndex < SelectedActors.Num(); ++ActorIndex ) { AActor* CurrentActor = SelectedActors[ ActorIndex ]; if( CurrentActor->HasAnyFlags( RF_ClassDefaultObject ) ) { continue; } ABrush* Brush = Cast< ABrush >( CurrentActor ); if( !Brush) { ActorInfo.bAllSelectedAreBrushes = false; } else { if( !ActorInfo.bHaveBuilderBrush ) { ActorInfo.bHaveBuilderBrush = FActorEditorUtils::IsABuilderBrush(Brush); } ActorInfo.bHaveBrush |= true; ActorInfo.bHaveBSPBrush |= (!Brush->IsVolumeBrush()); ActorInfo.bHaveVolume |= Brush->IsVolumeBrush(); } UClass* CurrentClass = CurrentActor->GetClass(); if( FirstClass != CurrentClass ) { ActorInfo.bAllSelectedActorsOfSameType = false; ActorInfo.SelectionClass = NULL; FirstClass = NULL; } else { ActorInfo.SelectionClass = CurrentActor->GetClass(); } ++ActorInfo.NumSelected; if( ActorInfo.bAllSelectedActorsBelongToCurrentLevel ) { if( !CurrentActor->GetOuter()->IsA(ULevel::StaticClass()) || !CurrentActor->GetLevel()->IsCurrentLevel() ) { ActorInfo.bAllSelectedActorsBelongToCurrentLevel = false; } } if( ActorInfo.bAllSelectedActorsBelongToSameWorld ) { if ( !ActorInfo.SharedWorld ) { ActorInfo.SharedWorld = CurrentActor->GetWorld(); check(ActorInfo.SharedWorld); } else { if( ActorInfo.SharedWorld != CurrentActor->GetWorld() ) { ActorInfo.bAllSelectedActorsBelongToCurrentLevel = false; ActorInfo.SharedWorld = NULL; } } } // To prevent move to other level for Landscape if its components are distributed in streaming levels if (CurrentActor->IsA(ALandscape::StaticClass())) { ALandscape* Landscape = CastChecked<ALandscape>(CurrentActor); if (!Landscape || !Landscape->HasAllComponent()) { if( !ActorInfo.bAllSelectedActorsBelongToCurrentLevel ) { ActorInfo.bAllSelectedActorsBelongToCurrentLevel = true; } } } if ( ActorInfo.bSelectedActorsBelongToSameLevel ) { ULevel* ActorLevel = CurrentActor->GetOuter()->IsA(ULevel::StaticClass()) ? CurrentActor->GetLevel() : NULL; if ( !ActorInfo.SharedLevel ) { // This is the first selected actor we've encountered. ActorInfo.SharedLevel = ActorLevel; } else { // Does this actor's level match the others? if ( ActorInfo.SharedLevel != ActorLevel ) { ActorInfo.bSelectedActorsBelongToSameLevel = false; ActorInfo.SharedLevel = NULL; } } } AGroupActor* FoundGroup = Cast<AGroupActor>(CurrentActor); if(!FoundGroup) { FoundGroup = AGroupActor::GetParentForActor(CurrentActor); } if( FoundGroup ) { if( !ActorInfo.bHaveSelectedSubGroup ) { ActorInfo.bHaveSelectedSubGroup = AGroupActor::GetParentForActor(FoundGroup) != NULL; } if( !ActorInfo.bHaveSelectedLockedGroup ) { ActorInfo.bHaveSelectedLockedGroup = FoundGroup->IsLocked(); } if( !ActorInfo.bHaveSelectedUnlockedGroup ) { AGroupActor* FoundRoot = AGroupActor::GetRootForActor(CurrentActor); ActorInfo.bHaveSelectedUnlockedGroup = !FoundGroup->IsLocked() || ( FoundRoot && !FoundRoot->IsLocked() ); } } else { ++ActorInfo.NumSelectedUngroupedActors; } USceneComponent* RootComp = CurrentActor->GetRootComponent(); if(RootComp != NULL && RootComp->AttachParent != NULL) { ActorInfo.bHaveAttachedActor = true; } TInlineComponentArray<UActorComponent*> ActorComponents; CurrentActor->GetComponents(ActorComponents); for( UActorComponent* Component : ActorComponents ) { if( UStaticMeshComponent* SMComp = Cast<UStaticMeshComponent>(Component) ) { if( SMComp->IsRegistered() ) { ActorInfo.bHaveStaticMeshComponent = true; } } // Check for experimental/early-access classes in the component hierarchy bool bIsExperimental, bIsEarlyAccess; FObjectEditorUtils::GetClassDevelopmentStatus(Component->GetClass(), bIsExperimental, bIsEarlyAccess); ActorInfo.bHaveExperimentalClass |= bIsExperimental; ActorInfo.bHaveEarlyAccessClass |= bIsEarlyAccess; } // Check for experimental/early-access classes in the actor hierarchy { bool bIsExperimental, bIsEarlyAccess; FObjectEditorUtils::GetClassDevelopmentStatus(CurrentClass, bIsExperimental, bIsEarlyAccess); ActorInfo.bHaveExperimentalClass |= bIsExperimental; ActorInfo.bHaveEarlyAccessClass |= bIsEarlyAccess; } if( CurrentActor->IsA( ALight::StaticClass() ) ) { ActorInfo.bHaveLight = true; } if( CurrentActor->IsA( AStaticMeshActor::StaticClass() ) ) { ActorInfo.bHaveStaticMesh = true; AStaticMeshActor* StaticMeshActor = CastChecked<AStaticMeshActor>( CurrentActor ); if ( StaticMeshActor->GetStaticMeshComponent() ) { UStaticMesh* StaticMesh = StaticMeshActor->GetStaticMeshComponent()->StaticMesh; ActorInfo.bAllSelectedStaticMeshesHaveCollisionModels &= ( (StaticMesh && StaticMesh->BodySetup) ? true : false ); } } if( CurrentActor->IsA( ASkeletalMeshActor::StaticClass() ) ) { ActorInfo.bHaveSkeletalMesh = true; } if( CurrentActor->IsA( APawn::StaticClass() ) ) { ActorInfo.bHavePawn = true; } if( CurrentActor->IsA( AEmitter::StaticClass() ) ) { ActorInfo.bHaveEmitter = true; } if ( CurrentActor->IsA( AMatineeActor::StaticClass() ) ) { ActorInfo.bHaveMatinee = true; } if ( CurrentActor->IsTemporarilyHiddenInEditor() ) { ActorInfo.bHaveHidden = true; } if ( CurrentActor->IsA( ALandscapeProxy::StaticClass() ) ) { ActorInfo.bHaveLandscape = true; } // Find our counterpart actor AActor* EditorWorldActor = EditorUtilities::GetEditorWorldCounterpartActor( CurrentActor ); if( EditorWorldActor != NULL ) { // Just count the total number of actors with counterparts ++ActorInfo.NumSimulationChanges; } } if( ActorInfo.SelectionClass != NULL ) { ActorInfo.SelectionStr = ActorInfo.SelectionClass->GetName(); } else { ActorInfo.SelectionStr = TEXT("Actor"); } } } // hack when only BSP is selected if( ActorInfo.SharedWorld == nullptr ) { ActorInfo.SharedWorld = GWorld; } return ActorInfo; }
/** * 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; }
bool FGeomObject::FinalizeSourceData() { if( !GEditorModeTools().IsModeActive(FBuiltinEditorModes::EM_Geometry) ) { return 0; } ABrush* brush = GetActualBrush(); bool Ret = false; double StartTime = FPlatformTime::Seconds(); const double TimeLimit = 10.0; // Remove invalid polygons from the brush for( int32 x = 0 ; x < brush->Brush->Polys->Element.Num() ; ++x ) { FPoly* Poly = &brush->Brush->Polys->Element[x]; if( Poly->Vertices.Num() < 3 ) { brush->Brush->Polys->Element.RemoveAt( x ); x = -1; } } for( int32 p = 0 ; p < brush->Brush->Polys->Element.Num() ; ++p ) { FPoly* Poly = &(brush->Brush->Polys->Element[p]); Poly->iLink = p; int32 SaveNumVertices = Poly->Vertices.Num(); const bool bTimeLimitExpired = TimeLimit < FPlatformTime::Seconds() - StartTime; if( !Poly->IsCoplanar() || !Poly->IsConvex() ) { // If the polygon is no longer coplanar and/or convex, break it up into separate triangles. FPoly WkPoly = *Poly; brush->Brush->Polys->Element.RemoveAt( p ); TArray<FPoly> Polygons; if( !bTimeLimitExpired && WkPoly.Triangulate( brush, Polygons ) > 0 ) { FPoly::OptimizeIntoConvexPolys( brush, Polygons ); for( int32 t = 0 ; t < Polygons.Num() ; ++t ) { brush->Brush->Polys->Element.Add( Polygons[t] ); } } p = -1; Ret = true; } else { int32 FixResult = Poly->Fix(); if( FixResult != SaveNumVertices ) { // If the polygon collapses after running "Fix" against it, it needs to be // removed from the brushes polygon list. if( bTimeLimitExpired || FixResult == 0 ) { brush->Brush->Polys->Element.RemoveAt( p ); } p = -1; Ret = true; } else { // If we get here, the polygon is valid and needs to be kept. Finalize its internals. Poly->Finalize(brush,1); } } } if (TimeLimit < FPlatformTime::Seconds() - StartTime) { UE_LOG(LogEditorGeometry, Error, TEXT("FGeomObject::FinalizeSourceData() failed because it took too long")); } brush->ReregisterAllComponents(); return Ret; }
void UEditorEngine::csgRebuild( UWorld* InWorld ) { GWarn->BeginSlowTask( NSLOCTEXT("UnrealEd", "RebuildingGeometry", "Rebuilding geometry"), false ); FBSPOps::GFastRebuild = 1; FinishAllSnaps(); // Empty the model out. InWorld->GetModel()->Modify(); InWorld->GetModel()->EmptyModel(1, 1); // Count brushes. int32 BrushTotal=0, BrushCount=0; for( FStaticBrushIterator It(InWorld); It; ++It ) { ABrush* Brush = CastChecked<ABrush>(*It); if( !FActorEditorUtils::IsABuilderBrush(Brush) ) { BrushTotal++; } } // Check for the giant cube brush that is created for subtractive levels. // If it's found, apply the RemoveSurfaceMaterial to its polygons so they aren't lit or drawn. for(FStaticBrushIterator GiantCubeBrushIt(InWorld);GiantCubeBrushIt;++GiantCubeBrushIt) { ABrush* GiantCubeBrush = CastChecked<ABrush>(*GiantCubeBrushIt); if(GiantCubeBrush->Brush && GiantCubeBrush->Brush->Bounds.SphereRadius > HALF_WORLD_MAX) { check(GiantCubeBrush->Brush->Polys); for(int32 PolyIndex = 0;PolyIndex < GiantCubeBrush->Brush->Polys->Element.Num();PolyIndex++) { FPoly& Polygon = GiantCubeBrush->Brush->Polys->Element[PolyIndex]; const float PolygonArea = Polygon.Area(); if(PolygonArea > WORLD_MAX * WORLD_MAX * 0.99f && PolygonArea < WORLD_MAX * WORLD_MAX * 1.01f) { Polygon.Material = GEngine->RemoveSurfaceMaterial; } } } } // Compose all structural brushes and portals. for( FStaticBrushIterator It(InWorld); It; ++It ) { ABrush* Brush = CastChecked<ABrush>(*It); if( !FActorEditorUtils::IsABuilderBrush(Brush) ) { if ( !(Brush->PolyFlags&PF_Semisolid) || (Brush->BrushType!=Brush_Add) || (Brush->PolyFlags&PF_Portal) ) { // Treat portals as solids for cutting. if( Brush->PolyFlags & PF_Portal ) { Brush->PolyFlags = (Brush->PolyFlags & ~PF_Semisolid) | PF_NotSolid; } BrushCount++; FFormatNamedArguments Args; Args.Add( TEXT("BrushCount"), BrushCount ); Args.Add( TEXT("BrushTotal"), BrushTotal ); GWarn->StatusUpdate( BrushCount, BrushTotal, FText::Format( NSLOCTEXT("UnrealEd", "ApplyingStructuralBrushF", "Applying structural brush {BrushCount} of {BrushTotal}"), Args ) ); Brush->Modify(); bspBrushCSG( Brush, InWorld->GetModel(), Brush->PolyFlags, (EBrushType)Brush->BrushType, CSG_None, false, true, false ); } } } // Repartition the structural BSP. { GWarn->StatusUpdate( 0, 4, NSLOCTEXT("UnrealEd", "RebuildBSPBuildingPolygons", "Rebuild BSP: Building polygons") ); bspBuildFPolys( InWorld->GetModel(), 1, 0 ); GWarn->StatusUpdate( 1, 4, NSLOCTEXT("UnrealEd", "RebuildBSPMergingPlanars", "Rebuild BSP: Merging planars") ); bspMergeCoplanars( InWorld->GetModel(), 0, 0 ); GWarn->StatusUpdate( 2, 4, NSLOCTEXT("UnrealEd", "RebuildBSPPartitioning", "Rebuild BSP: Partitioning") ); FBSPOps::bspBuild( InWorld->GetModel(), FBSPOps::BSP_Optimal, 15, 70, 0, 0 ); GWarn->UpdateProgress( 4, 4 ); } // Remember leaves. TArray<int32> iFronts, iBacks; if( InWorld->GetModel()->Nodes.Num() ) { EnlistLeaves( InWorld->GetModel(), iFronts, iBacks, 0 ); } // Compose all detail brushes. for( FStaticBrushIterator It(InWorld); It; ++It ) { ABrush* Brush = CastChecked<ABrush>(*It); if ( !FActorEditorUtils::IsABuilderBrush(Brush) && (Brush->PolyFlags&PF_Semisolid) && !(Brush->PolyFlags&PF_Portal) && Brush->BrushType==Brush_Add ) { BrushCount++; FFormatNamedArguments Args; Args.Add( TEXT("BrushCount"), BrushCount ); Args.Add( TEXT("BrushTotal"), BrushTotal ); GWarn->StatusUpdate( BrushCount, BrushTotal, FText::Format( NSLOCTEXT("UnrealEd", "ApplyingDetailBrushF", "Applying detail brush {BrushCount} of {BrushTotal}"), Args ) ); Brush->Modify(); bspBrushCSG( Brush, InWorld->GetModel(), Brush->PolyFlags, (EBrushType)Brush->BrushType, CSG_None, false, true, false ); } } // Optimize the sub-bsp's. GWarn->StatusUpdate( 0, 4, NSLOCTEXT("UnrealEd", "RebuildCSGOptimizingSubBSPs", "Rebuild CSG: Optimizing Sub-BSPs") ); int32 iNode; for( TArray<int32>::TIterator ItF(iFronts); ItF; ++ItF ) { if( (iNode=InWorld->GetModel()->Nodes[*ItF].iFront)!=INDEX_NONE ) { bspRepartition( InWorld, iNode ); } } for( TArray<int32>::TIterator ItB(iBacks); ItB; ++ItB ) { if( (iNode=InWorld->GetModel()->Nodes[*ItB].iBack)!=INDEX_NONE ) { bspRepartition( InWorld, iNode ); } } GWarn->StatusUpdate( 1, 4, NSLOCTEXT("UnrealEd", "RebuildBSPOptimizingGeometry", "Rebuild BSP: Optimizing geometry") ); bspOptGeom( InWorld->GetModel() ); // Build bounding volumes. GWarn->StatusUpdate( 2, 4, NSLOCTEXT("UnrealEd", "RebuildCSGBuildingBoundingVolumes", "Rebuild CSG: Building Bounding Volumes") ); FBSPOps::bspBuildBounds( InWorld->GetModel() ); // Rebuild dynamic brush BSP's. GWarn->StatusUpdate( 3, 4, NSLOCTEXT("UnrealEd", "RebuildCSGRebuildingDynamicBrushBSPs", "Rebuild CSG: Rebuilding Dynamic Brush BSPs") ); TArray<ABrush*> DynamicBrushes; DynamicBrushes.Empty(); for( TActorIterator<ABrush> It(InWorld); It; ++It ) { ABrush* B=*It; if ( B->Brush && !B->IsStaticBrush() ) { DynamicBrushes.Add(B); } } { FScopedSlowTask SlowTask(DynamicBrushes.Num(), NSLOCTEXT("UnrealEd", "RebuildCSGRebuildingDynamicBrushBSPs", "Rebuild CSG: Rebuilding Dynamic Brush BSPs") ); for ( int32 BrushIndex = 0; BrushIndex < DynamicBrushes.Num(); BrushIndex++ ) { SlowTask.EnterProgressFrame(); ABrush* B = DynamicBrushes[BrushIndex]; FBSPOps::csgPrepMovingBrush(B); if ( GEngine->GetMapBuildCancelled() ) { break; } } } GWarn->UpdateProgress( 4, 4 ); // update static navigable geometry in current level RebuildStaticNavigableGeometry(InWorld->GetCurrentLevel()); // Empty EdPolys. InWorld->GetModel()->Polys->Element.Empty(); // Done. FBSPOps::GFastRebuild = 0; InWorld->GetCurrentLevel()->MarkPackageDirty(); GWarn->EndSlowTask(); }