/** Updates GCurrentSelectedLightmapSample given a selected actor's components and the location of the click. */ void SetDebugLightmapSample(TArray<UActorComponent*>* Components, UModel* Model, int32 iSurf, FVector ClickLocation) { UStaticMeshComponent* SMComponent = NULL; if (Components) { // Find the first supported component for (int32 ComponentIndex = 0; ComponentIndex < Components->Num() && !SMComponent; ComponentIndex++) { SMComponent = Cast<UStaticMeshComponent>((*Components)[ComponentIndex]); if (SMComponent && (!SMComponent->StaticMesh || SMComponent->LODData.Num() == 0)) { SMComponent = NULL; } } } bool bFoundLightmapSample = false; // Only static mesh components and BSP handled for now if (SMComponent) { UStaticMesh* StaticMesh = SMComponent->StaticMesh; check(StaticMesh); check(StaticMesh->RenderData); check(StaticMesh->RenderData->LODResources.Num()); // Only supporting LOD0 const int32 LODIndex = 0; FStaticMeshLODResources& LODModel = StaticMesh->RenderData->LODResources[LODIndex]; FIndexArrayView Indices = LODModel.IndexBuffer.GetArrayView(); const bool bHasStaticLighting = SMComponent->HasStaticLighting(); if (bHasStaticLighting) { bool bUseTextureMap = false; int32 LightmapSizeX = 0; int32 LightmapSizeY = 0; SMComponent->GetLightMapResolution(LightmapSizeX, LightmapSizeY); if (LightmapSizeX > 0 && LightmapSizeY > 0 && StaticMesh->LightMapCoordinateIndex >= 0 && (uint32)StaticMesh->LightMapCoordinateIndex < LODModel.VertexBuffer.GetNumTexCoords() ) { bUseTextureMap = true; } else { bUseTextureMap = false; } // Search through the static mesh's triangles for the one that was hit (since we can't get triangle index from a line check) for(int32 TriangleIndex = 0; TriangleIndex < Indices.Num(); TriangleIndex += 3) { uint32 Index0 = Indices[TriangleIndex]; uint32 Index1 = Indices[TriangleIndex + 1]; uint32 Index2 = Indices[TriangleIndex + 2]; // Transform positions to world space FVector Position0 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index0)); FVector Position1 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index1)); FVector Position2 = SMComponent->ComponentToWorld.TransformPosition(LODModel.PositionVertexBuffer.VertexPosition(Index2)); FVector BaryCentricWeights; // Continue if click location is in the triangle and get its barycentric weights if (GetBarycentricWeights(Position0, Position1, Position2, ClickLocation, .001f, BaryCentricWeights)) { RestoreSelectedLightmapSample(); if (bUseTextureMap) { // Fetch lightmap UV's FVector2D LightmapUV0 = LODModel.VertexBuffer.GetVertexUV(Index0, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV1 = LODModel.VertexBuffer.GetVertexUV(Index1, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV2 = LODModel.VertexBuffer.GetVertexUV(Index2, StaticMesh->LightMapCoordinateIndex); // Interpolate lightmap UV's to the click location FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } const int32 LocalX = FMath::TruncToInt(InterpolatedUV.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(InterpolatedUV.Y * PaddedSizeY); if (LocalX < 0 || LocalX >= PaddedSizeX || LocalY < 0 || LocalY >= PaddedSizeY) { UE_LOG(LogStaticLightingSystem, Log, TEXT("Texel selection failed because the lightmap UV's wrap!")); } else { bFoundLightmapSample = UpdateSelectedTexel(SMComponent, -1, SMComponent->LODData[LODIndex].LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); } } break; } } if (!bFoundLightmapSample && Indices.Num() > 0) { const int32 SelectedTriangle = FMath::RandRange(0, Indices.Num() / 3 - 1); uint32 Index0 = Indices[SelectedTriangle]; uint32 Index1 = Indices[SelectedTriangle + 1]; uint32 Index2 = Indices[SelectedTriangle + 2]; FVector2D LightmapUV0 = LODModel.VertexBuffer.GetVertexUV(Index0, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV1 = LODModel.VertexBuffer.GetVertexUV(Index1, StaticMesh->LightMapCoordinateIndex); FVector2D LightmapUV2 = LODModel.VertexBuffer.GetVertexUV(Index2, StaticMesh->LightMapCoordinateIndex); FVector BaryCentricWeights; BaryCentricWeights.X = FMath::FRandRange(0, 1); BaryCentricWeights.Y = FMath::FRandRange(0, 1); if (BaryCentricWeights.X + BaryCentricWeights.Y >= 1) { BaryCentricWeights.X = 1 - BaryCentricWeights.X; BaryCentricWeights.Y = 1 - BaryCentricWeights.Y; } BaryCentricWeights.Z = 1 - BaryCentricWeights.X - BaryCentricWeights.Y; FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; UE_LOG(LogStaticLightingSystem, Log, TEXT("Failed to intersect any triangles, picking random texel")); int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } const int32 LocalX = FMath::TruncToInt(InterpolatedUV.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(InterpolatedUV.Y * PaddedSizeY); if (LocalX < 0 || LocalX >= PaddedSizeX || LocalY < 0 || LocalY >= PaddedSizeY) { UE_LOG(LogStaticLightingSystem, Log, TEXT("Texel selection failed because the lightmap UV's wrap!")); } else { bFoundLightmapSample = UpdateSelectedTexel(SMComponent, -1, SMComponent->LODData[LODIndex].LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); } } } } else if (Model) { UWorld* World = Model->LightingLevel->OwningWorld; check( World); for (int32 ModelIndex = 0; ModelIndex < World->GetCurrentLevel()->ModelComponents.Num(); ModelIndex++) { UModelComponent* CurrentComponent = World->GetCurrentLevel()->ModelComponents[ModelIndex]; int32 LightmapSizeX = 0; int32 LightmapSizeY = 0; CurrentComponent->GetLightMapResolution(LightmapSizeX, LightmapSizeY); if (LightmapSizeX > 0 && LightmapSizeY > 0) { for (int32 ElementIndex = 0; ElementIndex < CurrentComponent->GetElements().Num(); ElementIndex++) { FModelElement& Element = CurrentComponent->GetElements()[ElementIndex]; TScopedPointer<FRawIndexBuffer16or32>* IndexBufferRef = Model->MaterialIndexBuffers.Find(Element.Material); check(IndexBufferRef); for(uint32 TriangleIndex = Element.FirstIndex; TriangleIndex < Element.FirstIndex + Element.NumTriangles * 3; TriangleIndex += 3) { uint32 Index0 = (*IndexBufferRef)->Indices[TriangleIndex]; uint32 Index1 = (*IndexBufferRef)->Indices[TriangleIndex + 1]; uint32 Index2 = (*IndexBufferRef)->Indices[TriangleIndex + 2]; FModelVertex* ModelVertices = (FModelVertex*)Model->VertexBuffer.Vertices.GetData(); FVector Position0 = ModelVertices[Index0].Position; FVector Position1 = ModelVertices[Index1].Position; FVector Position2 = ModelVertices[Index2].Position; FVector BaryCentricWeights; // Continue if click location is in the triangle and get its barycentric weights if (GetBarycentricWeights(Position0, Position1, Position2, ClickLocation, .001f, BaryCentricWeights)) { RestoreSelectedLightmapSample(); // Fetch lightmap UV's FVector2D LightmapUV0 = ModelVertices[Index0].ShadowTexCoord; FVector2D LightmapUV1 = ModelVertices[Index1].ShadowTexCoord; FVector2D LightmapUV2 = ModelVertices[Index2].ShadowTexCoord; // Interpolate lightmap UV's to the click location FVector2D InterpolatedUV = LightmapUV0 * BaryCentricWeights.X + LightmapUV1 * BaryCentricWeights.Y + LightmapUV2 * BaryCentricWeights.Z; // Find the node index belonging to the selected triangle const UModel* CurrentModel = CurrentComponent->GetModel(); int32 SelectedNodeIndex = INDEX_NONE; for (int32 ElementNodeIndex = 0; ElementNodeIndex < Element.Nodes.Num(); ElementNodeIndex++) { const FBspNode& CurrentNode = CurrentModel->Nodes[Element.Nodes[ElementNodeIndex]]; if ((int32)Index0 >= CurrentNode.iVertexIndex && (int32)Index0 < CurrentNode.iVertexIndex + CurrentNode.NumVertices) { SelectedNodeIndex = Element.Nodes[ElementNodeIndex]; } } check(SelectedNodeIndex >= 0); TArray<ULightComponentBase*> DummyLights; // fill out the model's NodeGroups (not the mapping part of it, but the nodes part) Model->GroupAllNodes(World->GetCurrentLevel(), DummyLights); // Find the FGatheredSurface that the selected node got put into during the last lighting rebuild TArray<int32> GatheredNodes; // find the NodeGroup that this node went into, and get all of its node for (TMap<int32, FNodeGroup*>::TIterator It(Model->NodeGroups); It && GatheredNodes.Num() == 0; ++It) { FNodeGroup* NodeGroup = It.Value(); for (int32 NodeIndex = 0; NodeIndex < NodeGroup->Nodes.Num(); NodeIndex++) { if (NodeGroup->Nodes[NodeIndex] == SelectedNodeIndex) { GatheredNodes = NodeGroup->Nodes; break; } } } check(GatheredNodes.Num() > 0); // use the surface of the selected node, it will have to suffice for the GetSurfaceLightMapResolution() call int32 SelectedGatheredSurfIndex = Model->Nodes[SelectedNodeIndex].iSurf; // Get the lightmap resolution used by the FGatheredSurface containing the selected node FMatrix WorldToMap; CurrentComponent->GetSurfaceLightMapResolution(SelectedGatheredSurfIndex, 1, LightmapSizeX, LightmapSizeY, WorldToMap, &GatheredNodes); int32 PaddedSizeX = LightmapSizeX; int32 PaddedSizeY = LightmapSizeY; if (GLightmassDebugOptions.bPadMappings && GAllowLightmapPadding && LightmapSizeX - 2 > 0 && LightmapSizeY - 2 > 0) { PaddedSizeX -= 2; PaddedSizeY -= 2; } check(LightmapSizeX > 0 && LightmapSizeY > 0); // Apply the transform to the intersection position to find the local texel coordinates const FVector4 StaticLightingTextureCoordinate = WorldToMap.TransformPosition(ClickLocation); const int32 LocalX = FMath::TruncToInt(StaticLightingTextureCoordinate.X * PaddedSizeX); const int32 LocalY = FMath::TruncToInt(StaticLightingTextureCoordinate.Y * PaddedSizeY); check(LocalX >= 0 && LocalX < PaddedSizeX && LocalY >= 0 && LocalY < PaddedSizeY); bFoundLightmapSample = UpdateSelectedTexel( CurrentComponent, SelectedNodeIndex, Element.LightMap, ClickLocation, InterpolatedUV, LocalX, LocalY, LightmapSizeX, LightmapSizeY); if (!bFoundLightmapSample) { RestoreSelectedLightmapSample(); GCurrentSelectedLightmapSample = FSelectedLightmapSample(); } return; } } } } } } if (!bFoundLightmapSample) { RestoreSelectedLightmapSample(); GCurrentSelectedLightmapSample = FSelectedLightmapSample(); } }
/** Add a new statistic to the internal map (or update an existing one) from the supplied component */ UPrimitiveStats* Add(UPrimitiveComponent* InPrimitiveComponent, EPrimitiveObjectSets InObjectSet) { // Objects in transient package or transient objects are not part of level. if( InPrimitiveComponent->GetOutermost() == GetTransientPackage() || InPrimitiveComponent->HasAnyFlags( RF_Transient ) ) { return NULL; } // Owned by a default object? Not part of a level either. if(InPrimitiveComponent->GetOuter() && InPrimitiveComponent->GetOuter()->IsDefaultSubobject() ) { return NULL; } UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(InPrimitiveComponent); UModelComponent* ModelComponent = Cast<UModelComponent>(InPrimitiveComponent); USkeletalMeshComponent* SkeletalMeshComponent = Cast<USkeletalMeshComponent>(InPrimitiveComponent); ULandscapeComponent* LandscapeComponent = Cast<ULandscapeComponent>(InPrimitiveComponent); UObject* Resource = NULL; AActor* ActorOuter = Cast<AActor>(InPrimitiveComponent->GetOuter()); int32 VertexColorMem = 0; int32 InstVertexColorMem = 0; // Calculate number of direct and other lights relevant to this component. int32 LightsLMCount = 0; int32 LightsOtherCount = 0; bool bUsesOnlyUnlitMaterials = InPrimitiveComponent->UsesOnlyUnlitMaterials(); // The static mesh is a static mesh component's resource. if( StaticMeshComponent ) { UStaticMesh* Mesh = StaticMeshComponent->StaticMesh; Resource = Mesh; // Calculate vertex color memory on the actual mesh. if( Mesh && Mesh->RenderData ) { // Accumulate memory for each LOD for( int32 LODIndex = 0; LODIndex < Mesh->RenderData->LODResources.Num(); ++LODIndex ) { VertexColorMem += Mesh->RenderData->LODResources[LODIndex].ColorVertexBuffer.GetAllocatedSize(); } } // Calculate instanced vertex color memory used on the component. for( int32 LODIndex = 0; LODIndex < StaticMeshComponent->LODData.Num(); ++LODIndex ) { // Accumulate memory for each LOD const FStaticMeshComponentLODInfo& LODInfo = StaticMeshComponent->LODData[ LODIndex ]; if( LODInfo.OverrideVertexColors ) { InstVertexColorMem += LODInfo.OverrideVertexColors->GetAllocatedSize(); } } // Calculate the number of lightmap and shadow map lights if( !bUsesOnlyUnlitMaterials ) { if( StaticMeshComponent->LODData.Num() > 0 ) { FStaticMeshComponentLODInfo& ComponentLODInfo = StaticMeshComponent->LODData[0]; if( ComponentLODInfo.LightMap ) { LightsLMCount = ComponentLODInfo.LightMap->LightGuids.Num(); } } } } // A model component is its own resource. else if( ModelComponent ) { // Make sure model component is referenced by level. ULevel* Level = CastChecked<ULevel>(ModelComponent->GetOuter()); if( Level->ModelComponents.Find( ModelComponent ) != INDEX_NONE ) { Resource = ModelComponent->GetModel(); // Calculate the number of lightmap and shadow map lights if( !bUsesOnlyUnlitMaterials ) { const TIndirectArray<FModelElement> Elements = ModelComponent->GetElements(); if( Elements.Num() > 0 ) { if( Elements[0].LightMap ) { LightsLMCount = Elements[0].LightMap->LightGuids.Num(); } } } } } // The skeletal mesh of a skeletal mesh component is its resource. else if( SkeletalMeshComponent ) { USkeletalMesh* Mesh = SkeletalMeshComponent->SkeletalMesh; Resource = Mesh; // Calculate vertex color usage for skeletal meshes if( Mesh ) { FSkeletalMeshResource* SkelMeshResource = Mesh->GetResourceForRendering(); for( int32 LODIndex = 0; LODIndex < SkelMeshResource->LODModels.Num(); ++LODIndex ) { const FStaticLODModel& LODModel = SkelMeshResource->LODModels[ LODIndex ]; VertexColorMem += LODModel.ColorVertexBuffer.GetVertexDataSize(); } } } // The landscape of a landscape component is its resource. else if (LandscapeComponent) { Resource = LandscapeComponent->GetLandscapeProxy(); if (LandscapeComponent->LightMap) { LightsLMCount = LandscapeComponent->LightMap->LightGuids.Num(); } } UWorld* World = InPrimitiveComponent->GetWorld(); // check(World); // @todo: re-instate this check once the GWorld migration has completed /// If we should skip the actor. Skip if the actor has no outer or if we are only showing selected actors and the actor isn't selected const bool bShouldSkip = World == NULL || ActorOuter == NULL || (ActorOuter != NULL && InObjectSet == PrimitiveObjectSets_SelectedObjects && ActorOuter->IsSelected() == false ); // Dont' care about components without a resource. if( Resource // Require actor association for selection and to disregard mesh emitter components. The exception being model components. && (!bShouldSkip || (ModelComponent && InObjectSet != PrimitiveObjectSets_SelectedObjects ) ) // Only list primitives in visible levels && IsInVisibleLevel( InPrimitiveComponent, World ) // Don't list pending kill components. && !InPrimitiveComponent->IsPendingKill() ) { // Retrieve relevant lights. TArray<const ULightComponent*> RelevantLights; World->Scene->GetRelevantLights( InPrimitiveComponent, &RelevantLights ); // Only look for relevant lights if we aren't unlit. if( !bUsesOnlyUnlitMaterials ) { // Lightmap and shadow map lights are calculated above, per component type, infer the "other" light count here LightsOtherCount = RelevantLights.Num() >= LightsLMCount ? RelevantLights.Num() - LightsLMCount : 0; } // Figure out memory used by light and shadow maps and light/ shadow map resolution. int32 LightMapWidth = 0; int32 LightMapHeight = 0; InPrimitiveComponent->GetLightMapResolution( LightMapWidth, LightMapHeight ); int32 LMSMResolution = FMath::Sqrt( LightMapHeight * LightMapWidth ); int32 LightMapData = 0; int32 LegacyShadowMapData = 0; InPrimitiveComponent->GetLightAndShadowMapMemoryUsage( LightMapData, LegacyShadowMapData ); // Check whether we already have an entry for the associated static mesh. UPrimitiveStats** StatsEntryPtr = ResourceToStatsMap.Find( Resource ); if( StatsEntryPtr ) { check(*StatsEntryPtr); UPrimitiveStats* StatsEntry = *StatsEntryPtr; // We do. Update existing entry. StatsEntry->Count++; StatsEntry->Actors.AddUnique(ActorOuter); StatsEntry->RadiusMin = FMath::Min( StatsEntry->RadiusMin, InPrimitiveComponent->Bounds.SphereRadius ); StatsEntry->RadiusMax = FMath::Max( StatsEntry->RadiusMax, InPrimitiveComponent->Bounds.SphereRadius ); StatsEntry->RadiusAvg += InPrimitiveComponent->Bounds.SphereRadius; StatsEntry->LightsLM += LightsLMCount; StatsEntry->LightsOther += LightsOtherCount; StatsEntry->LightMapData += (float)LightMapData / 1024.0f; StatsEntry->LMSMResolution += LMSMResolution; StatsEntry->UpdateNames(); if ( !ModelComponent && !LandscapeComponent ) { // Count instanced sections StatsEntry->InstSections += StatsEntry->Sections; StatsEntry->InstTriangles += StatsEntry->Triangles; } // ... in the case of a model component (aka BSP). if( ModelComponent ) { // If Count represents the Model itself, we do NOT want to increment it now. StatsEntry->Count--; for (const auto& Element : ModelComponent->GetElements()) { StatsEntry->Triangles += Element.NumTriangles; StatsEntry->Sections++; } StatsEntry->InstSections = StatsEntry->Sections; StatsEntry->InstTriangles = StatsEntry->Triangles; } else if( StaticMeshComponent ) { // This stat is used by multiple components so accumulate instanced vertex color memory. StatsEntry->InstVertexColorMem += (float)InstVertexColorMem / 1024.0f; } else if (LandscapeComponent) { // If Count represents the Landscape itself, we do NOT want to increment it now. StatsEntry->Count--; } } else { // We don't. Create new base entry. UPrimitiveStats* NewStatsEntry = NewObject<UPrimitiveStats>(); NewStatsEntry->AddToRoot(); NewStatsEntry->Object = Resource; NewStatsEntry->Actors.AddUnique(ActorOuter); NewStatsEntry->Count = 1; NewStatsEntry->Triangles = 0; NewStatsEntry->InstTriangles = 0; NewStatsEntry->ResourceSize = (float)(FArchiveCountMem(Resource).GetNum() + Resource->GetResourceSize(EResourceSizeMode::Exclusive)) / 1024.0f; NewStatsEntry->Sections = 0; NewStatsEntry->InstSections = 0; NewStatsEntry->RadiusMin = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->RadiusAvg = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->RadiusMax = InPrimitiveComponent->Bounds.SphereRadius; NewStatsEntry->LightsLM = LightsLMCount; NewStatsEntry->LightsOther = (float)LightsOtherCount; NewStatsEntry->LightMapData = (float)LightMapData / 1024.0f; NewStatsEntry->LMSMResolution = LMSMResolution; NewStatsEntry->VertexColorMem = (float)VertexColorMem / 1024.0f; NewStatsEntry->InstVertexColorMem = (float)InstVertexColorMem / 1024.0f; NewStatsEntry->UpdateNames(); // Fix up triangle and section count... // ... in the case of a static mesh component. if( StaticMeshComponent ) { UStaticMesh* StaticMesh = StaticMeshComponent->StaticMesh; if( StaticMesh && StaticMesh->RenderData ) { for( int32 SectionIndex=0; SectionIndex<StaticMesh->RenderData->LODResources[0].Sections.Num(); SectionIndex++ ) { const FStaticMeshSection& StaticMeshSection = StaticMesh->RenderData->LODResources[0].Sections[SectionIndex]; NewStatsEntry->Triangles += StaticMeshSection.NumTriangles; NewStatsEntry->Sections++; } } } // ... in the case of a model component (aka BSP). else if( ModelComponent ) { TIndirectArray<FModelElement> Elements = ModelComponent->GetElements(); for( int32 ElementIndex=0; ElementIndex<Elements.Num(); ElementIndex++ ) { const FModelElement& Element = Elements[ElementIndex]; NewStatsEntry->Triangles += Element.NumTriangles; NewStatsEntry->Sections++; } } // ... in the case of skeletal mesh component. else if( SkeletalMeshComponent ) { USkeletalMesh* SkeletalMesh = SkeletalMeshComponent->SkeletalMesh; if( SkeletalMesh ) { FSkeletalMeshResource* SkelMeshResource = SkeletalMesh->GetResourceForRendering(); if (SkelMeshResource->LODModels.Num()) { const FStaticLODModel& BaseLOD = SkelMeshResource->LODModels[0]; for( int32 SectionIndex=0; SectionIndex<BaseLOD.Sections.Num(); SectionIndex++ ) { const FSkelMeshSection& Section = BaseLOD.Sections[SectionIndex]; NewStatsEntry->Triangles += Section.NumTriangles; NewStatsEntry->Sections++; } } } } else if (LandscapeComponent) { TSet<UTexture2D*> UniqueTextures; for (auto ItComponents = LandscapeComponent->GetLandscapeProxy()->LandscapeComponents.CreateConstIterator(); ItComponents; ++ItComponents) { const ULandscapeComponent* CurrentComponent = *ItComponents; // count triangles and sections in the landscape NewStatsEntry->Triangles += FMath::Square(CurrentComponent->ComponentSizeQuads) * 2; NewStatsEntry->Sections += FMath::Square(CurrentComponent->NumSubsections); // count resource usage of landscape bool bNotUnique = false; UniqueTextures.Add(CurrentComponent->HeightmapTexture, &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += CurrentComponent->HeightmapTexture->GetResourceSize(EResourceSizeMode::Exclusive); } if (CurrentComponent->XYOffsetmapTexture) { UniqueTextures.Add(CurrentComponent->XYOffsetmapTexture, &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += CurrentComponent->XYOffsetmapTexture->GetResourceSize(EResourceSizeMode::Exclusive); } } for (auto ItWeightmaps = CurrentComponent->WeightmapTextures.CreateConstIterator(); ItWeightmaps; ++ItWeightmaps) { UniqueTextures.Add((*ItWeightmaps), &bNotUnique); if (!bNotUnique) { NewStatsEntry->ResourceSize += (*ItWeightmaps)->GetResourceSize(EResourceSizeMode::Exclusive); } } } } NewStatsEntry->InstTriangles = NewStatsEntry->Triangles; NewStatsEntry->InstSections = NewStatsEntry->Sections; // Add to map. ResourceToStatsMap.Add( Resource, NewStatsEntry ); return NewStatsEntry; } } return NULL; }
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; }