138 lines
5.8 KiB
HLSL
138 lines
5.8 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
// Static mesh spawner kernel. Selects a primitive either using a primitive cumulative distribution function (CDF) or by an attribute, and packs attributes in custom floats.
|
|
// Passes through points to output.
|
|
|
|
// When spawning by attribute, we don't know in advance how many instances of each mesh will be spawned, so the worst case NumPoints * NumPrims is allocated in the GPU Scene.
|
|
|
|
[numthreads(64, 1, 1)]
|
|
void PCGStaticMeshSpawnerCS(uint3 GroupId : SV_GroupID, uint GroupIndex : SV_GroupIndex)
|
|
{
|
|
// Mark the kernel as having executed. Must run before we early out via thread index, because the kernel is still 'executed' even if the number of
|
|
// threads to iterate on is zero. Even if GetNumThreads() returns 0, the kernel will still have been dispatched on a single thread to set this flag.
|
|
if (all(GroupId == 0) && GroupIndex == 0)
|
|
{
|
|
Out_SetAsExecutedInternal();
|
|
}
|
|
|
|
const uint ThreadIndex = GetUnWrappedDispatchThreadId(GroupId, GroupIndex, 64);
|
|
|
|
uint In_DataIndex, In_ElementIndex;
|
|
if (!In_GetThreadData(ThreadIndex, In_DataIndex, In_ElementIndex))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint Out_DataIndex, Out_ElementIndex;
|
|
if (!Out_GetThreadData(ThreadIndex, Out_DataIndex, Out_ElementIndex))
|
|
{
|
|
// Should never happen.
|
|
return;
|
|
}
|
|
|
|
if (In_IsPointRemoved(In_DataIndex, In_ElementIndex))
|
|
{
|
|
// Propagate the 'Removed' status to the output point if input point is removed. Otherwise, this point will not be culled.
|
|
Out_RemovePoint(Out_DataIndex, Out_ElementIndex);
|
|
|
|
return;
|
|
}
|
|
|
|
uint PrimitiveIndex = (uint)-1;
|
|
|
|
const int NumPrimitives = InstanceData_GetNumPrimitives();
|
|
|
|
if (NumPrimitives > 0)
|
|
{
|
|
const uint SelectorAttributeId = SMSpawner_GetSelectorAttributeId();
|
|
|
|
if (SelectorAttributeId != (uint)-1)
|
|
{
|
|
// Select using string key
|
|
const int MeshStringKey = In_GetStringKey(In_DataIndex, In_ElementIndex, SelectorAttributeId);
|
|
PrimitiveIndex = SMSpawner_GetPrimitiveIndexFromStringKey(MeshStringKey);
|
|
}
|
|
else
|
|
{
|
|
// Randomly select a primitive by inverting the cumulative distribution function.
|
|
|
|
// Exactly matches UPCGBlueprintHelpers::GetRandomStreamFromPoint - three seeds passed in to consecutive arguments of ComputeSeed
|
|
// (argument count & order matters).
|
|
const uint SeedPoint = In_GetSeed(In_DataIndex, In_ElementIndex);
|
|
const uint SeedSettings = GetSettingsSeed();
|
|
const uint SeedComponent = GetComponentSeed();
|
|
uint Seed = ComputeSeed(SeedPoint, SeedSettings, SeedComponent);
|
|
|
|
const float RandomDraw = FRand(Seed);
|
|
|
|
for (PrimitiveIndex = 0; PrimitiveIndex < uint(NumPrimitives) - 1; ++PrimitiveIndex)
|
|
{
|
|
if (RandomDraw < SMSpawner_GetPrimitiveSelectionCDF(PrimitiveIndex))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get instance index to write to. Will return a negative value if we have saturated instances for this primitive, in which case we will not push the data.
|
|
const int InstanceIndexInt = (PrimitiveIndex != (uint)-1) ? InstanceData_GetIndexToWriteTo(PrimitiveIndex) : -1;
|
|
|
|
// Write instance transform, only if not culled by primitive instance index
|
|
if (InstanceIndexInt >= 0)
|
|
{
|
|
const uint InstanceIndex = InstanceIndexInt;
|
|
const float4x4 Transform = In_GetPointTransform(In_DataIndex, In_ElementIndex);
|
|
InstanceData_WriteInstanceLocalToWorld(InstanceIndex, transpose(Transform));
|
|
|
|
// Write instance custom float data.
|
|
const uint NumAttributes = SMSpawner_GetNumAttributes();
|
|
for (uint AttributeIndex = 0; AttributeIndex < NumAttributes; ++AttributeIndex)
|
|
{
|
|
const uint4 AttributeIdOffsetStride = SMSpawner_GetAttributeIdOffsetStride(AttributeIndex);
|
|
const uint AttributeId = AttributeIdOffsetStride[0];
|
|
const uint AttributeOffset = AttributeIdOffsetStride[1];
|
|
const uint AttributeStride = AttributeIdOffsetStride[2];
|
|
|
|
for (uint FloatIndex = 0; FloatIndex < AttributeStride; ++FloatIndex)
|
|
{
|
|
const uint AttributeValueAddress = In_GetElementAddressInternal(In_DataIndex, In_ElementIndex, AttributeId);
|
|
if (AttributeValueAddress > 0)
|
|
{
|
|
// Assumes floating point data. TODO add type information and deal with different types in different ways?
|
|
InstanceData_WriteCustomFloatRaw(InstanceIndex, AttributeOffset + FloatIndex, In_LoadBufferInternal(AttributeValueAddress + 4 * FloatIndex));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write output point data (pass through).
|
|
Out_SetPosition(Out_DataIndex, Out_ElementIndex, In_GetPosition(In_DataIndex, In_ElementIndex));
|
|
Out_SetRotation(Out_DataIndex, Out_ElementIndex, In_GetRotation(In_DataIndex, In_ElementIndex));
|
|
Out_SetScale(Out_DataIndex, Out_ElementIndex, In_GetScale(In_DataIndex, In_ElementIndex));
|
|
Out_SetColor(Out_DataIndex, Out_ElementIndex, In_GetColor(In_DataIndex, In_ElementIndex));
|
|
Out_SetSeed(Out_DataIndex, Out_ElementIndex, In_GetSeed(In_DataIndex, In_ElementIndex));
|
|
Out_SetDensity(Out_DataIndex, Out_ElementIndex, In_GetDensity(In_DataIndex, In_ElementIndex));
|
|
Out_SetSteepness(Out_DataIndex, Out_ElementIndex, In_GetSteepness(In_DataIndex, In_ElementIndex));
|
|
|
|
// Either apply SM bounds to points or pass bounds through.
|
|
if (PrimitiveIndex != (uint)-1 && SMSpawner_ShouldApplyBounds())
|
|
{
|
|
Out_SetBoundsMin(Out_DataIndex, Out_ElementIndex, SMSpawner_GetPrimitiveMeshBoundsMin(PrimitiveIndex));
|
|
Out_SetBoundsMax(Out_DataIndex, Out_ElementIndex, SMSpawner_GetPrimitiveMeshBoundsMax(PrimitiveIndex));
|
|
}
|
|
else
|
|
{
|
|
Out_SetBoundsMin(Out_DataIndex, Out_ElementIndex, In_GetBoundsMin(In_DataIndex, In_ElementIndex));
|
|
Out_SetBoundsMax(Out_DataIndex, Out_ElementIndex, In_GetBoundsMax(In_DataIndex, In_ElementIndex));
|
|
}
|
|
|
|
PCG_COPY_METADATA_ATTRIBUTES_TO_OUTPUT(Out, In, Out_DataIndex, Out_ElementIndex, In_DataIndex, In_ElementIndex);
|
|
|
|
if (SMSpawner_GetSelectedMeshAttributeId() != (uint)-1)
|
|
{
|
|
const int OutputMeshStringKey = (PrimitiveIndex != (uint)-1) ? SMSpawner_GetPrimitiveStringKey(PrimitiveIndex) : -1;
|
|
Out_SetStringKey(Out_DataIndex, Out_ElementIndex, SMSpawner_GetSelectedMeshAttributeId(), OutputMeshStringKey);
|
|
}
|
|
}
|