253 lines
7.1 KiB
HLSL
253 lines
7.1 KiB
HLSL
/* An update version of the boid system using a spatial hash map */
|
|
|
|
#include "shared/hash-functions.hlsl"
|
|
#include "shared/point.hlsl"
|
|
#include "points/spatial-hash-map/hash-map-settings.hlsl"
|
|
|
|
cbuffer ParamConstants : register(b0)
|
|
{
|
|
float EffectLayer;
|
|
float GridCellSize;
|
|
float WrapAround;
|
|
float Jitter;
|
|
|
|
float Time;
|
|
|
|
}
|
|
|
|
|
|
struct Boid
|
|
{
|
|
float CohesionRadius;
|
|
float CohesionDrive;
|
|
float AlignmentRadius;
|
|
float AlignmentDrive;
|
|
float SeparationRadius;
|
|
float SeparationDrive;
|
|
float MaxSpeed;
|
|
float _padding;
|
|
};
|
|
|
|
struct Agent {
|
|
float3 Position;
|
|
float BoidType;
|
|
float4 SpriteOrientation;
|
|
};
|
|
|
|
static const float3 Offsets[] =
|
|
{
|
|
float3(0, 0, 0),
|
|
float3(1, 0, 0),
|
|
float3(0, 1, 0),
|
|
float3(1, 1, 0),
|
|
};
|
|
|
|
|
|
#define mod(x,y) ((x)-(y)*floor((x)/(y)))
|
|
|
|
sampler texSampler : register(s0);
|
|
|
|
StructuredBuffer<uint> CellPointIndices :register(t0); // IndexToPointBuffer -> CellPointIndices
|
|
StructuredBuffer<uint2> PointCellIndices :register(t1); // CellIndicesBuffer -> PointCellIndices
|
|
StructuredBuffer<uint> HashGridCells :register(t2); // HashGridBuffer -> HashGridCells
|
|
StructuredBuffer<uint> CellPointCounts :register(t3); // CountBuffer -> CellPointCounts
|
|
StructuredBuffer<uint> CellRangeIndices :register(t4); // RangeIndexBuffer -> CellRangeIndices
|
|
|
|
StructuredBuffer<Boid> BoidsTypes : register(t5);
|
|
Texture2D<float4> InputTexture : register(t6);
|
|
|
|
RWStructuredBuffer<Agent> Agents : register(u0);
|
|
|
|
static const float3 FORWARD = float3(0,1,0);
|
|
static const float3 UP = float3(0,0,1);
|
|
|
|
|
|
bool GridFind(in float3 position, out uint startIndex, out uint endIndex)
|
|
{
|
|
uint i;
|
|
position+= 100 * GridCellSize;
|
|
int3 cell = int3(position / GridCellSize);
|
|
uint cellIndex = (pcg(cell.x + pcg(cell.y + pcg(cell.z))) % ParticleGridCellCount);
|
|
uint hashValue = max(xxhash(cell.x + xxhash(cell.y + xxhash(cell.z))), 1);
|
|
uint cellBegin = cellIndex * ParticleGridEntryCount;
|
|
uint cellEnd = cellBegin + ParticleGridEntryCount;
|
|
for(i = cellBegin; i < cellEnd; ++i)
|
|
{
|
|
const uint entryValue = HashGridCells[i];
|
|
if(entryValue == hashValue)
|
|
break; // found existing entry
|
|
|
|
if(entryValue == 0)
|
|
i = cellEnd;
|
|
}
|
|
if(i >= cellEnd)
|
|
return false;
|
|
|
|
startIndex = CellRangeIndices[i];
|
|
int count = min(CellPointCounts[i], 50);
|
|
|
|
endIndex = startIndex + count;
|
|
return true;
|
|
}
|
|
|
|
|
|
[numthreads(256,1,1)]
|
|
void main(uint3 Gid : SV_GroupID, uint3 DTid : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint Gi : SV_GroupIndex)
|
|
{
|
|
uint pointCount, stride;
|
|
Agents.GetDimensions(pointCount, stride);
|
|
|
|
if(DTid.x >= pointCount)
|
|
return; // out of bounds
|
|
|
|
// Setup Buffers
|
|
float3 centerForCohesion;
|
|
int countForCohesion =0;
|
|
|
|
float3 centerForSeparation;
|
|
int countForSeparation =0;
|
|
|
|
float3 averageDirection;
|
|
int countForAlignment =0;
|
|
|
|
int pointIndex = DTid.x;
|
|
Agent self = Agents[pointIndex];
|
|
|
|
// Rotate back
|
|
|
|
float3 direction = 0;
|
|
float3 position = self.Position;
|
|
|
|
if(true) {
|
|
direction = rotate_vector(FORWARD, self.SpriteOrientation);
|
|
}
|
|
else {
|
|
direction = float3(rotate_vector(FORWARD, self.SpriteOrientation).xy, 0);
|
|
position.z = 0;
|
|
}
|
|
|
|
|
|
int boidTypIndex = 0;
|
|
|
|
int startIndex, endIndex;
|
|
|
|
float3 lookupPos = position;
|
|
int foundNeighbours = 0;
|
|
|
|
float3 jitter = (hash13(Time* 123.3 % 421) -0.5) * GridCellSize * 0.2;
|
|
jitter.z = 0;
|
|
float3 jitteredPosition = position + jitter;
|
|
|
|
float3 posInCel = mod(jitteredPosition, GridCellSize) - GridCellSize /2;
|
|
float3 sign = posInCel < 0 ? -1 : 1;
|
|
|
|
for(uint offsetIndex = 0; offsetIndex < 4; offsetIndex++)
|
|
{
|
|
|
|
lookupPos = jitteredPosition + Offsets[offsetIndex] * GridCellSize * sign;
|
|
|
|
if(GridFind(lookupPos, startIndex, endIndex))
|
|
{
|
|
for(uint i=startIndex; i < endIndex; ++i)
|
|
{
|
|
uint otherIndex = CellPointIndices[i];
|
|
if(otherIndex == pointIndex)
|
|
continue;
|
|
|
|
float3 otherPos = Agents[otherIndex].Position;
|
|
float distance = length(otherPos - position);
|
|
|
|
if(distance < BoidsTypes[boidTypIndex].AlignmentRadius)
|
|
{
|
|
averageDirection += rotate_vector(FORWARD, Agents[otherIndex].SpriteOrientation);
|
|
countForAlignment++;
|
|
}
|
|
|
|
if(distance < BoidsTypes[boidTypIndex].CohesionRadius)
|
|
{
|
|
centerForCohesion += Agents[otherIndex].Position;
|
|
countForCohesion++;
|
|
}
|
|
|
|
if(distance < BoidsTypes[boidTypIndex].SeparationRadius)
|
|
{
|
|
centerForSeparation += Agents[otherIndex].Position;
|
|
countForSeparation++;
|
|
}
|
|
foundNeighbours++;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Aligment
|
|
if(countForAlignment > 0)
|
|
{
|
|
averageDirection /= countForAlignment;
|
|
float l = length(averageDirection);
|
|
if(l > 0.0001) {
|
|
direction = lerp(direction, averageDirection/l, BoidsTypes[boidTypIndex].AlignmentDrive);
|
|
}
|
|
}
|
|
|
|
// Separation
|
|
if(countForSeparation > 0)
|
|
{
|
|
centerForSeparation /= countForSeparation;
|
|
float3 toSeparation = position - centerForSeparation;
|
|
float lenToSeparation = length(position - centerForSeparation);
|
|
if(lenToSeparation > 0.0001) {
|
|
direction = lerp(direction, toSeparation / lenToSeparation, BoidsTypes[boidTypIndex].SeparationDrive );
|
|
}
|
|
}
|
|
|
|
// Cohesion
|
|
if(countForCohesion > 0)
|
|
{
|
|
centerForCohesion /= countForCohesion;
|
|
float3 toCohesion = -(position - centerForCohesion);
|
|
float lenToCohesion = length(position - centerForCohesion);
|
|
if(lenToCohesion > 0.0001) {
|
|
direction = lerp(direction, toCohesion / lenToCohesion, BoidsTypes[boidTypIndex].CohesionDrive );
|
|
}
|
|
}
|
|
|
|
// Effect Texture
|
|
// float2 uv= (position.xy * 0.5) +0.5;
|
|
// uv = float2(uv.x, 1- uv.y);
|
|
// float4 c = InputTexture.SampleLevel(texSampler, uv, 0);
|
|
// direction.xy -= c.xy * EffectLayer;
|
|
|
|
// float len = length(direction);
|
|
// if(isnan(len) || len == 0)
|
|
// {
|
|
// direction = float3(-1,-1,0);
|
|
// }
|
|
// else
|
|
// {
|
|
// direction /= len;
|
|
// }
|
|
|
|
float len = length(direction);
|
|
direction /= len;
|
|
position += direction * BoidsTypes[boidTypIndex].MaxSpeed / 60;
|
|
|
|
|
|
if(WrapAround)
|
|
{
|
|
position = mod(position + 1, 2) - 1;
|
|
}
|
|
|
|
|
|
//float4 rot = Agents[DTid.x].SpriteOrientation;
|
|
|
|
// Use look at velocity rotation and rotate back into xy plane
|
|
float4 rot = normalize(q_look_at(direction, float3(0,0,1)));
|
|
rot = qmul(rot, rotate_angle_axis(0.5*PI , float3(1,0,0)));
|
|
//rot = q_slerp(self.SpriteOrientation, rot, 0.9);
|
|
|
|
// 2d-rotation around z
|
|
Agents[DTid.x].SpriteOrientation = rot;
|
|
Agents[DTid.x].Position = position;
|
|
//Agents[DTid.x].Position += float3(0,0.001,0);
|
|
} |