// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved. /*============================================================================= LandscapeEditInterface.cpp: Landscape editing interface =============================================================================*/ #include "Landscape.h" #include "LandscapeDataAccess.h" #include "LandscapeEdit.h" #include "LandscapeRender.h" #include "Landscape.h" #include "LandscapeInfo.h" #include "LandscapeLayerInfoObject.h" #include "ComponentReregisterContext.h" #if WITH_EDITOR // // FLandscapeEditDataInterface // FLandscapeEditDataInterface::FLandscapeEditDataInterface(ULandscapeInfo* InLandscapeInfo) { if (InLandscapeInfo) { LandscapeInfo = InLandscapeInfo; ComponentSizeQuads = InLandscapeInfo->ComponentSizeQuads; SubsectionSizeQuads = InLandscapeInfo->SubsectionSizeQuads; ComponentNumSubsections = InLandscapeInfo->ComponentNumSubsections; DrawScale = InLandscapeInfo->DrawScale; } } FLandscapeEditDataInterface::~FLandscapeEditDataInterface() { Flush(); } void FLandscapeEditDataInterface::Flush() { bool bNeedToWaitForUpdate = false; // Update all textures for( TMap::TIterator It(TextureDataMap); It; ++It ) { if( It.Value()->UpdateTextureData() ) { bNeedToWaitForUpdate = true; } } if( bNeedToWaitForUpdate ) { FlushRenderingCommands(); } // delete all the FLandscapeTextureDataInfo allocations for( TMap::TIterator It(TextureDataMap); It; ++It ) { delete It.Value(); // FLandscapeTextureDataInfo destructors will unlock any texture data } TextureDataMap.Empty(); } #include "LevelUtils.h" void ALandscape::CalcComponentIndicesOverlap(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const int32 ComponentSizeQuads, int32& ComponentIndexX1, int32& ComponentIndexY1, int32& ComponentIndexX2, int32& ComponentIndexY2) { // Find component range for this block of data ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1; ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1; ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1; } void ALandscape::CalcComponentIndicesNoOverlap(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const int32 ComponentSizeQuads, int32& ComponentIndexX1, int32& ComponentIndexY1, int32& ComponentIndexX2, int32& ComponentIndexY2) { // Find component range for this block of data ComponentIndexX1 = (X1 >= 0) ? X1 / ComponentSizeQuads : (X1+1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components ComponentIndexY1 = (Y1 >= 0) ? Y1 / ComponentSizeQuads : (Y1+1) / ComponentSizeQuads - 1; ComponentIndexX2 = (X2-1 >= 0) ? (X2-1) / ComponentSizeQuads : (X2) / ComponentSizeQuads - 1; ComponentIndexY2 = (Y2-1 >= 0) ? (Y2-1) / ComponentSizeQuads : (Y2) / ComponentSizeQuads - 1; // Shrink indices for shared values if ( ComponentIndexX2 < ComponentIndexX1) { ComponentIndexX2 = ComponentIndexX1; } if ( ComponentIndexY2 < ComponentIndexY1) { ComponentIndexY2 = ComponentIndexY1; } } namespace { // Ugly helper function, all arrays should be only size 4 template FORCEINLINE void CalcInterpValue(const int32* Dist, const bool* Exist, const T* Value, F& ValueX, F& ValueY) { if (Exist[0] && Exist[1]) { ValueX = (F)(Dist[1] * Value[0] + Dist[0] * Value[1]) / (Dist[0] + Dist[1]); } else { if (Exist[0]) { ValueX = Value[0]; } else if (Exist[1]) { ValueX = Value[1]; } } if (Exist[2] && Exist[3]) { ValueY = (F)(Dist[3] * Value[2] + Dist[2] * Value[3]) / (Dist[2] + Dist[3]); } else { if (Exist[2]) { ValueY = Value[2]; } else if (Exist[3]) { ValueY = Value[3]; } } } template FORCEINLINE T CalcValueFromValueXY( const int32* Dist, const T& ValueX, const T& ValueY, const uint8& CornerSet, const T* CornerValues ) { T FinalValue; int32 DistX = FMath::Min(Dist[0], Dist[1]); int32 DistY = FMath::Min(Dist[2], Dist[3]); if (DistX+DistY > 0) { FinalValue = ((ValueX * DistY) + (ValueY * DistX)) / (float)(DistX + DistY); } else { if ((CornerSet & 1) && Dist[0] == 0 && Dist[2] == 0) { FinalValue = CornerValues[0]; } else if ((CornerSet & 1 << 1) && Dist[1] == 0 && Dist[2] == 0) { FinalValue = CornerValues[1]; } else if ((CornerSet & 1 << 2) && Dist[0] == 0 && Dist[3] == 0) { FinalValue = CornerValues[2]; } else if ((CornerSet & 1 << 3) && Dist[1] == 0 && Dist[3] == 0) { FinalValue = CornerValues[3]; } else { FinalValue = ValueX; } } return FinalValue; } }; bool FLandscapeEditDataInterface::GetComponentsInRegion(int32 X1, int32 Y1, int32 X2, int32 Y2, TSet* OutComponents /*= NULL*/) { if (ComponentSizeQuads <= 0 || !LandscapeInfo) { return false; } // Find component range for this block of data int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); bool bNotLocked = true; for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); if( Component ) { bNotLocked = bNotLocked && ( !FLevelUtils::IsLevelLocked(Component->GetLandscapeProxy()->GetLevel()) ) && FLevelUtils::IsLevelVisible(Component->GetLandscapeProxy()->GetLevel()); if (OutComponents) { OutComponents->Add(Component); } } } } return bNotLocked; } void FLandscapeEditDataInterface::SetHeightData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint16* Data, int32 Stride, bool CalcNormals, const uint16* NormalData /*= NULL*/, bool CreateComponents /*= false*/) { if( Stride==0 ) { Stride = (1+X2-X1); } check(ComponentSizeQuads > 0); // Find component range for this block of data int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); FVector* VertexNormals = NULL; if( CalcNormals ) { // Calculate the normals for each of the two triangles per quad. // Note that the normals at the edges are not correct because they include normals // from triangles outside the current area. They are not updated int32 NumVertsX = 1+X2-X1; int32 NumVertsY = 1+Y2-Y1; VertexNormals = new FVector[NumVertsX*NumVertsY]; FMemory::Memzero(VertexNormals, NumVertsX*NumVertsY*sizeof(FVector)); // Need to consider XYOffset for XY displacemented map FVector2D* XYOffsets = new FVector2D[NumVertsX*NumVertsY]; FMemory::Memzero(XYOffsets, NumVertsX*NumVertsY*sizeof(FVector2D)); GetXYOffsetDataFast(X1, Y1, X2, Y2, XYOffsets, 0); for( int32 Y=0;YXYtoComponentMap.FindRef(ComponentKey); // if NULL, it was painted away if( Component==NULL ) { if( CreateComponents ) { // not yet implemented continue; } else { continue; } } Component->Modify(); FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Component->HeightmapTexture); FColor* HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0); FColor* XYOffsetMipData = NULL; if (Component->XYOffsetmapTexture) { FLandscapeTextureDataInfo* XYTexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture); XYOffsetMipData = (FColor*)XYTexDataInfo->GetMipData(Component->CollisionMipLevel); } // Find the texture data corresponding to this vertex int32 SizeU = Component->HeightmapTexture->Source.GetSizeX(); int32 SizeV = Component->HeightmapTexture->Source.GetSizeY(); int32 HeightmapOffsetX = Component->HeightmapScaleBias.Z * (float)SizeU; int32 HeightmapOffsetY = Component->HeightmapScaleBias.W * (float)SizeV; // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); // To adjust bounding box uint16 MinHeight = MAX_uint16; uint16 MaxHeight = 0; for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; checkSlow( LandscapeX >= X1 && LandscapeX <= X2 ); checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 ); // Find the input data corresponding to this vertex int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1); const uint16& Height = Data[DataIndex]; // for bounding box if( Height < MinHeight ) { MinHeight = Height; } if( Height > MaxHeight ) { MaxHeight = Height; } int32 TexX = HeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = HeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; FColor& TexData = HeightmapTextureData[ TexX + TexY * SizeU ]; // Update the texture TexData.R = Height >> 8; TexData.G = Height & 255; // Update normals if we're not on an edge vertex if( VertexNormals && LandscapeX > X1 && LandscapeX < X2 && LandscapeY > Y1 && LandscapeY < Y2 ) { FVector Normal = VertexNormals[DataIndex].GetSafeNormal(); TexData.B = FMath::RoundToInt( 127.5f * (Normal.X + 1.0f) ); TexData.A = FMath::RoundToInt( 127.5f * (Normal.Y + 1.0f) ); } else if (NormalData) { // Need data validation? const uint16& Normal = NormalData[DataIndex]; TexData.B = Normal >> 8; TexData.A = Normal & 255; } } } // Record the areas of the texture we need to re-upload int32 TexX1 = HeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX1; int32 TexY1 = HeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY1; int32 TexX2 = HeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX2; int32 TexY2 = HeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY2; TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); } } // See if we need to adjust the bounds. Note we never shrink the bounding box at this point float MinLocalZ = LandscapeDataAccess::GetLocalHeight(MinHeight); float MaxLocalZ = LandscapeDataAccess::GetLocalHeight(MaxHeight); bool bUpdateBoxSphereBounds = false; if( MinLocalZ < Component->CachedLocalBox.Min.Z ) { Component->CachedLocalBox.Min.Z = MinLocalZ; bUpdateBoxSphereBounds = true; } if( MaxLocalZ > Component->CachedLocalBox.Max.Z ) { Component->CachedLocalBox.Max.Z = MaxLocalZ; bUpdateBoxSphereBounds = true; } if( bUpdateBoxSphereBounds ) { Component->UpdateComponentToWorld(); } // Update mipmaps // Work out how many mips should be calculated directly from one component's data. // The remaining mips are calculated on a per texture basis. // eg if subsection is 7x7 quads, we need one 3 mips total: (8x8, 4x4, 2x2 verts) int32 BaseNumMips = FMath::CeilLogTwo(SubsectionSizeQuads+1); TArray MipData; MipData.AddUninitialized(BaseNumMips); MipData[0] = HeightmapTextureData; for( int32 MipIdx=1;MipIdxGetMipData(MipIdx); } Component->GenerateHeightmapMips( MipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfo ); // Update collision Component->UpdateCollisionHeightData(MipData[Component->CollisionMipLevel], ComponentX1, ComponentY1, ComponentX2, ComponentY2, bUpdateBoxSphereBounds, XYOffsetMipData ); // Update GUID for Platform Data FPlatformMisc::CreateGuid(Component->StateId); } } if( VertexNormals ) { delete[] VertexNormals; } } // // RecalculateNormals - Regenerate normals for the entire landscape. Called after modifying DrawScale3D. // void FLandscapeEditDataInterface::RecalculateNormals() { if (!LandscapeInfo) return; // Recalculate normals for each component in turn for( auto It = LandscapeInfo->XYtoComponentMap.CreateIterator(); It; ++It ) { ULandscapeComponent* Component = It.Value(); // one extra row of vertex either side of the component int32 X1 = Component->GetSectionBase().X-1; int32 Y1 = Component->GetSectionBase().Y-1; int32 X2 = Component->GetSectionBase().X+ComponentSizeQuads+1; int32 Y2 = Component->GetSectionBase().Y+ComponentSizeQuads+1; int32 Stride = ComponentSizeQuads+3; uint16* HeightData = new uint16[FMath::Square(Stride)]; FVector* VertexNormals = new FVector[FMath::Square(Stride)]; FMemory::Memzero(VertexNormals, FMath::Square(Stride)*sizeof(FVector)); FVector2D* XYOffsets = new FVector2D[FMath::Square(Stride)]; FMemory::Memzero(XYOffsets, FMath::Square(Stride)*sizeof(FVector2D)); // Get XY offset GetXYOffsetDataFast(X1,Y1,X2,Y2,XYOffsets,0); // Get the vertex positions for entire quad GetHeightData(X1,Y1,X2,Y2,HeightData,0); // Contribute face normals for all triangles contributing to this components' normals for( int32 Y=0;YHeightmapTexture->Source.GetSizeX(); int32 SizeV = Component->HeightmapTexture->Source.GetSizeY(); int32 HeightmapOffsetX = Component->HeightmapScaleBias.Z * (float)SizeU; int32 HeightmapOffsetY = Component->HeightmapScaleBias.W * (float)SizeV; FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Component->HeightmapTexture); FColor* HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0); // Apply vertex normals to the component for( int32 SubIndexY=0;SubIndexYNumSubsections;SubIndexY++ ) { for( int32 SubIndexX=0;SubIndexXNumSubsections;SubIndexX++ ) { for( int32 SubY=0;SubY<=SubsectionSizeQuads;SubY++ ) { for( int32 SubX=0;SubX<=SubsectionSizeQuads;SubX++ ) { int32 X = (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 Y = (SubsectionSizeQuads+1) * SubIndexY + SubY; int32 DataIndex = (X+1) + (Y+1) * Stride; int32 TexX = HeightmapOffsetX + X; int32 TexY = HeightmapOffsetY + Y; FColor& TexData = HeightmapTextureData[ TexX + TexY * SizeU ]; // Update the texture FVector Normal = VertexNormals[DataIndex].GetSafeNormal(); TexData.B = FMath::RoundToInt( 127.5f * (Normal.X + 1.0f) ); TexData.A = FMath::RoundToInt( 127.5f * (Normal.Y + 1.0f) ); } } } } delete[] XYOffsets; delete[] HeightData; delete[] VertexNormals; // Record the areas of the texture we need to re-upload int32 TexX1 = HeightmapOffsetX; int32 TexY1 = HeightmapOffsetY; int32 TexX2 = HeightmapOffsetX + (SubsectionSizeQuads+1) * Component->NumSubsections - 1; int32 TexY2 = HeightmapOffsetY + (SubsectionSizeQuads+1) * Component->NumSubsections - 1; TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); // Work out how many mips should be calculated directly from one component's data. // The remaining mips are calculated on a per texture basis. // eg if subsection is 7x7 quads, we need one 3 mips total: (8x8, 4x4, 2x2 verts) int32 BaseNumMips = FMath::CeilLogTwo(SubsectionSizeQuads+1); TArray MipData; MipData.AddUninitialized(BaseNumMips); MipData[0] = HeightmapTextureData; for( int32 MipIdx=1;MipIdxGetMipData(MipIdx); } Component->GenerateHeightmapMips( MipData, 0, 0, ComponentSizeQuads, ComponentSizeQuads, TexDataInfo ); } } template void FLandscapeEditDataInterface::GetHeightDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData, TStoreData* NormalData /*= NULL*/) { if (!LandscapeInfo) return; int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); FLandscapeTextureDataInfo* TexDataInfo = NULL; FColor* HeightmapTextureData = NULL; if( Component ) { TexDataInfo = GetTextureDataInfo(Component->HeightmapTexture); HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0); } else { continue; } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the texture data corresponding to this vertex int32 SizeU = Component->HeightmapTexture->Source.GetSizeX(); int32 SizeV = Component->HeightmapTexture->Source.GetSizeY(); int32 HeightmapOffsetX = Component->HeightmapScaleBias.Z * (float)SizeU; int32 HeightmapOffsetY = Component->HeightmapScaleBias.W * (float)SizeV; int32 TexX = HeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = HeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; FColor& TexData = HeightmapTextureData[ TexX + TexY * SizeU ]; uint16 Height = (((uint16)TexData.R) << 8) | TexData.G; StoreData.Store(LandscapeX, LandscapeY, Height); if (NormalData) { uint16 Normals = (((uint16)TexData.B) << 8) | TexData.A; NormalData->Store(LandscapeX, LandscapeY, Normals); } } } } } } } } template void FLandscapeEditDataInterface::CalcMissingValues(const int32& X1, const int32& X2, const int32& Y1, const int32& Y2, const int32& ComponentIndexX1, const int32& ComponentIndexX2, const int32& ComponentIndexY1, const int32& ComponentIndexY2, const int32& ComponentSizeX, const int32& ComponentSizeY, TData* CornerValues, TArray& NoBorderY1, TArray& NoBorderY2, TArray& ComponentDataExist, TStoreData& StoreData) { bool NoBorderX1 = false, NoBorderX2 = false; // Init data... FMemory::Memzero(NoBorderY1.GetData(), ComponentSizeX*sizeof(bool)); FMemory::Memzero(NoBorderY2.GetData(), ComponentSizeX*sizeof(bool)); int32 BorderX1 = INT_MAX, BorderX2 = INT_MIN; TArray BorderY1, BorderY2; BorderY1.Empty(ComponentSizeX); BorderY2.Empty(ComponentSizeX); for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { new (BorderY1) int32(INT_MAX); new (BorderY2) int32(INT_MIN); } // fill up missing values... for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { NoBorderX1 = false; NoBorderX2 = false; BorderX1 = INT_MAX; BorderX2 = INT_MIN; for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1; if (!ComponentDataExist[ComponentIndexXY]) { int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1; int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1; uint8 CornerSet = 0; bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ]; bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ]; // Search for neighbor component for interpolation bool bShouldSearchX = (BorderX2 <= ComponentIndexX); bool bShouldSearchY = (BorderY2[ComponentIndexXX] <= ComponentIndexY); // Search for left-closest component if ( bShouldSearchX || (!NoBorderX1 && BorderX1 == INT_MAX) ) { NoBorderX1 = true; BorderX1 = INT_MAX; for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--) { if (ComponentDataExist[ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + X-ComponentIndexX1]) { NoBorderX1 = false; BorderX1 = X; break; } } } // Search for right-closest component if ( bShouldSearchX || (!NoBorderX2 && BorderX2 == INT_MIN) ) { NoBorderX2 = true; BorderX2 = INT_MIN; for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++) { if (ComponentDataExist[ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + X-ComponentIndexX1]) { NoBorderX2 = false; BorderX2 = X; break; } } } // Search for up-closest component if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && BorderY1[ComponentIndexXX] == INT_MAX)) { NoBorderY1[ComponentIndexXX] = true; BorderY1[ComponentIndexXX] = INT_MAX; for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--) { if (ComponentDataExist[ComponentSizeX*(Y-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1]) { NoBorderY1[ComponentIndexXX] = false; BorderY1[ComponentIndexXX] = Y; break; } } } // Search for bottom-closest component if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && BorderY2[ComponentIndexXX] == INT_MIN)) { NoBorderY2[ComponentIndexXX] = true; BorderY2[ComponentIndexXX] = INT_MIN; for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++) { if (ComponentDataExist[ComponentSizeX*(Y-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1]) { NoBorderY2[ComponentIndexXX] = false; BorderY2[ComponentIndexXX] = Y; break; } } } if (((ComponentIndexX == ComponentIndexX1) || (ComponentIndexY == ComponentIndexY1)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY-1-ComponentIndexY1) + ComponentIndexX-1-ComponentIndexX1]) { CornerSet |= 1; CornerValues[0] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads)); } if (((ComponentIndexX == ComponentIndexX2) || (ComponentIndexY == ComponentIndexY1)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY-1-ComponentIndexY1) + ComponentIndexX+1-ComponentIndexX1]) { CornerSet |= 1 << 1; CornerValues[1] = TData(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads)); } if (((ComponentIndexX == ComponentIndexX1) || (ComponentIndexY == ComponentIndexY2)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY+1-ComponentIndexY1) + ComponentIndexX-1-ComponentIndexX1]) { CornerSet |= 1 << 2; CornerValues[2] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads)); } if (((ComponentIndexX == ComponentIndexX2) || (ComponentIndexY == ComponentIndexY2)) ? false : ComponentDataExist[ComponentSizeX*(ComponentIndexY+1-ComponentIndexY1) + ComponentIndexX+1-ComponentIndexX1]) { CornerSet |= 1 << 3; CornerValues[3] = TData(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads)); } FillCornerValues(CornerSet, CornerValues); // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the texture data corresponding to this vertex TData Value[4]; FMemory::Memzero(Value, sizeof(TData)* 4); int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX}; FType ValueX, ValueY; FMemory::Memzero(&ValueX, sizeof(FType)); FMemory::Memzero(&ValueY, sizeof(FType)); bool Exist[4] = {false, false, false, false}; if (ExistLeft) { Value[0] = TData(StoreData.Load(ComponentIndexX*ComponentSizeQuads, LandscapeY)); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } else if ( BorderX1 != INT_MAX ) { int32 BorderIdxX = (BorderX1+1)*ComponentSizeQuads; Value[0] = TData(StoreData.Load(BorderIdxX, LandscapeY)); Dist[0] = LandscapeX - (BorderIdxX-1); Exist[0] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 2)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[0] = (FType)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } } if ( BorderX2 != INT_MIN ) { int32 BorderIdxX = BorderX2*ComponentSizeQuads; Value[1] = TData(StoreData.Load(BorderIdxX, LandscapeY)); Dist[1] = (BorderIdxX+1) - LandscapeX; Exist[1] = true; } else { if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[1] = (FType)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Exist[1] = true; } } if (ExistUp) { Value[2] = TData(StoreData.Load(LandscapeX, ComponentIndexY*ComponentSizeQuads)); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } else if ( BorderY1[ComponentIndexXX] != INT_MAX ) { int32 BorderIdxY = (BorderY1[ComponentIndexXX]+1)*ComponentSizeQuads; Value[2] = TData(StoreData.Load(LandscapeX, BorderIdxY)); Dist[2] = LandscapeY - BorderIdxY; Exist[2] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 1)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[2] = (FType)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } } if ( BorderY2[ComponentIndexXX] != INT_MIN ) { int32 BorderIdxY = BorderY2[ComponentIndexXX]*ComponentSizeQuads; Value[3] = TData(StoreData.Load(LandscapeX, BorderIdxY)); Dist[3] = BorderIdxY - LandscapeY; Exist[3] = true; } else { if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[3] = (FType)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY; Exist[3] = true; } } CalcInterpValue(Dist, Exist, Value, ValueX, ValueY); TData FinalValue; // Default Value FMemory::Memzero(&FinalValue, sizeof(TData)); if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) ) { FinalValue = CalcValueFromValueXY(Dist, ValueX, ValueY, CornerSet, CornerValues); } else if ( (Exist[0] || Exist[1]) ) { FinalValue = ValueX; } else if ( (Exist[2] || Exist[3]) ) { FinalValue = ValueY; } StoreData.Store(LandscapeX, LandscapeY, FinalValue); } } } } } } } } uint16 FLandscapeEditDataInterface::GetHeightMapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData /*= NULL*/) { check(Component); if (!TextureData) { FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Component->HeightmapTexture); TextureData = (FColor*)TexDataInfo->GetMipData(0); } int32 SizeU = Component->HeightmapTexture->Source.GetSizeX(); int32 SizeV = Component->HeightmapTexture->Source.GetSizeY(); int32 HeightmapOffsetX = Component->HeightmapScaleBias.Z * (float)SizeU; int32 HeightmapOffsetY = Component->HeightmapScaleBias.W * (float)SizeV; int32 TexX = HeightmapOffsetX + TexU; int32 TexY = HeightmapOffsetY + TexV; FColor& TexData = TextureData[ TexX + TexY * SizeU ]; return ((((uint16)TexData.R) << 8) | TexData.G); } template void FillCornerValues(uint8& CornerSet, T* CornerValues) { uint8 OriginalSet = CornerSet; if (CornerSet) { // Fill unset values while (CornerSet != 15) { if (CornerSet != 15 && (OriginalSet & 1)) { if (!(CornerSet & 1 << 1)) { CornerValues[1] = CornerValues[0]; CornerSet |= 1 << 1; } if (!(CornerSet & 1 << 2)) { CornerValues[2] = CornerValues[0]; CornerSet |= 1 << 2; } } if (CornerSet != 15 && (OriginalSet & 1 << 1)) { if (!(CornerSet & 1)) { CornerValues[0] = CornerValues[1]; CornerSet |= 1; } if (!(CornerSet & 1 << 3)) { CornerValues[3] = CornerValues[1]; CornerSet |= 1 << 3; } } if (CornerSet != 15 && (OriginalSet & 1 << 2)) { if (!(CornerSet & 1)) { CornerValues[0] = CornerValues[2]; CornerSet |= 1; } if (!(CornerSet & 1 << 3)) { CornerValues[3] = CornerValues[2]; CornerSet |= 1 << 3; } } if (CornerSet != 15 && (OriginalSet & 1 << 3)) { if (!(CornerSet & 1 << 1)) { CornerValues[1] = CornerValues[3]; CornerSet |= 1 << 1; } if (!(CornerSet & 1 << 2)) { CornerValues[2] = CornerValues[3]; CornerSet |= 1 << 2; } } OriginalSet = CornerSet; } } } template void FLandscapeEditDataInterface::GetHeightDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData) { // Copy variables int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2; ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN; int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); int32 ComponentSizeX = ComponentIndexX2-ComponentIndexX1+1; int32 ComponentSizeY = ComponentIndexY2-ComponentIndexY1+1; // Neighbor Components ULandscapeComponent* BorderComponent[4] = {0, 0, 0, 0}; ULandscapeComponent* CornerComponent[4] = {0, 0, 0, 0}; bool NoBorderX1 = false, NoBorderX2 = false; TArray NoBorderY1, NoBorderY2, ComponentDataExist; TArray BorderComponentY1, BorderComponentY2; ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY); ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY); bool bHasMissingValue = false; FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = {0, 0, 0, 0}; FColor* NeighborHeightmapTextureData[4] = {0, 0, 0, 0}; uint16 CornerValues[4] = {0, 0, 0, 0}; int32 EdgeCoord = (SubsectionSizeQuads+1) * ComponentNumSubsections - 1; //ComponentSizeQuads; // initial loop.... for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { NoBorderX1 = false; NoBorderX2 = false; BorderComponent[0] = BorderComponent[1] = NULL; for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { BorderComponent[2] = BorderComponent[3] = NULL; int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1; int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1; int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1; ComponentDataExist[ComponentIndexXY] = false; ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); FLandscapeTextureDataInfo* TexDataInfo = NULL; FColor* HeightmapTextureData = NULL; uint8 CornerSet = 0; bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ]; bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ]; if( Component ) { TexDataInfo = GetTextureDataInfo(Component->HeightmapTexture); HeightmapTextureData = (FColor*)TexDataInfo->GetMipData(0); ComponentDataExist[ComponentIndexXY] = true; // Update valid region ValidX1 = FMath::Min(Component->GetSectionBase().X, ValidX1); ValidX2 = FMath::Max(Component->GetSectionBase().X+ComponentSizeQuads, ValidX2); ValidY1 = FMath::Min(Component->GetSectionBase().Y, ValidY1); ValidY2 = FMath::Max(Component->GetSectionBase().Y+ComponentSizeQuads, ValidY2); } else { if (!bHasMissingValue) { NoBorderY1.Empty(ComponentSizeX); NoBorderY2.Empty(ComponentSizeX); NoBorderY1.AddZeroed(ComponentSizeX); NoBorderY2.AddZeroed(ComponentSizeX); BorderComponentY1.Empty(ComponentSizeX); BorderComponentY2.Empty(ComponentSizeX); BorderComponentY1.AddZeroed(ComponentSizeX); BorderComponentY2.AddZeroed(ComponentSizeX); bHasMissingValue = true; } // Search for neighbor component for interpolation bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX); bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY); // Search for left-closest component if ( bShouldSearchX || (!NoBorderX1 && !BorderComponent[0])) { NoBorderX1 = true; for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--) { BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY)); if (BorderComponent[0]) { NoBorderX1 = false; NeighborTexDataInfo[0] = GetTextureDataInfo(BorderComponent[0]->HeightmapTexture); NeighborHeightmapTextureData[0] = (FColor*)NeighborTexDataInfo[0]->GetMipData(0); break; } } } // Search for right-closest component if ( bShouldSearchX || (!NoBorderX2 && !BorderComponent[1])) { NoBorderX2 = true; for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++) { BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY)); if (BorderComponent[1]) { NoBorderX2 = false; NeighborTexDataInfo[1] = GetTextureDataInfo(BorderComponent[1]->HeightmapTexture); NeighborHeightmapTextureData[1] = (FColor*)NeighborTexDataInfo[1]->GetMipData(0); break; } } } // Search for up-closest component if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX])) { NoBorderY1[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--) { BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y)); if (BorderComponent[2]) { NoBorderY1[ComponentIndexXX] = false; NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->HeightmapTexture); NeighborHeightmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0); break; } } } else { BorderComponent[2] = BorderComponentY1[ComponentIndexXX]; if (BorderComponent[2]) { NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->HeightmapTexture); NeighborHeightmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0); } } // Search for bottom-closest component if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX])) { NoBorderY2[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++) { BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y)); if (BorderComponent[3]) { NoBorderY2[ComponentIndexXX] = false; NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->HeightmapTexture); NeighborHeightmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0); break; } } } else { BorderComponent[3] = BorderComponentY2[ComponentIndexXX]; if (BorderComponent[3]) { NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->HeightmapTexture); NeighborHeightmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0); } } CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY-1))) : NULL; CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY-1))) : NULL; CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY+1))) : NULL; CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY+1))) : NULL; if (CornerComponent[0]) { CornerSet |= 1; CornerValues[0] = GetHeightMapData(CornerComponent[0], EdgeCoord, EdgeCoord); } else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads ) { CornerSet |= 1; CornerValues[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads); } else if (BorderComponent[0]) { CornerSet |= 1; CornerValues[0] = GetHeightMapData(BorderComponent[0], EdgeCoord, 0, NeighborHeightmapTextureData[0]); } else if (BorderComponent[2]) { CornerSet |= 1; CornerValues[0] = GetHeightMapData(BorderComponent[2], 0, EdgeCoord, NeighborHeightmapTextureData[2]); } if (CornerComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetHeightMapData(CornerComponent[1], 0, EdgeCoord); } else if (ExistUp && X2 >= (ComponentIndexX+1)*ComponentSizeQuads) { CornerSet |= 1 << 1; CornerValues[1] = StoreData.Load( (ComponentIndexX+1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads); } else if (BorderComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetHeightMapData(BorderComponent[1], 0, 0, NeighborHeightmapTextureData[1]); } else if (BorderComponent[2]) { CornerSet |= 1 << 1; CornerValues[1] = GetHeightMapData(BorderComponent[2], EdgeCoord, EdgeCoord, NeighborHeightmapTextureData[2]); } if (CornerComponent[2]) { CornerSet |= 1 << 2; CornerValues[2] = GetHeightMapData(CornerComponent[2], EdgeCoord, 0); } else if (ExistLeft && Y2 >= (ComponentIndexY+1)*ComponentSizeQuads) // Use data already stored for 0, 2 { CornerSet |= 1 << 2; CornerValues[2] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, (ComponentIndexY+1)*ComponentSizeQuads); } else if (BorderComponent[0]) { CornerSet |= 1 << 2; CornerValues[2] = GetHeightMapData(BorderComponent[0], EdgeCoord, EdgeCoord, NeighborHeightmapTextureData[0]); } else if (BorderComponent[3]) { CornerSet |= 1 << 2; CornerValues[2] = GetHeightMapData(BorderComponent[3], 0, 0, NeighborHeightmapTextureData[3]); } if (CornerComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetHeightMapData(CornerComponent[3], 0, 0); } else if (BorderComponent[1]) { CornerSet |= 1 << 3; CornerValues[3] = GetHeightMapData(BorderComponent[1], 0, EdgeCoord, NeighborHeightmapTextureData[1]); } else if (BorderComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetHeightMapData(BorderComponent[3], EdgeCoord, 0, NeighborHeightmapTextureData[3]); } FillCornerValues(CornerSet, CornerValues); ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet; } if (!ComponentDataExist[ComponentIndexXY]) { continue; } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the input data corresponding to this vertex if( Component ) { // Find the texture data corresponding to this vertex uint16 Height = GetHeightMapData(Component, (SubsectionSizeQuads+1) * SubIndexX + SubX, (SubsectionSizeQuads+1) * SubIndexY + SubY, HeightmapTextureData); StoreData.Store(LandscapeX, LandscapeY, Height); } else { // Find the texture data corresponding to this vertex uint16 Value[4] = {0, 0, 0, 0}; int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX}; float ValueX = 0.0f, ValueY = 0.0f; bool Exist[4] = {false, false, false, false}; // Use data already stored for 0, 2 if (ExistLeft) { Value[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, LandscapeY); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } else if (BorderComponent[0]) { Value[0] = GetHeightMapData(BorderComponent[0], EdgeCoord, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborHeightmapTextureData[0]); Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads); Exist[0] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 2)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[0] = (float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } } if (BorderComponent[1]) { Value[1] = GetHeightMapData(BorderComponent[1], 0, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborHeightmapTextureData[1]); Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX; Exist[1] = true; } else { if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[1] = (float)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Exist[1] = true; } } if (ExistUp) { Value[2] = StoreData.Load( LandscapeX, ComponentIndexY*ComponentSizeQuads); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } else if (BorderComponent[2]) { Value[2] = GetHeightMapData(BorderComponent[2], (SubsectionSizeQuads+1) * SubIndexX + SubX, EdgeCoord, NeighborHeightmapTextureData[2]); Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads); Exist[2] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 1)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[2] = (float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } } if (BorderComponent[3]) { Value[3] = GetHeightMapData(BorderComponent[3], (SubsectionSizeQuads+1) * SubIndexX + SubX, 0, NeighborHeightmapTextureData[3]); Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY; Exist[3] = true; } else { if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[3] = (float)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY; Exist[3] = true; } } CalcInterpValue(Dist, Exist, Value, ValueX, ValueY); uint16 FinalValue = 0; // Default Value if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) ) { FinalValue = CalcValueFromValueXY(Dist, ValueX, ValueY, CornerSet, CornerValues); } else if ( (BorderComponent[0] || BorderComponent[1]) ) { FinalValue = ValueX; } else if ( (BorderComponent[2] || BorderComponent[3]) ) { FinalValue = ValueY; } else if ( (Exist[0] || Exist[1]) ) { FinalValue = ValueX; } else if ( (Exist[2] || Exist[3]) ) { FinalValue = ValueY; } StoreData.Store(LandscapeX, LandscapeY, FinalValue); //StoreData.StoreDefault(LandscapeX, LandscapeY); } } } } } } } if (bHasMissingValue) { CalcMissingValues( X1, X2, Y1, Y2, ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2, ComponentSizeX, ComponentSizeY, CornerValues, NoBorderY1, NoBorderY2, ComponentDataExist, StoreData ); // Update valid region ValidX1 = FMath::Max(X1, ValidX1); ValidX2 = FMath::Min(X2, ValidX2); ValidY1 = FMath::Max(Y1, ValidY1); ValidY2 = FMath::Min(Y2, ValidY2); } else { ValidX1 = X1; ValidX2 = X2; ValidY1 = Y1; ValidY2 = Y2; } } namespace { struct FArrayStoreData { int32 X1; int32 Y1; uint16* Data; int32 Stride; FArrayStoreData(int32 InX1, int32 InY1, uint16* InData, int32 InStride) : X1(InX1) , Y1(InY1) , Data(InData) , Stride(InStride) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint16 Height) { Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = Height; } // for interpolation inline uint16 Load(int32 LandscapeX, int32 LandscapeY) { return Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ]; } inline void StoreDefault(int32 LandscapeX, int32 LandscapeY) { Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = 0; } }; struct FSparseStoreData { TMap& SparseData; FSparseStoreData(TMap& InSparseData) : SparseData(InSparseData) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint16 Height) { SparseData.Add(ALandscape::MakeKey(LandscapeX,LandscapeY), Height); } inline uint16 Load(int32 LandscapeX, int32 LandscapeY) { return SparseData.FindRef(ALandscape::MakeKey(LandscapeX,LandscapeY)); } inline void StoreDefault(int32 LandscapeX, int32 LandscapeY) { } }; }; void FLandscapeEditDataInterface::GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, uint16* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } FArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetHeightDataTempl(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint16* Data, int32 Stride, uint16* NormalData /*= NULL*/) { if( Stride==0 ) { Stride = (1+X2-X1); } FArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); if (NormalData) { FArrayStoreData ArrayNormalData(X1, Y1, NormalData, Stride); GetHeightDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData, &ArrayNormalData); } else { GetHeightDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData); } } void FLandscapeEditDataInterface::GetHeightData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap& SparseData) { FSparseStoreData SparseStoreData(SparseData); GetHeightDataTempl(X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetHeightDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap& SparseData, TMap* NormalData /*= NULL*/) { FSparseStoreData SparseStoreData(SparseData); if (NormalData) { FSparseStoreData SparseNormalData(*NormalData); GetHeightDataTemplFast(X1, Y1, X2, Y2, SparseStoreData, &SparseNormalData); } else { GetHeightDataTemplFast(X1, Y1, X2, Y2, SparseStoreData); } } void ULandscapeComponent::DeleteLayer( ULandscapeLayerInfoObject* LayerInfo, struct FLandscapeEditDataInterface* LandscapeEdit ) { ULandscapeComponent* Component = this; //Landscape->LandscapeComponents(ComponentIdx); // Find the index for this layer in this component. int32 DeleteLayerIdx = INDEX_NONE; for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; if( Allocation.LayerInfo == LayerInfo ) { DeleteLayerIdx = LayerIdx; } } if( DeleteLayerIdx == INDEX_NONE ) { // Layer not used for this component. //continue; return; } ULandscapeInfo* Info = GetLandscapeInfo(); if (!Info) { return; } FWeightmapLayerAllocationInfo& DeleteLayerAllocation = Component->WeightmapLayerAllocations[DeleteLayerIdx]; int32 DeleteLayerWeightmapTextureIndex = DeleteLayerAllocation.WeightmapTextureIndex; // See if we'll be able to remove the texture completely. bool bCanRemoveLayerTexture = true; for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; // check if we will be able to remove the texture also if( LayerIdx!=DeleteLayerIdx && Allocation.WeightmapTextureIndex == DeleteLayerWeightmapTextureIndex ) { bCanRemoveLayerTexture = false; } } // See if the deleted layer is a NoWeightBlend layer - if not, we don't have to worry about normalization bool bDeleteLayerIsNoWeightBlend = (LayerInfo && LayerInfo->bNoWeightBlend); if( !bDeleteLayerIsNoWeightBlend ) { // Lock data for all the weightmaps TArray TexDataInfos; for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) { if (LandscapeEdit) { TexDataInfos.Add(LandscapeEdit->GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); } } // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; TArray LayerNoWeightBlends; // Array of NoWeightBlend flags TArray LayerDataPtrs; // Pointers to all layers' data // Get the data for each layer for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; LayerDataPtrs.Add( (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel] ); // Find the layer info and record if it is a bNoWeightBlend layer. LayerNoWeightBlends.Add( Allocation.LayerInfo && Allocation.LayerInfo->bNoWeightBlend ); } // Find the texture data corresponding to this vertex int32 SizeU = (SubsectionSizeQuads+1) * NumSubsections; //Component->WeightmapTextures(0)->SizeX; // not exactly correct. int32 SizeV = (SubsectionSizeQuads+1) * NumSubsections; //Component->WeightmapTextures(0)->SizeY; int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; for( int32 SubIndexY=0;SubIndexYWeightmapLayerAllocations.Num();LayerIdx++ ) { if( LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx]==false ) { OtherLayerWeightSum += LayerDataPtrs[LayerIdx][TexDataIndex]; } } if( OtherLayerWeightSum == 0 ) { // Set the first other weight-blend layer we can find to 255 to avoid a black hole // This isn't ideal but it's the best option // There's nothing we can easily do if this was the only weight-blend layer on this component for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx]==false ) { uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex]; Weight = 255; break; } } } else { // Adjust other layer weights for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( LayerIdx != DeleteLayerIdx && LayerNoWeightBlends[LayerIdx]==false ) { uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex]; Weight = FMath::Clamp( FMath::RoundToInt(255.0f * (float)Weight/(float)OtherLayerWeightSum), 0, 255 ); } } } } } } } // Update all the textures and mips for( int32 Idx=0;IdxWeightmapTextures.Num();Idx++) { if( bCanRemoveLayerTexture && Idx==DeleteLayerWeightmapTextureIndex ) { // We're going to remove this texture anyway, so don't bother updating continue; } UTexture2D* WeightmapTexture = Component->WeightmapTextures[Idx]; FLandscapeTextureDataInfo* WeightmapDataInfo = TexDataInfos[Idx]; int32 NumMips = WeightmapTexture->Source.GetNumMips(); TArray WeightmapTextureMipData; WeightmapTextureMipData.AddUninitialized(NumMips); for( int32 MipIdx=0;MipIdxGetMipData(MipIdx); } ULandscapeComponent::UpdateWeightmapMips(Component->NumSubsections, Component->SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo); WeightmapDataInfo->AddMipUpdateRegion(0,0,0,WeightmapTexture->Source.GetSizeX()-1,WeightmapTexture->Source.GetSizeY()-1); } } ALandscapeProxy* Proxy = GetLandscapeProxy(); //CastChecked(Component->GetOuter()); // Mark the channel as unallocated, so we can reuse it later FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(Component->WeightmapTextures[DeleteLayerAllocation.WeightmapTextureIndex]); //check(Usage); if (Usage) { Usage->ChannelUsage[DeleteLayerAllocation.WeightmapTextureChannel] = NULL; } // Remove the layer Component->WeightmapLayerAllocations.RemoveAt(DeleteLayerIdx); // If this layer was the last usage for this channel in this layer, we can remove it. if( bCanRemoveLayerTexture ) { Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->SetFlags(RF_Transactional); Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->Modify(); Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); Component->WeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); // Adjust WeightmapTextureIndex index for other layers for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; if( Allocation.WeightmapTextureIndex > DeleteLayerWeightmapTextureIndex ) { Allocation.WeightmapTextureIndex--; } check( Allocation.WeightmapTextureIndex < Component->WeightmapTextures.Num() ); } } // Update the shaders for this component Component->UpdateMaterialInstances(); } void FLandscapeEditDataInterface::DeleteLayer( ULandscapeLayerInfoObject* LayerInfo ) { if (!LandscapeInfo) return; for( auto It = LandscapeInfo->XYtoComponentMap.CreateIterator(); It; ++It ) { ULandscapeComponent* Component = It.Value(); Component->DeleteLayer(LayerInfo, this); // Update dominant layer info stored in collision component TArray CollisionWeightmapMipData; for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) { CollisionWeightmapMipData.Add( (FColor*)GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel) ); } Component->UpdateCollisionLayerData(CollisionWeightmapMipData); } } void ULandscapeComponent::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo, struct FLandscapeEditDataInterface* LandscapeEdit) { check(FromLayerInfo && ToLayerInfo && LandscapeEdit); if (FromLayerInfo == ToLayerInfo) { return; } // Find the index for this layer in this component. int32 FromLayerIdx = INDEX_NONE; for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) { FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo == FromLayerInfo) { FromLayerIdx = LayerIdx; } } if (FromLayerIdx == INDEX_NONE) { // Layer not used for this component, nothing to do. return; } bool bMerging = true; // Find the index for this layer in this component. int32 ToLayerIdx = INDEX_NONE; for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) { FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; if (Allocation.LayerInfo == ToLayerInfo) { ToLayerIdx = LayerIdx; } } if (ToLayerIdx == INDEX_NONE) { // Layer not used for this component, so do trivial replace. WeightmapLayerAllocations[FromLayerIdx].LayerInfo = ToLayerInfo; bMerging = false; } FWeightmapLayerAllocationInfo& FromLayerAllocation = WeightmapLayerAllocations[FromLayerIdx]; // See if we'll be able to remove the texture completely. bool bCanRemoveLayerTexture = false; if (bMerging) { bCanRemoveLayerTexture = true; for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) { FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; // check if we will be able to remove the texture also if (LayerIdx != FromLayerIdx && Allocation.WeightmapTextureIndex == FromLayerAllocation.WeightmapTextureIndex) { bCanRemoveLayerTexture = false; break; } } } // See if the deleted layer is a NoWeightBlend layer - if not, we don't have to worry about normalization const bool bFromLayerIsNoWeightBlend = (FromLayerInfo && FromLayerInfo->bNoWeightBlend); const bool bToLayerIsNoWeightBlend = (ToLayerInfo && ToLayerInfo->bNoWeightBlend); const bool bRequireNormalization = (bFromLayerIsNoWeightBlend != bToLayerIsNoWeightBlend); checkf(!bRequireNormalization, TEXT("It is not yet supported to replace a layer with another whose bNoWeightBlend setting does not match")) if (bMerging) { FWeightmapLayerAllocationInfo& ToLayerAllocation = WeightmapLayerAllocations[ToLayerIdx]; // Lock data for all the weightmaps FLandscapeTextureDataInfo* FromTexDataInfo = LandscapeEdit->GetTextureDataInfo(WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]); FLandscapeTextureDataInfo* ToTexDataInfo = LandscapeEdit->GetTextureDataInfo(WeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]); check(FromTexDataInfo->GetMipSizeX(0) == FromTexDataInfo->GetMipSizeY(0)); check(ToTexDataInfo->GetMipSizeX(0) == ToTexDataInfo->GetMipSizeY(0)); check(FromTexDataInfo->GetMipSizeX(0) == ToTexDataInfo->GetMipSizeX(0)); const int32 MipSize = FromTexDataInfo->GetMipSizeX(0); // Channel remapping const int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R), (int32)STRUCT_OFFSET(FColor,G), (int32)STRUCT_OFFSET(FColor,B), (int32)STRUCT_OFFSET(FColor,A)}; uint8* const SrcTextureData = (uint8*)FromTexDataInfo->GetMipData(0) + ChannelOffsets[FromLayerAllocation.WeightmapTextureChannel]; uint8* const DestTextureData = (uint8*)ToTexDataInfo->GetMipData(0) + ChannelOffsets[ToLayerAllocation.WeightmapTextureChannel]; for (int32 i = 0; i < FMath::Square(MipSize); i++) { DestTextureData[i*4] = FMath::Min(255, (uint16)DestTextureData[i*4] + (uint16)SrcTextureData[i*4]); } // Update all mips if (!bCanRemoveLayerTexture) { UTexture2D* WeightmapTexture = WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]; FLandscapeTextureDataInfo* WeightmapDataInfo = FromTexDataInfo; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); TArray WeightmapTextureMipData; WeightmapTextureMipData.AddUninitialized(NumMips); for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++) { WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx); } ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo); WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1); } if (FromTexDataInfo != ToTexDataInfo) { UTexture2D* WeightmapTexture = WeightmapTextures[ToLayerAllocation.WeightmapTextureIndex]; FLandscapeTextureDataInfo* WeightmapDataInfo = ToTexDataInfo; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); TArray WeightmapTextureMipData; WeightmapTextureMipData.AddUninitialized(NumMips); for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++) { WeightmapTextureMipData[MipIdx] = (FColor*)WeightmapDataInfo->GetMipData(MipIdx); } ULandscapeComponent::UpdateWeightmapMips(NumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, 0, 0, MAX_int32, MAX_int32, WeightmapDataInfo); WeightmapDataInfo->AddMipUpdateRegion(0, 0, 0, WeightmapTexture->Source.GetSizeX() - 1, WeightmapTexture->Source.GetSizeY() - 1); } } if (bRequireNormalization) { // TODO } // if merging into an existing layer, remove the layer and potentially the texture if (bMerging) { ALandscapeProxy* Proxy = GetLandscapeProxy(); // Mark the channel as unallocated, so we can reuse it later FLandscapeWeightmapUsage* Usage = Proxy->WeightmapUsageMap.Find(WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]); //check(Usage); if (Usage) { Usage->ChannelUsage[FromLayerAllocation.WeightmapTextureChannel] = NULL; } // If this layer was the last usage for this texture, we can remove it. if (bCanRemoveLayerTexture) { WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->SetFlags(RF_Transactional); WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->Modify(); WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->MarkPackageDirty(); WeightmapTextures[FromLayerAllocation.WeightmapTextureIndex]->ClearFlags(RF_Standalone); WeightmapTextures.RemoveAt(FromLayerAllocation.WeightmapTextureIndex); // Adjust WeightmapTextureIndex index for other layers for (int32 LayerIdx = 0; LayerIdx < WeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerIdx == FromLayerIdx) { continue; } FWeightmapLayerAllocationInfo& Allocation = WeightmapLayerAllocations[LayerIdx]; if (Allocation.WeightmapTextureIndex > FromLayerAllocation.WeightmapTextureIndex) { Allocation.WeightmapTextureIndex--; } check(Allocation.WeightmapTextureIndex < WeightmapTextures.Num()); } } // Remove the layer WeightmapLayerAllocations.RemoveAt(FromLayerIdx); // Update the shaders for this component UpdateMaterialInstances(); } } void FLandscapeEditDataInterface::ReplaceLayer(ULandscapeLayerInfoObject* FromLayerInfo, ULandscapeLayerInfoObject* ToLayerInfo) { if (!LandscapeInfo) return; for( auto It = LandscapeInfo->XYtoComponentMap.CreateIterator(); It; ++It ) { ULandscapeComponent* Component = It.Value(); Component->ReplaceLayer(FromLayerInfo, ToLayerInfo, this); // Update dominant layer info stored in collision component TArray CollisionWeightmapMipData; for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) { CollisionWeightmapMipData.Add( (FColor*)GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])->GetMipData(Component->CollisionMipLevel) ); } Component->UpdateCollisionLayerData(CollisionWeightmapMipData); } } // simple classes for the template.... namespace { template struct TArrayStoreData { int32 X1; int32 Y1; TDataType* Data; int32 Stride; int32 ArraySize; TArrayStoreData(int32 InX1, int32 InY1, TDataType* InData, int32 InStride) : X1(InX1) , Y1(InY1) , Data(InData) , Stride(InStride) , ArraySize(1) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) {} inline void Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) {} inline TDataType Load(int32 LandscapeX, int32 LandscapeY) { return 0; } inline void PreInit(int32 InArraySize) { ArraySize = InArraySize; } }; template<> void TArrayStoreData::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) { Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = Weight; } template<> uint8 TArrayStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ]; } template<> FVector2D TArrayStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return Data[(LandscapeY - Y1) * Stride + (LandscapeX - X1)]; } template<> FVector TArrayStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return Data[(LandscapeY - Y1) * Stride + (LandscapeX - X1)]; } template<> void TArrayStoreData::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) { Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = Offset; } template<> void TArrayStoreData::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) { Data[ (LandscapeY-Y1) * Stride + (LandscapeX-X1) ] = FVector(Offset.X, Offset.Y, 0.0f); } // Data items should be initialized with ArraySize template<> void TArrayStoreData>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) { TArray& Value = Data[ ((LandscapeY-Y1) * Stride + (LandscapeX-X1)) ]; if (Value.Num() != ArraySize) { Value.Empty(ArraySize); Value.AddZeroed(ArraySize); } Value[LayerIdx] = Weight; } template struct TSparseStoreData { TMap& SparseData; int32 ArraySize; TSparseStoreData(TMap& InSparseData) : SparseData(InSparseData) , ArraySize(1) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) {} inline void Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) {} inline void Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) {} inline TDataType Load(int32 LandscapeX, int32 LandscapeY) { return 0; } inline void PreInit(int32 InArraySize) { ArraySize = InArraySize; } }; template<> void TSparseStoreData::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight) { SparseData.Add(ALandscape::MakeKey(LandscapeX,LandscapeY), Weight); } template<> uint8 TSparseStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return SparseData.FindRef(ALandscape::MakeKey(LandscapeX,LandscapeY)); } template<> FVector2D TSparseStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return SparseData.FindRef(ALandscape::MakeKey(LandscapeX, LandscapeY)); } template<> FVector TSparseStoreData::Load(int32 LandscapeX, int32 LandscapeY) { return SparseData.FindRef(ALandscape::MakeKey(LandscapeX, LandscapeY)); } template<> void TSparseStoreData>::Store(int32 LandscapeX, int32 LandscapeY, uint8 Weight, int32 LayerIdx) { TArray* Value = SparseData.Find(ALandscape::MakeKey(LandscapeX,LandscapeY)); if (Value) { (*Value)[LayerIdx] = Weight; } else { TArray Values; Values.Empty(ArraySize); Values.AddZeroed(ArraySize); Values[LayerIdx] = Weight; SparseData.Add(ALandscape::MakeKey(LandscapeX, LandscapeY), Values); } } template<> void TSparseStoreData::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) { SparseData.Add(ALandscape::MakeKey(LandscapeX,LandscapeY), Offset); } template<> void TSparseStoreData::Store(int32 LandscapeX, int32 LandscapeY, FVector2D Offset) { // Preserve old Z value FVector* PrevValue = SparseData.Find(ALandscape::MakeKey(LandscapeX,LandscapeY)); if (PrevValue != NULL) { PrevValue->X = Offset.X; PrevValue->Y = Offset.Y; } else { SparseData.Add(ALandscape::MakeKey(LandscapeX,LandscapeY), FVector(Offset.X, Offset.Y, 0.0f)); } } }; bool DeleteLayerIfAllZero(ULandscapeComponent* const Component, const uint8* const TexDataPtr, int32 TexSize, int32 LayerIdx) { // Check the data for the entire component and to see if it's all zero for (int32 TexY = 0; TexY < TexSize; TexY++) { for (int32 TexX = 0; TexX < TexSize; TexX++) { const int32 TexDataIndex = 4 * (TexX + TexY * TexSize); // Stop the first time we see any non-zero data const uint8& Weight = TexDataPtr[TexDataIndex]; if (Weight != 0) { return false; } } } Component->Modify(); // Mark the channel as unallocated, so we can reuse it later const int32 DeleteLayerWeightmapTextureIndex = Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; FLandscapeWeightmapUsage& Usage = Component->GetLandscapeProxy()->WeightmapUsageMap.FindChecked(Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]); Usage.ChannelUsage[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel] = NULL; // Remove the layer as it's totally painted away. Component->WeightmapLayerAllocations.RemoveAt(LayerIdx); // Check if the weightmap texture used by the layer we just removed is used by any other layer, and if so, remove the texture too bool bCanRemoveLayerTexture = !Component->WeightmapLayerAllocations.ContainsByPredicate([DeleteLayerWeightmapTextureIndex](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.WeightmapTextureIndex == DeleteLayerWeightmapTextureIndex; }); if (bCanRemoveLayerTexture) { Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->MarkPackageDirty(); Component->WeightmapTextures[DeleteLayerWeightmapTextureIndex]->ClearFlags(RF_Standalone); Component->WeightmapTextures.RemoveAt(DeleteLayerWeightmapTextureIndex); // Adjust WeightmapTextureChannel index for other layers for (auto It = Component->WeightmapLayerAllocations.CreateIterator(); It; ++It) { FWeightmapLayerAllocationInfo& Allocation = *It; if (Allocation.WeightmapTextureIndex > DeleteLayerWeightmapTextureIndex) { Allocation.WeightmapTextureIndex--; } } } return true; } void FLandscapeEditDataInterface::SetAlphaData(ULandscapeLayerInfoObject* const LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction::Type PaintingRestriction /*= None*/, bool bWeightAdjust /*= true*/, bool bTotalWeightAdjust /*= false*/) { check(LayerInfo != NULL); if (LayerInfo->bNoWeightBlend) { bWeightAdjust = false; } if (Stride == 0) { Stride = (1+X2-X1); } // Channel remapping size_t ChannelOffsets[4] = {STRUCT_OFFSET(FColor,R), STRUCT_OFFSET(FColor,G), STRUCT_OFFSET(FColor,B), STRUCT_OFFSET(FColor,A)}; check(ComponentSizeQuads > 0); // Find component range for this block of data int32 ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components int32 ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1; int32 ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1; int32 ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1; TArray> TexDataInfos; TArray> LayerDataPtrs; // Pointers to all layers' data TArray> LayerNoWeightBlends; // NoWeightBlend flags TArray> LayerEditDataAllZero; // Whether the data we are editing for this layer is all zero TArray CollisionWeightmapMipData; TArray WeightmapTextureMipData; for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++) { for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++) { FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY); ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey); // if NULL, there is no component at this location if (Component == NULL) { continue; } int32 UpdateLayerIdx = Component->WeightmapLayerAllocations.IndexOfByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); // Need allocation for weightmap if (UpdateLayerIdx == INDEX_NONE) { const int32 LayerLimit = Component->GetLandscapeProxy()->MaxPaintedLayersPerComponent; // if we can't allocate a layer, then there is nothing to paint if (PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly || (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && LayerLimit > 0 && Component->WeightmapLayerAllocations.Num() >= LayerLimit)) { continue; } Component->Modify(); UpdateLayerIdx = Component->WeightmapLayerAllocations.Num(); new (Component->WeightmapLayerAllocations) FWeightmapLayerAllocationInfo(LayerInfo); Component->ReallocateWeightmaps(this); Component->UpdateMaterialInstances(); if( Component->EditToolRenderData ) { Component->EditToolRenderData->UpdateDebugColorMaterial(); } } // Lock data for all the weightmaps TexDataInfos.Reset(); TexDataInfos.AddUninitialized(Component->WeightmapTextures.Num()); for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); ++WeightmapIdx) { TexDataInfos[WeightmapIdx] = GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx]); }; LayerDataPtrs.Reset(); // Pointers to all layers' data LayerDataPtrs.AddUninitialized(Component->WeightmapLayerAllocations.Num()); LayerNoWeightBlends.Reset(); // NoWeightBlend flags LayerNoWeightBlends.AddUninitialized(Component->WeightmapLayerAllocations.Num()); LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero LayerEditDataAllZero.AddUninitialized(Component->WeightmapLayerAllocations.Num()); for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; LayerDataPtrs[LayerIdx] = (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]; LayerNoWeightBlends[LayerIdx] = Allocation.LayerInfo->bNoWeightBlend; LayerEditDataAllZero[LayerIdx] = true; } // Find the texture data corresponding to this vertex const int32 TexSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections; // Find coordinates of box that lies inside component const int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box const int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections const int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); const int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); const int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++) { for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++) { // Find coordinates of box that lies inside subsection const int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); const int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); const int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); const int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for (int32 SubY = SubY1; SubY <= SubY2; SubY++) { for (int32 SubX = SubX1; SubX <= SubX2; SubX++) { const int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; const int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; checkSlow( LandscapeX >= X1 && LandscapeX <= X2 ); checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 ); // Find the input data corresponding to this vertex const int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1); uint8 NewWeight = Data[DataIndex]; // Adjust all layer weights const int32 TexX = (SubsectionSizeQuads+1) * SubIndexX + SubX; const int32 TexY = (SubsectionSizeQuads+1) * SubIndexY + SubY; const int32 TexDataIndex = 4 * (TexX + TexY * TexSize); int32 OtherLayerWeightSum = 0; if (bWeightAdjust) { if (bTotalWeightAdjust) { int32 MaxLayerIdx = -1; int32 MaxWeight = INT_MIN; // Adjust other layers' weights accordingly for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex]; if (LayerIdx == UpdateLayerIdx) { ExistingWeight = NewWeight; } // Exclude bNoWeightBlend layers if (LayerNoWeightBlends[LayerIdx] == false) { OtherLayerWeightSum += ExistingWeight; if (MaxWeight < ExistingWeight) { MaxWeight = ExistingWeight; MaxLayerIdx = LayerIdx; } } } if (OtherLayerWeightSum != 255) { const float Factor = 255.0f / OtherLayerWeightSum; OtherLayerWeightSum = 0; // Normalize for (int32 LayerIdx = 0; LayerIdxWeightmapLayerAllocations.Num(); LayerIdx++) { uint8& ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex]; if (LayerNoWeightBlends[LayerIdx] == false) { // normalization... ExistingWeight = (uint8)(Factor * ExistingWeight); OtherLayerWeightSum += ExistingWeight; if (ExistingWeight != 0) { LayerEditDataAllZero[LayerIdx] = false; } } } if ((255 - OtherLayerWeightSum) && MaxLayerIdx >= 0) { LayerDataPtrs[MaxLayerIdx][TexDataIndex] += 255 - OtherLayerWeightSum; } } } else { // Adjust other layers' weights accordingly for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { const uint8 ExistingWeight = LayerDataPtrs[LayerIdx][TexDataIndex]; // Exclude bNoWeightBlend layers if (LayerIdx != UpdateLayerIdx && LayerNoWeightBlends[LayerIdx] == false) { OtherLayerWeightSum += ExistingWeight; } } if (OtherLayerWeightSum == 0) { NewWeight = 255; OtherLayerWeightSum = 1; } for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { uint8& Weight = LayerDataPtrs[LayerIdx][TexDataIndex]; if (LayerIdx == UpdateLayerIdx) { Weight = NewWeight; } else { // Exclude bNoWeightBlend layers if (LayerNoWeightBlends[LayerIdx] == false) { Weight = FMath::Clamp( FMath::RoundToInt((float)(255 - NewWeight) * (float)Weight/(float)OtherLayerWeightSum), 0, 255 ); } } if (Weight != 0) { LayerEditDataAllZero[LayerIdx] = false; } } } } else { // Weight value set without adjusting other layers' weights uint8& Weight = LayerDataPtrs[UpdateLayerIdx][TexDataIndex]; Weight = NewWeight; if (Weight != 0) { LayerEditDataAllZero[UpdateLayerIdx] = false; } } } } // Record the areas of the texture we need to re-upload const int32 TexX1 = (SubsectionSizeQuads+1) * SubIndexX + SubX1; const int32 TexY1 = (SubsectionSizeQuads+1) * SubIndexY + SubY1; const int32 TexX2 = (SubsectionSizeQuads+1) * SubIndexX + SubX2; const int32 TexY2 = (SubsectionSizeQuads+1) * SubIndexY + SubY2; for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) { if (TexDataInfos[WeightmapIdx] != NULL) { TexDataInfos[WeightmapIdx]->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); } } } } // Update mipmaps CollisionWeightmapMipData.Reset(); CollisionWeightmapMipData.AddUninitialized(Component->WeightmapTextures.Num()); for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) { UTexture2D* const WeightmapTexture = Component->WeightmapTextures[WeightmapIdx]; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); WeightmapTextureMipData.Reset(); WeightmapTextureMipData.AddUninitialized(NumMips); for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++) { FColor* const MipData = (FColor*)TexDataInfos[WeightmapIdx]->GetMipData(MipIdx); WeightmapTextureMipData[MipIdx] = MipData; } CollisionWeightmapMipData[WeightmapIdx] = WeightmapTextureMipData[Component->CollisionMipLevel]; ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfos[WeightmapIdx]); WeightmapTextureMipData.Reset(); } // Update dominant layer info stored in collision component Component->UpdateCollisionLayerData(CollisionWeightmapMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2); CollisionWeightmapMipData.Reset(); // Check if we need to remove weightmap allocations for layers that were completely painted away bool bRemovedLayer = false; for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerEditDataAllZero[LayerIdx]) { bool bLayerDeleted = DeleteLayerIfAllZero(Component, LayerDataPtrs[LayerIdx], TexSize, LayerIdx); if (bLayerDeleted) { LayerEditDataAllZero.RemoveAt(LayerIdx); LayerDataPtrs.RemoveAt(LayerIdx); LayerIdx--; bRemovedLayer = true; } } } if (bRemovedLayer) { Component->UpdateMaterialInstances(); if (Component->EditToolRenderData) { Component->EditToolRenderData->UpdateDebugColorMaterial(); } } } } } void FLandscapeEditDataInterface::SetAlphaData(const TSet& DirtyLayerInfos, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, const uint8* Data, int32 Stride, ELandscapeLayerPaintingRestriction::Type PaintingRestriction /*= None*/) { if (DirtyLayerInfos.Num() == 0) { return; } for (ULandscapeLayerInfoObject* LayerInfo : DirtyLayerInfos) { // The Data[] array passed in is indexed by LandscapeInfo->GetLayerInfoIndex(), // so if we're trying to write a layer which isn't in the LandscapeInfo, // its data is either missing or written where another layer's data should be. // Either way it's *very bad*. check(LandscapeInfo->GetLayerInfoIndex(LayerInfo) != INDEX_NONE); } if (Stride == 0) { Stride = (1+X2-X1) * LandscapeInfo->Layers.Num(); } // Channel remapping size_t ChannelOffsets[4] = {STRUCT_OFFSET(FColor,R), STRUCT_OFFSET(FColor,G), STRUCT_OFFSET(FColor,B), STRUCT_OFFSET(FColor,A)}; check(ComponentSizeQuads > 0); // Find component range for this block of data int32 ComponentIndexX1 = (X1-1 >= 0) ? (X1-1) / ComponentSizeQuads : (X1) / ComponentSizeQuads - 1; // -1 because we need to pick up vertices shared between components int32 ComponentIndexY1 = (Y1-1 >= 0) ? (Y1-1) / ComponentSizeQuads : (Y1) / ComponentSizeQuads - 1; int32 ComponentIndexX2 = (X2 >= 0) ? X2 / ComponentSizeQuads : (X2+1) / ComponentSizeQuads - 1; int32 ComponentIndexY2 = (Y2 >= 0) ? Y2 / ComponentSizeQuads : (Y2+1) / ComponentSizeQuads - 1; TArray> NeedAllocationInfos; TArray> TexDataInfos; struct FLayerDataInfo { const uint8* InDataPtr; uint8* TexDataPtr; }; TArray> LayerDataInfos; // Pointers to all layers' data TArray> LayerEditDataAllZero; // Whether the data we are editing for this layer is all zero TArray CollisionWeightmapMipData; TArray WeightmapTextureMipData; for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++) { for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++) { FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY); ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey); // if NULL, there is no component at this location if (Component == NULL) { continue; } const int32 LayerLimit = Component->GetLandscapeProxy()->MaxPaintedLayersPerComponent; NeedAllocationInfos.Reset(); for (ULandscapeLayerInfoObject* LayerInfo : DirtyLayerInfos) { const bool bFound = Component->WeightmapLayerAllocations.ContainsByPredicate([LayerInfo](const FWeightmapLayerAllocationInfo& Allocation){ return Allocation.LayerInfo == LayerInfo; }); if (!bFound) { NeedAllocationInfos.Add(LayerInfo); } } // Need allocation for weightmaps if (NeedAllocationInfos.Num() > 0) { if (NeedAllocationInfos.Num() == DirtyLayerInfos.Num()) { if (PaintingRestriction == ELandscapeLayerPaintingRestriction::ExistingOnly || (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && Component->WeightmapLayerAllocations.Num() >= LayerLimit)) { // nothing to paint to this component due to layer limit continue; } } if (PaintingRestriction != ELandscapeLayerPaintingRestriction::ExistingOnly) { Component->Modify(); for (ULandscapeLayerInfoObject* LayerInfoNeedingAllocation : NeedAllocationInfos) { if (PaintingRestriction == ELandscapeLayerPaintingRestriction::UseMaxLayers && LayerLimit > 0 && Component->WeightmapLayerAllocations.Num() >= LayerLimit) { break; } Component->WeightmapLayerAllocations.Emplace(LayerInfoNeedingAllocation); } Component->ReallocateWeightmaps(this); Component->UpdateMaterialInstances(); if (Component->EditToolRenderData) { Component->EditToolRenderData->UpdateDebugColorMaterial(); } } } // Lock data for all the weightmaps TexDataInfos.Reset(); TexDataInfos.AddUninitialized(Component->WeightmapTextures.Num()); for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); ++WeightmapIdx) { TexDataInfos[WeightmapIdx] = GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx]); } LayerDataInfos.Reset(); // Pointers to all layers' data LayerDataInfos.AddUninitialized(Component->WeightmapLayerAllocations.Num()); LayerEditDataAllZero.Reset(); // Whether the data we are editing for this layer is all zero LayerEditDataAllZero.AddUninitialized(Component->WeightmapLayerAllocations.Num()); for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { FWeightmapLayerAllocationInfo& Allocation = Component->WeightmapLayerAllocations[LayerIdx]; const int32 LayerDataIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); check(LayerDataIdx != INDEX_NONE); LayerDataInfos[LayerIdx].InDataPtr = Data + LayerDataIdx; LayerDataInfos[LayerIdx].TexDataPtr = (uint8*)TexDataInfos[Allocation.WeightmapTextureIndex]->GetMipData(0) + ChannelOffsets[Allocation.WeightmapTextureChannel]; LayerEditDataAllZero[LayerIdx] = true; } // Find the texture data corresponding to this vertex const int32 TexSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections; // Find coordinates of box that lies inside component const int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); const int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box const int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections const int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); const int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); const int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++) { for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++) { // Find coordinates of box that lies inside subsection const int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); const int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); const int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); const int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for (int32 SubY = SubY1; SubY <= SubY2; SubY++) { for (int32 SubX = SubX1; SubX <= SubX2; SubX++) { const int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; const int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; checkSlow( LandscapeX >= X1 && LandscapeX <= X2 ); checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 ); // Find the input data corresponding to this vertex const int32 DataIndex = (LandscapeY-Y1) * Stride + (LandscapeX-X1) * LandscapeInfo->Layers.Num(); // Adjust all layer weights const int32 TexX = (SubsectionSizeQuads+1) * SubIndexX + SubX; const int32 TexY = (SubsectionSizeQuads+1) * SubIndexY + SubY; const int32 TexDataIndex = 4 * (TexX + TexY * TexSize); int32 OtherLayerWeightSum = 0; // Apply weights to all layers simultaneously. for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { // this is equivalent to saying if (DirtyLayerInfos.Contains(Allocation.LayerInfo)) // which is what we really mean here, but this is quicker // and I've lost count of the depth we've nested for loops at this point if (LayerDataInfos[LayerIdx].TexDataPtr != NULL) { uint8& Weight = LayerDataInfos[LayerIdx].TexDataPtr[TexDataIndex]; Weight = LayerDataInfos[LayerIdx].InDataPtr[DataIndex]; // Only for whole weight if (Weight != 0) { LayerEditDataAllZero[LayerIdx] = false; } } } } } // Record the areas of the texture we need to re-upload const int32 TexX1 = (SubsectionSizeQuads+1) * SubIndexX + SubX1; const int32 TexY1 = (SubsectionSizeQuads+1) * SubIndexY + SubY1; const int32 TexX2 = (SubsectionSizeQuads+1) * SubIndexX + SubX2; const int32 TexY2 = (SubsectionSizeQuads+1) * SubIndexY + SubY2; for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) { if (TexDataInfos[WeightmapIdx] != NULL) { TexDataInfos[WeightmapIdx]->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); } } } } // Update mipmaps CollisionWeightmapMipData.Reset(); CollisionWeightmapMipData.AddUninitialized(Component->WeightmapTextures.Num()); for (int32 WeightmapIdx = 0; WeightmapIdx < Component->WeightmapTextures.Num(); WeightmapIdx++) { UTexture2D* const WeightmapTexture = Component->WeightmapTextures[WeightmapIdx]; const int32 NumMips = WeightmapTexture->Source.GetNumMips(); WeightmapTextureMipData.Reset(); WeightmapTextureMipData.AddUninitialized(NumMips); for (int32 MipIdx = 0; MipIdx < NumMips; MipIdx++) { FColor* const MipData = (FColor*)TexDataInfos[WeightmapIdx]->GetMipData(MipIdx); WeightmapTextureMipData[MipIdx] = MipData; } CollisionWeightmapMipData[WeightmapIdx] = WeightmapTextureMipData[Component->CollisionMipLevel]; ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, WeightmapTexture, WeightmapTextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfos[WeightmapIdx]); WeightmapTextureMipData.Reset(); } // Update dominant layer info stored in collision component Component->UpdateCollisionLayerData(CollisionWeightmapMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2); CollisionWeightmapMipData.Reset(); // Check if we need to remove weightmap allocations for layers that were completely painted away bool bRemovedLayer = false; for (int32 LayerIdx = 0; LayerIdx < Component->WeightmapLayerAllocations.Num(); LayerIdx++) { if (LayerEditDataAllZero[LayerIdx]) { bool bLayerDeleted = DeleteLayerIfAllZero(Component, LayerDataInfos[LayerIdx].TexDataPtr, TexSize, LayerIdx); if (bLayerDeleted) { LayerEditDataAllZero.RemoveAt(LayerIdx); LayerDataInfos.RemoveAt(LayerIdx); LayerIdx--; bRemovedLayer = true; } } } if (bRemovedLayer) { Component->UpdateMaterialInstances(); if (Component->EditToolRenderData) { Component->EditToolRenderData->UpdateDebugColorMaterial(); } } } } } template void FLandscapeEditDataInterface::GetWeightDataTemplFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData) { int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); if( !Component ) { continue; } UTexture2D* WeightmapTexture = NULL; FLandscapeTextureDataInfo* TexDataInfo = NULL; uint8* WeightmapTextureData = NULL; uint8 WeightmapChannelOffset = 0; TArray TexDataInfos; // added for whole weight case... if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( Component->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { WeightmapTexture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; TexDataInfo = GetTextureDataInfo(WeightmapTexture); WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0); WeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } else { // Lock data for all the weightmaps for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) { TexDataInfos.Add(GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); } } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; if (LayerInfo != NULL) { // Find the input data corresponding to this vertex uint8 Weight; if( WeightmapTexture ) { // Find the texture data corresponding to this vertex int32 SizeU = WeightmapTexture->Source.GetSizeX(); int32 SizeV = WeightmapTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; Weight = WeightmapTextureData[ 4 * (TexX + TexY * SizeU) + WeightmapChannelOffset ]; } else { Weight = 0; } StoreData.Store(LandscapeX, LandscapeY, Weight); } else // Whole weight map case... { StoreData.PreInit(LandscapeInfo->Layers.Num()); for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { int32 Idx = Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; UTexture2D* ComponentWeightmapTexture = Component->WeightmapTextures[Idx]; uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0); uint8 ComponentWeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; // Find the texture data corresponding to this vertex int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX(); int32 SizeV = ComponentWeightmapTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; uint8 Weight = ComponentWeightmapTextureData[ 4 * (TexX + TexY * SizeU) + ComponentWeightmapChannelOffset ]; // Find index in LayerInfos { int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); if (LayerInfoIdx != INDEX_NONE) { StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx); } } } } } } } } } } } uint8 FLandscapeEditDataInterface::GetWeightMapData(const ULandscapeComponent* Component, ULandscapeLayerInfoObject* LayerInfo, int32 TexU, int32 TexV, uint8 Offset /*= 0*/, UTexture2D* Texture /*= NULL*/, uint8* TextureData /*= NULL*/) { check(Component); int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; if (!Texture || !TextureData) { if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( Component->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { Texture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Texture); TextureData = (uint8*)TexDataInfo->GetMipData(0); Offset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } } if (Texture && TextureData) { int32 SizeU = Texture->Source.GetSizeX(); int32 SizeV = Texture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; int32 TexX = WeightmapOffsetX + TexU; int32 TexY = WeightmapOffsetY + TexV; return TextureData[ 4 * (TexX + TexY * SizeU) + Offset ]; } return 0; } template void FLandscapeEditDataInterface::GetWeightDataTempl(ULandscapeLayerInfoObject* LayerInfo, int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData) { // Copy variables int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2; ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN; int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); int32 ComponentSizeX = ComponentIndexX2-ComponentIndexX1+1; int32 ComponentSizeY = ComponentIndexY2-ComponentIndexY1+1; // Neighbor Components ULandscapeComponent* BorderComponent[4] = {0, 0, 0, 0}; ULandscapeComponent* CornerComponent[4] = {0, 0, 0, 0}; bool NoBorderX1 = false, NoBorderX2 = false; TArray NoBorderY1, NoBorderY2, ComponentDataExist; TArray BorderComponentY1, BorderComponentY2; ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY); ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY); bool bHasMissingValue = false; UTexture2D* NeighborWeightmapTexture[4] = {0, 0, 0, 0}; FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = {0, 0, 0, 0}; uint8* NeighborWeightmapTextureData[4] = {0, 0, 0, 0}; uint8 NeighborWeightmapChannelOffset[4] = {0, 0, 0, 0}; uint8 CornerValues[4] = {0, 0, 0, 0}; int32 EdgeCoord = (SubsectionSizeQuads+1) * ComponentNumSubsections - 1; //ComponentSizeQuads; // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; // initial loop.... for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { NoBorderX1 = false; NoBorderX2 = false; BorderComponent[0] = BorderComponent[1] = NULL; for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { BorderComponent[2] = BorderComponent[3] = NULL; int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY-ComponentIndexY1) + ComponentIndexX-ComponentIndexX1; int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1; int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1; ComponentDataExist[ComponentIndexXY] = false; ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); UTexture2D* WeightmapTexture = NULL; FLandscapeTextureDataInfo* TexDataInfo = NULL; uint8* WeightmapTextureData = NULL; uint8 WeightmapChannelOffset = 0; TArray TexDataInfos; // added for whole weight case... uint8 CornerSet = 0; bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ ComponentIndexXX-1 + ComponentIndexYY * ComponentSizeX ]; bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ ComponentIndexXX + (ComponentIndexYY-1) * ComponentSizeX ]; if( Component ) { if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( Component->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { WeightmapTexture = Component->WeightmapTextures[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; TexDataInfo = GetTextureDataInfo(WeightmapTexture); WeightmapTextureData = (uint8*)TexDataInfo->GetMipData(0); WeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } else { // Lock data for all the weightmaps for( int32 WeightmapIdx=0;WeightmapIdx < Component->WeightmapTextures.Num();WeightmapIdx++ ) { TexDataInfos.Add(GetTextureDataInfo(Component->WeightmapTextures[WeightmapIdx])); } } ComponentDataExist[ComponentIndexXY] = true; // Update valid region ValidX1 = FMath::Min(Component->GetSectionBase().X, ValidX1); ValidX2 = FMath::Max(Component->GetSectionBase().X+ComponentSizeQuads, ValidX2); ValidY1 = FMath::Min(Component->GetSectionBase().Y, ValidY1); ValidY2 = FMath::Max(Component->GetSectionBase().Y+ComponentSizeQuads, ValidY2); } else { if (!bHasMissingValue) { NoBorderY1.Empty(ComponentSizeX); NoBorderY2.Empty(ComponentSizeX); NoBorderY1.AddZeroed(ComponentSizeX); NoBorderY2.AddZeroed(ComponentSizeX); BorderComponentY1.Empty(ComponentSizeX); BorderComponentY2.Empty(ComponentSizeX); BorderComponentY1.AddZeroed(ComponentSizeX); BorderComponentY2.AddZeroed(ComponentSizeX); bHasMissingValue = true; } // Search for neighbor component for interpolation bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX); bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY); // Search for left-closest component if ( bShouldSearchX || (!NoBorderX1 && !BorderComponent[0])) { NoBorderX1 = true; for (int32 X = ComponentIndexX-1; X >= ComponentIndexX1; X--) { BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY)); if (BorderComponent[0]) { NoBorderX1 = false; if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[0]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[0] = BorderComponent[0]->WeightmapTextures[BorderComponent[0]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[0] = GetTextureDataInfo(NeighborWeightmapTexture[0]); NeighborWeightmapTextureData[0] = (uint8*)NeighborTexDataInfo[0]->GetMipData(0); NeighborWeightmapChannelOffset[0] = ChannelOffsets[BorderComponent[0]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } break; } } } // Search for right-closest component if ( bShouldSearchX || (!NoBorderX2 && !BorderComponent[1])) { NoBorderX2 = true; for (int32 X = ComponentIndexX+1; X <= ComponentIndexX2; X++) { BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X,ComponentIndexY)); if (BorderComponent[1]) { NoBorderX2 = false; if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[1]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[1] = BorderComponent[1]->WeightmapTextures[BorderComponent[1]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[1] = GetTextureDataInfo(NeighborWeightmapTexture[1]); NeighborWeightmapTextureData[1] = (uint8*)NeighborTexDataInfo[1]->GetMipData(0); NeighborWeightmapChannelOffset[1] = ChannelOffsets[BorderComponent[1]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } } } } // Search for up-closest component if ( bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX])) { NoBorderY1[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY-1; Y >= ComponentIndexY1; Y--) { BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y)); if (BorderComponent[2]) { NoBorderY1[ComponentIndexXX] = false; if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[2] = BorderComponent[2]->WeightmapTextures[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]); NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0); NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } } } } else { BorderComponent[2] = BorderComponentY1[ComponentIndexXX]; if (BorderComponent[2]) { if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[2] = BorderComponent[2]->WeightmapTextures[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[2] = GetTextureDataInfo(NeighborWeightmapTexture[2]); NeighborWeightmapTextureData[2] = (uint8*)NeighborTexDataInfo[2]->GetMipData(0); NeighborWeightmapChannelOffset[2] = ChannelOffsets[BorderComponent[2]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } } } // Search for bottom-closest component if ( bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX])) { NoBorderY2[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY+1; Y <= ComponentIndexY2; Y++) { BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,Y)); if (BorderComponent[3]) { NoBorderY2[ComponentIndexXX] = false; if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[3] = BorderComponent[3]->WeightmapTextures[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]); NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0); NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } break; } } } else { BorderComponent[3] = BorderComponentY2[ComponentIndexXX]; if (BorderComponent[3]) { if (LayerInfo != NULL) { for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { if( BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].LayerInfo == LayerInfo ) { NeighborWeightmapTexture[3] = BorderComponent[3]->WeightmapTextures[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex]; NeighborTexDataInfo[3] = GetTextureDataInfo(NeighborWeightmapTexture[3]); NeighborWeightmapTextureData[3] = (uint8*)NeighborTexDataInfo[3]->GetMipData(0); NeighborWeightmapChannelOffset[3] = ChannelOffsets[BorderComponent[3]->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; break; } } } } } CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY-1))) : NULL; CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY-1))) : NULL; CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX-1),(ComponentIndexY+1))) : NULL; CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX+1),(ComponentIndexY+1))) : NULL; if (CornerComponent[0]) { CornerSet |= 1; CornerValues[0] = GetWeightMapData(CornerComponent[0], LayerInfo, EdgeCoord, EdgeCoord); } else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads ) { CornerSet |= 1; CornerValues[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads); } else if (BorderComponent[0]) { CornerSet |= 1; CornerValues[0] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, 0, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]); } else if (BorderComponent[2]) { CornerSet |= 1; CornerValues[0] = GetWeightMapData(BorderComponent[2], LayerInfo, 0, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]); } if (CornerComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetWeightMapData(CornerComponent[1], LayerInfo, 0, EdgeCoord); } else if (ExistUp && X2 >= (ComponentIndexX+1)*ComponentSizeQuads) { CornerSet |= 1 << 1; CornerValues[1] = StoreData.Load( (ComponentIndexX+1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads); } else if (BorderComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, 0, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]); } else if (BorderComponent[2]) { CornerSet |= 1 << 1; CornerValues[1] = GetWeightMapData(BorderComponent[2], LayerInfo, EdgeCoord, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]); } if (CornerComponent[2]) { CornerSet |= 1 << 2; CornerValues[2] = GetWeightMapData(CornerComponent[2], LayerInfo, EdgeCoord, 0); } else if (ExistLeft && Y2 >= (ComponentIndexY+1)*ComponentSizeQuads) // Use data already stored for 0, 2 { CornerSet |= 1 << 2; CornerValues[2] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, (ComponentIndexY+1)*ComponentSizeQuads); } else if (BorderComponent[0]) { CornerSet |= 1 << 2; CornerValues[2] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, EdgeCoord, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]); } else if (BorderComponent[3]) { CornerSet |= 1 << 2; CornerValues[2] = GetWeightMapData(BorderComponent[3], LayerInfo, 0, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]); } if (CornerComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetWeightMapData(CornerComponent[3], LayerInfo, 0, 0); } else if (BorderComponent[1]) { CornerSet |= 1 << 3; CornerValues[3] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, EdgeCoord, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]); } else if (BorderComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetWeightMapData(BorderComponent[3], LayerInfo, EdgeCoord, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]); } FillCornerValues(CornerSet, CornerValues); ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet; } if (!ComponentDataExist[ComponentIndexXY]) { continue; } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; if (LayerInfo != NULL) { // Find the input data corresponding to this vertex uint8 Weight; if( WeightmapTexture ) { // Find the texture data corresponding to this vertex Weight = GetWeightMapData(Component, LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, (SubsectionSizeQuads+1) * SubIndexY + SubY, WeightmapChannelOffset, WeightmapTexture, WeightmapTextureData ); StoreData.Store(LandscapeX, LandscapeY, Weight); } else { // Find the texture data corresponding to this vertex uint8 Value[4] = {0, 0, 0, 0}; int32 Dist[4] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX}; float ValueX = 0.0f, ValueY = 0.0f; bool Exist[4] = {false, false, false, false}; // Use data already stored for 0, 2 if (ExistLeft && SubX == 0) { Value[0] = StoreData.Load( ComponentIndexX*ComponentSizeQuads, LandscapeY); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } else if (BorderComponent[0] && NeighborWeightmapTexture[0]) { Value[0] = GetWeightMapData(BorderComponent[0], LayerInfo, EdgeCoord, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborWeightmapChannelOffset[0], NeighborWeightmapTexture[0], NeighborWeightmapTextureData[0]); Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads); Exist[0] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 2)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[0] = (float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } } if (BorderComponent[1] && NeighborWeightmapTexture[1]) { Value[1] = GetWeightMapData(BorderComponent[1], LayerInfo, 0, (SubsectionSizeQuads+1) * SubIndexY + SubY, NeighborWeightmapChannelOffset[1], NeighborWeightmapTexture[1], NeighborWeightmapTextureData[1]); Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX; Exist[1] = true; } else { if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY+1)*ComponentSizeQuads) - LandscapeY; Value[1] = (float)(Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[1] = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Exist[1] = true; } } if (ExistUp && SubY == 0) { Value[2] = StoreData.Load( LandscapeX, ComponentIndexY*ComponentSizeQuads); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } else if (BorderComponent[2] && NeighborWeightmapTexture[2]) { Value[2] = GetWeightMapData(BorderComponent[2], LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, EdgeCoord, NeighborWeightmapChannelOffset[2], NeighborWeightmapTexture[2], NeighborWeightmapTextureData[2]); Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads); Exist[2] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 1)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[2] = (float)(Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } } if (BorderComponent[3] && NeighborWeightmapTexture[3]) { Value[3] = GetWeightMapData(BorderComponent[3], LayerInfo, (SubsectionSizeQuads+1) * SubIndexX + SubX, 0, NeighborWeightmapChannelOffset[3], NeighborWeightmapTexture[3], NeighborWeightmapTextureData[3]); Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY; Exist[3] = true; } else { if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX+1)*ComponentSizeQuads - LandscapeX; Value[3] = (float)(Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[3] = (ComponentIndexY+1)*ComponentSizeQuads - LandscapeY; Exist[3] = true; } } CalcInterpValue(Dist, Exist, Value, ValueX, ValueY); uint8 FinalValue = 0; // Default Value if ( (Exist[0] || Exist[1]) && (Exist[2] || Exist[3]) ) { FinalValue = CalcValueFromValueXY(Dist, ValueX, ValueY, CornerSet, CornerValues); } else if ( (Exist[0] || Exist[1]) ) { FinalValue = ValueX; } else if ( (Exist[2] || Exist[3]) ) { FinalValue = ValueY; } Weight = FinalValue; } StoreData.Store(LandscapeX, LandscapeY, Weight); } else // Whole weight map case... no interpolation now... { StoreData.PreInit(LandscapeInfo->Layers.Num()); for( int32 LayerIdx=0;LayerIdxWeightmapLayerAllocations.Num();LayerIdx++ ) { int32 Idx = Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureIndex; UTexture2D* ComponentWeightmapTexture = Component->WeightmapTextures[Idx]; uint8* ComponentWeightmapTextureData = (uint8*)TexDataInfos[Idx]->GetMipData(0); uint8 ComponentWeightmapChannelOffset = ChannelOffsets[Component->WeightmapLayerAllocations[LayerIdx].WeightmapTextureChannel]; // Find the texture data corresponding to this vertex int32 SizeU = ComponentWeightmapTexture->Source.GetSizeX(); int32 SizeV = ComponentWeightmapTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; uint8 Weight = ComponentWeightmapTextureData[ 4 * (TexX + TexY * SizeU) + ComponentWeightmapChannelOffset ]; // Find index in LayerInfos { int32 LayerInfoIdx = LandscapeInfo->GetLayerInfoIndex(Component->WeightmapLayerAllocations[LayerIdx].LayerInfo); if (LayerInfoIdx != INDEX_NONE) { StoreData.Store(LandscapeX, LandscapeY, Weight, LayerInfoIdx); } } } } } } } } } } if (bHasMissingValue) { CalcMissingValues( X1, X2, Y1, Y2, ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2, ComponentSizeX, ComponentSizeY, CornerValues, NoBorderY1, NoBorderY2, ComponentDataExist, StoreData ); // Update valid region ValidX1 = FMath::Max(X1, ValidX1); ValidX2 = FMath::Min(X2, ValidX2); ValidY1 = FMath::Max(Y1, ValidY1); ValidY2 = FMath::Min(Y2, ValidY2); } else { ValidX1 = X1; ValidX2 = X2; ValidY1 = Y1; ValidY2 = Y2; } } void FLandscapeEditDataInterface::GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, uint8* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetWeightDataTempl(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetWeightData(ULandscapeLayerInfoObject* LayerInfo, int32& X1, int32& Y1, int32& X2, int32& Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetWeightDataTempl(LayerInfo, X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TArray* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData> ArrayStoreData(X1, Y1, Data, Stride); GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetWeightDataFast(ULandscapeLayerInfoObject* LayerInfo, const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap>& SparseData) { TSparseStoreData> SparseStoreData(SparseData); GetWeightDataTemplFast(LayerInfo, X1, Y1, X2, Y2, SparseStoreData); } FLandscapeTextureDataInfo* FLandscapeEditDataInterface::GetTextureDataInfo(UTexture2D* Texture) { FLandscapeTextureDataInfo* Result = TextureDataMap.FindRef(Texture); if( !Result ) { Result = TextureDataMap.Add(Texture, new FLandscapeTextureDataInfo(Texture)); } return Result; } void FLandscapeEditDataInterface::CopyTextureChannel(UTexture2D* Dest, int32 DestChannel, UTexture2D* Src, int32 SrcChannel) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest); FLandscapeTextureDataInfo* SrcDataInfo = GetTextureDataInfo(Src); int32 MipSize = Dest->Source.GetSizeX(); check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY() && Src->Source.GetSizeX() == Dest->Source.GetSizeX()); // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; for( int32 MipIdx=0;MipIdxNumMips();MipIdx++ ) { uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel]; uint8* SrcTextureData = (uint8*)SrcDataInfo->GetMipData(MipIdx) + ChannelOffsets[SrcChannel]; for( int32 i=0;iAddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1); MipSize >>= 1; } } void FLandscapeEditDataInterface::CopyTextureFromHeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, int32 SrcChannel) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest); int32 MipSize = Dest->Source.GetSizeX(); check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY()); // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; for( int32 MipIdx=0;MipIdxNumMips();MipIdx++ ) { FLandscapeComponentDataInterface DataInterface(Comp, MipIdx); TArray Heightmap; DataInterface.GetHeightmapTextureData(Heightmap); uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel]; uint8* SrcTextureData = (uint8*)Heightmap.GetData() + ChannelOffsets[SrcChannel]; for( int32 i=0;iAddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1); MipSize >>= 1; } } void FLandscapeEditDataInterface::CopyTextureFromWeightmap(UTexture2D* Dest, int32 DestChannel, ULandscapeComponent* Comp, ULandscapeLayerInfoObject* LayerInfo) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest); int32 MipSize = Dest->Source.GetSizeX(); check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY()); // Channel remapping int32 ChannelOffsets[4] = { (int32)STRUCT_OFFSET(FColor, R), (int32)STRUCT_OFFSET(FColor, G), (int32)STRUCT_OFFSET(FColor, B), (int32)STRUCT_OFFSET(FColor, A) }; for (int32 MipIdx = 0; MipIdx < DestDataInfo->NumMips(); MipIdx++) { FLandscapeComponentDataInterface DataInterface(Comp, MipIdx); TArray WeightData; DataInterface.GetWeightmapTextureData(LayerInfo, WeightData); uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel]; for (int32 i = 0; i < FMath::Square(MipSize); i++) { DestTextureData[i * 4] = WeightData[i]; } DestDataInfo->AddMipUpdateRegion(MipIdx, 0, 0, MipSize - 1, MipSize - 1); MipSize >>= 1; } } void FLandscapeEditDataInterface::ZeroTextureChannel(UTexture2D* Dest, int32 DestChannel) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest); int32 MipSize = Dest->Source.GetSizeX(); check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY()); // Channel remapping int32 ChannelOffsets[4] = {(int32)STRUCT_OFFSET(FColor,R),(int32)STRUCT_OFFSET(FColor,G),(int32)STRUCT_OFFSET(FColor,B),(int32)STRUCT_OFFSET(FColor,A)}; for( int32 MipIdx=0;MipIdxNumMips();MipIdx++ ) { uint8* DestTextureData = (uint8*)DestDataInfo->GetMipData(MipIdx) + ChannelOffsets[DestChannel]; for( int32 i=0;iAddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1); MipSize >>= 1; } } template void FLandscapeEditDataInterface::SetTextureValueTempl(UTexture2D* Dest, TData Value) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Dest); int32 MipSize = Dest->Source.GetSizeX(); check(Dest->Source.GetSizeX() == Dest->Source.GetSizeY()); for( int32 MipIdx=0;MipIdxNumMips();MipIdx++ ) { TData* DestTextureData = (TData*)DestDataInfo->GetMipData(MipIdx); for( int32 i=0;iAddMipUpdateRegion(MipIdx, 0, 0, MipSize-1, MipSize-1); MipSize >>= 1; } } void FLandscapeEditDataInterface::ZeroTexture(UTexture2D* Dest) { SetTextureValueTempl(Dest, 0); } void FLandscapeEditDataInterface::SetTextureValue(UTexture2D* Dest, FColor Value) { SetTextureValueTempl(Dest, Value); } template bool FLandscapeEditDataInterface::EqualTextureValueTempl(UTexture2D* Src, TData Value) { FLandscapeTextureDataInfo* DestDataInfo = GetTextureDataInfo(Src); TData* DestTextureData = (TData*)DestDataInfo->GetMipData(0); int32 Size = Src->Source.GetSizeX() * Src->Source.GetSizeY(); for( int32 i = 0 ;i < Size; ++i ) { if (DestTextureData[i] != Value) { return false; } } return true; } bool FLandscapeEditDataInterface::EqualTextureValue(UTexture2D* Src, FColor Value) { return EqualTextureValueTempl(Src, Value); } template void FLandscapeEditDataInterface::GetSelectDataTempl(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData) { int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); FLandscapeTextureDataInfo* TexDataInfo = NULL; uint8* SelectTextureData = NULL; if( Component && Component->EditToolRenderData && Component->EditToolRenderData->DataTexture ) { TexDataInfo = GetTextureDataInfo(Component->EditToolRenderData->DataTexture); SelectTextureData = (uint8*)TexDataInfo->GetMipData(0); } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the input data corresponding to this vertex if( Component && SelectTextureData ) { // Find the texture data corresponding to this vertex int32 SizeU = Component->EditToolRenderData->DataTexture->Source.GetSizeX(); int32 SizeV = Component->EditToolRenderData->DataTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; uint8& TexData = SelectTextureData[ TexX + TexY * SizeU ]; StoreData.Store(LandscapeX, LandscapeY, TexData); } else { StoreData.Store(LandscapeX, LandscapeY, 0); } } } } } } } } void FLandscapeEditDataInterface::GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetSelectDataTempl(X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetSelectData(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, uint8* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetSelectDataTempl(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::SetSelectData(int32 X1, int32 Y1, int32 X2, int32 Y2, const uint8* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } check(ComponentSizeQuads > 0); // Find component range for this block of data int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY); ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey); UTexture2D* DataTexture = NULL; // if NULL, it was painted away if( Component==NULL || Component->EditToolRenderData==NULL) { continue; } else if (Component->EditToolRenderData->DataTexture==NULL) { //FlushRenderingCommands(); // Construct Texture... int32 WeightmapSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections; DataTexture = Component->GetLandscapeProxy()->CreateLandscapeTexture(WeightmapSize, WeightmapSize, TEXTUREGROUP_Terrain_Weightmap, TSF_G8); // Alloc dummy mips ULandscapeComponent::CreateEmptyTextureMips(DataTexture, true); DataTexture->PostEditChange(); //FlushRenderingCommands(); ZeroTexture(DataTexture); FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(DataTexture); int32 NumMips = DataTexture->Source.GetNumMips(); TArray TextureMipData; TextureMipData.AddUninitialized(NumMips); for( int32 MipIdx=0;MipIdxGetMipData(MipIdx); } ULandscapeComponent::UpdateDataMips(ComponentNumSubsections, SubsectionSizeQuads, DataTexture, TextureMipData, 0, 0, MAX_int32, MAX_int32, TexDataInfo); Component->SelectDataTexture = DataTexture; ENQUEUE_UNIQUE_RENDER_COMMAND_TWOPARAMETER( UpdateEditToolRenderDataDataTexture, FLandscapeEditToolRenderData*, LandscapeEditToolRenderData, Component->EditToolRenderData, UTexture2D*, InDataTexture, DataTexture, { LandscapeEditToolRenderData->DataTexture = InDataTexture; }); } else { DataTexture = Component->EditToolRenderData->DataTexture; } FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(DataTexture); uint8* SelectTextureData = (uint8*)TexDataInfo->GetMipData(0); // Find the texture data corresponding to this vertex int32 SizeU = DataTexture->Source.GetSizeX(); int32 SizeV = DataTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; checkSlow( LandscapeX >= X1 && LandscapeX <= X2 ); checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 ); // Find the input data corresponding to this vertex int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1); const uint8& Value = Data[DataIndex]; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; uint8& TexData = SelectTextureData[ TexX + TexY * SizeU ]; TexData = Value; } } // Record the areas of the texture we need to re-upload int32 TexX1 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX1; int32 TexY1 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY1; int32 TexX2 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX2; int32 TexY2 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY2; TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); } } // Update mipmaps int32 NumMips = DataTexture->Source.GetNumMips(); TArray TextureMipData; TextureMipData.AddUninitialized(NumMips); for( int32 MipIdx=0;MipIdxGetMipData(MipIdx); } ULandscapeComponent::UpdateDataMips(ComponentNumSubsections, SubsectionSizeQuads, DataTexture, TextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfo); } } } template void FLandscapeEditDataInterface::SetXYOffsetDataTempl(int32 X1, int32 Y1, int32 X2, int32 Y2, const T* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } check(ComponentSizeQuads > 0); // Find component range for this block of data int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); const FColor DefaultValue(128, 0, 128, 0); for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { FIntPoint ComponentKey(ComponentIndexX,ComponentIndexY); ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(ComponentKey); UTexture2D* XYOffsetTexture = NULL; if( Component==NULL) { continue; } else { if (Component->XYOffsetmapTexture == NULL) { Component->Modify(); //FlushRenderingCommands(); // Construct Texture... int32 WeightmapSize = (Component->SubsectionSizeQuads+1) * Component->NumSubsections; XYOffsetTexture = Component->GetLandscapeProxy()->CreateLandscapeTexture(WeightmapSize, WeightmapSize, TEXTUREGROUP_Terrain_Weightmap, TSF_BGRA8); // Alloc dummy mips ULandscapeComponent::CreateEmptyTextureMips(XYOffsetTexture, true); XYOffsetTexture->PostEditChange(); //FlushRenderingCommands(); SetTextureValue(XYOffsetTexture, DefaultValue); FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(XYOffsetTexture); int32 NumMips = XYOffsetTexture->Source.GetNumMips(); TArray TextureMipData; TextureMipData.AddUninitialized(NumMips); for( int32 MipIdx=0;MipIdxGetMipData(MipIdx); } ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, XYOffsetTexture, TextureMipData, 0, 0, MAX_int32, MAX_int32, TexDataInfo); Component->XYOffsetmapTexture = XYOffsetTexture; FComponentReregisterContext ReregisterContext(Component); } else { XYOffsetTexture = Component->XYOffsetmapTexture; } } FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(XYOffsetTexture); FColor* XYOffsetTextureData = (FColor*)TexDataInfo->GetMipData(0); // Find the texture data corresponding to this vertex int32 SizeU = XYOffsetTexture->Source.GetSizeX(); int32 SizeV = XYOffsetTexture->Source.GetSizeY(); int32 WeightmapOffsetX = Component->WeightmapScaleBias.Z * (float)SizeU; int32 WeightmapOffsetY = Component->WeightmapScaleBias.W * (float)SizeV; // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; checkSlow( LandscapeX >= X1 && LandscapeX <= X2 ); checkSlow( LandscapeY >= Y1 && LandscapeY <= Y2 ); // Find the input data corresponding to this vertex int32 DataIndex = (LandscapeX-X1) + Stride * (LandscapeY-Y1); const T& Value = Data[DataIndex]; int32 TexX = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX; int32 TexY = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY; FColor& TexData = XYOffsetTextureData[ TexX + TexY * SizeU ]; uint16 XOffset = FMath::Clamp(Value.X * LANDSCAPE_INV_XYOFFSET_SCALE + 32768.0f, 0, 65535); uint16 YOffset = FMath::Clamp(Value.Y * LANDSCAPE_INV_XYOFFSET_SCALE + 32768.0f, 0, 65535); TexData.R = XOffset >> 8; TexData.G = XOffset & 255; TexData.B = YOffset >> 8; TexData.A = YOffset & 255; } } // Record the areas of the texture we need to re-upload int32 TexX1 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX1; int32 TexY1 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY1; int32 TexX2 = WeightmapOffsetX + (SubsectionSizeQuads+1) * SubIndexX + SubX2; int32 TexY2 = WeightmapOffsetY + (SubsectionSizeQuads+1) * SubIndexY + SubY2; TexDataInfo->AddMipUpdateRegion(0,TexX1,TexY1,TexX2,TexY2); } } // Update mipmaps int32 NumMips = XYOffsetTexture->Source.GetNumMips(); TArray TextureMipData; TextureMipData.AddUninitialized(NumMips); for( int32 MipIdx=0;MipIdxGetMipData(MipIdx); } ULandscapeComponent::UpdateWeightmapMips(ComponentNumSubsections, SubsectionSizeQuads, XYOffsetTexture, TextureMipData, ComponentX1, ComponentY1, ComponentX2, ComponentY2, TexDataInfo); } } } void FLandscapeEditDataInterface::SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector2D* Data, int32 Stride) { SetXYOffsetDataTempl(X1, Y1, X2, Y2, Data, Stride); } void FLandscapeEditDataInterface::SetXYOffsetData(int32 X1, int32 Y1, int32 X2, int32 Y2, const FVector* Data, int32 Stride) { SetXYOffsetDataTempl(X1, Y1, X2, Y2, Data, Stride); } FVector2D FLandscapeEditDataInterface::GetXYOffsetmapData(const ULandscapeComponent* Component, int32 TexU, int32 TexV, FColor* TextureData/* = NULL*/) { check(Component); if (!TextureData && Component->XYOffsetmapTexture) { FLandscapeTextureDataInfo* TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture); TextureData = (FColor*)TexDataInfo->GetMipData(0); } if (TextureData) { int32 SizeU = Component->NumSubsections * (Component->SubsectionSizeQuads + 1); int32 SizeV = Component->NumSubsections * (Component->SubsectionSizeQuads + 1); int32 TexX = TexU; int32 TexY = TexV; FColor& TexData = TextureData[ TexX + TexY * SizeU ]; return FVector2D(((TexData.R * 256.0 + TexData.G) - 32768.0) * LANDSCAPE_XYOFFSET_SCALE, ((TexData.B * 256.0 + TexData.A) - 32768.0) * LANDSCAPE_XYOFFSET_SCALE ); } return FVector2D::ZeroVector; } // XYOffset Interpolation version template void FLandscapeEditDataInterface::GetXYOffsetDataTempl(int32& ValidX1, int32& ValidY1, int32& ValidX2, int32& ValidY2, TStoreData& StoreData) { // Copy variables int32 X1 = ValidX1, X2 = ValidX2, Y1 = ValidY1, Y2 = ValidY2; ValidX1 = INT_MAX; ValidX2 = INT_MIN; ValidY1 = INT_MAX; ValidY2 = INT_MIN; int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); int32 ComponentSizeX = ComponentIndexX2 - ComponentIndexX1 + 1; int32 ComponentSizeY = ComponentIndexY2 - ComponentIndexY1 + 1; // Neighbor Components ULandscapeComponent* BorderComponent[4] = { 0, 0, 0, 0 }; ULandscapeComponent* CornerComponent[4] = { 0, 0, 0, 0 }; bool NoBorderX1 = false, NoBorderX2 = false; TArray NoBorderY1, NoBorderY2, ComponentDataExist; TArray BorderComponentY1, BorderComponentY2; ComponentDataExist.Empty(ComponentSizeX*ComponentSizeY); ComponentDataExist.AddZeroed(ComponentSizeX*ComponentSizeY); bool bHasMissingValue = false; FLandscapeTextureDataInfo* NeighborTexDataInfo[4] = { 0, 0, 0, 0 }; FColor* NeighborXYOffsetmapTextureData[4] = { 0, 0, 0, 0 }; FVector2D CornerValues[4] = { FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector }; int32 EdgeCoord = (SubsectionSizeQuads + 1) * ComponentNumSubsections - 1; //ComponentSizeQuads; TArray EmptyXYOffset; int32 XYOffsetSize = (LandscapeInfo->SubsectionSizeQuads + 1) * LandscapeInfo->ComponentNumSubsections; XYOffsetSize = XYOffsetSize * XYOffsetSize; EmptyXYOffset.Empty(XYOffsetSize); for (int32 i = 0; i < XYOffsetSize; ++i) { EmptyXYOffset.Add(FColor(128, 0, 128, 0)); } // initial loop.... for (int32 ComponentIndexY = ComponentIndexY1; ComponentIndexY <= ComponentIndexY2; ComponentIndexY++) { NoBorderX1 = false; NoBorderX2 = false; BorderComponent[0] = BorderComponent[1] = NULL; for (int32 ComponentIndexX = ComponentIndexX1; ComponentIndexX <= ComponentIndexX2; ComponentIndexX++) { BorderComponent[2] = BorderComponent[3] = NULL; int32 ComponentIndexXY = ComponentSizeX*(ComponentIndexY - ComponentIndexY1) + ComponentIndexX - ComponentIndexX1; int32 ComponentIndexXX = ComponentIndexX - ComponentIndexX1; int32 ComponentIndexYY = ComponentIndexY - ComponentIndexY1; ComponentDataExist[ComponentIndexXY] = false; ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, ComponentIndexY)); FLandscapeTextureDataInfo* TexDataInfo = NULL; FColor* XYOffsetmapTextureData = NULL; uint8 CornerSet = 0; bool ExistLeft = ComponentIndexXX > 0 && ComponentDataExist[ComponentIndexXX - 1 + ComponentIndexYY * ComponentSizeX]; bool ExistUp = ComponentIndexYY > 0 && ComponentDataExist[ComponentIndexXX + (ComponentIndexYY - 1) * ComponentSizeX]; if (Component) { if (Component->XYOffsetmapTexture) { TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture); XYOffsetmapTextureData = (FColor*)TexDataInfo->GetMipData(0); } else { XYOffsetmapTextureData = EmptyXYOffset.GetData(); } ComponentDataExist[ComponentIndexXY] = true; // Update valid region ValidX1 = FMath::Min(Component->GetSectionBase().X, ValidX1); ValidX2 = FMath::Max(Component->GetSectionBase().X + ComponentSizeQuads, ValidX2); ValidY1 = FMath::Min(Component->GetSectionBase().Y, ValidY1); ValidY2 = FMath::Max(Component->GetSectionBase().Y + ComponentSizeQuads, ValidY2); } else { if (!bHasMissingValue) { NoBorderY1.Empty(ComponentSizeX); NoBorderY2.Empty(ComponentSizeX); NoBorderY1.AddZeroed(ComponentSizeX); NoBorderY2.AddZeroed(ComponentSizeX); BorderComponentY1.Empty(ComponentSizeX); BorderComponentY2.Empty(ComponentSizeX); BorderComponentY1.AddZeroed(ComponentSizeX); BorderComponentY2.AddZeroed(ComponentSizeX); bHasMissingValue = true; } // Search for neighbor component for interpolation bool bShouldSearchX = (BorderComponent[1] && BorderComponent[1]->GetSectionBase().X / ComponentSizeQuads <= ComponentIndexX); bool bShouldSearchY = (BorderComponentY2[ComponentIndexXX] && BorderComponentY2[ComponentIndexXX]->GetSectionBase().Y / ComponentSizeQuads <= ComponentIndexY); // Search for left-closest component if (bShouldSearchX || (!NoBorderX1 && !BorderComponent[0])) { NoBorderX1 = true; for (int32 X = ComponentIndexX - 1; X >= ComponentIndexX1; X--) { BorderComponent[0] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, ComponentIndexY)); if (BorderComponent[0]) { NoBorderX1 = false; if (BorderComponent[0]->XYOffsetmapTexture) { NeighborTexDataInfo[0] = GetTextureDataInfo(BorderComponent[0]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[0] = (FColor*)NeighborTexDataInfo[0]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[0] = EmptyXYOffset.GetData(); } break; } } } // Search for right-closest component if (bShouldSearchX || (!NoBorderX2 && !BorderComponent[1])) { NoBorderX2 = true; for (int32 X = ComponentIndexX + 1; X <= ComponentIndexX2; X++) { BorderComponent[1] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(X, ComponentIndexY)); if (BorderComponent[1]) { NoBorderX2 = false; if (BorderComponent[1]->XYOffsetmapTexture) { NeighborTexDataInfo[1] = GetTextureDataInfo(BorderComponent[1]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[1] = (FColor*)NeighborTexDataInfo[1]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[1] = EmptyXYOffset.GetData(); } break; } } } // Search for up-closest component if (bShouldSearchY || (!NoBorderY1[ComponentIndexXX] && !BorderComponentY1[ComponentIndexXX])) { NoBorderY1[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY - 1; Y >= ComponentIndexY1; Y--) { BorderComponentY1[ComponentIndexXX] = BorderComponent[2] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, Y)); if (BorderComponent[2]) { NoBorderY1[ComponentIndexXX] = false; if (BorderComponent[2]->XYOffsetmapTexture) { NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[2] = EmptyXYOffset.GetData(); } break; } } } else { BorderComponent[2] = BorderComponentY1[ComponentIndexXX]; if (BorderComponent[2]) { if (BorderComponent[2]->XYOffsetmapTexture) { NeighborTexDataInfo[2] = GetTextureDataInfo(BorderComponent[2]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[2] = (FColor*)NeighborTexDataInfo[2]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[2] = EmptyXYOffset.GetData(); } } } // Search for bottom-closest component if (bShouldSearchY || (!NoBorderY2[ComponentIndexXX] && !BorderComponentY2[ComponentIndexXX])) { NoBorderY2[ComponentIndexXX] = true; for (int32 Y = ComponentIndexY + 1; Y <= ComponentIndexY2; Y++) { BorderComponentY2[ComponentIndexXX] = BorderComponent[3] = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX, Y)); if (BorderComponent[3]) { NoBorderY2[ComponentIndexXX] = false; if (BorderComponent[3]->XYOffsetmapTexture) { NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[3] = EmptyXYOffset.GetData(); } break; } } } else { BorderComponent[3] = BorderComponentY2[ComponentIndexXX]; if (BorderComponent[3]) { if (BorderComponent[3]->XYOffsetmapTexture) { NeighborTexDataInfo[3] = GetTextureDataInfo(BorderComponent[3]->XYOffsetmapTexture); NeighborXYOffsetmapTextureData[3] = (FColor*)NeighborTexDataInfo[3]->GetMipData(0); } else { NeighborXYOffsetmapTextureData[3] = EmptyXYOffset.GetData(); } } } CornerComponent[0] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX - 1), (ComponentIndexY - 1))) : NULL; CornerComponent[1] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY >= ComponentIndexY1 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX + 1), (ComponentIndexY - 1))) : NULL; CornerComponent[2] = ComponentIndexX >= ComponentIndexX1 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX - 1), (ComponentIndexY + 1))) : NULL; CornerComponent[3] = ComponentIndexX <= ComponentIndexX2 && ComponentIndexY <= ComponentIndexY2 ? LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint((ComponentIndexX + 1), (ComponentIndexY + 1))) : NULL; if (CornerComponent[0]) { CornerSet |= 1; CornerValues[0] = GetXYOffsetmapData(CornerComponent[0], EdgeCoord, EdgeCoord); } else if ((ExistLeft || ExistUp) && X1 <= ComponentIndexX*ComponentSizeQuads && Y1 <= ComponentIndexY*ComponentSizeQuads) { CornerSet |= 1; CornerValues[0] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads)); } else if (BorderComponent[0]) { CornerSet |= 1; CornerValues[0] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, 0, NeighborXYOffsetmapTextureData[0]); } else if (BorderComponent[2]) { CornerSet |= 1; CornerValues[0] = GetXYOffsetmapData(BorderComponent[2], 0, EdgeCoord, NeighborXYOffsetmapTextureData[2]); } if (CornerComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetXYOffsetmapData(CornerComponent[1], 0, EdgeCoord); } else if (ExistUp && X2 >= (ComponentIndexX + 1)*ComponentSizeQuads) { CornerSet |= 1 << 1; CornerValues[1] = FVector2D(StoreData.Load((ComponentIndexX + 1)*ComponentSizeQuads, ComponentIndexY*ComponentSizeQuads)); } else if (BorderComponent[1]) { CornerSet |= 1 << 1; CornerValues[1] = GetXYOffsetmapData(BorderComponent[1], 0, 0, NeighborXYOffsetmapTextureData[1]); } else if (BorderComponent[2]) { CornerSet |= 1 << 1; CornerValues[1] = GetXYOffsetmapData(BorderComponent[2], EdgeCoord, EdgeCoord, NeighborXYOffsetmapTextureData[2]); } if (CornerComponent[2]) { CornerSet |= 1 << 2; CornerValues[2] = GetXYOffsetmapData(CornerComponent[2], EdgeCoord, 0); } else if (ExistLeft && Y2 >= (ComponentIndexY + 1)*ComponentSizeQuads) // Use data already stored for 0, 2 { CornerSet |= 1 << 2; CornerValues[2] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, (ComponentIndexY + 1)*ComponentSizeQuads)); } else if (BorderComponent[0]) { CornerSet |= 1 << 2; CornerValues[2] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, EdgeCoord, NeighborXYOffsetmapTextureData[0]); } else if (BorderComponent[3]) { CornerSet |= 1 << 2; CornerValues[2] = GetXYOffsetmapData(BorderComponent[3], 0, 0, NeighborXYOffsetmapTextureData[3]); } if (CornerComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetXYOffsetmapData(CornerComponent[3], 0, 0); } else if (BorderComponent[1]) { CornerSet |= 1 << 3; CornerValues[3] = GetXYOffsetmapData(BorderComponent[1], 0, EdgeCoord, NeighborXYOffsetmapTextureData[1]); } else if (BorderComponent[3]) { CornerSet |= 1 << 3; CornerValues[3] = GetXYOffsetmapData(BorderComponent[3], EdgeCoord, 0, NeighborXYOffsetmapTextureData[3]); } FillCornerValues(CornerSet, CornerValues); ComponentDataExist[ComponentIndexXY] = ExistLeft || ExistUp || (BorderComponent[0] || BorderComponent[1] || BorderComponent[2] || BorderComponent[3]) || CornerSet; } if (!ComponentDataExist[ComponentIndexXY]) { continue; } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2 - ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2 - ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1 - 1) / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads, 0, ComponentNumSubsections - 1); for (int32 SubIndexY = SubIndexY1; SubIndexY <= SubIndexY2; SubIndexY++) { for (int32 SubIndexX = SubIndexX1; SubIndexX <= SubIndexX2; SubIndexX++) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2 - SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2 - SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for (int32 SubY = SubY1; SubY <= SubY2; SubY++) { for (int32 SubX = SubX1; SubX <= SubX2; SubX++) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the input data corresponding to this vertex if (Component) { // Find the texture data corresponding to this vertex FVector2D XYOffset = GetXYOffsetmapData(Component, (SubsectionSizeQuads + 1) * SubIndexX + SubX, (SubsectionSizeQuads + 1) * SubIndexY + SubY, XYOffsetmapTextureData); StoreData.Store(LandscapeX, LandscapeY, XYOffset); } else { // Find the texture data corresponding to this vertex FVector2D Value[4] = { FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector, FVector2D::ZeroVector }; int32 Dist[4] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX }; FVector2D ValueX = FVector2D::ZeroVector, ValueY = FVector2D::ZeroVector; bool Exist[4] = { false, false, false, false }; // Use data already stored for 0, 2 if (ExistLeft) { Value[0] = FVector2D(StoreData.Load(ComponentIndexX*ComponentSizeQuads, LandscapeY)); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } else if (BorderComponent[0]) { Value[0] = GetXYOffsetmapData(BorderComponent[0], EdgeCoord, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborXYOffsetmapTextureData[0]); Dist[0] = LandscapeX - (BorderComponent[0]->GetSectionBase().X + ComponentSizeQuads); Exist[0] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 2)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY + 1)*ComponentSizeQuads) - LandscapeY; Value[0] = (Dist2 * CornerValues[0] + Dist1 * CornerValues[2]) / (Dist1 + Dist2); Dist[0] = LandscapeX - (ComponentIndexX*ComponentSizeQuads); Exist[0] = true; } } if (BorderComponent[1]) { Value[1] = GetXYOffsetmapData(BorderComponent[1], 0, (SubsectionSizeQuads + 1) * SubIndexY + SubY, NeighborXYOffsetmapTextureData[1]); Dist[1] = (BorderComponent[1]->GetSectionBase().X) - LandscapeX; Exist[1] = true; } else { if ((CornerSet & 1 << 1) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeY - (ComponentIndexY*ComponentSizeQuads); int32 Dist2 = ((ComponentIndexY + 1)*ComponentSizeQuads) - LandscapeY; Value[1] = (Dist2 * CornerValues[1] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[1] = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX; Exist[1] = true; } } if (ExistUp) { Value[2] = FVector2D(StoreData.Load(LandscapeX, ComponentIndexY*ComponentSizeQuads)); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } else if (BorderComponent[2]) { Value[2] = GetXYOffsetmapData(BorderComponent[2], (SubsectionSizeQuads + 1) * SubIndexX + SubX, EdgeCoord, NeighborXYOffsetmapTextureData[2]); Dist[2] = LandscapeY - (BorderComponent[2]->GetSectionBase().Y + ComponentSizeQuads); Exist[2] = true; } else { if ((CornerSet & 1) && (CornerSet & 1 << 1)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX; Value[2] = (Dist2 * CornerValues[0] + Dist1 * CornerValues[1]) / (Dist1 + Dist2); Dist[2] = LandscapeY - (ComponentIndexY*ComponentSizeQuads); Exist[2] = true; } } if (BorderComponent[3]) { Value[3] = GetXYOffsetmapData(BorderComponent[3], (SubsectionSizeQuads + 1) * SubIndexX + SubX, 0, NeighborXYOffsetmapTextureData[3]); Dist[3] = (BorderComponent[3]->GetSectionBase().Y) - LandscapeY; Exist[3] = true; } else { if ((CornerSet & 1 << 2) && (CornerSet & 1 << 3)) { int32 Dist1 = LandscapeX - (ComponentIndexX*ComponentSizeQuads); int32 Dist2 = (ComponentIndexX + 1)*ComponentSizeQuads - LandscapeX; Value[3] = (Dist2 * CornerValues[2] + Dist1 * CornerValues[3]) / (Dist1 + Dist2); Dist[3] = (ComponentIndexY + 1)*ComponentSizeQuads - LandscapeY; Exist[3] = true; } } CalcInterpValue(Dist, Exist, Value, ValueX, ValueY); FVector2D FinalValue = FVector2D::ZeroVector; // Default Value if ((Exist[0] || Exist[1]) && (Exist[2] || Exist[3])) { FinalValue = CalcValueFromValueXY(Dist, ValueX, ValueY, CornerSet, CornerValues); } else if ((BorderComponent[0] || BorderComponent[1])) { FinalValue = ValueX; } else if ((BorderComponent[2] || BorderComponent[3])) { FinalValue = ValueY; } else if ((Exist[0] || Exist[1])) { FinalValue = ValueX; } else if ((Exist[2] || Exist[3])) { FinalValue = ValueY; } StoreData.Store(LandscapeX, LandscapeY, FinalValue); //StoreData.StoreDefault(LandscapeX, LandscapeY); } } } } } } } if (bHasMissingValue) { CalcMissingValues(X1, X2, Y1, Y2, ComponentIndexX1, ComponentIndexX2, ComponentIndexY1, ComponentIndexY2, ComponentSizeX, ComponentSizeY, CornerValues, NoBorderY1, NoBorderY2, ComponentDataExist, StoreData); // Update valid region ValidX1 = FMath::Max(X1, ValidX1); ValidX2 = FMath::Min(X2, ValidX2); ValidY1 = FMath::Max(Y1, ValidY1); ValidY2 = FMath::Min(Y2, ValidY2); } else { ValidX1 = X1; ValidX2 = X2; ValidY1 = Y1; ValidY2 = Y2; } } void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector2D* Data, int32 Stride) { if (Stride == 0) { Stride = (1 + X2 - X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetXYOffsetDataTempl(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetXYOffsetDataTempl(X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, FVector* Data, int32 Stride) { if (Stride == 0) { Stride = (1 + X2 - X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetXYOffsetDataTempl(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetXYOffsetData(int32& X1, int32& Y1, int32& X2, int32& Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetXYOffsetDataTempl(X1, Y1, X2, Y2, SparseStoreData); } template void FLandscapeEditDataInterface::GetXYOffsetDataTemplFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TStoreData& StoreData) { int32 ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2; ALandscape::CalcComponentIndicesNoOverlap(X1, Y1, X2, Y2, ComponentSizeQuads, ComponentIndexX1, ComponentIndexY1, ComponentIndexX2, ComponentIndexY2); for( int32 ComponentIndexY=ComponentIndexY1;ComponentIndexY<=ComponentIndexY2;ComponentIndexY++ ) { for( int32 ComponentIndexX=ComponentIndexX1;ComponentIndexX<=ComponentIndexX2;ComponentIndexX++ ) { ULandscapeComponent* Component = LandscapeInfo->XYtoComponentMap.FindRef(FIntPoint(ComponentIndexX,ComponentIndexY)); FLandscapeTextureDataInfo* TexDataInfo = NULL; FColor* OffsetTextureData = NULL; if( Component && Component->XYOffsetmapTexture ) { TexDataInfo = GetTextureDataInfo(Component->XYOffsetmapTexture); OffsetTextureData = (FColor*)TexDataInfo->GetMipData(0); } // Find coordinates of box that lies inside component int32 ComponentX1 = FMath::Clamp(X1-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY1 = FMath::Clamp(Y1-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentX2 = FMath::Clamp(X2-ComponentIndexX*ComponentSizeQuads, 0, ComponentSizeQuads); int32 ComponentY2 = FMath::Clamp(Y2-ComponentIndexY*ComponentSizeQuads, 0, ComponentSizeQuads); // Find subsection range for this box int32 SubIndexX1 = FMath::Clamp((ComponentX1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); // -1 because we need to pick up vertices shared between subsections int32 SubIndexY1 = FMath::Clamp((ComponentY1-1) / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexX2 = FMath::Clamp(ComponentX2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); int32 SubIndexY2 = FMath::Clamp(ComponentY2 / SubsectionSizeQuads,0,ComponentNumSubsections-1); for( int32 SubIndexY=SubIndexY1;SubIndexY<=SubIndexY2;SubIndexY++ ) { for( int32 SubIndexX=SubIndexX1;SubIndexX<=SubIndexX2;SubIndexX++ ) { // Find coordinates of box that lies inside subsection int32 SubX1 = FMath::Clamp(ComponentX1-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY1 = FMath::Clamp(ComponentY1-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); int32 SubX2 = FMath::Clamp(ComponentX2-SubsectionSizeQuads*SubIndexX, 0, SubsectionSizeQuads); int32 SubY2 = FMath::Clamp(ComponentY2-SubsectionSizeQuads*SubIndexY, 0, SubsectionSizeQuads); // Update texture data for the box that lies inside subsection for( int32 SubY=SubY1;SubY<=SubY2;SubY++ ) { for( int32 SubX=SubX1;SubX<=SubX2;SubX++ ) { int32 LandscapeX = SubIndexX*SubsectionSizeQuads + ComponentIndexX*ComponentSizeQuads + SubX; int32 LandscapeY = SubIndexY*SubsectionSizeQuads + ComponentIndexY*ComponentSizeQuads + SubY; // Find the input data corresponding to this vertex if( Component && OffsetTextureData ) { FVector2D Value = GetXYOffsetmapData(Component, SubX, SubY, OffsetTextureData); StoreData.Store(LandscapeX, LandscapeY, Value); } else { StoreData.Store(LandscapeX, LandscapeY, FVector2D(0.0f, 0.0f) ); } } } } } } } } void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector2D* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, SparseStoreData); } void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, FVector* Data, int32 Stride) { if( Stride==0 ) { Stride = (1+X2-X1); } TArrayStoreData ArrayStoreData(X1, Y1, Data, Stride); GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, ArrayStoreData); } void FLandscapeEditDataInterface::GetXYOffsetDataFast(const int32 X1, const int32 Y1, const int32 X2, const int32 Y2, TMap& SparseData) { TSparseStoreData SparseStoreData(SparseData); GetXYOffsetDataTemplFast(X1, Y1, X2, Y2, SparseStoreData); } // // FLandscapeTextureDataInfo // FLandscapeTextureDataInfo::FLandscapeTextureDataInfo(UTexture2D* InTexture) : Texture(InTexture) { MipInfo.AddZeroed(Texture->Source.GetNumMips()); Texture->SetFlags(RF_Transactional); Texture->TemporarilyDisableStreaming(); Texture->Modify(); } bool FLandscapeTextureDataInfo::UpdateTextureData() { bool bNeedToWaitForUpdate = false; int32 DataSize = sizeof(FColor); if (Texture->GetPixelFormat() == PF_G8) { DataSize = sizeof(uint8); } for( int32 i=0;i0 ) { Texture->UpdateTextureRegions( i, MipInfo[i].MipUpdateRegions.Num(), &MipInfo[i].MipUpdateRegions[0], ((Texture->Source.GetSizeX())>>i)*DataSize, DataSize, (uint8*)MipInfo[i].MipData, false ); bNeedToWaitForUpdate = true; } } return bNeedToWaitForUpdate; } FLandscapeTextureDataInfo::~FLandscapeTextureDataInfo() { // Unlock any mips still locked. for( int32 i=0;iSource.UnlockMip(i); MipInfo[i].MipData = NULL; } } Texture->ClearFlags(RF_Transactional); } #endif // WITH_EDITOR