Skip to content

Commit 5251a7c

Browse files
committed
Added category support to asset collections / staging
1 parent 30658e3 commit 5251a7c

File tree

9 files changed

+399
-251
lines changed

9 files changed

+399
-251
lines changed

Source/PCGExtendedToolkit/Private/AssetStaging/PCGExAssetStaging.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,8 @@ namespace PCGExAssetStaging
212212
Variations.Init(Settings->Seed);
213213

214214

215-
Helper = MakeUnique<PCGExAssetCollection::TDistributionHelper<UPCGExAssetCollection, FPCGExAssetCollectionEntry>>(Context->MainCollection, Settings->DistributionSettings);
216-
if (!Helper->Init(ExecutionContext, PointDataFacade)) { return false; }
215+
Helper = MakeShared<PCGExAssetCollection::TDistributionHelper<UPCGExAssetCollection, FPCGExAssetCollectionEntry>>(Context->MainCollection, Settings->DistributionSettings);
216+
if (!Helper->Init(PointDataFacade)) { return false; }
217217

218218
bOutputWeight = Settings->WeightToAttribute != EPCGExWeightOutputMode::NoOutput;
219219
bNormalizedWeight = Settings->WeightToAttribute != EPCGExWeightOutputMode::Raw;

Source/PCGExtendedToolkit/Private/AssetStaging/PCGExMeshSelectorStaged.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ bool UPCGExMeshSelectorStaged::SelectMeshInstances(FPCGStaticMeshSpawnerContext&
142142
int16 MaterialPick = -1;
143143

144144
if (!CollectionMap->ResolveEntry(Partition.Key, Entry, MaterialPick)) { continue; }
145-
145+
146146
FPCGMeshInstanceList& InstanceList = OutMeshInstances[Partition.Value];
147147

148148
InstanceList.Descriptor = TemplateDescriptor;
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2025 Timothé Lapetite and contributors
2+
// Released under the MIT license https://opensource.org/license/MIT/
3+
4+
#include "AssetStaging/PCGExStaging.h"
5+
6+
namespace PCGExStaging
7+
{
8+
FPickPacker::FPickPacker(FPCGExContext* InContext)
9+
: Context(InContext)
10+
{
11+
BaseHash = static_cast<uint16>(InContext->GetInputSettings<UPCGSettings>()->UID);
12+
}
13+
14+
uint64 FPickPacker::GetPickIdx(const UPCGExAssetCollection* InCollection, const int16 InIndex, const int16 InSecondaryIndex)
15+
{
16+
const uint32 ItemHash = PCGEx::H32(InIndex, InSecondaryIndex + 1);
17+
18+
{
19+
FReadScopeLock ReadScopeLock(AssetCollectionsLock);
20+
if (const uint32* ColIdxPtr = CollectionMap.Find(InCollection)) { return PCGEx::H64(*ColIdxPtr, ItemHash); }
21+
}
22+
23+
{
24+
FWriteScopeLock WriteScopeLock(AssetCollectionsLock);
25+
if (const uint32* ColIdxPtr = CollectionMap.Find(InCollection)) { return PCGEx::H64(*ColIdxPtr, ItemHash); }
26+
27+
uint32 ColIndex = PCGEx::H32(BaseHash, AssetCollections.Add(InCollection));
28+
CollectionMap.Add(InCollection, ColIndex);
29+
return PCGEx::H64(ColIndex, ItemHash);
30+
}
31+
}
32+
33+
void FPickPacker::PackToDataset(const UPCGParamData* InAttributeSet)
34+
{
35+
FPCGMetadataAttribute<int32>* CollectionIdx = InAttributeSet->Metadata->FindOrCreateAttribute<int32>(Tag_CollectionIdx, 0, false, true, true);
36+
FPCGMetadataAttribute<FSoftObjectPath>* CollectionPath = InAttributeSet->Metadata->FindOrCreateAttribute<FSoftObjectPath>(Tag_CollectionPath, FSoftObjectPath(), false, true, true);
37+
38+
for (const TPair<const UPCGExAssetCollection*, uint32>& Pair : CollectionMap)
39+
{
40+
const int64 Key = InAttributeSet->Metadata->AddEntry();
41+
CollectionIdx->SetValue(Key, Pair.Value);
42+
CollectionPath->SetValue(Key, FSoftObjectPath(Pair.Key));
43+
}
44+
}
45+
46+
bool IPickUnpacker::UnpackDataset(FPCGContext* InContext, const UPCGParamData* InAttributeSet)
47+
{
48+
const UPCGMetadata* Metadata = InAttributeSet->Metadata;
49+
TUniquePtr<FPCGAttributeAccessorKeysEntries> Keys = MakeUnique<FPCGAttributeAccessorKeysEntries>(Metadata);
50+
51+
const int32 NumEntries = Keys->GetNum();
52+
if (NumEntries == 0)
53+
{
54+
PCGE_LOG_C(Error, GraphAndLog, InContext, FTEXT("Attribute set is empty."));
55+
return false;
56+
}
57+
58+
CollectionMap.Reserve(CollectionMap.Num() + NumEntries);
59+
60+
const FPCGMetadataAttribute<int32>* CollectionIdx = InAttributeSet->Metadata->GetConstTypedAttribute<int32>(Tag_CollectionIdx);
61+
const FPCGMetadataAttribute<FSoftObjectPath>* CollectionPath = InAttributeSet->Metadata->GetConstTypedAttribute<FSoftObjectPath>(Tag_CollectionPath);
62+
63+
if (!CollectionIdx || !CollectionPath)
64+
{
65+
PCGE_LOG_C(Error, GraphAndLog, InContext, FTEXT("Missing required attributes, or unsupported type."));
66+
return false;
67+
}
68+
69+
for (int i = 0; i < NumEntries; i++)
70+
{
71+
int32 Idx = CollectionIdx->GetValueFromItemKey(i);
72+
73+
UPCGExAssetCollection* Collection = PCGExHelpers::LoadBlocking_AnyThread<UPCGExAssetCollection>(TSoftObjectPtr<UPCGExAssetCollection>(CollectionPath->GetValueFromItemKey(i)));
74+
75+
if (!Collection)
76+
{
77+
PCGE_LOG_C(Error, GraphAndLog, InContext, FTEXT("Some collections could not be loaded."));
78+
return false;
79+
}
80+
81+
if (CollectionMap.Contains(Idx))
82+
{
83+
if (CollectionMap[Idx] == Collection) { continue; }
84+
85+
PCGE_LOG_C(Error, GraphAndLog, InContext, FTEXT("Collection Idx collision."));
86+
return false;
87+
}
88+
89+
CollectionMap.Add(Idx, Collection);
90+
NumUniqueEntries += Collection->GetValidEntryNum();
91+
}
92+
93+
return true;
94+
}
95+
96+
void IPickUnpacker::UnpackPin(FPCGContext* InContext, const FName InPinLabel)
97+
{
98+
for (TArray<FPCGTaggedData> Params = InContext->InputData.GetParamsByPin(InPinLabel);
99+
const FPCGTaggedData& InTaggedData : Params)
100+
{
101+
const UPCGParamData* ParamData = Cast<UPCGParamData>(InTaggedData.Data);
102+
103+
if (!ParamData) { continue; }
104+
const TSharedPtr<PCGEx::FAttributesInfos> Infos = PCGEx::FAttributesInfos::Get(ParamData->Metadata);
105+
106+
if (!ParamData->Metadata->HasAttribute(Tag_CollectionIdx) || !ParamData->Metadata->HasAttribute(Tag_CollectionPath)) { continue; }
107+
108+
UnpackDataset(InContext, ParamData);
109+
}
110+
}
111+
112+
bool IPickUnpacker::BuildPartitions(const UPCGBasePointData* InPointData, TArray<FPCGMeshInstanceList>& InstanceLists)
113+
{
114+
TRACE_CPUPROFILER_EVENT_SCOPE(TPickUnpacker::BuildPartitions_Indexed);
115+
116+
FPCGAttributePropertyInputSelector HashSelector;
117+
HashSelector.Update(Tag_EntryIdx.ToString());
118+
119+
TUniquePtr<const IPCGAttributeAccessor> HashAttributeAccessor = PCGAttributeAccessorHelpers::CreateConstAccessor(InPointData, HashSelector);
120+
TUniquePtr<const IPCGAttributeAccessorKeys> HashKeys = PCGAttributeAccessorHelpers::CreateConstKeys(InPointData, HashSelector);
121+
122+
if (!HashAttributeAccessor || !HashKeys) { return false; }
123+
124+
TArray<int64> Hashes;
125+
Hashes.SetNumUninitialized(HashKeys->GetNum());
126+
127+
if (!HashAttributeAccessor->GetRange<int64>(Hashes, 0, *HashKeys, EPCGAttributeAccessorFlags::AllowBroadcastAndConstructible))
128+
{
129+
return false;
130+
}
131+
132+
const int32 NumPoints = InPointData->GetNumPoints();
133+
const int32 SafeReserve = NumPoints / (NumUniqueEntries * 2);
134+
135+
// Build partitions
136+
for (int i = 0; i < NumPoints; i++)
137+
{
138+
const uint64 EntryHash = Hashes[i];
139+
if (const int32* Index = IndexedPartitions.Find(EntryHash); !Index)
140+
{
141+
FPCGMeshInstanceList& NewInstanceList = InstanceLists.Emplace_GetRef();
142+
NewInstanceList.AttributePartitionIndex = EntryHash;
143+
NewInstanceList.PointData = InPointData;
144+
NewInstanceList.InstancesIndices.Reserve(SafeReserve);
145+
NewInstanceList.InstancesIndices.Emplace(i);
146+
147+
IndexedPartitions.Add(EntryHash, InstanceLists.Num() - 1);
148+
}
149+
else
150+
{
151+
InstanceLists[*Index].InstancesIndices.Emplace(i);
152+
}
153+
}
154+
155+
return !IndexedPartitions.IsEmpty();
156+
}
157+
158+
void IPickUnpacker::RetrievePartitions(const UPCGBasePointData* InPointData, TArray<FPCGMeshInstanceList>& InstanceLists)
159+
{
160+
TRACE_CPUPROFILER_EVENT_SCOPE(TPickUnpacker::BuildPartitions_Indexed);
161+
162+
PointData = InPointData;
163+
164+
for (FPCGMeshInstanceList& InstanceList : InstanceLists)
165+
{
166+
IndexedPartitions.Add(InstanceList.AttributePartitionIndex, InstanceLists.Num() - 1);
167+
}
168+
}
169+
170+
void IPickUnpacker::InsertEntry(const uint64 EntryHash, const int32 EntryIndex, TArray<FPCGMeshInstanceList>& InstanceLists)
171+
{
172+
if (const int32* Index = IndexedPartitions.Find(EntryHash); !Index)
173+
{
174+
FPCGMeshInstanceList& NewInstanceList = InstanceLists.Emplace_GetRef();
175+
NewInstanceList.AttributePartitionIndex = EntryHash;
176+
NewInstanceList.PointData = PointData;
177+
NewInstanceList.InstancesIndices.Reserve(PointData->GetNumPoints() / (NumUniqueEntries * 2));
178+
NewInstanceList.InstancesIndices.Emplace(EntryIndex);
179+
180+
IndexedPartitions.Add(EntryHash, InstanceLists.Num() - 1);
181+
}
182+
else
183+
{
184+
InstanceLists[*Index].InstancesIndices.Emplace(EntryIndex);
185+
}
186+
}
187+
188+
UPCGExAssetCollection* IPickUnpacker::UnpackHash(const uint64 EntryHash, int16& OutPrimaryIndex, int16& OutSecondaryIndex)
189+
{
190+
uint32 CollectionIdx = 0;
191+
uint32 OutEntryIndices = 0;
192+
193+
PCGEx::H64(EntryHash, CollectionIdx, OutEntryIndices);
194+
195+
uint16 EntryIndex = 0;
196+
uint16 SecondaryIndex = 0;
197+
198+
PCGEx::H32(OutEntryIndices, EntryIndex, SecondaryIndex);
199+
OutSecondaryIndex = SecondaryIndex - 1; // minus one because we do +1 during packing
200+
201+
UPCGExAssetCollection** Collection = CollectionMap.Find(CollectionIdx);
202+
if (!Collection || !(*Collection)->IsValidIndex(EntryIndex)) { return nullptr; }
203+
204+
OutPrimaryIndex = EntryIndex;
205+
206+
return *Collection;
207+
}
208+
}

Source/PCGExtendedToolkit/Private/Collections/PCGExAssetCollection.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
namespace PCGExAssetCollection
2222
{
23-
bool FCategory::IsEmpty() const { return Order.IsEmpty(); }
24-
2523
int32 FCategory::GetPick(const int32 Index, const EPCGExIndexPickMode PickMode) const
2624
{
2725
switch (PickMode)
@@ -554,4 +552,37 @@ UPCGExAssetCollection* FPCGExRoamingAssetCollectionDetails::TryBuildCollection(F
554552

555553
namespace PCGExAssetCollection
556554
{
555+
IDistributionHelper::IDistributionHelper(UPCGExAssetCollection* InCollection, const FPCGExAssetDistributionDetails& InDetails)
556+
: MainCollection(InCollection), Details(InDetails)
557+
{
558+
}
559+
560+
bool IDistributionHelper::Init(const TSharedRef<PCGExData::FFacade>& InDataFacade)
561+
{
562+
Cache = MainCollection->LoadCache();
563+
564+
if (Cache->IsEmpty())
565+
{
566+
PCGE_LOG_C(Error, GraphAndLog, InDataFacade->GetContext(), FTEXT("TDistributionHelper got an empty Collection."));
567+
return false;
568+
}
569+
570+
if (Details.bUseCategories)
571+
{
572+
CategoryGetter = Details.GetValueSettingCategory();
573+
if (!CategoryGetter->Init(InDataFacade)) { return false; }
574+
}
575+
576+
if (Details.Distribution == EPCGExDistribution::Index)
577+
{
578+
const bool bWantsMinMax = Details.IndexSettings.bRemapIndexToCollectionSize;
579+
580+
IndexGetter = Details.IndexSettings.GetValueSettingIndex();
581+
if (!IndexGetter->Init(InDataFacade, !bWantsMinMax, bWantsMinMax)) { return false; }
582+
583+
MaxInputIndex = IndexGetter->Max();
584+
}
585+
586+
return true;
587+
}
557588
}

Source/PCGExtendedToolkit/Private/Paths/PCGExPathSplineMesh.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ namespace PCGExPathSplineMesh
184184
TangentsHandler = MakeShared<PCGExTangents::FTangentsHandler>(bClosedLoop);
185185
if (!TangentsHandler->Init(Context, Context->Tangents, PointDataFacade)) { return false; }
186186

187-
Helper = MakeUnique<PCGExAssetCollection::TDistributionHelper<UPCGExMeshCollection, FPCGExMeshCollectionEntry>>(Context->MainCollection, Settings->DistributionSettings);
188-
if (!Helper->Init(ExecutionContext, PointDataFacade)) { return false; }
187+
Helper = MakeShared<PCGExAssetCollection::TDistributionHelper<UPCGExMeshCollection, FPCGExMeshCollectionEntry>>(Context->MainCollection, Settings->DistributionSettings);
188+
if (!Helper->Init(PointDataFacade)) { return false; }
189189

190190
if (Settings->SplineMeshUpMode == EPCGExSplineMeshUpMode::Attribute)
191191
{

Source/PCGExtendedToolkit/Public/AssetStaging/PCGExAssetStaging.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class UPCGExAssetStagingSettings : public UPCGExPointsProcessorSettings
6767
FName AssetPathAttributeName = "AssetPath";
6868

6969
/** Distribution details */
70-
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Settings, meta=(PCG_Overridable, DisplayName="Distribution", ShowOnlyInnerProperties))
70+
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Settings, meta=(PCG_Overridable, DisplayName="Distribution"))
7171
FPCGExAssetDistributionDetails DistributionSettings;
7272

7373
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = Settings, meta=(PCG_Overridable))
@@ -155,7 +155,7 @@ namespace PCGExAssetStaging
155155
FPCGExFittingDetailsHandler FittingHandler;
156156
FPCGExFittingVariationsDetails Variations;
157157

158-
TUniquePtr<PCGExAssetCollection::TDistributionHelper<UPCGExAssetCollection, FPCGExAssetCollectionEntry>> Helper;
158+
TSharedPtr<PCGExAssetCollection::TDistributionHelper<UPCGExAssetCollection, FPCGExAssetCollectionEntry>> Helper;
159159

160160
TSharedPtr<PCGExData::TBuffer<int32>> WeightWriter;
161161
TSharedPtr<PCGExData::TBuffer<double>> NormalizedWeightWriter;

0 commit comments

Comments
 (0)