Advertisement
malice936

ESMDataMapper.cpp

Apr 17th, 2025
287
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 21.54 KB | Gaming | 0 0
  1. // Warning! Experimental! No guarantees on functionality! Use at your own risk!!!!!
  2. #include "ESMDataMapper.h"
  3. #include "Landscape.h"
  4. #include "LandscapeInfo.h"
  5. #include "LandscapeSubsystem.h"
  6. #include "UObject/ConstructorHelpers.h"
  7. #include "Misc/Guid.h"
  8. #include "Logging/LogMacros.h"
  9. #include "Engine/Texture2D.h"
  10.  
  11. // Extracts height data from a single LAND record in an ESM file.
  12. // Parameters:
  13. // - Record: The FRecord object containing LAND record data.
  14. // - CellX, CellY: Expected cell coordinates to validate against the record.
  15. // - OutHeightData: Output array to store height values (65x65 grid).
  16. // - OutHeightOffset: Output float for the height offset from the VHGT subrecord.
  17. // Returns: True if extraction succeeds, false otherwise.
  18. bool UESMDataMapper::ExtractHeightDataFromRecord(const FRecord& Record, int32 CellX, int32 CellY, TArray<float>& OutHeightData, float& OutHeightOffset)
  19. {
  20.     // Log the record type and cell coordinates being processed.
  21.     UE_LOG(LogTemp, Log, TEXT("Processing record with Type: '%s' for cell (%d, %d)"), *Record.Type, CellX, CellY);
  22.  
  23.     // Validate that the record is a LAND record.
  24.     if (Record.Type != TEXT("LAND"))
  25.     {
  26.         UE_LOG(LogTemp, Warning, TEXT("Record is not a LAND record (Type: %s)"), *Record.Type);
  27.         return false;
  28.     }
  29.  
  30.     // Extract the INTV subrecord to validate cell coordinates.
  31.     FSubrecord INTVSubrecord;
  32.     if (URecordHelper::GetSubrecordByType(Record, TEXT("INTV"), INTVSubrecord) && INTVSubrecord.RawData.Num() >= sizeof(int32) * 2)
  33.     {
  34.         // Read the cell coordinates from the INTV subrecord.
  35.         int32 RecordCellX = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData());
  36.         int32 RecordCellY = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData() + sizeof(int32));
  37.         // Ensure the record’s coordinates match the expected CellX, CellY.
  38.         if (RecordCellX != CellX || RecordCellY != CellY)
  39.         {
  40.             UE_LOG(LogTemp, Warning, TEXT("Cell coordinates (%d, %d) do not match expected (%d, %d)"), RecordCellX, RecordCellY, CellX, CellY);
  41.             return false;
  42.         }
  43.         UE_LOG(LogTemp, Log, TEXT("LAND record validated for cell (%d, %d)"), RecordCellX, RecordCellY);
  44.     }
  45.     else
  46.     {
  47.         UE_LOG(LogTemp, Error, TEXT("Invalid or missing INTV subrecord in LAND record"));
  48.         return false;
  49.     }
  50.  
  51.     // Extract the VHGT subrecord containing height data.
  52.     FSubrecord VHGTSubrecord;
  53.     if (!URecordHelper::GetSubrecordByType(Record, TEXT("VHGT"), VHGTSubrecord))
  54.     {
  55.         UE_LOG(LogTemp, Error, TEXT("Missing VHGT subrecord in LAND record"));
  56.         OutHeightData.Empty();
  57.         OutHeightOffset = 0.0f;
  58.         return false;
  59.     }
  60.  
  61.     // Validate the size of the VHGT subrecord data.
  62.     // Expected size: sizeof(float) for HeightOffset + 1 byte padding + 65x65 height deltas + sizeof(uint16) padding.
  63.     if (VHGTSubrecord.RawData.Num() < sizeof(float) + 1 + (65 * 65) + sizeof(uint16))
  64.     {
  65.         UE_LOG(LogTemp, Error, TEXT("VHGT subrecord data too small, expected >= 4232 bytes, got %d"), VHGTSubrecord.RawData.Num());
  66.         OutHeightData.Empty();
  67.         OutHeightOffset = 0.0f;
  68.         return false;
  69.     }
  70.  
  71.     // Read the height offset from the VHGT subrecord.
  72.     OutHeightOffset = *reinterpret_cast<const float*>(VHGTSubrecord.RawData.GetData());
  73.     UE_LOG(LogTemp, Log, TEXT("VHGT HeightOffset: %f"), OutHeightOffset);
  74.  
  75.     // Initialize the output height data array to a 65x65 grid.
  76.     OutHeightData.SetNum(65 * 65);
  77.     // Pointer to the height delta values in the VHGT subrecord (after the offset and padding).
  78.     const int8* HeightDeltas = reinterpret_cast<const int8*>(VHGTSubrecord.RawData.GetData() + sizeof(float) + 1);
  79.     // Start with the base height offset.
  80.     float CurrentHeight = OutHeightOffset;
  81.  
  82.     // Iterate over the 65x65 grid to compute height values.
  83.     for (int32 Y = 0; Y < 65; Y++)
  84.     {
  85.         for (int32 X = 0; X < 65; X++)
  86.         {
  87.             // Unreal Engine expects a flipped Y-axis, so adjust the index accordingly.
  88.             int32 Index = (64 - Y) * 65 + X;
  89.             int32 DataIndex = Y * 65 + X;
  90.             // Accumulate the height delta to get the absolute height.
  91.             CurrentHeight += HeightDeltas[DataIndex];
  92.             // Scale the height by 8.0f (Morrowind to Unreal units conversion factor).
  93.             OutHeightData[Index] = CurrentHeight * 8.0f;
  94.         }
  95.     }
  96.  
  97.     // Log the first 5 height values and the total size for debugging.
  98.     for (int32 i = 0; i < FMath::Min(5, OutHeightData.Num()); i++)
  99.     {
  100.         UE_LOG(LogTemp, Log, TEXT("OutHeightData[%d]: %f"), i, OutHeightData[i]);
  101.     }
  102.     UE_LOG(LogTemp, Log, TEXT("OutHeightData size: %d"), OutHeightData.Num());
  103.  
  104.     return true;
  105. }
  106.  
  107. // Extracts height data from an array of records for a specific cell.
  108. // Parameters:
  109. // - Records: Array of FRecord objects to search through.
  110. // - CellX, CellY: Target cell coordinates to find a matching LAND record.
  111. // - OutHeights: Output array to store height values.
  112. // - OutHeightOffset: Output float for the height offset.
  113. // Returns: True if a matching LAND record is found and extracted, false otherwise.
  114. bool UESMDataMapper::ExtractHeightDataFromRecords(const TArray<FRecord>& Records, int32 CellX, int32 CellY, TArray<float>& OutHeights, float& OutHeightOffset)
  115. {
  116.     // Log the cell coordinates being processed.
  117.     UE_LOG(LogTemp, Log, TEXT("ExtractHeightDataFromRecords called with CellX=%d, CellY=%d"), CellX, CellY);
  118.  
  119.     // Search for a LAND record matching the specified cell coordinates.
  120.     FRecord LandRecord;
  121.     bool bFound = false;
  122.     for (const FRecord& Record : Records)
  123.     {
  124.         if (Record.Type == TEXT("LAND"))
  125.         {
  126.             FSubrecord INTVSubrecord;
  127.             if (URecordHelper::GetSubrecordByType(Record, TEXT("INTV"), INTVSubrecord) && INTVSubrecord.RawData.Num() >= sizeof(int32) * 2)
  128.             {
  129.                 int32 RecordCellX = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData());
  130.                 int32 RecordCellY = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData() + sizeof(int32));
  131.                 if (RecordCellX == CellX && RecordCellY == CellY)
  132.                 {
  133.                     LandRecord = Record;
  134.                     bFound = true;
  135.                     break;
  136.                 }
  137.             }
  138.         }
  139.     }
  140.  
  141.     // If no matching LAND record is found, log a warning and return false.
  142.     if (!bFound)
  143.     {
  144.         UE_LOG(LogTemp, Warning, TEXT("No LAND record found for cell (%d, %d)"), CellX, CellY);
  145.         return false;
  146.     }
  147.  
  148.     // Extract height data from the found LAND record.
  149.     return ExtractHeightDataFromRecord(LandRecord, CellX, CellY, OutHeights, OutHeightOffset);
  150. }
  151.  
  152. // Extracts height data from LAND records in a plugin for a specific cell.
  153. // Parameters:
  154. // - Plugin: The FPlugin object containing ESM records.
  155. // - CellX, CellY: Target cell coordinates.
  156. // - OutHeights: Output array to store height values.
  157. // - OutHeightOffset: Output float for the height offset.
  158. // Returns: True if extraction succeeds, false otherwise.
  159. bool UESMDataMapper::ExtractLandHeightDataFromPlugin(const FPlugin& Plugin, int32 CellX, int32 CellY, TArray<float>& OutHeights, float& OutHeightOffset)
  160. {
  161.     // Retrieve all records from the plugin.
  162.     TArray<FRecord> Records = UPluginHelper::GetPluginRecords(Plugin);
  163.     // Delegate to ExtractHeightDataFromRecords to process the records.
  164.     return ExtractHeightDataFromRecords(Records, CellX, CellY, OutHeights, OutHeightOffset);
  165. }
  166.  
  167. // Handles individual LAND record processing, extracting cell coordinates and height data.
  168. // Parameters:
  169. // - Record: The FRecord object to process.
  170. // - CellX, CellY: Output parameters for the extracted cell coordinates.
  171. // - OutHeightData: Output array for height values.
  172. // - OutHeightOffset: Output float for the height offset.
  173. // Returns: True if processing succeeds, false otherwise.
  174. bool UESMDataMapper::LANDHandler(const FRecord& Record, int32& CellX, int32& CellY, TArray<float>& OutHeightData, float& OutHeightOffset)
  175. {
  176.     UE_LOG(LogTemp, Log, TEXT("Handling LAND record"));
  177.  
  178.     // Validate that the record is a LAND record.
  179.     if (Record.Type != TEXT("LAND"))
  180.     {
  181.         UE_LOG(LogTemp, Warning, TEXT("Record is not a LAND record (Type: %s)"), *Record.Type);
  182.         return false;
  183.     }
  184.  
  185.     // Extract the INTV subrecord to get cell coordinates.
  186.     FSubrecord INTVSubrecord;
  187.     if (URecordHelper::GetSubrecordByType(Record, TEXT("INTV"), INTVSubrecord) && INTVSubrecord.RawData.Num() == 8)
  188.     {
  189.         // Log the raw data for debugging.
  190.         UE_LOG(LogTemp, Log, TEXT("INTV subrecord raw data length: %d"), INTVSubrecord.RawData.Num());
  191.         FString RawDataHex;
  192.         for (uint8 Byte : INTVSubrecord.RawData)
  193.         {
  194.             RawDataHex += FString::Printf(TEXT("%02X "), Byte);
  195.         }
  196.         UE_LOG(LogTemp, Log, TEXT("INTV raw data (hex): %s"), *RawDataHex);
  197.  
  198.         // Extract CellX and CellY from the INTV subrecord.
  199.         CellX = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData());
  200.         CellY = *reinterpret_cast<const int32*>(INTVSubrecord.RawData.GetData() + sizeof(int32));
  201.         UE_LOG(LogTemp, Log, TEXT("Extracted coordinates: CellX=%d, CellY=%d"), CellX, CellY);
  202.     }
  203.     else
  204.     {
  205.         UE_LOG(LogTemp, Error, TEXT("Invalid INTV subrecord: expected 8 bytes, got %d"), INTVSubrecord.RawData.Num());
  206.         return false;
  207.     }
  208.  
  209.     // Extract height data using the validated coordinates.
  210.     return ExtractHeightDataFromRecord(Record, CellX, CellY, OutHeightData, OutHeightOffset);
  211. }
  212.  
  213. // Converts raw height data (float) to a heightmap in byte format for Unreal Engine (legacy method).
  214. // Parameters:
  215. // - Heights: Input array of height values in float format.
  216. // - MinHeight, MaxHeight: The height range for Unreal Engine (e.g., -2048 to 2048).
  217. // Returns: A TArray<uint8> representing the heightmap as uint16 values.
  218. TArray<uint8> UESMDataMapper::ConvertESMHeightToHeightmapBytes(const TArray<float>& Heights, float MinHeight, float MaxHeight)
  219. {
  220.     // Validate input data.
  221.     if (Heights.Num() == 0)
  222.     {
  223.         UE_LOG(LogTemp, Warning, TEXT("No height data provided"));
  224.         return TArray<uint8>();
  225.     }
  226.  
  227.     // Initialize the heightmap array to store uint16 values.
  228.     TArray<uint16> Heightmap;
  229.     Heightmap.SetNum(Heights.Num());
  230.  
  231.     // Find the min and max height values in the input data.
  232.     float DataMin = Heights[0];
  233.     float DataMax = Heights[0];
  234.     for (float Height : Heights)
  235.     {
  236.         DataMin = FMath::Min(DataMin, Height);
  237.         DataMax = FMath::Max(DataMax, Height);
  238.     }
  239.     UE_LOG(LogTemp, Log, TEXT("Height data range: Min=%f, Max=%f"), DataMin, DataMax);
  240.  
  241.     // Normalize and scale the height values to the uint16 range (0-65535).
  242.     float DataRange = DataMax - DataMin;
  243.     float UnrealRange = MaxHeight - MinHeight;
  244.     for (int32 i = 0; i < Heights.Num(); i++)
  245.     {
  246.         // Normalize the height to [0, 1] based on the data range.
  247.         float Normalized = (DataRange > 0) ? (Heights[i] - DataMin) / DataRange : 0.5f;
  248.         // Scale to Unreal’s height range.
  249.         float ScaledHeight = MinHeight + (Normalized * UnrealRange);
  250.         // Convert to uint16 (0-65535).
  251.         Heightmap[i] = static_cast<uint16>((ScaledHeight - MinHeight) / UnrealRange * 65535);
  252.         // Log the first 5 values for debugging.
  253.         if (i < 5)
  254.         {
  255.             UE_LOG(LogTemp, Log, TEXT("Heightmap[%d]: %d (ScaledHeight=%f)"), i, Heightmap[i], ScaledHeight);
  256.         }
  257.     }
  258.  
  259.     // Convert the uint16 array to a byte array for Unreal Engine.
  260.     TArray<uint8> Bytes;
  261.     Bytes.SetNum(Heightmap.Num() * sizeof(uint16));
  262.     FMemory::Memcpy(Bytes.GetData(), Heightmap.GetData(), Heightmap.Num() * sizeof(uint16));
  263.     UE_LOG(LogTemp, Log, TEXT("HeightmapBytes size: %d"), Bytes.Num());
  264.     return Bytes;
  265. }
  266.  
  267. // Creates a landscape actor from a heightmap in byte format (legacy method, causes LandscapeGuid.IsValid() crash).
  268. // Parameters:
  269. // - World: The UWorld to spawn the landscape in.
  270. // - HeightmapBytes: Byte array containing the heightmap as uint16 values.
  271. // - Width, Height: Dimensions of the heightmap (e.g., 65x65).
  272. // - ScaleX, ScaleY, ScaleZ: Scaling factors for the landscape.
  273. // Returns: Pointer to the created ALandscape actor, or nullptr on failure.
  274. ALandscape* UESMDataMapper::CreateLandscapeFromHeightmapBytes(
  275.     UWorld* World,
  276.     const TArray<uint8>& HeightmapBytes,
  277.     int32 Width,
  278.     int32 Height,
  279.     float ScaleX,
  280.     float ScaleY,
  281.     float ScaleZ)
  282. {
  283.     // Validate the world pointer.
  284.     if (!World)
  285.     {
  286.         UE_LOG(LogTemp, Error, TEXT("World is null"));
  287.         return nullptr;
  288.     }
  289.  
  290.     // Validate the heightmap size (should match Width * Height * sizeof(uint16)).
  291.     if (HeightmapBytes.Num() == 0 || HeightmapBytes.Num() != Width * Height * sizeof(uint16))
  292.     {
  293.         UE_LOG(LogTemp, Error, TEXT("HeightmapBytes size (%d) does not match Width * Height * 2 (%d)"), HeightmapBytes.Num(), Width * Height * sizeof(uint16));
  294.         return nullptr;
  295.     }
  296.  
  297.     // Reinterpret the byte array as uint16 values.
  298.     const uint16* HeightmapData = reinterpret_cast<const uint16*>(HeightmapBytes.GetData());
  299.  
  300.     // Spawn a new landscape actor.
  301.     ALandscape* Landscape = World->SpawnActor<ALandscape>();
  302.     if (!Landscape)
  303.     {
  304.         UE_LOG(LogTemp, Error, TEXT("Failed to spawn landscape actor"));
  305.         return nullptr;
  306.     }
  307.  
  308.     // Generate a new GUID for the landscape.
  309.     FGuid LandscapeGuid = FGuid::NewGuid();
  310.     if (!LandscapeGuid.IsValid())
  311.     {
  312.         UE_LOG(LogTemp, Error, TEXT("Generated LandscapeGuid is invalid"));
  313.         Landscape->Destroy();
  314.         return nullptr;
  315.     }
  316.  
  317.     // Prepare the height data map for the Import function.
  318.     TMap<FGuid, TArray<uint16>> HeightDataMap;
  319.     TArray<uint16> HeightArray;
  320.     HeightArray.SetNum(Width * Height);
  321.     FMemory::Memcpy(HeightArray.GetData(), HeightmapData, Width * Height * sizeof(uint16));
  322.     HeightDataMap.Add(LandscapeGuid, MoveTemp(HeightArray));
  323.  
  324.     // Create a LandscapeInfo object to manage metadata.
  325.     ULandscapeInfo* LandscapeInfo = Landscape->CreateLandscapeInfo();
  326.     if (!LandscapeInfo)
  327.     {
  328.         UE_LOG(LogTemp, Error, TEXT("Failed to create LandscapeInfo"));
  329.         Landscape->Destroy();
  330.         return nullptr;
  331.     }
  332.  
  333.     // Assign the GUID to the LandscapeInfo.
  334.     LandscapeInfo->LandscapeGuid = LandscapeGuid;
  335.  
  336.     // Import the heightmap into the landscape (this method causes the crash).
  337.     Landscape->Import(
  338.         LandscapeGuid,
  339.         0,                      // Min X
  340.         0,                      // Min Y
  341.         Width - 1,              // Max X
  342.         Height - 1,             // Max Y
  343.         1,                      // Num subsections
  344.         Width - 1,              // Subsection size quads
  345.         HeightDataMap,          // Height data
  346.         TEXT("None"),           // Material (none for now)
  347.         TMap<FGuid, TArray<FLandscapeImportLayerInfo>>(), // No layers
  348.         ELandscapeImportAlphamapType::Additive, // Alphamap type
  349.         nullptr                 // No error message
  350.     );
  351.  
  352.     // Apply the scaling to the landscape actor.
  353.     Landscape->SetActorScale3D(FVector(ScaleX, ScaleY, ScaleZ));
  354.     return Landscape;
  355. }
  356.  
  357. // Creates a landscape actor using a modern Unreal Engine 5 approach with raw height data.
  358. // Parameters:
  359. // - World: The UWorld to spawn the landscape in.
  360. // - Heights: Array of height values in float format.
  361. // - CellX, CellY: Cell coordinates for positioning the landscape.
  362. // - Width, Height: Dimensions of the heightmap (e.g., 65x65).
  363. // - ScaleX, ScaleY, ScaleZ: Scaling factors for the landscape.
  364. // Returns: Pointer to the created ALandscape actor, or nullptr on failure.
  365. ALandscape* UESMDataMapper::CreateLandscapeModern(
  366.     UWorld* World,
  367.     const TArray<float>& Heights,
  368.     int32 CellX,
  369.     int32 CellY,
  370.     int32 Width,
  371.     int32 Height,
  372.     float ScaleX,
  373.     float ScaleY,
  374.     float ScaleZ)
  375. {
  376.     // Validate the world pointer.
  377.     if (!World)
  378.     {
  379.         UE_LOG(LogTemp, Error, TEXT("World is null"));
  380.         return nullptr;
  381.     }
  382.  
  383.     // Validate that the input heights array matches the expected size (Width * Height).
  384.     if (Heights.Num() != Width * Height)
  385.     {
  386.         UE_LOG(LogTemp, Error, TEXT("Heights size (%d) does not match Width * Height (%d)"), Heights.Num(), Width * Height);
  387.         return nullptr;
  388.     }
  389.  
  390.     // Normalize height data to uint16 range (0-65535) for Unreal Engine.
  391.     TArray<uint16> Heightmap;
  392.     Heightmap.SetNum(Heights.Num());
  393.     // Define the Unreal Engine height range.
  394.     float MinHeight = -2048.0f;
  395.     float MaxHeight = 2048.0f;
  396.     // Find the min and max height values in the input data.
  397.     float DataMin = Heights[0];
  398.     float DataMax = Heights[0];
  399.     for (float HeightValue : Heights)
  400.     {
  401.         DataMin = FMath::Min(DataMin, HeightValue);
  402.         DataMax = FMath::Max(DataMax, HeightValue);
  403.     }
  404.     // Normalize and scale the heights.
  405.     float DataRange = DataMax - DataMin;
  406.     float UnrealRange = MaxHeight - MinHeight;
  407.     for (int32 i = 0; i < Heights.Num(); i++)
  408.     {
  409.         // Normalize to [0, 1] based on the data range.
  410.         float Normalized = (DataRange > 0) ? (Heights[i] - DataMin) / DataRange : 0.5f;
  411.         // Scale to Unreal’s height range.
  412.         float ScaledHeight = MinHeight + (Normalized * UnrealRange);
  413.         // Convert to uint16 (0-65535).
  414.         Heightmap[i] = static_cast<uint16>((ScaledHeight - MinHeight) / UnrealRange * 65535);
  415.         // Log the first 5 values for debugging.
  416.         if (i < 5)
  417.         {
  418.             UE_LOG(LogTemp, Log, TEXT("Heightmap[%d]: %d (ScaledHeight=%f)"), i, Heightmap[i], ScaledHeight);
  419.         }
  420.     }
  421.  
  422.     // Convert Heightmap (uint16) to FColor format for InitHeightmapData.
  423.     TArray<FColor> HeightmapColors;
  424.     HeightmapColors.SetNum(Heightmap.Num());
  425.     for (int32 i = 0; i < Heightmap.Num(); i++)
  426.     {
  427.         // Landscape heightmaps store height in R (high byte) and G (low byte).
  428.         HeightmapColors[i].R = (Heightmap[i] >> 8) & 0xFF; // High byte
  429.         HeightmapColors[i].G = Heightmap[i] & 0xFF;        // Low byte
  430.         HeightmapColors[i].B = 0;
  431.         HeightmapColors[i].A = 0;
  432.     }
  433.  
  434.     // Get the landscape subsystem to manage landscape creation.
  435.     ULandscapeSubsystem* LandscapeSubsystem = World->GetSubsystem<ULandscapeSubsystem>();
  436.     if (!LandscapeSubsystem)
  437.     {
  438.         UE_LOG(LogTemp, Error, TEXT("Failed to get LandscapeSubsystem"));
  439.         return nullptr;
  440.     }
  441.  
  442.     // Create or find a LandscapeInfo object to manage the landscape metadata.
  443.     FGuid LandscapeGuid = FGuid::NewGuid();
  444.     if (!LandscapeGuid.IsValid())
  445.     {
  446.         UE_LOG(LogTemp, Error, TEXT("Generated LandscapeGuid is invalid"));
  447.         return nullptr;
  448.     }
  449.  
  450.     ULandscapeInfo* LandscapeInfo = ULandscapeInfo::FindOrCreate(World, LandscapeGuid);
  451.     if (!LandscapeInfo)
  452.     {
  453.         UE_LOG(LogTemp, Error, TEXT("Failed to create LandscapeInfo"));
  454.         return nullptr;
  455.     }
  456.  
  457.     // Spawn a new landscape actor.
  458.     ALandscape* Landscape = World->SpawnActor<ALandscape>();
  459.     if (!Landscape)
  460.     {
  461.         UE_LOG(LogTemp, Error, TEXT("Failed to spawn landscape actor"));
  462.         return nullptr;
  463.     }
  464.  
  465.     // Set basic landscape properties.
  466.     Landscape->ComponentSizeQuads = Width - 1; // Quads = Verts - 1
  467.     Landscape->SubsectionSizeQuads = Width - 1; // Single subsection for simplicity
  468.     Landscape->SetActorScale3D(FVector(ScaleX, ScaleY, ScaleZ));
  469.  
  470.     // Calculate the section base position based on CellX, CellY.
  471.     // Each cell is (Width-1) quads wide/tall, so SectionBase aligns the landscape in the world.
  472.     FIntPoint SectionBase(CellX * (Width - 1), CellY * (Height - 1));
  473.  
  474.     // Find or add a landscape proxy using the subsystem.
  475.     ALandscapeProxy* LandscapeProxy = LandscapeSubsystem->FindOrAddLandscapeProxy(LandscapeInfo, SectionBase);
  476.     if (!LandscapeProxy)
  477.     {
  478.         UE_LOG(LogTemp, Error, TEXT("Failed to find or add LandscapeProxy"));
  479.         Landscape->Destroy();
  480.         return nullptr;
  481.     }
  482.  
  483.     // Create a single landscape component to hold the heightmap data.
  484.     ULandscapeComponent* Component = NewObject<ULandscapeComponent>(Landscape);
  485.     if (!Component)
  486.     {
  487.         UE_LOG(LogTemp, Error, TEXT("Failed to create LandscapeComponent"));
  488.         Landscape->Destroy();
  489.         return nullptr;
  490.     }
  491.  
  492.     // Set component properties to match the landscape dimensions.
  493.     Component->ComponentSizeQuads = Width - 1;
  494.     Component->SubsectionSizeQuads = Width - 1;
  495.     Component->NumSubsections = 1;
  496.     // Set the component's position using SetSectionBase.
  497.     Component->SetSectionBase(SectionBase);
  498.  
  499.     // Initialize the component with the height data.
  500.     Component->InitHeightmapData(HeightmapColors, false); // false for bUpdateCollision, as we’re not handling collision here
  501.  
  502.     // Register the component with the world and the LandscapeInfo.
  503.     Component->RegisterComponent();
  504.     LandscapeInfo->RegisterActorComponent(Component);
  505.  
  506.     // Update the LandscapeInfo with the landscape properties.
  507.     LandscapeInfo->LandscapeActor = Landscape;
  508.     LandscapeInfo->ComponentSizeQuads = Width - 1;
  509.     LandscapeInfo->SubsectionSizeQuads = Width - 1;
  510.     LandscapeInfo->ComponentNumSubsections = 1;
  511.     LandscapeInfo->DrawScale = FVector(ScaleX, ScaleY, ScaleZ);
  512.  
  513.     // Log success message with the cell coordinates.
  514.     UE_LOG(LogTemp, Log, TEXT("Successfully created landscape for cell (%d, %d) with height data applied"), CellX, CellY);
  515.     return Landscape;
  516. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement