From 8e9a31a6ee7e37453ee5cea4144ea0f692971091 Mon Sep 17 00:00:00 2001 From: mikec316 Date: Sun, 8 Mar 2026 20:13:29 +0000 Subject: [PATCH 1/3] Fix weight updates from grid view Fix: Added an OnTilePropertyChanged delegate to SPCGExCollectionGridTile that fires after tile-level property edits complete (weight slider end, weight text commit, and subcollection checkbox toggle). The grid view binds this delegate to call UpdateDetailForSelection(), which re-copies the entry data into the detail panel's struct, keeping it in sync immediately. --- .../Collections/SPCGExCollectionGridTile.cpp | 4 ++++ .../Collections/SPCGExCollectionGridView.cpp | 14 +++++++++++--- .../Details/Collections/SPCGExCollectionGridTile.h | 2 ++ .../Details/Collections/SPCGExCollectionGridView.h | 1 + 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridTile.cpp b/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridTile.cpp index ca8bea2fc..eda419f27 100644 --- a/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridTile.cpp +++ b/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridTile.cpp @@ -36,6 +36,7 @@ void SPCGExCollectionGridTile::Construct(const FArguments& InArgs) OnTileClicked = InArgs._OnTileClicked; OnTileDragDetected = InArgs._OnTileDragDetected; OnTileCategoryChanged = InArgs._OnTileCategoryChanged; + OnTilePropertyChanged = InArgs._OnTilePropertyChanged; ThumbnailCachePtr = InArgs._ThumbnailCachePtr; BatchFlagPtr = InArgs._BatchFlagPtr; @@ -247,6 +248,7 @@ void SPCGExCollectionGridTile::Construct(const FArguments& InArgs) Coll->PostEditChange(); if (BatchFlagPtr) { *BatchFlagPtr = false; } RefreshThumbnail(); + OnTilePropertyChanged.ExecuteIfBound(); }) ] ] @@ -298,6 +300,7 @@ void SPCGExCollectionGridTile::Construct(const FArguments& InArgs) if (UPCGExAssetCollection* Coll = WeakColl.Get()) { Coll->PostEditChange(); } if (GEditor) { GEditor->EndTransaction(); } if (BatchFlagPtr) { *BatchFlagPtr = false; } + OnTilePropertyChanged.ExecuteIfBound(); }) .OnValueCommitted_Lambda([this, WeakColl, Idx](int32 NewVal, ETextCommit::Type CommitType) { @@ -311,6 +314,7 @@ void SPCGExCollectionGridTile::Construct(const FArguments& InArgs) Entry->Weight = NewVal; Coll->PostEditChange(); if (BatchFlagPtr) { *BatchFlagPtr = false; } + OnTilePropertyChanged.ExecuteIfBound(); }) .Font(FCoreStyle::GetDefaultFontStyle("Regular", 8)) ] diff --git a/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridView.cpp b/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridView.cpp index b07f35e8f..5560d8ce1 100644 --- a/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridView.cpp +++ b/Source/PCGExCollectionsEditor/Private/Details/Collections/SPCGExCollectionGridView.cpp @@ -326,7 +326,8 @@ void SPCGExCollectionGridView::RebuildGroupedLayout() .BatchFlagPtr(&bIsBatchOperation) .OnTileClicked(FOnTileClicked::CreateSP(this, &SPCGExCollectionGridView::OnTileClicked)) .OnTileDragDetected(FOnTileDragDetected::CreateSP(this, &SPCGExCollectionGridView::OnTileDragDetected)) - .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)); + .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)) + .OnTilePropertyChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTilePropertyChanged)); Group->AddTile(TileWidget); ActiveTiles.Add(EntryIdx, Tile); @@ -453,7 +454,8 @@ void SPCGExCollectionGridView::IncrementalCategoryRefresh() .BatchFlagPtr(&bIsBatchOperation) .OnTileClicked(FOnTileClicked::CreateSP(this, &SPCGExCollectionGridView::OnTileClicked)) .OnTileDragDetected(FOnTileDragDetected::CreateSP(this, &SPCGExCollectionGridView::OnTileDragDetected)) - .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)); + .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)) + .OnTilePropertyChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTilePropertyChanged)); Group->AddTile(TileWidget); ActiveTiles.Add(EntryIdx, Tile); @@ -648,6 +650,11 @@ void SPCGExCollectionGridView::OnTileCategoryChanged() } } +void SPCGExCollectionGridView::OnTilePropertyChanged() +{ + UpdateDetailForSelection(); +} + // ── Category operations ───────────────────────────────────────────────────── void SPCGExCollectionGridView::OnTileDropOnCategory(FName TargetCategory, const TArray& Indices, int32 InsertBeforeLocalIndex) @@ -1036,7 +1043,8 @@ void SPCGExCollectionGridView::PopulateCategoryTiles(FName Category) .BatchFlagPtr(&bIsBatchOperation) .OnTileClicked(FOnTileClicked::CreateSP(this, &SPCGExCollectionGridView::OnTileClicked)) .OnTileDragDetected(FOnTileDragDetected::CreateSP(this, &SPCGExCollectionGridView::OnTileDragDetected)) - .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)); + .OnTileCategoryChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTileCategoryChanged)) + .OnTilePropertyChanged(FSimpleDelegate::CreateSP(this, &SPCGExCollectionGridView::OnTilePropertyChanged)); Group->AddTile(TileWidget); ActiveTiles.Add(EntryIdx, Tile); diff --git a/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridTile.h b/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridTile.h index 33834872d..5697898f8 100644 --- a/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridTile.h +++ b/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridTile.h @@ -56,6 +56,7 @@ class PCGEXCOLLECTIONSEDITOR_API SPCGExCollectionGridTile : public SCompoundWidg SLATE_EVENT(FOnTileClicked, OnTileClicked) SLATE_EVENT(FOnTileDragDetected, OnTileDragDetected) SLATE_EVENT(FSimpleDelegate, OnTileCategoryChanged) + SLATE_EVENT(FSimpleDelegate, OnTilePropertyChanged) SLATE_END_ARGS() void Construct(const FArguments& InArgs); @@ -97,6 +98,7 @@ class PCGEXCOLLECTIONSEDITOR_API SPCGExCollectionGridTile : public SCompoundWidg FOnTileClicked OnTileClicked; FOnTileDragDetected OnTileDragDetected; FSimpleDelegate OnTileCategoryChanged; + FSimpleDelegate OnTilePropertyChanged; // Batch flag — pointer to grid view's bIsBatchOperation (suppresses OnObjectModified during tile edits) bool* BatchFlagPtr = nullptr; diff --git a/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridView.h b/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridView.h index ec03ed8b3..b832c05f4 100644 --- a/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridView.h +++ b/Source/PCGExCollectionsEditor/Public/Details/Collections/SPCGExCollectionGridView.h @@ -133,6 +133,7 @@ class PCGEXCOLLECTIONSEDITOR_API SPCGExCollectionGridView : public SCompoundWidg void OnTileClicked(int32 Index, const FPointerEvent& MouseEvent); FReply OnTileDragDetected(int32 Index, const FPointerEvent& MouseEvent); void OnTileCategoryChanged(); + void OnTilePropertyChanged(); // Detail panel management void UpdateDetailForSelection(); From 917b79b6409205fdbb8a5f4da6830b7bb0099dde Mon Sep 17 00:00:00 2001 From: mikec316 Date: Thu, 12 Mar 2026 11:20:26 +0000 Subject: [PATCH 2/3] Fix: Material variant picks correlated with entry picks due to shared seed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Material variant selection in PCGExAssetStaging and PCGExStagingSplineMesh always produced the same variant for a given entry because the material pick used the same Seed as the entry pick. Both create a FRandomStream(Seed) independently, and since the stream is deterministic, the first FRand() from each is identical — making variant selection perfectly correlated with entry selection. - Fixed by deriving a decorrelated seed for material variant picking: PCGHelpers::ComputeSeed(Seed, Staging.InternalIndex) mixes in the entry's internal index, breaking the correlation while remaining deterministic. --- .../Private/Elements/PCGExAssetStaging.cpp | 7 ++++++- .../Private/Elements/PCGExStagingSplineMesh.cpp | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp b/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp index efec7366c..962cff01d 100644 --- a/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp +++ b/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp @@ -14,6 +14,7 @@ #include "Data/PCGExPointIO.h" #include "Helpers/PCGExAssetLoader.h" #include "Helpers/PCGExCollectionsHelpers.h" +#include "Helpers/PCGHelpers.h" #include "Helpers/PCGExRandomHelpers.h" #include "Helpers/PCGExSocketHelpers.h" @@ -504,11 +505,15 @@ namespace PCGExAssetStaging // MicroCache holds per-entry sub-distribution data (e.g., material variants for meshes). // SecondaryIndex selects which variant to use for this point. + // Use a decorrelated seed: the entry pick already consumed FRandomStream(Seed).RandRange(), + // so a fresh FRandomStream(Seed) would produce the exact same first random value, + // correlating entry and variant picks. if (const PCGExAssetCollection::FMicroCache* MicroCache = Entry->MicroCache.Get(); MicroHelper && MicroCache && MicroCache->GetTypeId() == PCGExAssetCollection::TypeIds::Mesh) { const PCGExMeshCollection::FMicroCache* EntryMicroCache = static_cast(MicroCache); - SecondaryIndex = MicroHelper->GetPick(EntryMicroCache, Index, Seed); + const int32 MaterialSeed = PCGHelpers::ComputeSeed(Seed, Staging.InternalIndex); + SecondaryIndex = MicroHelper->GetPick(EntryMicroCache, Index, MaterialSeed); if (Context->bPickMaterials) { diff --git a/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp b/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp index de17c2fd3..fed83eac7 100644 --- a/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp +++ b/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp @@ -5,6 +5,7 @@ #include "PCGComponent.h" #include "Components/SplineMeshComponent.h" +#include "Helpers/PCGHelpers.h" #include "Helpers/PCGExRandomHelpers.h" #include "Data/PCGExData.h" #include "Data/PCGExDataTags.h" @@ -501,7 +502,8 @@ namespace PCGExPathSplineMesh if (bLocalFitting) { const PCGExMeshCollection::FMicroCache* EntryMicroCache = static_cast(MicroCache); - Segment.MaterialPick = MicroHelper->GetPick(EntryMicroCache, Index, Seed); + const int32 MaterialSeed = PCGHelpers::ComputeSeed(Seed, MeshEntry->Staging.InternalIndex); + Segment.MaterialPick = MicroHelper->GetPick(EntryMicroCache, Index, MaterialSeed); } if (Segment.MaterialPick != -1) { MeshEntry->GetMaterialPaths(Segment.MaterialPick, *ScopedMaterials->Get(Scope)); } From 039532d0ba15cb493a28861dcc097d4ae64a7b54 Mon Sep 17 00:00:00 2001 From: mikec316 Date: Thu, 12 Mar 2026 22:34:28 +0000 Subject: [PATCH 3/3] Revert "Fix: Material variant picks correlated with entry picks due to shared seed" This reverts commit 917b79b6409205fdbb8a5f4da6830b7bb0099dde. --- .../Private/Elements/PCGExAssetStaging.cpp | 7 +------ .../Private/Elements/PCGExStagingSplineMesh.cpp | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp b/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp index 962cff01d..efec7366c 100644 --- a/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp +++ b/Source/PCGExCollections/Private/Elements/PCGExAssetStaging.cpp @@ -14,7 +14,6 @@ #include "Data/PCGExPointIO.h" #include "Helpers/PCGExAssetLoader.h" #include "Helpers/PCGExCollectionsHelpers.h" -#include "Helpers/PCGHelpers.h" #include "Helpers/PCGExRandomHelpers.h" #include "Helpers/PCGExSocketHelpers.h" @@ -505,15 +504,11 @@ namespace PCGExAssetStaging // MicroCache holds per-entry sub-distribution data (e.g., material variants for meshes). // SecondaryIndex selects which variant to use for this point. - // Use a decorrelated seed: the entry pick already consumed FRandomStream(Seed).RandRange(), - // so a fresh FRandomStream(Seed) would produce the exact same first random value, - // correlating entry and variant picks. if (const PCGExAssetCollection::FMicroCache* MicroCache = Entry->MicroCache.Get(); MicroHelper && MicroCache && MicroCache->GetTypeId() == PCGExAssetCollection::TypeIds::Mesh) { const PCGExMeshCollection::FMicroCache* EntryMicroCache = static_cast(MicroCache); - const int32 MaterialSeed = PCGHelpers::ComputeSeed(Seed, Staging.InternalIndex); - SecondaryIndex = MicroHelper->GetPick(EntryMicroCache, Index, MaterialSeed); + SecondaryIndex = MicroHelper->GetPick(EntryMicroCache, Index, Seed); if (Context->bPickMaterials) { diff --git a/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp b/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp index fed83eac7..de17c2fd3 100644 --- a/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp +++ b/Source/PCGExCollections/Private/Elements/PCGExStagingSplineMesh.cpp @@ -5,7 +5,6 @@ #include "PCGComponent.h" #include "Components/SplineMeshComponent.h" -#include "Helpers/PCGHelpers.h" #include "Helpers/PCGExRandomHelpers.h" #include "Data/PCGExData.h" #include "Data/PCGExDataTags.h" @@ -502,8 +501,7 @@ namespace PCGExPathSplineMesh if (bLocalFitting) { const PCGExMeshCollection::FMicroCache* EntryMicroCache = static_cast(MicroCache); - const int32 MaterialSeed = PCGHelpers::ComputeSeed(Seed, MeshEntry->Staging.InternalIndex); - Segment.MaterialPick = MicroHelper->GetPick(EntryMicroCache, Index, MaterialSeed); + Segment.MaterialPick = MicroHelper->GetPick(EntryMicroCache, Index, Seed); } if (Segment.MaterialPick != -1) { MeshEntry->GetMaterialPaths(Segment.MaterialPick, *ScopedMaterials->Get(Scope)); }