去掉obi,使用自写绳索

This commit is contained in:
2026-02-23 20:51:03 +08:00
parent cb636f862d
commit 91e2309eeb
2011 changed files with 2593 additions and 190578 deletions

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 55c2294f77a2c4783b788be18cc01798
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,61 +0,0 @@
#pragma kernel Project
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<float> aerodynamicCoeffs;
StructuredBuffer<float4> positions;
StructuredBuffer<uint4> normals;
StructuredBuffer<float4> wind;
StructuredBuffer<float> invMasses;
RWStructuredBuffer<float4> velocities;
// Variables set from the CPU
uint activeConstraintCount;
float deltaTime;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p = particleIndices[i];
float area = aerodynamicCoeffs[i * 3];
float dragCoeff = aerodynamicCoeffs[i * 3 + 1];
float liftCoeff = aerodynamicCoeffs[i * 3 + 2];
if (invMasses[p] > 0)
{
float4 relVelocity = velocities[p] - wind[p];
float rvSqrMag = dot(relVelocity, relVelocity);
if (rvSqrMag < EPSILON)
return;
float4 rvNorm = relVelocity / sqrt(rvSqrMag);
// calculate surface normal (always facing wind)
float4 surfNormal = asfloat(normals[p]) * sign(dot(asfloat(normals[p]), rvNorm));
// aerodynamic_factor was originally multiplied by air_density. The density is now premultiplied in lift and drag.
float aerodynamicFactor = 0.5f * rvSqrMag * area;
float attackAngle = dot(surfNormal,rvNorm);
float3 liftDirection = normalizesafe(cross(cross(surfNormal.xyz, rvNorm.xyz), rvNorm.xyz));
//drag:
velocities[p] += (-dragCoeff * rvNorm +
// lift:
liftCoeff * float4(liftDirection.xyz,0)) *
// scale
attackAngle * min(aerodynamicFactor * invMasses[p] * deltaTime, 1000);
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a819ec002d4434b5da29879a26e3211a
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,90 +0,0 @@
#ifndef ATOMICDELTAS_INCLUDE
#define ATOMICDELTAS_INCLUDE
#include "InterlockedUtils.cginc"
RWStructuredBuffer<uint4> deltasAsInt;
RWStructuredBuffer<uint> positionConstraintCounts;
RWStructuredBuffer<uint4> orientationDeltasAsInt;
RWStructuredBuffer<uint> orientationConstraintCounts;
// atomic delta add:
void AtomicAddPositionDelta(in int index, in float4 delta)
{
InterlockedAddFloat(deltasAsInt, index, 0, delta.x);
InterlockedAddFloat(deltasAsInt, index, 1, delta.y);
InterlockedAddFloat(deltasAsInt, index, 2, delta.z);
InterlockedAdd(positionConstraintCounts[index], 1);
}
void AtomicAddOrientationDelta(in int index, in quaternion delta)
{
InterlockedAddFloat(orientationDeltasAsInt, index, 0, delta.x);
InterlockedAddFloat(orientationDeltasAsInt, index, 1, delta.y);
InterlockedAddFloat(orientationDeltasAsInt, index, 2, delta.z);
InterlockedAddFloat(orientationDeltasAsInt, index, 3, delta.w);
InterlockedAdd(orientationConstraintCounts[index], 1);
}
// non-atomic versions:
void AddPositionDelta(in int index, in float4 delta)
{
deltasAsInt[index] = asuint(delta + asfloat(deltasAsInt[index]));
positionConstraintCounts[index]++;
}
void AddOrientationDelta(in int index, in quaternion delta)
{
orientationDeltasAsInt[index] = asuint(delta + asfloat(orientationDeltasAsInt[index]));
orientationConstraintCounts[index]++;
}
// applying deltas:
void ApplyPositionDelta(RWStructuredBuffer<float4> positions, in int index, in float SOR)
{
int count = positionConstraintCounts[index];
if (count > 0)
{
positions[index].xyz += float3(asfloat(deltasAsInt[index].x),
asfloat(deltasAsInt[index].y),
asfloat(deltasAsInt[index].z)) * SOR / count;
deltasAsInt[index] = uint4(0, 0, 0, 0);
positionConstraintCounts[index] = 0;
}
}
void ApplyOrientationDelta(RWStructuredBuffer<quaternion> orientations, in int index, in float SOR)
{
int count = orientationConstraintCounts[index];
if (count > 0)
{
orientations[index] += quaternion(asfloat(orientationDeltasAsInt[index].x),
asfloat(orientationDeltasAsInt[index].y),
asfloat(orientationDeltasAsInt[index].z),
asfloat(orientationDeltasAsInt[index].w)) * SOR / count;
orientations[index] = normalize(orientations[index]);
orientationDeltasAsInt[index] = uint4(0, 0, 0, 0);
orientationConstraintCounts[index] = 0;
}
}
void ApplyUserDataDelta(RWStructuredBuffer<float4> userData, in int index)
{
int count = orientationConstraintCounts[index];
if (count > 0)
{
userData[index] += float4(asfloat(orientationDeltasAsInt[index].x),
asfloat(orientationDeltasAsInt[index].y),
asfloat(orientationDeltasAsInt[index].z),
asfloat(orientationDeltasAsInt[index].w));
orientationDeltasAsInt[index] = uint4(0, 0, 0, 0);
orientationConstraintCounts[index] = 0;
}
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 1a767b30eef4240859cf6158473bd06a
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,83 +0,0 @@
#pragma kernel Project
#pragma kernel Apply
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<float> restBends;
StructuredBuffer<float2> stiffnesses;
RWStructuredBuffer<float> lambdas;
RWStructuredBuffer<float4> positions;
StructuredBuffer<float> invMasses;
// Variables set from the CPU
uint activeConstraintCount;
float deltaTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p1 = particleIndices[i * 3];
int p2 = particleIndices[i * 3 + 1];
int p3 = particleIndices[i * 3 + 2];
float w1 = invMasses[p1];
float w2 = invMasses[p2];
float w3 = invMasses[p3];
float wsum = w1 + w2 + 2 * w3;
if (wsum > 0)
{
float4 bendVector = positions[p3] - (positions[p1] + positions[p2] + positions[p3]) / 3.0f;
float bend = length(bendVector);
if (bend > 0)
{
float constraint = 1.0f - (stiffnesses[i].x + restBends[i]) / bend;
// remove this to force a certain curvature.
if (constraint >= 0)
{
// calculate time adjusted compliance
float compliance = stiffnesses[i].y / (deltaTime * deltaTime);
// since the third particle moves twice the amount of the other 2, the modulus of its gradient is 2:
float dlambda = (-constraint - compliance * lambdas[i]) / (wsum + compliance + EPSILON);
float4 correction = dlambda * bendVector;
lambdas[i] += dlambda;
float4 delta1 = -correction * 2 * w1;
float4 delta2 = -correction * 2 * w2;
float4 delta3 = correction * 4 * w3;
AddPositionDelta(p1, delta1);
AddPositionDelta(p2, delta2);
AddPositionDelta(p3, delta3);
}
}
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p1 = particleIndices[i * 3];
int p2 = particleIndices[i * 3 + 1];
int p3 = particleIndices[i * 3 + 2];
ApplyPositionDelta(positions, p1, sorFactor);
ApplyPositionDelta(positions, p2, sorFactor);
ApplyPositionDelta(positions, p3, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 92b982b3ba8824f1cb17f9313e0b11ac
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,78 +0,0 @@
#pragma kernel Project
#pragma kernel Apply
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> orientationIndices;
StructuredBuffer<float3> stiffnesses;
StructuredBuffer<float2> plasticity;
RWStructuredBuffer<quaternion> restDarboux;
RWStructuredBuffer<float3> lambdas;
RWStructuredBuffer<quaternion> orientations;
StructuredBuffer<float> invRotationalMasses;
// Variables set from the CPU
uint activeConstraintCount;
float deltaTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int q1 = orientationIndices[i * 2];
int q2 = orientationIndices[i * 2 + 1];
float w1 = invRotationalMasses[q1];
float w2 = invRotationalMasses[q2];
// calculate time adjusted compliance
float3 compliances = stiffnesses[i] / (deltaTime * deltaTime);
// rest and current darboux vectors
quaternion rest = restDarboux[i];
quaternion omega = qmul(q_conj(orientations[q1]), orientations[q2]);
quaternion omega_plus;
omega_plus = omega + rest; //delta Omega with - omega_0
omega -= rest; //delta Omega with + omega_0
if (dot(omega,omega) > dot(omega_plus,omega_plus))
omega = omega_plus;
// plasticity
if (dot(omega.xyz, omega.xyz) > plasticity[i].x * plasticity[i].x)
{
rest += omega * plasticity[i].y * deltaTime;
restDarboux[i] = rest;
}
float3 dlambda = (omega.xyz - compliances * lambdas[i]) / (compliances + w1 + w2 + EPSILON);
//discrete Darboux vector does not have vanishing scalar part
quaternion dlambdaQ = quaternion(dlambda[0], dlambda[1], dlambda[2],0);
AddOrientationDelta(q1, qmul(orientations[q2], dlambdaQ) * w1);
AddOrientationDelta(q2,-qmul(orientations[q1], dlambdaQ) * w2);
lambdas[i] += dlambda;
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int q1 = orientationIndices[i * 2];
int q2 = orientationIndices[i * 2 + 1];
ApplyOrientationDelta(orientations, q1, sorFactor);
ApplyOrientationDelta(orientations, q2, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: e5ad8a2b8563941539b7551b0a93b911
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,39 +0,0 @@
#pragma kernel BitonicSort
const uint numEntries;
const uint groupWidth;
const uint groupHeight;
const uint stepIndex;
RWStructuredBuffer<float> Keys;
RWStructuredBuffer<float> Values;
[numthreads(128,1,1)]
void BitonicSort(uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
uint hIndex = i & (groupWidth - 1);
uint indexLeft = hIndex + (groupHeight + 1) * (i / groupWidth);
uint rightStepSize = stepIndex == 0 ? groupHeight - 2 * hIndex : (groupHeight + 1) / 2;
uint indexRight = indexLeft + rightStepSize;
// Exit if out of bounds (for non-power of 2 input sizes)
if (indexRight >= numEntries) return;
float keyLeft = Keys[indexLeft];
float keyRight = Keys[indexRight];
float valueLeft = Values[indexLeft];
float valueRight = Values[indexRight];
// Swap entries if value is descending
if (valueLeft > valueRight)
{
Keys[indexLeft] = keyRight;
Keys[indexRight] = keyLeft;
Values[indexLeft] = valueRight;
Values[indexRight] = valueLeft;
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ceefe1197724d4a408783ad72b2c56ab
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,127 +0,0 @@
#ifndef BOUNDS_INCLUDE
#define BOUNDS_INCLUDE
#include "Transform.cginc"
#include "Matrix.cginc"
struct aabb
{
float4 min_;
float4 max_;
void FromTriangle(float4 v1, float4 v2, float4 v3, float4 margin)
{
min_ = min(min(v1, v2), v3) - margin;
max_ = max(max(v1, v2), v3) + margin;
}
void FromEdge(float4 v1, float4 v2, float4 radius)
{
min_ = min(v2 - radius, v1 - radius);
max_ = max(v2 + radius, v1 + radius);
}
void FromParticle(float4 v1, float radius)
{
min_ = v1 - radius;
max_ = v1 + radius;
}
bool IntersectsAabb(in aabb b, bool in2D = false)
{
if (in2D)
{
return (min_[0] <= b.max_[0] && max_[0] >= b.min_[0]) &&
(min_[1] <= b.max_[1] && max_[1] >= b.min_[1]);
}
else
{
return (min_[0] <= b.max_[0] && max_[0] >= b.min_[0]) &&
(min_[1] <= b.max_[1] && max_[1] >= b.min_[1]) &&
(min_[2] <= b.max_[2] && max_[2] >= b.min_[2]);
}
}
float AverageAxisLength()
{
float4 d = max_ - min_;
return (d.x + d.y + d.z) * 0.33f;
}
float MaxAxisLength()
{
float4 d = max_ - min_;
return max(max(d.x,d.y),d.z);
}
void EncapsulateParticle(in float4 position, float radius)
{
min_ = min(min(min_, position - radius), position - radius);
max_ = max(max(max_, position + radius), position + radius);
}
void EncapsulateParticle(in float4 previousPosition, in float4 position, float radius)
{
min_ = min(min(min_, position - radius), previousPosition - radius);
max_ = max(max(max_, position + radius), previousPosition + radius);
}
void EncapsulateBounds(in aabb bounds)
{
min_ = min(min_,bounds.min_);
max_ = max(max_,bounds.max_);
}
void Expand(float4 amount)
{
min_ -= amount;
max_ += amount;
}
void Sweep(float4 velocity)
{
min_ = min(min_, min_ + velocity);
max_ = max(max_, max_ + velocity);
}
float4 Center()
{
return (min_ + (max_ - min_) * 0.5f);
}
void Transform(in float4x4 transform)
{
float3 xa = transform._m00_m10_m20 * min_.x;
float3 xb = transform._m00_m10_m20 * max_.x;
float3 ya = transform._m01_m11_m21 * min_.y;
float3 yb = transform._m01_m11_m21 * max_.y;
float3 za = transform._m02_m12_m22 * min_.z;
float3 zb = transform._m02_m12_m22 * max_.z;
min_ = float4(min(xa, xb) + min(ya, yb) + min(za, zb) + transform._m03_m13_m23, 0);
max_ = float4(max(xa, xb) + max(ya, yb) + max(za, zb) + transform._m03_m13_m23, 0);
}
void Transform(in transform transform)
{
Transform(TRS(transform.translation.xyz, transform.rotation, transform.scale.xyz));
}
aabb Transformed(in float4x4 trfm)
{
aabb cpy = this;
cpy.Transform(trfm);
return cpy;
}
aabb Transformed(in transform trfm)
{
aabb cpy = this;
cpy.Transform(trfm);
return cpy;
}
};
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 52c09ed0a553243328693072f77d1465
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,94 +0,0 @@
#pragma kernel RuntimeSimplexBounds SIMPLEX_BOUNDS=RuntimeSimplexBounds USE_COLLISION_MAT
#pragma kernel EditSimplexBounds SIMPLEX_BOUNDS=EditSimplexBounds
#pragma kernel Reduce
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "CollisionMaterial.cginc"
#include "MathUtils.cginc"
#include "Integration.cginc"
#include "SolverParameters.cginc"
StructuredBuffer<int> simplices;
StructuredBuffer<float4> positions;
StructuredBuffer<float4> velocities;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> fluidMaterials;
RWStructuredBuffer<aabb> simplexBounds;
RWStructuredBuffer<aabb> reducedBounds;
float deltaTime;
groupshared aabb sdata[128];
[numthreads(256, 1, 1)]
void SIMPLEX_BOUNDS (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= pointCount + edgeCount + triangleCount)
{
reducedBounds[i].min_ = float4(FLT_MAX,FLT_MAX,FLT_MAX,0);
reducedBounds[i].max_ = -float4(FLT_MAX,FLT_MAX,FLT_MAX,0);
return;
}
int simplexSize;
int simplexStart = GetSimplexStartAndSize(i, simplexSize);
aabb sxBounds, soBounds;
sxBounds.min_ = soBounds.min_ = float4(FLT_MAX,FLT_MAX,FLT_MAX,0);
sxBounds.max_ = soBounds.max_ = float4(-FLT_MAX,-FLT_MAX,-FLT_MAX,0);
for (int j = 0; j < simplexSize; ++j)
{
int p = simplices[simplexStart + j];
#if USE_COLLISION_MAT
int m = collisionMaterialIndices[p];
float solidRadius = principalRadii[p].x + (m >= 0 ? collisionMaterials[m].stickDistance : 0);
#else
float solidRadius = principalRadii[p].x;
#endif
// Expand simplex bounds, using both the particle's original position and its velocity.
// Add collision margin for both fluid neighborhood too (prevents explosions at high pressures due to neighborhood deficiency)
sxBounds.EncapsulateParticle(positions[p],
IntegrateLinear(positions[p], velocities[p], deltaTime * particleCCD),
max(solidRadius, fluidMaterials[p].x * 0.5f) + collisionMargin);
soBounds.EncapsulateParticle(positions[p],
IntegrateLinear(positions[p], velocities[p], deltaTime),
solidRadius);
}
simplexBounds[i] = sxBounds;
reducedBounds[i] = soBounds;
}
[numthreads( 256, 1, 1)]
void Reduce( uint3 threadIdx : SV_GroupThreadID, uint3 groupIdx : SV_GroupID)
{
// each thread loads two elements from global to shared mem and combines them:
unsigned int tid = threadIdx.x;
unsigned int i = groupIdx.x * 256 + tid;
sdata[tid] = reducedBounds[i];
sdata[tid].EncapsulateBounds(reducedBounds[i + 128]);
GroupMemoryBarrierWithGroupSync();
// do reduction in shared mem
for (unsigned int s = 64; s > 0; s >>= 1)
{
if (tid < s)
{
sdata[tid].EncapsulateBounds(sdata[tid + s]);
}
GroupMemoryBarrierWithGroupSync();
}
// write result for this group to global mem
if (tid == 0)
reducedBounds[groupIdx.x] = sdata[0];
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4eaa80a9c435f42fa9837a3594766a1f
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,73 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "DistanceFunctions.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateContacts
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<int> simplices;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
[numthreads(128, 1, 1)]
void GenerateContacts (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4*BOX_SHAPE]) return;
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
int firstPair = contactOffsetsPerType[BOX_SHAPE];
int simplexIndex = contactPairs[firstPair + i].x;
int colliderIndex = contactPairs[firstPair + i].y;
contact c = (contact)0;
Box boxShape;
boxShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
boxShape.s = shapes[colliderIndex];
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
float4 simplexPoint;
SurfacePoint surfacePoint = Optimize(boxShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, simplexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
c.pointB = surfacePoint.pos;
c.normal = surfacePoint.normal * boxShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 9df6755bffe0b4dc5a30f4715e8a503c
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,136 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "QueryDefinitions.cginc"
#include "ContactHandling.cginc"
#include "Transform.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateResults
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<int> simplices;
StructuredBuffer<transform> transforms;
StructuredBuffer<queryShape> shapes;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<queryResult> results;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
struct Box : IDistanceFunction
{
queryShape s;
transform colliderToSolver;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 center = s.center * colliderToSolver.scale;
float4 size = s.size * colliderToSolver.scale * 0.5f;
// clamp the point to the surface of the box:
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
// get minimum distance for each axis:
float4 distances = size - abs(pnt);
if (distances.x >= 0 && distances.y >= 0 && distances.z >= 0)
{
projectedPoint.normal = float4(0,0,0,0);
projectedPoint.pos = pnt;
// find minimum distance in all three axes and the axis index:
if (distances.y < distances.x && distances.y < distances.z)
{
projectedPoint.normal[1] = sign(pnt[1]);
projectedPoint.pos[1] = size[1] * projectedPoint.normal[1];
}
else if (distances.z < distances.x && distances.z < distances.y)
{
projectedPoint.normal[2] = sign(pnt[2]);
projectedPoint.pos[2] = size[2] * projectedPoint.normal[2];
}
else
{
projectedPoint.normal[0] = sign(pnt[0]);
projectedPoint.pos[0] = size[0] * projectedPoint.normal[0];
}
}
else
{
projectedPoint.pos = clamp(pnt, -size, size);
projectedPoint.normal = normalizesafe(pnt - projectedPoint.pos);
}
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(projectedPoint.pos + center + projectedPoint.normal * s.contactOffset);
projectedPoint.normal = colliderToSolver.TransformDirection(projectedPoint.normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
[numthreads(128, 1, 1)]
void GenerateResults (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4*BOX_QUERY]) return;
int firstPair = contactOffsetsPerType[BOX_QUERY];
int simplexIndex = contactPairs[firstPair + i].x;
int queryIndex = contactPairs[firstPair + i].y;
queryResult c = (queryResult)0;
Box boxShape;
boxShape.colliderToSolver = worldToSolver[0].Multiply(transforms[queryIndex]);
boxShape.s = shapes[queryIndex];
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
float4 simplexPoint;
SurfacePoint surfacePoint = Optimize(boxShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, simplexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
float4 simplexPrevPosition = FLOAT4_ZERO;
float simplexRadius = 0;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexPrevPosition += positions[particleIndex] * simplexBary[j];
simplexRadius += EllipsoidRadius(surfacePoint.normal, orientations[particleIndex], principalRadii[particleIndex].xyz) * simplexBary[j];
}
c.queryPoint = surfacePoint.pos;
c.normal = surfacePoint.normal;
c.simplexBary = simplexBary;
c.simplexIndex = simplexIndex;
c.queryIndex = queryIndex;
c.dist = dot(simplexPrevPosition - surfacePoint.pos,surfacePoint.normal) - simplexRadius;
if (c.dist <= boxShape.s.maxDistance)
{
uint count = results.IncrementCounter();
if (count < maxContacts)
{
results[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 409c5f7d65dd84df9853de82bd3fbf46
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,72 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "DistanceFunctions.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateContacts
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<int> simplices;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
[numthreads(128, 1, 1)]
void GenerateContacts (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4*CAPSULE_SHAPE]) return;
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
int firstPair = contactOffsetsPerType[CAPSULE_SHAPE];
int simplexIndex = contactPairs[firstPair + i].x;
int colliderIndex = contactPairs[firstPair + i].y;
contact c = (contact)0;
Capsule capsuleShape;
capsuleShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
capsuleShape.s = shapes[colliderIndex];
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
float4 simplexPoint;
SurfacePoint surfacePoint = Optimize(capsuleShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, simplexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
c.pointB = surfacePoint.pos;
c.normal = surfacePoint.normal * capsuleShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: bd92ed8a5b1bc404c9b114747495f080
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,159 +0,0 @@
#pragma kernel Project
#pragma kernel Apply
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> firstIndex;
StructuredBuffer<int> numIndices;
StructuredBuffer<float2> restLengths;
RWStructuredBuffer<float4> ni; // (ni:constraint gradient, di:desired lenght)
RWStructuredBuffer<float3> diagonals; // (subdiagonals), bi (diagonals) and ci (superdiagonals):
RWStructuredBuffer<float4> positions;
StructuredBuffer<float> invMasses;
// Variables set from the CPU
uint activeConstraintCount;
float deltaTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int c = id.x;
if (c >= activeConstraintCount) return;
int numEdges = numIndices[c] - 1;
int first = firstIndex[c];
float minLength = restLengths[c].x;
float maxLength = restLengths[c].y;
int i;
for (i = 0; i < numEdges; ++i)
{
int edge = first + i;
float4 p1 = positions[particleIndices[edge]];
float4 p2 = positions[particleIndices[edge+1]];
float4 diff = p1 - p2;
float dist = length(diff);
ni[edge] = float4(diff/(dist + EPSILON));
}
// calculate ai, bi and ci
for (i = 0; i < numEdges; ++i)
{
int edge = first + i;
float w_i_ = invMasses[particleIndices[edge]];
float w__i = invMasses[particleIndices[edge+1]];
float4 ni__ = FLOAT4_ZERO;
if (i > 0) ni__ = ni[edge - 1];
float4 n__i = FLOAT4_ZERO;
if (i < numEdges - 1) n__i = ni[edge + 1];
diagonals[edge] = float3(-w_i_ * dot(ni[edge], ni__), // ai
w_i_ + w__i, // bi
-w__i * dot(ni[edge], n__i));// ci
}
// solve step #1, forward sweep:
// reuse diagonals.xy to store sweep results ci_ and di_:
for (i = 0; i < numEdges; ++i)
{
int edge = first + i;
float4 p1 = positions[particleIndices[edge]];
float4 p2 = positions[particleIndices[edge + 1]];
float cip_ = 0;
float dip_ = 0;
if (i > 0)
{
cip_ = diagonals[edge - 1].x;
dip_ = diagonals[edge - 1].y;
}
float3 d = diagonals[edge];
float den = d.y - cip_ * d.x;
if (abs(den) > EPSILON)
{
float dist = distance(p1, p2);
float correction = 0;
if (dist >= maxLength)
correction = dist - maxLength;
else if (dist <= minLength)
correction = dist - minLength;
d.xy = float2(d.z / den, (correction - dip_ * d.x) / den);
}
else
d.xy = float2(0,0);
diagonals[edge] = d;
}
// solve step #2, backward sweep. reuse diagonals.z to store solution xi:
for (i = numEdges - 1; i >= 0; --i)
{
int edge = first + i;
float xi_ = (i < numEdges - 1) ? diagonals[edge + 1].z : 0;
float3 d = diagonals[edge];
d.z = d.y - d.x * xi_;
diagonals[edge] = d;
}
// calculate deltas:
for (i = 0; i < numIndices[c]; ++i)
{
int index = first + i;
float4 ni__ = FLOAT4_ZERO;
float xi_ = 0;
if (i > 0)
{
ni__ = ni[index - 1];
xi_ = diagonals[index - 1].z;
}
float4 n_i_ = FLOAT4_ZERO;
float nxi = 0;
if (i < numIndices[c] - 1)
{
n_i_ = ni[index];
nxi = diagonals[index].z;
}
int p = particleIndices[index];
AddPositionDelta(p, invMasses[p] * (ni__ * xi_ - n_i_ * nxi));
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int first = firstIndex[i];
int last = first + numIndices[i];
for (int k = first; k < last; ++k)
ApplyPositionDelta(positions, particleIndices[k], sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2a5f3e8f1330846819f1abe7a6181c51
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,180 +0,0 @@
#pragma kernel UpdateSkinConstraints
#pragma kernel UpdateClothMesh
#include "MathUtils.cginc"
struct Influence
{
int index;
float weight;
};
struct SkinmapData
{
int firstInfluence;
int firstInfNumber;
int firstParticleBindPose;
int firstSkinWeight;
int firstSkinWeightNumber;
int firstBoneBindPose;
int bindPoseCount;
};
struct SkeletonData
{
int firstBone;
int boneCount;
};
struct MeshData
{
int firstVertex;
int vertexCount;
int firstTriangle;
int triangleCount;
};
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> rendererIndices; // for each vertex/particle, index of its renderer.
StructuredBuffer<float4> renderablePositions;
StructuredBuffer<quaternion> renderableOrientations;
StructuredBuffer<float4> colors;
StructuredBuffer<float4> restPositions;
StructuredBuffer<quaternion> restOrientations;
StructuredBuffer<int> skinConstraintOffsets;
StructuredBuffer<int> skinmapIndices; // for each renderer, index of its skinmap.
StructuredBuffer<int> meshIndices; // for each renderer, index of its mesh.
StructuredBuffer<int> skeletonIndices; // for each renderer, index of its skeleton.
StructuredBuffer<int> particleOffsets; // for each renderer, index of its first particle in the batch.
StructuredBuffer<int> vertexOffsets; // for each renderer, index of its first vertex in the batch.
StructuredBuffer<SkinmapData> skinData;
StructuredBuffer<Influence> influences;
StructuredBuffer<int> influenceOffsets;
StructuredBuffer<float4x4> particleBindMatrices;
StructuredBuffer<float4x4> boneBindMatrices;
StructuredBuffer<SkeletonData> skeletonData;
StructuredBuffer<float3> bonePos;
StructuredBuffer<quaternion> boneRot;
StructuredBuffer<float3> boneScl;
StructuredBuffer<MeshData> meshData;
StructuredBuffer<float3> positions;
StructuredBuffer<float3> normals;
StructuredBuffer<float4> tangents;
RWStructuredBuffer<float4> skinConstraintPoints;
RWStructuredBuffer<float4> skinConstraintNormals;
RWByteAddressBuffer vertices;
// Variables set from the CPU
uint vertexCount;
uint constraintCount;
float4x4 world2Solver;
[numthreads(128, 1, 1)]
void UpdateSkinConstraints (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= constraintCount) return;
int rendererIndex = rendererIndices[i];
// get skin map and mesh data:
SkinmapData skin = skinData[skinmapIndices[rendererIndex]];
SkeletonData skel = skeletonData[skeletonIndices[rendererIndex]];
// invalid skeleton:
if (skel.boneCount <= 0)
return;
// get index of this particle in its original actor:
int originalParticleIndex = i - particleOffsets[rendererIndex];
// get first influence and amount of influences for this particle:
int influenceStart = influenceOffsets[skin.firstSkinWeightNumber + originalParticleIndex];
int influenceCount = influenceOffsets[skin.firstSkinWeightNumber + originalParticleIndex + 1] - influenceStart;
float4 pos = FLOAT4_ZERO;
float4 norm = FLOAT4_ZERO;
for (int k = influenceStart; k < influenceStart + influenceCount; ++k)
{
Influence inf = influences[skin.firstSkinWeight + k];
float4x4 bind = boneBindMatrices[skin.firstBoneBindPose + inf.index];
int boneIndex = skel.firstBone + inf.index;
float4x4 deform = TRS(bonePos[boneIndex], boneRot[boneIndex], boneScl[boneIndex]);
float4x4 trfm = mul(world2Solver, mul(deform, bind));
pos.xyz += mul(trfm, float4(restPositions[particleIndices[i]].xyz, 1)).xyz * inf.weight;
norm.xyz += mul(trfm, float4(rotate_vector(restOrientations[particleIndices[i]], float3(0, 0, 1)), 0)).xyz * inf.weight;
}
int constraintIndex = skinConstraintOffsets[rendererIndex] + originalParticleIndex;
skinConstraintPoints[constraintIndex] = pos;
skinConstraintNormals[constraintIndex] = norm;
}
[numthreads(128, 1, 1)]
void UpdateClothMesh (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= vertexCount) return;
int rendererIndex = rendererIndices[i];
// get skin map and mesh data:
SkinmapData skin = skinData[skinmapIndices[rendererIndex]];
MeshData mesh = meshData[meshIndices[rendererIndex]];
// get index of this vertex in its original mesh:
int originalVertexIndex = i - vertexOffsets[rendererIndex];
// get index of the vertex in the mesh batch:
int batchedVertexIndex = mesh.firstVertex + originalVertexIndex;
// get first influence and amount of influences for this vertex:
int influenceStart = influenceOffsets[skin.firstInfNumber + originalVertexIndex];
int influenceCount = influenceOffsets[skin.firstInfNumber + originalVertexIndex + 1] - influenceStart;
float3 position = float3(0,0,0);
float3 normal = float3(0,0,0);
float4 tangent = FLOAT4_ZERO;
float4 color = FLOAT4_ZERO;
for (int k = influenceStart; k < influenceStart + influenceCount; ++k)
{
Influence inf = influences[skin.firstInfluence + k];
int p = particleIndices[particleOffsets[rendererIndex] + inf.index];
float4x4 deform = mul(m_translate(FLOAT4X4_IDENTITY,renderablePositions[p].xyz), q_toMatrix(renderableOrientations[p]));
float4x4 trfm = mul(deform, particleBindMatrices[skin.firstParticleBindPose + inf.index]);
// update vertex/normal/tangent:
position += mul(trfm, float4(positions[batchedVertexIndex], 1)).xyz * inf.weight;
normal += mul(trfm, float4(normals[batchedVertexIndex], 0)).xyz * inf.weight;
tangent += float4(mul(trfm, float4(tangents[batchedVertexIndex].xyz, 0)).xyz, tangents[batchedVertexIndex].w) * inf.weight;
color += colors[p] * inf.weight;
}
int base = i * 14;
vertices.Store3( base<<2, asuint(position));
vertices.Store3((base + 3)<<2, asuint(normal));
vertices.Store4((base + 6)<<2, asuint(tangent));
vertices.Store4((base + 10)<<2, asuint(color));
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 7620e03e082f448828570f6659b73301
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,219 +0,0 @@
#include "ContactHandling.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
#include "Simplex.cginc"
#include "CollisionMaterial.cginc"
#include "AtomicDeltas.cginc"
#pragma kernel Clear
#pragma kernel Initialize
#pragma kernel Project
#pragma kernel Apply
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> simplices;
StructuredBuffer<float> invMasses;
StructuredBuffer<float> invRotationalMasses;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<float4> prevOrientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
RWStructuredBuffer<rigidbody> RW_rigidbodies;
RWStructuredBuffer<float4> positions;
RWStructuredBuffer<float4> orientations;
RWStructuredBuffer<float4> deltas;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<contactMasses> effectiveMasses;
StructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<inertialFrame> inertialSolverFrame;
// Variables set from the CPU
uint particleCount;
float maxDepenetration;
float substepTime;
float stepTime;
int steps;
float timeLeft;
float sorFactor;
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int rigidbodyIndex = shapes[contacts[i].bodyB].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
int orig;
InterlockedExchange(RW_rigidbodies[rigidbodyIndex].constraintCount, 0, orig);
}
}
[numthreads(128, 1, 1)]
void Initialize (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int simplexSizeA;
int simplexStartA = GetSimplexStartAndSize(contacts[i].bodyA, simplexSizeA);
// get the material from the first particle in the simplex:
int aMaterialIndex = collisionMaterialIndices[simplices[simplexStartA]];
bool rollingContacts = aMaterialIndex >= 0 ? collisionMaterials[aMaterialIndex].rollingContacts > 0 : false;
float4 relativeVelocity = FLOAT4_ZERO;
float4 simplexPrevPosition = FLOAT4_ZERO;
quaternion simplexPrevOrientation = quaternion(0, 0, 0, 0);
float simplexRadius = 0;
float simplexInvMass = 0;
float simplexInvRotationalMass = 0;
for (int j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
relativeVelocity += velocities[particleIndex] * contacts[i].pointA[j];
simplexPrevPosition += prevPositions[particleIndex] * contacts[i].pointA[j];
simplexPrevOrientation += prevOrientations[particleIndex] * contacts[i].pointA[j];
simplexInvMass += invMasses[particleIndex] * contacts[i].pointA[j];
simplexInvRotationalMass += invRotationalMasses[particleIndex] * contacts[i].pointA[j];
simplexRadius += EllipsoidRadius(contacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * contacts[i].pointA[j];
}
// if there's a rigidbody present, subtract its velocity from the relative velocity:
int rigidbodyIndex = shapes[contacts[i].bodyB].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
// Note: unlike rA, that is expressed in solver space, rB is expressed in world space.
relativeVelocity -= GetRigidbodyVelocityAtPoint(RW_rigidbodies[rigidbodyIndex], contacts[i].pointB,
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]);
int bMaterialIndex = shapes[contacts[i].bodyB].materialIndex;
rollingContacts = rollingContacts | (bMaterialIndex >= 0 ? collisionMaterials[bMaterialIndex].rollingContacts > 0 : false);
}
// update contact distance
contacts[i].dist = dot(simplexPrevPosition - contacts[i].pointB, contacts[i].normal) - simplexRadius;
// calculate contact point in A's surface:
float4 contactPoint = contacts[i].pointB + contacts[i].normal * contacts[i].dist;
// update contact basis:
CalculateBasis(relativeVelocity, contacts[i].normal, contacts[i].tangent);
// calculate A's contact mass.
float4 invInertiaTensor = 1.0/(GetParticleInertiaTensor(simplexRadius, simplexInvRotationalMass) + FLOAT4_EPSILON);
CalculateContactMassesA(simplexInvMass, invInertiaTensor, simplexPrevPosition, simplexPrevOrientation, contactPoint, rollingContacts, contacts[i].normal, contacts[i].tangent, GetBitangent(contacts[i]), effectiveMasses[i].normalInvMassA, effectiveMasses[i].tangentInvMassA, effectiveMasses[i].bitangentInvMassA);
// clear B's contact mass.
if (rigidbodyIndex >= 0)
{
CalculateContactMassesB(RW_rigidbodies[rigidbodyIndex], inertialSolverFrame[0].frame, contacts[i].pointB, contacts[i].normal, contacts[i].tangent, GetBitangent(contacts[i]), effectiveMasses[i].normalInvMassB, effectiveMasses[i].tangentInvMassB, effectiveMasses[i].bitangentInvMassB);
InterlockedAdd(RW_rigidbodies[rigidbodyIndex].constraintCount, 1);
}
else
{
ClearContactMasses(effectiveMasses[i].normalInvMassB, effectiveMasses[i].tangentInvMassB, effectiveMasses[i].bitangentInvMassB);
}
}
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
// Skip contacts involving triggers:
if (shapes[contacts[i].bodyB].isTrigger())
return;
int simplexSize;
int simplexStart = GetSimplexStartAndSize(contacts[i].bodyA, simplexSize);
int colliderIndex = contacts[i].bodyB;
// Get the rigidbody index (might be < 0, in that case there's no rigidbody present)
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
float frameEnd = stepTime * steps;
float substepsToEnd = timeLeft / substepTime;
// Combine collision materials (use material from first particle in simplex)
collisionMaterial material = CombineCollisionMaterials(collisionMaterialIndices[simplices[simplexStart]], shapes[colliderIndex].materialIndex);
// Get relative velocity at contact point.
// As we do not consider true ellipses for collision detection, particle contact points are never off-axis.
// So particle angular velocity does not contribute to normal impulses, and we can skip it.
float4 simplexPosition = FLOAT4_ZERO;
float4 simplexPrevPosition = FLOAT4_ZERO;
float simplexRadius = 0;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexPosition += positions[particleIndex] * contacts[i].pointA[j];
simplexPrevPosition += prevPositions[particleIndex] * contacts[i].pointA[j];
simplexRadius += EllipsoidRadius(contacts[i].normal, orientations[particleIndex], principalRadii[particleIndex].xyz) * contacts[i].pointA[j];
}
// project position to the end of the full step:
float4 posA = lerp(simplexPrevPosition, simplexPosition, substepsToEnd);
posA += -contacts[i].normal * simplexRadius;
float4 posB = contacts[i].pointB;
int rbContacts = 1;
if (rigidbodyIndex >= 0)
{
posB += GetRigidbodyVelocityAtPoint(rigidbodies[rigidbodyIndex], contacts[i].pointB,
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]) * frameEnd;
rbContacts = rigidbodies[rigidbodyIndex].constraintCount;
}
float normalInvMass = effectiveMasses[i].normalInvMassA + effectiveMasses[i].normalInvMassB * rbContacts;
float lambda = SolveAdhesion(contacts[i], normalInvMass, posA, posB, material.stickDistance, material.stickiness, stepTime);
lambda += SolvePenetration(contacts[i], normalInvMass, posA, posB, maxDepenetration * stepTime);
if (abs(lambda) > EPSILON)
{
float4 delta = lambda * contacts[i].normal * BaryScale(contacts[i].pointA) / substepsToEnd;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
float4 delta1 = delta * invMasses[particleIndex] * contacts[i].pointA[j];
AtomicAddPositionDelta(particleIndex, delta1);
}
if (rigidbodyIndex >= 0)
{
ApplyImpulse(rigidbodyIndex, -lambda / frameEnd * contacts[i].normal, contacts[i].pointB, inertialSolverFrame[0].frame);
}
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= particleCount) return;
int p = particleIndices[threadIndex];
ApplyPositionDelta(positions, p, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ff4c3e041ffef4a5c91cf275735a7ecc
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,81 +0,0 @@
#ifndef COLLIDERDEFS_INCLUDE
#define COLLIDERDEFS_INCLUDE
#define SPHERE_SHAPE 0
#define BOX_SHAPE 1
#define CAPSULE_SHAPE 2
#define HEIGHTMAP_SHAPE 3
#define TRIANGLE_MESH_SHAPE 4
#define EDGE_MESH_SHAPE 5
#define SDF_SHAPE 6
#define FORCEMODE_FORCE 0
#define FORCEMODE_ACCEL 1
#define FORCEMODE_WIND 2
#define DAMPDIR_ALL 0
#define DAMPDIR_FORCE 1
#define DAMPDIR_SURFACE 2
#define ZONETYPE_DIRECTIONAL 0
#define ZONETYPE_RADIAL 1
#define ZONETYPE_VORTEX 2
#define ZONETYPE_VOID 3
struct shape
{
float4 center;
float4 size; /**< box: size of the box in each axis.
sphere: radius of sphere (x,y,z),
capsule: radius (x), height(y), direction (z, can be 0, 1 or 2).
heightmap: width (x axis), height (y axis) and depth (z axis) in world units.*/
uint type; /**< Sphere = 0,
Box = 1,
Capsule = 2,
Heightmap = 3,
TriangleMesh = 4,
EdgeMesh = 5,
SignedDistanceField = 6*/
float contactOffset;
int dataIndex;
int rigidbodyIndex; // index of the associated rigidbody in the collision world.
int materialIndex; // index of the associated material in the collision world.
int forceZoneIndex; // index of the associated force zone in the collision world.
int phase;
int flags; // first bit whether the collider is 2D (1) or 3D (0), second bit whether it's a trigger (1) or regular collider (0).
// third bit determines whether shape is inverted or not.
bool is2D()
{
return (flags & 1) != 0;
}
bool isTrigger()
{
// TODO: using bools doesn't work... why?
int a = (flags & 1 << 1) != 0;
int b = forceZoneIndex >= 0;
return a || b;
}
float isInverted()
{
return (flags & 1 << 2) != 0 ? -1 : 1;
}
};
struct forceZone
{
float4 color;
uint type;
uint mode;
uint dampingDir;
float intensity;
float minDistance;
float maxDistance;
float falloffPower;
float damping;
};
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: fe486a46344cd4c299b4475a4e046796
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,193 +0,0 @@
#include "ContactHandling.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
#include "Simplex.cginc"
#include "CollisionMaterial.cginc"
#include "Integration.cginc"
#include "AtomicDeltas.cginc"
#include "FluidKernels.cginc"
#pragma kernel Project
#pragma kernel Apply
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> simplices;
StructuredBuffer<float> invMasses;
StructuredBuffer<float> invRotationalMasses;
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<quaternion> prevOrientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
RWStructuredBuffer<float4> RW_positions;
RWStructuredBuffer<quaternion> RW_orientations;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<contactMasses> effectiveMasses;
StructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> solverToWorld;
StructuredBuffer<inertialFrame> inertialSolverFrame;
// Variables set from the CPU
uint particleCount;
float substepTime;
float stepTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
// Skip contacts involving triggers:
if (shapes[contacts[i].bodyB].isTrigger())
return;
int simplexSize;
int simplexStart = GetSimplexStartAndSize(contacts[i].bodyA, simplexSize);
int colliderIndex = contacts[i].bodyB;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
// Combine collision materials (use material from first particle in simplex)
collisionMaterial material = CombineCollisionMaterials(collisionMaterialIndices[simplices[simplexStart]], shapes[colliderIndex].materialIndex);
// Calculate relative velocity:
float4 rA = float4(0,0,0,0), rB = float4(0,0,0,0);
float4 prevPositionA = float4(0,0,0,0);
float4 linearVelocityA = float4(0,0,0,0);
float4 angularVelocityA = float4(0,0,0,0);
float invRotationalMassA = 0;
quaternion orientationA = quaternion(0, 0, 0, 0);
float simplexRadiusA = 0;
int j = 0;
for (j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
prevPositionA += prevPositions[particleIndex] * contacts[i].pointA[j];
linearVelocityA += DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * contacts[i].pointA[j];
angularVelocityA += DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * contacts[i].pointA[j];
invRotationalMassA += invRotationalMasses[particleIndex] * contacts[i].pointA[j];
orientationA += orientations[particleIndex] * contacts[i].pointA[j];
simplexRadiusA += EllipsoidRadius(contacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * contacts[i].pointA[j];
}
float4 relativeVelocity = linearVelocityA;
// Add particle angular velocity if rolling contacts are enabled:
if (material.rollingContacts > 0)
{
rA = -contacts[i].normal * simplexRadiusA; // for fluid particles:
relativeVelocity += float4(cross(angularVelocityA.xyz, rA.xyz), 0); //* (Poly6(contacts[i].dist,simplexRadiusA*6) / Poly6(0,simplexRadiusA*6));
}
// Subtract rigidbody velocity:
int rbContacts = 1;
if (rigidbodyIndex >= 0)
{
// Note: unlike rA, that is expressed in solver space, rB is expressed in world space.
rB = solverToWorld[0].TransformPoint(contacts[i].pointB) - rigidbodies[rigidbodyIndex].com;
relativeVelocity -= GetRigidbodyVelocityAtPoint(rigidbodies[rigidbodyIndex], contacts[i].pointB,
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]);
rbContacts = rigidbodies[rigidbodyIndex].constraintCount;
}
// Determine impulse magnitude:
float tangentMass = effectiveMasses[i].tangentInvMassA + effectiveMasses[i].tangentInvMassB * rbContacts;
float bitangentMass = effectiveMasses[i].bitangentInvMassA + effectiveMasses[i].bitangentInvMassB * rbContacts;
float2 impulses = SolveFriction(contacts[i], tangentMass, bitangentMass, relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime);
if (abs(impulses.x) > EPSILON || abs(impulses.y) > EPSILON)
{
float4 tangentImpulse = impulses.x * contacts[i].tangent;
float4 bitangentImpulse = impulses.y * GetBitangent(contacts[i]);
float4 totalImpulse = tangentImpulse + bitangentImpulse;
float baryScale = BaryScale(contacts[i].pointA);
for (j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
float4 delta1 = (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].bitangentInvMassA) * substepTime * contacts[i].pointA[j] * baryScale;
AtomicAddPositionDelta(particleIndex, delta1);
}
if (rigidbodyIndex >= 0)
{
ApplyImpulse(rigidbodyIndex, -totalImpulse, contacts[i].pointB, inertialSolverFrame[0].frame);
}
// Rolling contacts:
if (material.rollingContacts > 0)
{
// Calculate angular velocity deltas due to friction impulse:
float4 invInertiaTensor = 1.0/(GetParticleInertiaTensor(simplexRadiusA, invRotationalMassA) + FLOAT4_EPSILON);
float4x4 solverInertiaA = TransformInertiaTensor(invInertiaTensor, orientationA);
float4 angVelDeltaA = mul(solverInertiaA, float4(cross(rA.xyz, totalImpulse.xyz), 0));
float4 angVelDeltaB = FLOAT4_ZERO;
// Final angular velocities, after adding the deltas:
angularVelocityA += angVelDeltaA;
float4 angularVelocityB = FLOAT4_ZERO;
// Calculate weights (inverse masses):
float invMassA = length(mul(solverInertiaA, normalizesafe(angularVelocityA)));
float invMassB = 0;
if (rigidbodyIndex >= 0)
{
angVelDeltaB = mul(-rigidbodies[rigidbodyIndex].inverseInertiaTensor, float4(cross(rB.xyz, totalImpulse.xyz), 0));
angularVelocityB = rigidbodies[rigidbodyIndex].angularVelocity + angVelDeltaB;
invMassB = length(mul(rigidbodies[rigidbodyIndex].inverseInertiaTensor, normalizesafe(angularVelocityB))) * rbContacts;
}
// Calculate rolling axis and angular velocity deltas:
float4 rollAxis = FLOAT4_ZERO;
float rollingImpulse = SolveRollingFriction(contacts[i], angularVelocityA, angularVelocityB, material.rollingFriction, invMassA, invMassB, rollAxis);
angVelDeltaA += rollAxis * rollingImpulse * invMassA;
angVelDeltaB -= rollAxis * rollingImpulse * invMassB;
// Apply orientation delta to particles:
quaternion orientationDelta = AngularVelocityToSpinQuaternion(orientationA, angVelDeltaA, substepTime);
for (j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
AtomicAddOrientationDelta(particleIndex, orientationDelta);
}
// Apply angular velocity delta to rigidbody:
if (rigidbodyIndex >= 0)
{
AtomicAddAngularDelta(rigidbodyIndex, angVelDeltaB);
}
}
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= particleCount) return;
int p = particleIndices[threadIndex];
ApplyPositionDelta(RW_positions, p, sorFactor);
ApplyOrientationDelta(RW_orientations, p, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ec52fd9d30c594294a7c2f0f50cc6096
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,478 +0,0 @@
#include "GridUtils.cginc"
#include "CollisionMaterial.cginc"
#include "ContactHandling.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "AtomicDeltas.cginc"
#include "Phases.cginc"
#define MAX_CONTACTS_PER_SIMPLEX 64
#pragma kernel Clear
#pragma kernel BuildUnsortedList
#pragma kernel FindPopulatedLevels
#pragma kernel SortList
#pragma kernel BuildContactList
#pragma kernel PrefixSumColliderCounts
#pragma kernel SortContactPairs
#pragma kernel ApplyForceZones
#pragma kernel WriteForceZoneResults
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float> invMasses;
StructuredBuffer<float4> velocities;
RWStructuredBuffer<float4> externalForces;
RWStructuredBuffer<float4> wind;
RWStructuredBuffer<float4> colors;
RWStructuredBuffer<float> life;
StructuredBuffer<int> activeParticles;
StructuredBuffer<int> simplices;
StructuredBuffer<int> filters;
RWStructuredBuffer<aabb> simplexBounds; // bounding box of each simplex.
StructuredBuffer<aabb> aabbs;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
StructuredBuffer<forceZone> forceZones;
RWStructuredBuffer<uint> sortedColliderIndices;
RWStructuredBuffer<uint> colliderTypeCounts;
RWStructuredBuffer<uint> contactOffsetsPerType;
RWStructuredBuffer<uint2> unsortedContactPairs;
RWStructuredBuffer<uint> cellIndices;
RWStructuredBuffer<uint> cellOffsets;
RWStructuredBuffer<uint> cellCounts;
RWStructuredBuffer<uint> offsetInCells;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint2> contactPairs;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> solverToWorld;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
uint colliderCount; // amount of colliders in the grid.
uint cellsPerCollider; // max amount of cells a collider can be inserted into. Typically this is 8.
int shapeTypeCount; // number of different collider shapes, ie: box, sphere, sdf, etc.
uint particleCount;
float deltaTime;
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i == 0)
{
for (int l = 0; l <= GRID_LEVELS; ++l)
levelPopulation[l] = 0;
}
// clear all cell offsets to invalid, so that we can later use atomic minimum to calculate the offset.
if (i < maxCells)
{
cellOffsets[i] = INVALID;
cellCounts[i] = 0;
}
// clear all cell indices to invalid.
if (i < colliderCount)
{
for (uint j = 0; j < cellsPerCollider; ++j)
cellIndices[i*cellsPerCollider+j] = INVALID;
}
}
[numthreads(128, 1, 1)]
void BuildUnsortedList (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= colliderCount) return;
aabb bounds = aabbs[i];
int rb = shapes[i].rigidbodyIndex;
// Expand bounds by rigidbody's linear velocity
// (check against out of bounds rigidbody access, can happen when a destroyed collider references a rigidbody that has just been destroyed too)
if (rb >= 0)// && rb < rigidbodies.Length)
bounds.Sweep(rigidbodies[rb].velocity * deltaTime);
// Expand bounds by collision material's stick distance:
if (shapes[i].materialIndex >= 0)
bounds.Expand(collisionMaterials[shapes[i].materialIndex].stickDistance);
// calculate bounds size, grid level and cell size:
float4 size = bounds.max_ - bounds.min_;
float maxSize = max(max (size.x, size.y), size.z);
int level = GridLevelForSize(maxSize);
float cellSize = CellSizeOfLevel(level);
// calculate max and min cell coordinates (force 4th component to zero, might not be after expanding)
int4 minCell = floor(bounds.min_ / cellSize);
int4 maxCell = floor(bounds.max_ / cellSize);
minCell[3] = 0;
maxCell[3] = 0;
// if the collider is 2D, project it to the z = 0 cells.
if (shapes[i].is2D())
{
minCell[2] = 0;
maxCell[2] = 0;
}
int4 cellSpan = maxCell - minCell;
// insert collider in cells:
for (int x = 0; x <= cellSpan[0]; ++x)
{
for (int y = 0; y <= cellSpan[1]; ++y)
{
for (int z = 0; z <= cellSpan[2]; ++z)
{
int cellIndex = GridHash(minCell + int4(x, y, z, level));
// calculate flat index of this cell into arrays:
int k = x + y*2 + z*4 + i*cellsPerCollider;
cellIndices[k] = cellIndex;
InterlockedAdd(cellCounts[cellIndex],1,offsetInCells[k]);
}
}
}
// atomically increase this level's population by one:
InterlockedAdd(levelPopulation[1 + level],1);
}
[numthreads(128, 1, 1)]
void SortList (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= colliderCount * cellsPerCollider) return;
uint cellIndex = cellIndices[i];
if (cellIndex != INVALID)
{
// write collider to its sorted index:
uint sortedIndex = cellOffsets[cellIndex] + offsetInCells[i];
sortedColliderIndices[sortedIndex] = i;
}
}
[numthreads(128, 1, 1)]
void BuildContactList (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= pointCount + edgeCount + triangleCount) return;
uint cellCount = colliderCount * cellsPerCollider;
int candidateCount = 0;
uint candidates[MAX_CONTACTS_PER_SIMPLEX];
int simplexSize;
int simplexStart = GetSimplexStartAndSize(threadIndex, simplexSize);
aabb b = simplexBounds[threadIndex].Transformed(solverToWorld[0]);
// max size of the particle bounds in cells:
int4 maxSize = int4(10,10,10,10);
// build a list of candidate colliders:
for (uint m = 1; m <= levelPopulation[0]; ++m)
{
uint l = levelPopulation[m];
float cellSize = CellSizeOfLevel(l);
int4 minCell = floor(b.min_ / cellSize);
int4 maxCell = floor(b.max_ / cellSize);
maxCell = minCell + min(maxCell - minCell, maxSize);
for (int x = minCell[0]; x <= maxCell[0]; ++x)
{
for (int y = minCell[1]; y <= maxCell[1]; ++y)
{
// for 2D mode, project each cell at z == 0 and check them too. This way we ensure 2D colliders
// (which are inserted in cells with z == 0) are accounted for in the broadphase.
if (mode == 1)
{
uint flatCellIndex = GridHash(int4(x,y,0,l));
uint cellStart = cellOffsets[flatCellIndex];
uint cellCount = cellCounts[flatCellIndex];
// iterate through colliders in the neighbour cell
for (uint n = cellStart; n < cellStart + cellCount; ++n)
{
// sorted insert into the candidates list:
if (candidateCount < MAX_CONTACTS_PER_SIMPLEX)
candidates[candidateCount++] = sortedColliderIndices[n] / cellsPerCollider;
}
}
for (int z = minCell[2]; z <= maxCell[2]; ++z)
{
uint flatCellIndex = GridHash(int4(x,y,z,l));
uint cellStart = cellOffsets[flatCellIndex];
uint cellCount = cellCounts[flatCellIndex];
// iterate through colliders in the neighbour cell
for (uint n = cellStart; n < cellStart + cellCount; ++n)
{
if (candidateCount < MAX_CONTACTS_PER_SIMPLEX)
candidates[candidateCount++] = sortedColliderIndices[n] / cellsPerCollider;
}
}
}
}
}
//evaluate candidates and create contacts:
if (candidateCount > 0)
{
// insert sort:
for (int k = 1; k < candidateCount; ++k)
{
uint key = candidates[k];
int j = k - 1;
while (j >= 0 && candidates[j] > key)
candidates[j + 1] = candidates[j--];
candidates[j + 1] = key;
}
// make sure each candidate only shows up once in the list:
int first = 0, contactCount = 0;
while(++first != candidateCount)
{
if (candidates[contactCount] != candidates[first])
candidates[++contactCount] = candidates[first];
}
contactCount++;
// append contacts:
for (int i = 0; i < contactCount; i++)
{
int c = candidates[i];
aabb colliderBoundsWS = aabbs[c];
int rb = shapes[c].rigidbodyIndex;
// Expand bounds by rigidbody's linear velocity:
if (rb >= 0)
colliderBoundsWS.Sweep(rigidbodies[rb].velocity * deltaTime);
// Expand bounds by collision material's stick distance:
if (shapes[c].materialIndex >= 0)
colliderBoundsWS.Expand(collisionMaterials[shapes[c].materialIndex].stickDistance);
// check if any simplex particle and the collider should collide:
bool shouldCollide = false;
int colliderCategory = shapes[c].phase & CategoryMask;
int colliderMask = (shapes[c].phase & MaskMask) >> 16;
for (int j = 0; j < simplexSize; ++j)
{
int simplexCategory = filters[simplices[simplexStart + j]] & CategoryMask;
int simplexMask = (filters[simplices[simplexStart + j]] & MaskMask) >> 16;
shouldCollide = shouldCollide || ((simplexCategory & colliderMask) != 0 && (simplexMask & colliderCategory) != 0);
}
if (shouldCollide && b.IntersectsAabb(colliderBoundsWS, mode == 1))
{
uint count;
InterlockedAdd(dispatchBuffer[7], 1, count);
// technically incorrect, as number of pairs != number of contacts but
// we will ignore either excess pairs or contacts.
if (count < maxContacts)
{
// increment the amount of contacts for this shape type:
InterlockedAdd(colliderTypeCounts[shapes[c].type],1);
// enqueue a new contact pair:
unsortedContactPairs[count] = uint2(threadIndex,c);
InterlockedMax(dispatchBuffer[4],(count + 1) / 128 + 1);
}
}
}
}
}
[numthreads(1, 1, 1)]
void PrefixSumColliderCounts (uint3 id : SV_DispatchThreadID)
{
contactOffsetsPerType[0] = 0;
int i;
for (i = 0; i < shapeTypeCount; ++i)
{
contactOffsetsPerType[i+1] = contactOffsetsPerType[i] + colliderTypeCounts[i];
// write amount of pairs per collider type in the dispatch buffer:
dispatchBuffer[8 + i*4] = colliderTypeCounts[i] / 128 + 1;
dispatchBuffer[8 + i*4 + 3] = colliderTypeCounts[i];
}
}
[numthreads(128, 1, 1)]
void SortContactPairs (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= dispatchBuffer[7] || i >= maxContacts) return;
uint2 pair = unsortedContactPairs[i];
int shapeType = (int)shapes[pair.y].type;
// decrement amount of pairs for the given collider type:
uint count;
InterlockedAdd(colliderTypeCounts[shapeType],-1, count);
// write the pair directly at its position in the sorted array:
contactPairs[contactOffsetsPerType[shapeType] + count - 1] = pair;
}
void AtomicAddExternalForceDelta(in int index, in float4 delta)
{
InterlockedAddFloat(deltasAsInt, index, 0, delta.x);
InterlockedAddFloat(deltasAsInt, index, 1, delta.y);
InterlockedAddFloat(deltasAsInt, index, 2, delta.z);
}
void AtomicAddWindDelta(in int index, in float4 delta)
{
InterlockedAddFloat(orientationDeltasAsInt, index, 0, delta.x);
InterlockedAddFloat(orientationDeltasAsInt, index, 1, delta.y);
InterlockedAddFloat(orientationDeltasAsInt, index, 2, delta.z);
}
void AtomicAddLifeDelta(in int index, in float delta)
{
InterlockedAddFloat(deltasAsInt, index, 3, delta);
}
[numthreads(128, 1, 1)]
void ApplyForceZones (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int forceZoneIndex = shapes[contacts[i].bodyB].forceZoneIndex;
if (forceZoneIndex >= 0)
{
int simplexSize;
int simplexStart = GetSimplexStartAndSize(contacts[i].bodyA, simplexSize);
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
float dist = -dot(positions[particleIndex] - contacts[i].pointB, contacts[i].normal);
if (dist < 0) continue;
float4 axis = worldToSolver[0].Multiply(transforms[contacts[i].bodyB]).TransformDirection(float4(0, 0, 1, 0));
// calculate falloff region based on min/max distances:
float falloff = 1;
float range = forceZones[forceZoneIndex].maxDistance - forceZones[forceZoneIndex].minDistance;
if (abs(range) > EPSILON)
falloff = pow(saturate((dist - forceZones[forceZoneIndex].minDistance) / range), forceZones[forceZoneIndex].falloffPower);
float forceIntensity = forceZones[forceZoneIndex].intensity * falloff;
float dampIntensity = forceZones[forceZoneIndex].damping * falloff;
// tint particles:
float mix = pow(1 - saturate(forceZones[forceZoneIndex].color.a * falloff),deltaTime);
colors[particleIndex] = lerp(forceZones[forceZoneIndex].color, colors[particleIndex], mix);
// calculate force direction, depending on the type of the force field:
float4 result = FLOAT4_ZERO;
switch (forceZones[forceZoneIndex].type)
{
case ZONETYPE_RADIAL:
result = contacts[i].normal * forceIntensity;
break;
case ZONETYPE_VORTEX:
result = float4(cross(axis.xyz * forceIntensity, contacts[i].normal.xyz),0);
break;
case ZONETYPE_DIRECTIONAL:
result = axis * forceIntensity;
break;
default:
AtomicAddLifeDelta(particleIndex, -forceIntensity * deltaTime);
return;
}
// calculate damping along force direction:
float4 dampingDir;
switch (forceZones[forceZoneIndex].dampingDir)
{
case DAMPDIR_FORCE:
{
float4 forceDir = normalizesafe(result);
result -= forceDir * dot(velocities[particleIndex], forceDir) * dampIntensity;
}
break;
case DAMPDIR_SURFACE:
result -= contacts[i].normal * dot(velocities[particleIndex], contacts[i].normal) * dampIntensity;
break;
default:
result -= velocities[particleIndex] * dampIntensity;
break;
}
if (invMasses[particleIndex] > 0)
{
// here we reuse position and orientation delta buffers as velocity and wind buffers for atomic writes:
switch (forceZones[forceZoneIndex].mode)
{
case FORCEMODE_ACCEL:
AtomicAddExternalForceDelta(particleIndex, result / simplexSize / invMasses[particleIndex]);
break;
case FORCEMODE_FORCE:
AtomicAddExternalForceDelta(particleIndex, result / simplexSize);
break;
case FORCEMODE_WIND:
AtomicAddWindDelta(particleIndex, result / simplexSize);
break;
}
}
}
}
}
[numthreads(128, 1, 1)]
void WriteForceZoneResults (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= particleCount) return;
int p = activeParticles[i];
externalForces[p].xyz += float3(asfloat(deltasAsInt[p].x),
asfloat(deltasAsInt[p].y),
asfloat(deltasAsInt[p].z));
wind[p].xyz += float3(asfloat(orientationDeltasAsInt[p].x),
asfloat(orientationDeltasAsInt[p].y),
asfloat(orientationDeltasAsInt[p].z));
life[p] += asfloat(deltasAsInt[p].w);
deltasAsInt[p] = uint4(0, 0, 0, 0);
orientationDeltasAsInt[p] = uint4(0, 0, 0, 0);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: df152b65921c34856a1ea566d782acc1
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,109 +0,0 @@
#ifndef COLLISIONMATERIAL_INCLUDE
#define COLLISIONMATERIAL_INCLUDE
struct collisionMaterial
{
float dynamicFriction;
float staticFriction;
float rollingFriction;
float stickiness;
float stickDistance;
int frictionCombine;
int stickinessCombine;
int rollingContacts;
};
StructuredBuffer<int> collisionMaterialIndices;
StructuredBuffer<collisionMaterial> collisionMaterials;
collisionMaterial EmptyCollisionMaterial()
{
collisionMaterial m;
m.dynamicFriction = 0;
m.staticFriction = 0;
m.rollingFriction = 0;
m.stickiness = 0;
m.stickDistance = 0;
m.frictionCombine = 0;
m.stickinessCombine = 0;
m.rollingContacts = 0;
return m;
}
collisionMaterial CombineWith(collisionMaterial a, collisionMaterial b)
{
collisionMaterial result;
int frictionCombineMode = max(a.frictionCombine, b.frictionCombine);
int stickCombineMode = max(a.stickinessCombine, b.stickinessCombine);
switch (frictionCombineMode)
{
case 0:
default:
result.dynamicFriction = (a.dynamicFriction + b.dynamicFriction) * 0.5f;
result.staticFriction = (a.staticFriction + b.staticFriction) * 0.5f;
result.rollingFriction = (a.rollingFriction + b.rollingFriction) * 0.5f;
break;
case 1:
result.dynamicFriction = min(a.dynamicFriction, b.dynamicFriction);
result.staticFriction = min(a.staticFriction, b.staticFriction);
result.rollingFriction = min(a.rollingFriction, b.rollingFriction);
break;
case 2:
result.dynamicFriction = a.dynamicFriction * b.dynamicFriction;
result.staticFriction = a.staticFriction * b.staticFriction;
result.rollingFriction = a.rollingFriction * b.rollingFriction;
break;
case 3:
result.dynamicFriction = max(a.dynamicFriction, b.dynamicFriction);
result.staticFriction = max(a.staticFriction, b.staticFriction);
result.rollingFriction = max(a.rollingFriction, b.rollingFriction);
break;
}
switch (stickCombineMode)
{
case 0:
default:
result.stickiness = (a.stickiness + b.stickiness) * 0.5f;
break;
case 1:
result.stickiness = min(a.stickiness, b.stickiness);
break;
case 2:
result.stickiness = a.stickiness * b.stickiness;
break;
case 3:
result.stickiness = max(a.stickiness, b.stickiness);
break;
}
result.stickDistance = max(a.stickDistance, b.stickDistance);
result.rollingContacts = a.rollingContacts | b.rollingContacts;
return result;
}
collisionMaterial CombineCollisionMaterials(int materialA, int materialB)
{
// Combine collision materials:
collisionMaterial combined;
if (materialA >= 0 && materialB >= 0)
combined = CombineWith(collisionMaterials[materialA], collisionMaterials[materialB]);
else if (materialA >= 0)
combined = collisionMaterials[materialA];
else if (materialB >= 0)
combined = collisionMaterials[materialB];
else
combined = EmptyCollisionMaterial();
return combined;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 034a508161c9948969266fd0cbf21988
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,228 +0,0 @@
#ifndef CONTACTHANDLING_INCLUDE
#define CONTACTHANDLING_INCLUDE
#include "MathUtils.cginc"
#include "Transform.cginc"
struct contact // 96 bytes
{
float4 pointA; // point A, expressed as simplex barycentric coords for simplices, as a solver-space position for colliders.
float4 pointB; // point B, expressed as simplex barycentric coords for simplices, as a solver-space position for colliders.
float4 normal; /**< Normal direction. */
float4 tangent; /**< Tangent direction. */
float dist; /** distance between both colliding entities at the beginning of the timestep.*/
float normalLambda;
float tangentLambda;
float bitangentLambda;
float stickLambda;
float rollingFrictionImpulse;
int bodyA;
int bodyB;
};
// 24 bytes
struct contactMasses
{
float normalInvMassA;
float tangentInvMassA;
float bitangentInvMassA;
float normalInvMassB;
float tangentInvMassB;
float bitangentInvMassB;
};
float4 GetBitangent(in contact c)
{
return normalizesafe(float4(cross(c.normal.xyz,c.tangent.xyz),0));
}
void CalculateBasis(in float4 relativeVelocity, in float4 normal, out float4 tangent)
{
tangent = normalizesafe(relativeVelocity - dot(relativeVelocity, normal) * normal);
}
void CalculateContactMassesA(float invMass,
float4 inverseInertiaTensor,
float4 position,
quaternion orientation,
float4 contactPoint,
bool rollingContacts,
float4 normal,
float4 bitangent,
float4 tangent,
out float normalInvMassA,
out float tangentInvMassA,
out float bitangentInvMassA)
{
// initialize inverse linear masses:
normalInvMassA = tangentInvMassA = bitangentInvMassA = invMass;
if (rollingContacts)
{
float4 rA = contactPoint - position;
float4x4 solverInertiaA = TransformInertiaTensor(inverseInertiaTensor, orientation);
normalInvMassA += RotationalInvMass(solverInertiaA, rA, normal);
tangentInvMassA += RotationalInvMass(solverInertiaA, rA, tangent);
bitangentInvMassA += RotationalInvMass(solverInertiaA, rA, bitangent);
}
}
void CalculateContactMassesB(float invMass,
float4 inverseInertiaTensor,
float4 position,
quaternion orientation,
float4 contactPoint,
bool rollingContacts,
float4 normal,
float4 bitangent,
float4 tangent,
out float normalInvMassB,
out float tangentInvMassB,
out float bitangentInvMassB)
{
// initialize inverse linear masses:
normalInvMassB = tangentInvMassB = bitangentInvMassB = invMass;
if (rollingContacts)
{
float4 rB = contactPoint - position;
float4x4 solverInertiaB = TransformInertiaTensor(inverseInertiaTensor, orientation);
normalInvMassB += RotationalInvMass(solverInertiaB, rB, normal);
tangentInvMassB += RotationalInvMass(solverInertiaB, rB, tangent);
bitangentInvMassB += RotationalInvMass(solverInertiaB, rB, bitangent);
}
}
void ClearContactMasses(out float normalInvMass,
out float tangentInvMass,
out float bitangentInvMass)
{
normalInvMass = tangentInvMass = bitangentInvMass = 0;
}
float SolveAdhesion(inout contact c, float normalMass, float4 posA, float4 posB, float stickDistance, float stickiness, float dt)
{
float lambdaChange = 0;
if (normalMass > 0 && stickDistance > 0 && stickiness > 0 && dt > 0)
{
c.dist = dot(posA - posB, c.normal);
// calculate stickiness position correction:
float constraint = stickiness * (1 - max(c.dist / stickDistance, 0)) * dt;
// calculate lambda multiplier:
float dlambda = -constraint / normalMass;
// accumulate lambda:
float newStickinessLambda = min(c.stickLambda + dlambda, 0);
// calculate lambda change and update accumulated lambda:
lambdaChange = newStickinessLambda - c.stickLambda;
c.stickLambda = newStickinessLambda;
}
return lambdaChange;
}
float SolvePenetration(inout contact c, float normalMass, float4 posA, float4 posB, float maxDepenetrationDelta)
{
float lambdaChange = 0;
if (normalMass > 0)
{
//project position delta to normal vector:
c.dist = dot(posA - posB, c.normal);
// calculate max projection distance based on depenetration velocity:
float maxProjection = max(-c.dist - maxDepenetrationDelta, 0);
// calculate lambda multiplier:
float dlambda = -(c.dist + maxProjection) / normalMass;
// accumulate lambda:
float newLambda = max(c.normalLambda + dlambda, 0);
// calculate lambda change and update accumulated lambda:
lambdaChange = newLambda - c.normalLambda;
c.normalLambda = newLambda;
}
return lambdaChange;
}
float2 SolveFriction(inout contact c, float tangentMass, float bitangentMass, float4 relativeVelocity, float staticFriction, float dynamicFriction, float dt)
{
float2 lambdaChange = float2(0,0);
if (tangentMass > 0 && bitangentMass > 0 &&
(dynamicFriction > 0 || staticFriction > 0) && (c.normalLambda > 0 /*|| stickLambda > 0*/))
{
// calculate delta projection on both friction axis:
float tangentPosDelta = dot(relativeVelocity, c.tangent);
float bitangentPosDelta = dot(relativeVelocity, GetBitangent(c));
// calculate friction pyramid limit:
float dynamicFrictionCone = c.normalLambda / dt * dynamicFriction;
float staticFrictionCone = c.normalLambda / dt * staticFriction;
// tangent impulse:
float tangentLambdaDelta = -tangentPosDelta / tangentMass;
float newTangentLambda = c.tangentLambda + tangentLambdaDelta;
if (abs(newTangentLambda) > staticFrictionCone)
newTangentLambda = clamp(newTangentLambda, -dynamicFrictionCone, dynamicFrictionCone);
lambdaChange[0] = newTangentLambda - c.tangentLambda;
c.tangentLambda = newTangentLambda;
// bitangent impulse:
float bitangentLambdaDelta = -bitangentPosDelta / bitangentMass;
float newBitangentLambda = c.bitangentLambda + bitangentLambdaDelta;
if (abs(newBitangentLambda) > staticFrictionCone)
newBitangentLambda = clamp(newBitangentLambda, -dynamicFrictionCone, dynamicFrictionCone);
lambdaChange[1] = newBitangentLambda - c.bitangentLambda;
c.bitangentLambda = newBitangentLambda;
}
return lambdaChange;
}
float SolveRollingFriction(inout contact c,
float4 angularVelocityA,
float4 angularVelocityB,
float rollingFriction,
float invMassA,
float invMassB,
inout float4 rolling_axis)
{
float rolling_impulse_change = 0;
float totalInvMass = invMassA + invMassB;
if (totalInvMass > 0)
{
rolling_axis = normalizesafe(angularVelocityA - angularVelocityB);
float vel1 = dot(angularVelocityA,rolling_axis);
float vel2 = dot(angularVelocityB,rolling_axis);
float relativeVelocity = vel1 - vel2;
float maxImpulse = c.normalLambda * rollingFriction;
float newRollingImpulse = clamp(c.rollingFrictionImpulse - relativeVelocity / totalInvMass, -maxImpulse, maxImpulse);
rolling_impulse_change = newRollingImpulse - c.rollingFrictionImpulse;
c.rollingFrictionImpulse = newRollingImpulse;
}
return rolling_impulse_change;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 28492658e279e4adaab298f430af09a2
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,143 +0,0 @@
#pragma kernel ResetNormals
#pragma kernel UpdateNormals
#pragma kernel UpdateEdgeNormals
#pragma kernel OrientationFromNormals
#include "InterlockedUtils.cginc"
#include "MathUtils.cginc"
StructuredBuffer<int> deformableTriangles;
StructuredBuffer<float2> deformableTriangleUVs;
StructuredBuffer<int> deformableEdges;
StructuredBuffer<float4> renderablePositions;
StructuredBuffer<float4> velocities;
StructuredBuffer<float4> wind;
StructuredBuffer<int> phases;
RWStructuredBuffer<float4> normals;
RWStructuredBuffer<uint4> normalsInt;
RWStructuredBuffer<uint4> tangentsInt;
RWStructuredBuffer<quaternion> renderableOrientations;
// Variables set from the CPU
uint normalsCount;
uint triangleCount;
uint edgeCount;
void AccumulateNormal(in int index, in float4 delta)
{
InterlockedAddFloat(normalsInt, index, 0, delta.x);
InterlockedAddFloat(normalsInt, index, 1, delta.y);
InterlockedAddFloat(normalsInt, index, 2, delta.z);
}
void AccumulateTangent(in int index, in float4 delta)
{
InterlockedAddFloat(tangentsInt, index, 0, delta.x);
InterlockedAddFloat(tangentsInt, index, 1, delta.y);
InterlockedAddFloat(tangentsInt, index, 2, delta.z);
InterlockedAddFloat(tangentsInt, index, 3, delta.w);
}
[numthreads(128, 1, 1)]
void ResetNormals (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= normalsCount) return;
// leave fluid and softbody normalsInt intact.
if ((phases[i] & (int)PHASE_FLUID) == 0 && normals[i].w >= 0)
{
normals[i] = FLOAT4_ZERO;
tangentsInt[i] = asuint(FLOAT4_ZERO);
}
}
[numthreads(128, 1, 1)]
void UpdateNormals (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= triangleCount) return;
int p1 = deformableTriangles[i*3];
int p2 = deformableTriangles[i*3 + 1];
int p3 = deformableTriangles[i*3 + 2];
float2 w1 = deformableTriangleUVs[i*3];
float2 w2 = deformableTriangleUVs[i*3 + 1];
float2 w3 = deformableTriangleUVs[i*3 + 2];
float4 v1 = renderablePositions[p1];
float4 v2 = renderablePositions[p2];
float4 v3 = renderablePositions[p3];
float3 m1 = (v2 - v1).xyz;
float3 m2 = (v3 - v1).xyz;
float2 s = w2 - w1;
float2 t = w3 - w1;
float4 normal = float4(cross(m1, m2), 0);
float4 tangent = FLOAT4_ZERO;
float area = s.x * t.y - t.x * s.y;
if (abs(area) > EPSILON)
{
tangent = float4(t.y * m1.x - s.y * m2.x,
t.y * m1.y - s.y * m2.y,
t.y * m1.z - s.y * m2.z, 0) / area;
}
AccumulateNormal(p1,normal);
AccumulateNormal(p2,normal);
AccumulateNormal(p3,normal);
AccumulateTangent(p1,tangent);
AccumulateTangent(p2,tangent);
AccumulateTangent(p3,tangent);
}
[numthreads(128, 1, 1)]
void UpdateEdgeNormals (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= edgeCount) return;
int p1 = deformableEdges[i * 2];
int p2 = deformableEdges[i * 2 + 1];
float4 edge = renderablePositions[p2] - renderablePositions[p1];
float4 avgWind = (velocities[p1] + velocities[p2]) * 0.5f - (wind[p1] + wind[p2]) * 0.5f;
float denom = dot(edge, edge);
float4 normal = avgWind - (denom < EPSILON ? FLOAT4_ZERO : edge * dot(avgWind, edge) / denom);
AccumulateNormal(p1,normal);
AccumulateNormal(p2,normal);
}
[numthreads(128, 1, 1)]
void OrientationFromNormals (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= normalsCount) return;
if ((phases[i] & (int)PHASE_FLUID) == 0 && normals[i].w >= 0) // not fluid or softbody (no SDF stored))
{
float4 normal = normals[i];
float4 tangent = asfloat(tangentsInt[i]);
if (dot(normal, normal) > EPSILON &&
dot(tangent, tangent) > EPSILON)
{
normals[i] = normalizesafe(normal);
tangentsInt[i] = asuint(normalizesafe(tangent));
// particle orientation from normal/tangent:
renderableOrientations[i] = q_look_at(normals[i].xyz, asfloat(tangentsInt[i]).xyz);
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2444094f2fb2c43cfb9c90b5949f39ec
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,430 +0,0 @@
#pragma kernel UpdateDensities
#pragma kernel Apply
#pragma kernel ApplyPositionDeltas
#pragma kernel CalculateAtmosphere
#pragma kernel ApplyAtmosphere
#pragma kernel AccumulateSmoothPositions
#pragma kernel AccumulateAnisotropy
#pragma kernel AverageAnisotropy
#include "MathUtils.cginc"
#include "Quaternion.cginc"
#include "AtomicDeltas.cginc"
#include "FluidKernels.cginc"
StructuredBuffer<uint> neighbors;
StructuredBuffer<uint> neighborCounts;
StructuredBuffer<int> sortedToOriginal;
StructuredBuffer<float4> sortedPositions;
StructuredBuffer<float4> sortedPrevPositions;
StructuredBuffer<float4> sortedFluidMaterials;
StructuredBuffer<float4> sortedFluidInterface;
StructuredBuffer<float4> sortedPrincipalRadii;
StructuredBuffer<float4> sortedUserData;
StructuredBuffer<float4> sortedFluidData_RO;
RWStructuredBuffer<float4> sortedFluidData;
StructuredBuffer<quaternion> prevOrientations;
StructuredBuffer<float4> wind;
StructuredBuffer<float4> fluidMaterials2;
RWStructuredBuffer<float4> fluidData;
RWStructuredBuffer<float4> positions;
RWStructuredBuffer<float4> prevPositions;
RWStructuredBuffer<float4> orientations;
RWStructuredBuffer<float4> velocities;
RWStructuredBuffer<float4> angularVelocities;
RWStructuredBuffer<float4> userData;
RWStructuredBuffer<float4> normals;
RWStructuredBuffer<float4> massCenters;
RWStructuredBuffer<float4> prevMassCenters;
RWStructuredBuffer<float4> vorticity;
RWStructuredBuffer<float4> vorticityAccelerations;
RWStructuredBuffer<float4> linearAccelerations;
RWStructuredBuffer<float4> linearFromAngular;
RWStructuredBuffer<float4x4> angularDiffusion;
StructuredBuffer<float4> normals_RO;
StructuredBuffer<float4> fluidData_RO;
StructuredBuffer<float4> vorticity_RO;
StructuredBuffer<float4> velocities_RO;
StructuredBuffer<float4> angularVelocities_RO;
StructuredBuffer<float4> linearFromAngular_RO;
RWStructuredBuffer<float4> renderablePositions;
RWStructuredBuffer<quaternion> renderableOrientations;
RWStructuredBuffer<float4> renderableRadii;
StructuredBuffer<float> life;
RWStructuredBuffer<float4x4> anisotropies;
StructuredBuffer<uint> dispatchBuffer;
// Variables set from the CPU
uint maxNeighbors;
float deltaTime;
[numthreads(128, 1, 1)]
void UpdateDensities (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
float4 positionA = sortedPositions[i];
float4 fluidMaterialA = sortedFluidMaterials[i];
// self-contribution:
float avgKernel = Poly6(0,fluidMaterialA.x);
float restVolumeA = pow(abs(sortedPrincipalRadii[i].x * 2),3-mode); // in 2D, mode == 1 so amount of dimensions is 2.
float grad = restVolumeA * Spiky(0,fluidMaterialA.x);
float4 fluidDataA = float4(avgKernel,0,grad,grad*grad);
float4 massCenterA = float4(positionA.xyz, 1) / positionA.w;
float4 prevMassCenterA = float4(sortedPrevPositions[i].xyz, 1) / positionA.w;
float4x4 anisotropyA = (multrnsp4(positionA, sortedPrevPositions[i]) + FLOAT4X4_IDENTITY * 0.001 * sortedPrincipalRadii[i].x * sortedPrincipalRadii[i].x) / positionA.w;
float4 fluidMaterialB;
float4 positionB;
// iterate over neighborhood, calculate density and gradient.
uint count = min(maxNeighbors, neighborCounts[i]);
for (uint j = 0; j < count; ++j)
{
int n = neighbors[maxNeighbors * i + j];
fluidMaterialB = sortedFluidMaterials[n];
positionB = sortedPositions[n];
float dist = length((positionA - positionB).xyz);
float avgKernel = (Poly6(dist,fluidMaterialA.x) + Poly6(dist,fluidMaterialB.x)) * 0.5f;
float restVolumeB = pow(abs(sortedPrincipalRadii[n].x * 2),3-mode);
float grad = restVolumeB * Spiky(dist,fluidMaterialA.x);
fluidDataA += float4(restVolumeB / restVolumeA * avgKernel,0,grad,grad*grad);
// accumulate masses for COMs and moment matrices:
massCenterA += float4(positionB.xyz, 1) / positionB.w;
prevMassCenterA += float4(sortedPrevPositions[n].xyz, 1) / positionB.w;
anisotropyA += (multrnsp4(positionB, sortedPrevPositions[n]) + FLOAT4X4_IDENTITY * 0.001 * sortedPrincipalRadii[n].x * sortedPrincipalRadii[n].x) / positionB.w;
}
// self particle contribution to density and gradient:
fluidDataA[3] += fluidDataA[2] * fluidDataA[2];
// usually, we'd weight density by mass (density contrast formulation) by dividing by invMass. Then, multiply by invMass when
// calculating the state equation (density / restDensity - 1, restDensity = mass / volume, so density * invMass * restVolume - 1
// We end up with density / invMass * invMass * restVolume - 1, invMass cancels out.
float constraint = max(0, fluidDataA[0] * restVolumeA - 1) * fluidMaterialA.w;
// calculate lambda:
fluidDataA[1] = -constraint / (positionA.w * fluidDataA[3] + EPSILON);
// get total neighborhood mass:
float M = massCenterA[3];
massCenterA /= massCenterA[3];
prevMassCenterA /= prevMassCenterA[3];
// update moment:
anisotropyA -= M * multrnsp4(massCenterA, prevMassCenterA);
// extract neighborhood orientation delta:
renderableOrientations[i] = ExtractRotation(anisotropyA, QUATERNION_IDENTITY, 5);
sortedFluidData[i] = fluidDataA;
massCenters[i] = massCenterA;
prevMassCenters[i] = prevMassCenterA;
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
float restVolumeA = pow(abs(sortedPrincipalRadii[i].x * 2),3-mode);
float4 fluidMaterialA = sortedFluidMaterials[i];
float4 positionA = sortedPositions[i];
float4 prevPositionA = sortedPrevPositions[i];
float4 massCenterA = massCenters[i];
float lambdaA = sortedFluidData[i][1];
float4 fluidMaterialB;
float4 fluidInterfaceB;
float4 massCenterB;
float4 positionB;
float4 pressureDelta = FLOAT4_ZERO;
float4 viscVortDelta = FLOAT4_ZERO;
uint count = min(maxNeighbors, neighborCounts[i]);
for (uint j = 0; j < count; ++j)
{
int n = neighbors[maxNeighbors * i + j];
fluidMaterialB = sortedFluidMaterials[n];
massCenterB = massCenters[n];
positionB = sortedPositions[n];
float4 normal = float4((positionA - positionB).xyz,0);
float dist = length(normal);
float restVolumeB = pow(abs(sortedPrincipalRadii[n].x * 2),3-mode);
// calculate lambda correction due to polarity (cohesion):
float cAvg = (Cohesion(dist,fluidMaterialA.x * 1.4) + Cohesion(dist,fluidMaterialB.x * 1.4)) * 0.5;
float st = 0.2 * cAvg * (1 - saturate(abs(fluidMaterialA.y - fluidMaterialB.y))) * (fluidMaterialA.y + fluidMaterialB.y) * 0.5;
float scorrA = -st / (positionA.w * sortedFluidData[i][3] + EPSILON);
float scorrB = -st / (positionB.w * sortedFluidData[n][3] + EPSILON);
float avgGradient = (Spiky(dist,fluidMaterialA.x) + Spiky(dist,fluidMaterialB.x)) * 0.5;
pressureDelta += normal / (dist + EPSILON) * avgGradient * ((lambdaA + scorrA) * restVolumeB + (sortedFluidData[n][1] + scorrB) * restVolumeA);
// viscosity:
float4 viscGoal = float4(massCenterB.xyz + rotate_vector(renderableOrientations[n], (prevPositionA - prevMassCenters[n]).xyz), 0);
viscVortDelta += (viscGoal - positionA) * min(fluidMaterialB.z, fluidMaterialA.z);
}
// viscosity:
float4 viscGoal = float4(massCenterA.xyz + rotate_vector(renderableOrientations[i], (prevPositionA - prevMassCenters[i]).xyz), 0);
viscVortDelta += (viscGoal - positionA) * fluidMaterialA.z;
AddPositionDelta(sortedToOriginal[i], pressureDelta * positionA.w + viscVortDelta / (neighborCounts[i] + 1));
}
[numthreads(128, 1, 1)]
void ApplyPositionDeltas (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int p = sortedToOriginal[i];
ApplyPositionDelta(positions, p, 1);
renderableOrientations[p] = FLOAT4_ZERO;
fluidData[p] = sortedFluidData[i];
}
[numthreads(128, 1, 1)]
void CalculateAtmosphere (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int originalIndex = sortedToOriginal[i];
float4 normal = FLOAT4_ZERO;
float4 linearVel = FLOAT4_ZERO;
float4 curl = FLOAT4_ZERO;
float4 angularCurl = FLOAT4_ZERO;
float4 vorticityDiff = FLOAT4_ZERO;
float4 baroclinityDiff = FLOAT4_ZERO;
float velDiff = 0;
float restVolumeA = pow(abs(sortedPrincipalRadii[i].x * 2),3 - mode);
float4 velocityA = velocities_RO[originalIndex];
float4 angularVelocityA = angularVelocities_RO[originalIndex];
float4 positionA = sortedPositions[i];
float radiiA = sortedFluidMaterials[i].x;
float4 userDataA = sortedUserData[i];
float invDensityA = positionA.w / sortedFluidData_RO[i].x; // density contrast * mass;
float radiiB;
float4 positionB;
float4 velocityB;
float4 angularVelocityB;
uint count = min(maxNeighbors, neighborCounts[i]);
for (uint j = 0; j < count; ++j)
{
int n = neighbors[maxNeighbors * i + j];
float restVolumeB = pow(abs(sortedPrincipalRadii[n].x * 2),3 - mode);
radiiB = sortedFluidMaterials[n].x;
positionB = sortedPositions[n];
// Can't sort velocities as these are calculated *after* constraint projection.
// maybe a pre-sort step before velocity postprocess?
angularVelocityB = angularVelocities_RO[sortedToOriginal[n]];
velocityB = velocities_RO[sortedToOriginal[n]];
float3 relVort = vorticity_RO[originalIndex].xyz - vorticity_RO[sortedToOriginal[n]].xyz;
float3 relAng = angularVelocityA.xyz - angularVelocityB.xyz;
float3 relVel = velocityA.xyz - velocityB.xyz;
float4 d = float4((positionA - positionB).xyz,0);
float dist = length(d);
float avgGradient = (Spiky(dist,radiiA) + Spiky(dist,radiiB)) * 0.5f;
float avgKernel = (Poly6(dist,radiiA) + Poly6(dist,radiiB)) * 0.5f;
float avgNorm = (Poly6(0,radiiA) + Poly6(0,radiiB)) * 0.5;
// property diffusion:
float diffusionSpeed = (sortedFluidInterface[i].w + sortedFluidInterface[n].w) * avgKernel * deltaTime;
float4 userDelta = (sortedUserData[n] - userDataA) * diffusionMask * diffusionSpeed;
userDataA += restVolumeB / restVolumeA * userDelta;
// calculate color field normal:
float radius = (radiiA + radiiB) * 0.5f;
float4 normGrad = d / (dist + EPSILON);
float4 vgrad = normGrad * avgGradient;
normal += vgrad * radius * restVolumeB;
// measure relative velocity for foam generation:
float relVelMag = length(relVel) + EPSILON;
velDiff += relVelMag * (1 - dot(relVel / relVelMag, normGrad.xyz)) * (1 - min(1,dist/(radius + EPSILON)));
// linear vel due to angular velocity:
linearVel += float4(cross(angularVelocityB.xyz, d.xyz) * avgKernel / avgNorm,0);
// micropolar vorticity curls:
curl += float4(cross(relVel, vgrad.xyz) / positionB.w * invDensityA,0);
angularCurl += float4(cross(relVort, vgrad.xyz) / positionB.w * invDensityA,0);
// baroclinity and vorticity diffusion:
baroclinityDiff += float4(relAng * avgKernel / positionB.w * invDensityA, 0);
vorticityDiff += float4(relVort * avgKernel / positionB.w * invDensityA, 0);
}
linearAccelerations[originalIndex] = angularCurl;
vorticityAccelerations[originalIndex] = curl;
linearFromAngular[originalIndex] = linearVel;
angularDiffusion[originalIndex]._m00_m10_m20_m30 = baroclinityDiff;
angularDiffusion[originalIndex]._m01_m11_m21_m31 = vorticityDiff;
fluidData[originalIndex].z = velDiff;
normals[originalIndex] = normal;
userData[originalIndex] = userDataA;
}
[numthreads(128, 1, 1)]
void ApplyAtmosphere (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int originalIndex = sortedToOriginal[i];
float restVolume = pow(abs(sortedPrincipalRadii[i].x * 2),3 - mode);
// particles near the surface should experience drag:
float4 velocityDiff = float4((velocities[originalIndex] - wind[originalIndex]).xyz,0);
velocities[originalIndex] -= sortedFluidInterface[i].x * velocityDiff * max(0, 1 - fluidData_RO[originalIndex].x * restVolume) * deltaTime;
// external ambient pressure along normal:
velocities[originalIndex] += sortedFluidInterface[i].y * normals_RO[originalIndex] * deltaTime;
// angular acceleration due to baroclinity:
angularVelocities[originalIndex] += float4(fluidMaterials2[originalIndex].z * cross(-normals_RO[originalIndex].xyz, -velocityDiff.xyz),0) * deltaTime;
angularVelocities[originalIndex] -= fluidMaterials2[originalIndex].w * angularDiffusion[originalIndex]._m00_m10_m20_m30;
// micropolar vorticity:
velocities[originalIndex] += fluidMaterials2[originalIndex].x * linearAccelerations[originalIndex] * deltaTime;
vorticity[originalIndex] += fluidMaterials2[originalIndex].x * (vorticityAccelerations[originalIndex] * 0.5 - vorticity[originalIndex]) * deltaTime;
vorticity[originalIndex] -= fluidMaterials2[originalIndex].y * angularDiffusion[originalIndex]._m01_m11_m21_m31;
linearAccelerations[originalIndex] = FLOAT4_ZERO;
vorticityAccelerations[originalIndex] = FLOAT4_ZERO;
angularDiffusion[originalIndex] = FLOAT4X4_ZERO;
// we want to add together linear and angular velocity fields and use result to advect particles without modifying either field:
positions[originalIndex] += linearFromAngular_RO[originalIndex] * deltaTime;
prevPositions[originalIndex] += linearFromAngular_RO[originalIndex] * deltaTime;
}
[numthreads(128, 1, 1)]
void AccumulateSmoothPositions (uint3 id : SV_DispatchThreadID)
{
unsigned int p1 = id.x;
if (p1 >= dispatchBuffer[3]) return;
anisotropies[p1] = FLOAT4X4_ZERO;
float4 renderablePositionA = renderablePositions[p1];
float radiiA = sortedFluidMaterials[p1].x;
float4 avgPosition = float4(renderablePositionA.xyz, 1);//FLOAT4_ZERO;
uint count = min(maxNeighbors, neighborCounts[p1]);
for (uint j = 0; j < count; ++j)
{
int p2 = neighbors[maxNeighbors * p1 + j];
float4 renderablePositionB = renderablePositions[p2];
float dist = length((renderablePositionA - renderablePositionB).xyz);
float avgKernel = (Poly6(dist,radiiA) + Poly6(dist,sortedFluidMaterials[p2].x)) * 0.5;
avgPosition += float4(renderablePositionB.xyz,1) * avgKernel;
}
anisotropies[p1]._m03_m13_m23_m33 = avgPosition / avgPosition.w;
}
[numthreads(128, 1, 1)]
void AccumulateAnisotropy (uint3 id : SV_DispatchThreadID)
{
unsigned int p1 = id.x;
if (p1 >= dispatchBuffer[3]) return;
float4x4 anisotropyA = anisotropies[p1];
float4 renderablePositionA = renderablePositions[p1];
float radiiA = sortedFluidMaterials[p1].x;
uint count = min(maxNeighbors, neighborCounts[p1]);
for (uint j = 0; j < count; ++j)
{
int p2 = neighbors[maxNeighbors * p1 + j];
float4 renderablePositionB = renderablePositions[p2];
float dist = length((renderablePositionA - renderablePositionB).xyz);
float avgKernel = (Poly6(dist,radiiA) + Poly6(dist,sortedFluidMaterials[p2].x)) * 0.5;
float4 r = (renderablePositionB - anisotropyA._m03_m13_m23_m33) * avgKernel;
anisotropyA += multrnsp4(r, r);
}
anisotropies[p1] = anisotropyA;
}
[numthreads(128, 1, 1)]
void AverageAnisotropy (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int o = sortedToOriginal[i];
if (anisotropies[i]._m00 + anisotropies[i]._m11 + anisotropies[i]._m22 > 0.01f)
{
float3 singularValues;
float3x3 u;
EigenSolve((float3x3)anisotropies[i], singularValues, u);
float maxVal = singularValues[0];
float3 s = max(singularValues, maxVal / maxAnisotropy) / maxVal * sortedPrincipalRadii[i].x;
renderableOrientations[o] = q_look_at(u._m02_m12_m22,u._m01_m11_m21);
renderableRadii[o] = float4(s.xyz,1);
}
else
{
float radius = sortedPrincipalRadii[i].x / maxAnisotropy;
renderableOrientations[o] = QUATERNION_IDENTITY;
renderableRadii[o] = float4(radius,radius,radius,1);
fluidData[o].x = 1 / pow(abs(radius * 2),3-mode); // normal volume of an isolated particle.
}
renderablePositions[o] = lerp(renderablePositions[o],anisotropies[i]._m03_m13_m23_m33,min((maxAnisotropy - 1)/3.0f,1));
// inactive particles have radii.w == 0, set it right away for particles killed during this frame
// to keep them from being rendered during this frame instead of waiting for the CPU to do it at the start of next sim step:
float4 radii = renderableRadii[o];
radii.w = life[o] <= 0 ? 0: radii.w;
renderableRadii[o] = radii;
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 80856d7c741a940a7bbf0d7d4f472e1d
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,69 +0,0 @@
#pragma kernel Project
#pragma kernel Apply
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<float> restLengths;
StructuredBuffer<float2> stiffnesses;
RWStructuredBuffer<float> lambdas;
RWStructuredBuffer<float4> positions;
StructuredBuffer<float> invMasses;
// Variables set from the CPU
uint activeConstraintCount;
float deltaTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p1 = particleIndices[i * 2];
int p2 = particleIndices[i * 2 + 1];
float w1 = invMasses[p1];
float w2 = invMasses[p2];
// calculate time adjusted compliance
float compliance = stiffnesses[i].x / (deltaTime * deltaTime);
// calculate position and lambda deltas:
float4 dist = positions[p1] - positions[p2];
float d = length(dist);
// calculate constraint value:
float constraint = d - restLengths[i];
constraint -= max(min(constraint, 0), -stiffnesses[i].y);
// calculate lambda and position deltas:
float dlambda = (-constraint - compliance * lambdas[i]) / (w1 + w2 + compliance + EPSILON);
float4 delta = dlambda * dist / (d + EPSILON);
lambdas[i] += dlambda;
float4 delta1 = delta * w1;
float4 delta2 = -delta * w2;
AddPositionDelta(p1, delta1);
AddPositionDelta(p2, delta2);
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p1 = particleIndices[i * 2];
int p2 = particleIndices[i * 2 + 1];
ApplyPositionDelta(positions, p1, sorFactor);
ApplyPositionDelta(positions, p2, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 4883886c2cc7740e18f906dcf663c1f1
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,101 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "DistanceFunctions.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateContacts
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<int> simplices;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
// distance field data:
StructuredBuffer<DistanceFieldHeader> distanceFieldHeaders;
StructuredBuffer<DFNode> dfNodes;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
float deltaTime;
[numthreads(128, 1, 1)]
void GenerateContacts (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4 * SDF_SHAPE]) return;
int firstPair = contactOffsetsPerType[SDF_SHAPE];
int simplexIndex = contactPairs[firstPair + i].x;
int colliderIndex = contactPairs[firstPair + i].y;
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
DistanceField dfShape;
dfShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
dfShape.s = s;
dfShape.distanceFieldHeaders = distanceFieldHeaders;
dfShape.dfNodes = dfNodes;
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
float4 simplexPoint;
SurfacePoint colliderPoint = Optimize(dfShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, simplexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
float4 velocity = FLOAT4_ZERO;
float simplexRadius = 0;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexRadius += principalRadii[particleIndex].x * simplexBary[j];
velocity += velocities[particleIndex] * simplexBary[j];
}
/*float4 rbVelocity = float4.zero;
if (rigidbodyIndex >= 0)
rbVelocity = BurstMath.GetRigidbodyVelocityAtPoint(rigidbodyIndex, colliderPoint.point, rigidbodies, solverToWorld);*/
//float dAB = dot(simplexPoint - colliderPoint.pos, colliderPoint.normal);
//float vel = dot(velocity /*- rbVelocity*/, colliderPoint.normal);
//if (vel * deltaTime + dAB <= simplexRadius + s.contactOffset + collisionMargin)
{
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
contact c = (contact)0;
c.pointB = colliderPoint.pos;
c.normal = colliderPoint.normal * dfShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2391a8c719b4c4193a8be38dcf83e8fd
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,374 +0,0 @@
#ifndef DISTANCEFUNCTIONS_INCLUDE
#define DISTANCEFUNCTIONS_INCLUDE
#include "SurfacePoint.cginc"
#include "Transform.cginc"
#include "Bounds.cginc"
struct Sphere : IDistanceFunction
{
shape s;
transform colliderToSolver;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 center = s.center * colliderToSolver.scale;
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
if (s.is2D())
pnt[2] = 0;
float radius = s.size.x * cmax(colliderToSolver.scale.xyz);
float distanceToCenter = length(pnt);
float4 normal = pnt / (distanceToCenter + EPSILON);
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(center + normal * (radius + s.contactOffset));
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct Box : IDistanceFunction
{
shape s;
transform colliderToSolver;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 center = s.center * colliderToSolver.scale;
float4 size = s.size * colliderToSolver.scale * 0.5f;
// clamp the point to the surface of the box:
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
if (s.is2D())
pnt[2] = 0;
// get minimum distance for each axis:
float4 distances = size - abs(pnt);
if (distances.x >= 0 && distances.y >= 0 && distances.z >= 0)
{
projectedPoint.normal = float4(0,0,0,0);
projectedPoint.pos = pnt;
// find minimum distance in all three axes and the axis index:
if (distances.y < distances.x && distances.y < distances.z)
{
projectedPoint.normal[1] = sign(pnt[1]);
projectedPoint.pos[1] = size[1] * projectedPoint.normal[1];
}
else if (distances.z < distances.x && distances.z < distances.y)
{
projectedPoint.normal[2] = sign(pnt[2]);
projectedPoint.pos[2] = size[2] * projectedPoint.normal[2];
}
else
{
projectedPoint.normal[0] = sign(pnt[0]);
projectedPoint.pos[0] = size[0] * projectedPoint.normal[0];
}
}
else
{
projectedPoint.pos = clamp(pnt, -size, size);
projectedPoint.normal = normalizesafe(pnt - projectedPoint.pos);
}
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(projectedPoint.pos + center + projectedPoint.normal * s.contactOffset);
projectedPoint.normal = colliderToSolver.TransformDirection(projectedPoint.normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct Capsule : IDistanceFunction
{
shape s;
transform colliderToSolver;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 center = s.center * colliderToSolver.scale;
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos) - center;
if (s.is2D())
pnt[2] = 0;
int direction = (int)s.size.z;
float height;
float radius;
float4 halfVector = float4(0,0,0,0);
if (direction == 0)
{
radius = s.size.x * max(colliderToSolver.scale[1], colliderToSolver.scale[2]);
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[0]);
halfVector[0] = height - radius;
}
else if (direction == 1)
{
radius = s.size.x * max(colliderToSolver.scale[2], colliderToSolver.scale[0]);
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[1]);
halfVector[1] = height - radius;
}
else
{
radius = s.size.x * max(colliderToSolver.scale[0], colliderToSolver.scale[1]);
height = max(radius, s.size.y * 0.5f * colliderToSolver.scale[2]);
halfVector[2] = height - radius;
}
float mu;
float4 centerLine = NearestPointOnEdge(-halfVector, halfVector, pnt, mu);
float4 centerToPoint = pnt - centerLine;
float distanceToCenter = length(centerToPoint);
float4 normal = centerToPoint / (distanceToCenter + EPSILON);
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(center + centerLine + normal * (radius + s.contactOffset));
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct BIHNode
{
int firstChild; /**< index of the first child node. The second one is right after the first.*/
int start; /**< index of the first element in this node.*/
int count; /**< amount of elements in this node.*/
int axis; /**< axis of the split plane (0,1,2 = x,y,z)*/
float min_; /**< minimum split plane*/
float max_; /**< maximum split plane*/
};
struct TriangleMeshHeader
{
int firstNode;
int nodeCount;
int firstTriangle;
int triangleCount;
int firstVertex;
int vertexCount;
};
struct Triangle
{
int i1;
int i2;
int i3;
aabb b;
};
struct TriangleMesh : IDistanceFunction
{
shape s;
transform colliderToSolver;
CachedTri tri;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos);
if (s.is2D())
pnt[2] = 0;
float4 bary = FLOAT4_ZERO;
float4 nearestPoint = NearestPointOnTri(tri, pnt, bary);
float4 normal = normalizesafe(pnt - nearestPoint);
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * s.contactOffset);
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct HeightFieldHeader
{
int firstSample;
int sampleCount;
};
struct Heightfield : IDistanceFunction
{
shape s;
transform colliderToSolver;
CachedTri tri;
float4 triNormal;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 pnt = colliderToSolver.InverseTransformPoint(pos);
float4 bary;
float4 nearestPoint = NearestPointOnTri(tri, pnt, bary);
float4 normal = normalizesafe(pnt - nearestPoint);
// flip the contact normal if it points below ground: (doesn't work with holes)
//OneSidedNormal(triNormal, normal);
projectedPoint.pos = colliderToSolver.TransformPoint(nearestPoint + normal * s.contactOffset);
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct DistanceFieldHeader
{
int firstNode;
int nodeCount;
};
struct DFNode
{
float4 distancesA;
float4 distancesB;
float4 center;
int firstChild;
// add 12 bytes of padding to ensure correct memory alignment:
int pad0;
int pad1;
int pad2;
float4 GetNormalizedPos(float4 position)
{
float4 corner = center - float4(center[3],center[3],center[3],center[3]);
return (position - corner) / (center[3] * 2);
}
float4 SampleWithGradient(float4 position)
{
float4 nPos = GetNormalizedPos(position);
// trilinear interpolation of distance:
float4 x = distancesA + (distancesB - distancesA) * nPos[0];
float2 y = x.xy + (x.zw - x.xy) * nPos[1];
float dist = y[0] + (y[1] - y[0]) * nPos[2];
// gradient estimation:
// x == 0
float2 a = distancesA.xy + (distancesA.zw - distancesA.xy) * nPos[1];
float x0 = a[0] + (a[1] - a[0]) * nPos[2];
// x == 1
a = distancesB.xy + (distancesB.zw - distancesB.xy) * nPos[1];
float x1 = a[0] + (a[1] - a[0]) * nPos[2];
// y == 0
float y0 = x[0] + (x[1] - x[0]) * nPos[2];
// y == 1
float y1 = x[2] + (x[3] - x[2]) * nPos[2];
return float4(x1 - x0, y1 - y0, y[1] - y[0], dist);
}
int GetOctant(float4 position)
{
int index = 0;
if (position[0] > center[0]) index |= 4;
if (position[1] > center[1]) index |= 2;
if (position[2] > center[2]) index |= 1;
return index;
}
};
struct DistanceField : IDistanceFunction
{
shape s;
transform colliderToSolver;
StructuredBuffer<DistanceFieldHeader> distanceFieldHeaders;
StructuredBuffer<DFNode> dfNodes;
float4 DFTraverse(float4 particlePosition,
in DistanceFieldHeader header)
{
int stack[12];
int stackTop = 0;
stack[stackTop++] = 0;
while (stackTop > 0)
{
// pop node index from the stack:
int nodeIndex = stack[--stackTop];
DFNode node = dfNodes[header.firstNode + nodeIndex];
// if the child node exists, recurse down the df octree:
if (node.firstChild >= 0)
stack[stackTop++] = node.firstChild + node.GetOctant(particlePosition);
else
return node.SampleWithGradient(particlePosition);
}
return FLOAT4_ZERO;
}
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 pnt = colliderToSolver.InverseTransformPoint(pos);
if (s.is2D())
pnt[2] = 0;
float4 sample = DFTraverse(pnt, distanceFieldHeaders[s.dataIndex]);
float4 normal = float4(normalize(sample.xyz), 0);
projectedPoint.pos = colliderToSolver.TransformPoint(pnt - normal * (sample[3] - s.contactOffset));
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
struct EdgeMeshHeader
{
int firstNode;
int nodeCount;
int firstEdge;
int edgeCount;
int firstVertex;
int vertexCount;
};
struct Edge
{
int i1;
int i2;
aabb b;
};
struct EdgeMesh : IDistanceFunction
{
shape s;
transform colliderToSolver;
int dataOffset;
CachedEdge edge;
void Evaluate(in float4 pos, in float4 radii, in quaternion orientation, inout SurfacePoint projectedPoint)
{
float4 pnt = colliderToSolver.InverseTransformPointUnscaled(pos);
if (s.is2D())
pnt[2] = 0;
float mu = 0;
float4 nearestPoint = NearestPointOnEdge(edge, pnt, mu);
float4 normal = normalizesafe(pnt - nearestPoint);
projectedPoint.pos = colliderToSolver.TransformPointUnscaled(nearestPoint + normal * s.contactOffset);
projectedPoint.normal = colliderToSolver.TransformDirection(normal);
projectedPoint.bary = float4(1,0,0,0);
}
};
#endif

View File

@@ -1,7 +0,0 @@
fileFormatVersion: 2
guid: 5b1df68bf523b4106a6b113980018d93
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,147 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "DistanceFunctions.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateContacts
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<int> simplices;
StructuredBuffer<aabb> simplexBounds; // bounding box of each simplex.
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
// edge mesh data:
StructuredBuffer<EdgeMeshHeader> edgeMeshHeaders;
StructuredBuffer<BIHNode> edgeBihNodes;
StructuredBuffer<Edge> edges;
StructuredBuffer<float2> edgeVertices;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
float deltaTime;
[numthreads(128, 1, 1)]
void GenerateContacts (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4 * EDGE_MESH_SHAPE]) return;
int firstPair = contactOffsetsPerType[EDGE_MESH_SHAPE];
int simplexIndex = contactPairs[firstPair + i].x;
int colliderIndex = contactPairs[firstPair + i].y;
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
EdgeMeshHeader header = edgeMeshHeaders[s.dataIndex];
EdgeMesh meshShape;
meshShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
meshShape.s = s;
// invert a full matrix here to accurately represent collider bounds scale.
float4x4 solverToCollider = Inverse(TRS(meshShape.colliderToSolver.translation.xyz, meshShape.colliderToSolver.rotation, meshShape.colliderToSolver.scale.xyz));
aabb simplexBound = simplexBounds[simplexIndex].Transformed(solverToCollider);
float4 marginCS = float4((s.contactOffset + collisionMargin) / meshShape.colliderToSolver.scale.xyz, 0);
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
int stack[12];
int stackTop = 0;
stack[stackTop++] = 0;
while (stackTop > 0)
{
// pop node index from the stack:
int nodeIndex = stack[--stackTop];
BIHNode node = edgeBihNodes[header.firstNode + nodeIndex];
// leaf node:
if (node.firstChild < 0)
{
// check for contact against all triangles:
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
{
Edge t = edges[header.firstEdge + dataOffset];
float4 v1 = float4(edgeVertices[header.firstVertex + t.i1],0,0) + s.center;
float4 v2 = float4(edgeVertices[header.firstVertex + t.i2],0,0) + s.center;
aabb edgeBounds;
edgeBounds.FromEdge(v1, v2, marginCS);
if (edgeBounds.IntersectsAabb(simplexBound, s.is2D()))
{
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
float4 simplexPoint;
meshShape.edge.Cache(v1 * meshShape.colliderToSolver.scale, v2 * meshShape.colliderToSolver.scale);
SurfacePoint surfacePoint = Optimize(meshShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, simplexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
float4 velocity = FLOAT4_ZERO;
float simplexRadius = 0;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexRadius += principalRadii[particleIndex].x * simplexBary[j];
velocity += velocities[particleIndex] * simplexBary[j];
}
float dAB = dot(simplexPoint - surfacePoint.pos, surfacePoint.normal);
float vel = dot(velocity, surfacePoint.normal);
if (vel * deltaTime + dAB <= simplexRadius + s.contactOffset + collisionMargin)
{
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
contact c = (contact)0;
c.pointB = surfacePoint.pos;
c.normal = surfacePoint.normal * meshShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
}
}
}
else // check min and/or max children:
{
// visit min node:
if (simplexBound.min_[node.axis] <= node.min_)
stack[stackTop++] = node.firstChild;
// visit max node:
if (simplexBound.max_[node.axis] >= node.max_)
stack[stackTop++] = node.firstChild + 1;
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 229868ae412914f11bbfa8c3ab9474c8
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,33 +0,0 @@
#ifndef FLUIDCHUNKDEFS_INCLUDE
#define FLUIDCHUNKDEFS_INCLUDE
#define chunkResolution 4u // amount of voxels in width/height/depth
struct keyvalue
{
uint key;
uint handle;
};
uint3 chunkGridResolution; // height/width/depth of chunk grid
float3 chunkGridOrigin;
float voxelSize;
uint maxChunks;
uint VoxelID(uint3 coords)
{
return coords.x + coords.y * chunkGridResolution.x + coords.z * chunkGridResolution.x * chunkGridResolution.y;
}
uint hash(uint k)
{
k ^= k >> 16;
k *= 0x85ebca6b;
k ^= k >> 13;
k *= 0xc2b2ae35;
k ^= k >> 16;
return k % maxChunks;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: bc0e240276e8a42a2ac5a11368e4f7fa
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,482 +0,0 @@
#pragma kernel SortFluidData
#pragma kernel Emit
#pragma kernel EmitShape
#pragma kernel CopyAliveCount
#pragma kernel Update
#pragma kernel Integrate
#pragma kernel Copy
#pragma kernel Sort
#pragma kernel ClearMesh
#pragma kernel BuildMesh
#include "InterlockedUtils.cginc"
#include "MathUtils.cginc"
#include "GridUtils.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "FluidKernels.cginc"
StructuredBuffer<int> sortedToOriginal;
RWStructuredBuffer<float4> sortedPositions;
RWStructuredBuffer<float4> sortedVelocities;
RWStructuredBuffer<float4> sortedAngularVelocities;
RWStructuredBuffer<quaternion> sortedOrientations;
RWStructuredBuffer<float4> sortedRadii;
StructuredBuffer<uint> cellOffsets; // start of each cell in the sorted item array.
StructuredBuffer<uint> cellCounts; // number of item in each cell.
StructuredBuffer<int> gridHashToSortedIndex;
StructuredBuffer<aabb> solverBounds;
StructuredBuffer<uint> fluidSimplices;
StructuredBuffer<int> activeParticles;
StructuredBuffer<float4> positions;
StructuredBuffer<float4> orientations;
StructuredBuffer<float4> velocities;
RWStructuredBuffer<float4> angularVelocities;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> fluidMaterial;
StructuredBuffer<float4> fluidData;
StructuredBuffer<float4> inputPositions; // w component is distance traveled inside volume (approximate volumetric lighting).
StructuredBuffer<float4> inputVelocities; // w component is buoyancy
StructuredBuffer<float4> inputColors; // rgba diffuse color
StructuredBuffer<float4> inputAttributes; // currentlifetime, maxlifetime, size, drag
RWStructuredBuffer<float4> outputPositions;
RWStructuredBuffer<float4> outputVelocities;
RWStructuredBuffer<float4> outputColors;
RWStructuredBuffer<float4> outputAttributes;
RWStructuredBuffer<uint> dispatch;
RWByteAddressBuffer vertices;
RWByteAddressBuffer indices;
// Variables set from the CPU
uint activeParticleCount;
uint maxFoamParticles;
uint particlesToEmit;
uint emitterShape;
float4 emitterPosition;
quaternion emitterRotation;
float4 emitterSize;
uint minFluidNeighbors;
float2 vorticityRange;
float2 velocityRange;
float foamGenerationRate;
float potentialIncrease;
float potentialDiffusion;
float advectionRadius;
float lifetime;
float lifetimeRandom;
float particleSize;
float buoyancy;
float drag;
float airDrag;
float sizeRandom;
float isosurface;
float airAging;
float3 agingOverPopulation;
float4 foamColor;
float4 sortAxis;
const uint groupWidth;
const uint groupHeight;
const uint stepIndex;
float deltaTime;
float randomSeed;
static const int4 quadrantOffsets[] =
{
int4(0, 0, 0, 1),
int4(1, 0, 0, 1),
int4(0, 1, 0, 1),
int4(1, 1, 0, 1),
int4(0, 0, 1, 1),
int4(1, 0, 1, 1),
int4(0, 1, 1, 1),
int4(1, 1, 1, 1)
};
void RandomInCylinder(float seed, float4 pos, float4 dir, float len, float radius, out float4 position, out float3 velocity)
{
float3 rand = hash31(seed);
float3 b1 = dir.xyz;
float3 b2 = normalizesafe(cross(b1, float3(1,0,0)));
float3 b3 = cross(b2, b1);
float theta = rand.y * 2 * PI;
float2 disc = radius * sqrt(rand.x) * float2(cos(theta),sin(theta));
velocity = b2 * disc.x + b3 * disc.y;
position = float4(pos.xyz + b1 * len * rand.z + velocity,0);
}
void RandomInBox(float seed, float4 center, float4 size, out float4 position, out float3 velocity)
{
float3 rand = hash31(seed);
velocity = (rand - float3(0.5,0.5,0.5)) * size.xyz;
position = float4(center.xyz + velocity,0);
}
[numthreads(128, 1, 1)]
void SortFluidData (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
int original = sortedToOriginal[i];
sortedPositions[i] = positions[original];
sortedVelocities[i] = velocities[original];
sortedAngularVelocities[i] = float4(angularVelocities[original].xyz,0);
sortedOrientations[i] = orientations[original];
sortedRadii[i] = principalRadii[original];
}
[numthreads(128, 1, 1)]
void EmitShape (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= particlesToEmit) return;
// atomically increment alive particle counter:
uint count;
InterlockedAdd(dispatch[3], 1, count);
if (count < maxFoamParticles)
{
// initialize foam particle in a random position inside the cylinder spawned by fluid particle:
float3 radialVelocity = float3(0,0,0);
if (emitterShape == 0)
RandomInCylinder(randomSeed + i, -float4(0,1,0,0)*emitterSize.y*0.5, float4(0,1,0,0), emitterSize.y, max(emitterSize.x, emitterSize.z) * 0.5, outputPositions[count], radialVelocity);
else
RandomInBox(randomSeed + i, FLOAT4_ZERO, emitterSize, outputPositions[count], radialVelocity);
float2 random = hash21(randomSeed - i);
// calculate initial life/size/color:
float initialLife = max(0, lifetime - lifetime * random.x * lifetimeRandom);
float initialSize = particleSize - particleSize * random.y * sizeRandom;
outputPositions[count] = float4(emitterPosition.xyz + rotate_vector(emitterRotation, outputPositions[count].xyz),0);
outputVelocities[count] = float4(0,0,0, buoyancy);
outputColors[count] = foamColor;
outputAttributes[count] = float4(1, 1/initialLife,initialSize,PackFloatRGBA(float4(airAging / 50.0, airDrag, drag, isosurface)));
}
}
[numthreads(128, 1, 1)]
void Emit (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= activeParticleCount) return;
int p = activeParticles[i];
float4 angVel = angularVelocities[p];
float2 potential = UnpackFloatRG(angVel.w);
// calculate fluid potential for foam generation:
float vorticityPotential = Remap01(fluidData[p].z, vorticityRange.x, vorticityRange.y);
float velocityPotential = Remap01(length(velocities[p]), velocityRange.x, velocityRange.y);
float potentialDelta = velocityPotential * vorticityPotential * deltaTime * potentialIncrease;
// update foam potential:
potential.y = saturate(potential.y * potentialDiffusion + potentialDelta);
// calculate amount of emitted particles
potential.x += foamGenerationRate * potential.y * deltaTime;
int emitCount = (int)potential.x;
potential.x -= emitCount;
for (int j = 0; j < emitCount; ++j)
{
// atomically increment alive particle counter:
uint count;
InterlockedAdd(dispatch[3], 1, count);
if (count < maxFoamParticles)
{
// initialize foam particle in a random position inside the cylinder spawned by fluid particle:
float3 radialVelocity;
RandomInCylinder(randomSeed + p + j, positions[p], normalizesafe(velocities[p]), length(velocities[p]) * deltaTime, principalRadii[p].x, outputPositions[count], radialVelocity);
// calculate initial life/size/color:
float2 random = hash21(randomSeed - p - j);
float initialLife = max(0, potential.y * (lifetime - lifetime * random.x * lifetimeRandom));
float initialSize = particleSize - particleSize * random.y * sizeRandom;
outputVelocities[count] = velocities[p] + float4(radialVelocity, buoyancy);
outputColors[count] = foamColor;
outputAttributes[count] = float4(1, 1/initialLife,initialSize,PackFloatRGBA(float4(airAging / 50.0, airDrag, drag, isosurface)));
}
}
angVel.w = PackFloatRG(potential);
angularVelocities[p] = angVel;
}
[numthreads(1, 1, 1)]
void CopyAliveCount (uint3 id : SV_DispatchThreadID)
{
dispatch[0] = dispatch[3] / 128 + 1;
dispatch[8] = dispatch[3];
dispatch[4] = dispatch[7] = 0;
}
[numthreads(128, 1, 1)]
void Update (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= dispatch[8]) return;
uint count;
InterlockedAdd(dispatch[3], -1, count);
count--;
if (count < maxFoamParticles && inputAttributes[count].x > 0)
{
uint aliveCount;
InterlockedAdd(dispatch[7], 1, aliveCount);
InterlockedMax(dispatch[4],(aliveCount + 1) / 128 + 1);
float4 attributes = inputAttributes[count];
float4 packedData = UnpackFloatRGBA(attributes.w);
int offsetCount = (mode == 1) ? 4 : 8;
float4 advectedVelocity = FLOAT4_ZERO;
float4 advectedAngVelocity = FLOAT4_ZERO;
float kernelSum = -packedData.w;
uint neighbourCount = 0;
float4 diffusePos = inputPositions[count];
for (uint m = 1; m <= levelPopulation[0]; ++m)
{
uint l = levelPopulation[m];
float radius = CellSizeOfLevel(l);
float interactionDist = radius * 0.5;
float4 cellCoords = floor((diffusePos - solverBounds[0].min_) / radius);
cellCoords[3] = 0;
if (mode == 1)
cellCoords[2] = 0;
float4 posInCell = diffusePos - (solverBounds[0].min_ + cellCoords * radius + float4(interactionDist,interactionDist,interactionDist,0));
int4 quadrant = (int4)sign(posInCell);
quadrant[3] = l;
for (int j = 0; j < offsetCount; ++j)
{
int4 neighborCoord = (int4)cellCoords + quadrantOffsets[j] * quadrant;
int cellIndex = gridHashToSortedIndex[GridHash(neighborCoord)];
uint n = cellOffsets[cellIndex];
uint end = n + cellCounts[cellIndex];
for (;n < end; ++n)
{
uint p = fluidSimplices[n];
int4 particleCoord = int4(floor((positions[p].xyz - solverBounds[0].min_.xyz)/ radius).xyz,l);
if (any (particleCoord - neighborCoord))
continue;
float4 normal = diffusePos - positions[p];
normal[3] = 0;
if (mode == 1)
normal[2] = 0;
float d = length(normal);
if (d <= interactionDist)
{
float3 radii = fluidMaterial[p].x * (principalRadii[p].xyz / principalRadii[p].x);
float4 angVel = float4(cross(angularVelocities[p].xyz, normal.xyz),0);
advectedAngVelocity += angVel * Poly6(d, radii.x) / Poly6(0, radii.x);
normal.xyz = rotate_vector(q_conj(orientations[p]), normal.xyz) / radii;
d = length(normal.xyz) * radii.x;
// scale by volume (* 1 / normalized density)
float w = Poly6(d, radii.x) / fluidData[p].x;
kernelSum += w;
advectedVelocity += float4(velocities[p].xyz,0) * w;
neighbourCount++;
}
}
}
}
float4 forces = FLOAT4_ZERO;
float velocityScale = 1;
float agingScale = 1 + Remap01(dispatch[8] / (float)maxFoamParticles,agingOverPopulation.x,agingOverPopulation.y) * (agingOverPopulation.z - 1);
// foam/bubble particle:
if (kernelSum > EPSILON && neighbourCount >= minFluidNeighbors)
{
// advection:
forces = packedData.z / deltaTime * (advectedVelocity / (kernelSum + packedData.w) + advectedAngVelocity - inputVelocities[count]);
// buoyancy:
forces -= float4(gravity,0) * inputVelocities[count].w * saturate(kernelSum); // TODO: larger particles should rise faster.
}
else // spray:
{
// gravity:
forces += float4(gravity,0);
// atmospheric drag/aging:
velocityScale = packedData.y;
agingScale *= packedData.x * 50;
}
// don't change 4th component, as its used to store buoyancy control parameter.
forces[3] = 0;
// update particle data:
attributes.x -= attributes.y * deltaTime * agingScale;
//attributes.z += (attributes.y * deltaTime * agingScale) * 0.02; // increase size with age. TODO: maybe do in render shader?
outputAttributes[aliveCount] = attributes;
outputColors[aliveCount] = inputColors[count];
// add forces to velocity:
outputPositions[aliveCount] = inputPositions[count];
outputVelocities[aliveCount] = (inputVelocities[count] + forces * deltaTime) * velocityScale;
}
}
[numthreads(128, 1, 1)]
void Integrate (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
outputPositions[i].xyz += outputVelocities[i].xyz * deltaTime;
}
[numthreads(128, 1, 1)]
void Copy (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i == 0)
{
dispatch[0] = dispatch[4];
dispatch[3] = dispatch[7];
}
if (i >= dispatch[7]) return;
outputPositions[i] = inputPositions[i];
outputVelocities[i] = inputVelocities[i];
outputColors[i] = inputColors[i];
outputAttributes[i] = inputAttributes[i];
}
[numthreads(128,1,1)]
void Sort(uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
uint hIndex = i & (groupWidth - 1);
uint indexLeft = hIndex + (groupHeight + 1) * (i / groupWidth);
uint rightStepSize = stepIndex == 0 ? groupHeight - 2 * hIndex : (groupHeight + 1) / 2;
uint indexRight = indexLeft + rightStepSize;
// Exit if out of bounds
if (indexRight >= dispatch[3]) return;
float4 posLeft = outputPositions[indexLeft];
float4 posRight = outputPositions[indexRight];
float4 velLeft = outputVelocities[indexLeft];
float4 velRight = outputVelocities[indexRight];
float4 colorLeft = outputColors[indexLeft];
float4 colorRight = outputColors[indexRight];
float4 attrLeft = outputAttributes[indexLeft];
float4 attrRight = outputAttributes[indexRight];
// calculate distance to camera:
float distLeft = dot(posLeft.xyz, sortAxis.xyz);
float distRight = dot(posRight.xyz, sortAxis.xyz);
// Swap entries if order is incorrect
if (distLeft < distRight)
{
outputPositions[indexLeft] = posRight;
outputPositions[indexRight] = posLeft;
outputVelocities[indexLeft] = velRight;
outputVelocities[indexRight] = velLeft;
outputColors[indexLeft] = colorRight;
outputColors[indexRight] = colorLeft;
outputAttributes[indexLeft] = attrRight;
outputAttributes[indexRight] = attrLeft;
}
}
[numthreads(128, 1, 1)]
void ClearMesh (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxFoamParticles) return;
indices.Store((i*6)<<2, 0);
indices.Store((i*6+1)<<2, 0);
indices.Store((i*6+2)<<2, 0);
indices.Store((i*6+3)<<2, 0);
indices.Store((i*6+4)<<2, 0);
indices.Store((i*6+5)<<2, 0);
}
[numthreads(128, 1, 1)]
void BuildMesh (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
// <<2 = multiply by 4 to get byte address, since a float/int is 4 bytes in size.
// particle data is the same for all 4 vertices:
for (uint v = i*4; v < i*4 + 4; ++v)
{
int base = v*19;
// pos
vertices.Store4(base<<2, asuint(float4(inputPositions[i].xyz, 1)));
// color:
vertices.Store4((base+7)<<2, asuint( inputColors[i] ));
// velocity and attributes
vertices.Store4((base+11)<<2, asuint( float4(inputVelocities[i].xyz, inputPositions[i].w)));
vertices.Store4((base+15)<<2, asuint( inputAttributes[i] ));
}
//different offset for each vertex:
int base = i*4;
vertices.Store3((base*19 + 4)<<2, asuint(float3(1,1,0)));
vertices.Store3(((base+1)*19 + 4)<<2, asuint(float3(-1,1,0)));
vertices.Store3(((base+2)*19 + 4)<<2, asuint(float3(-1,-1,0)));
vertices.Store3(((base+3)*19 + 4)<<2, asuint(float3(1,-1,0)));
// indices:
indices.Store((i*6)<<2, asuint(i*4+2));
indices.Store((i*6+1)<<2, asuint(i*4+1));
indices.Store((i*6+2)<<2, asuint(i*4));
indices.Store((i*6+3)<<2, asuint(i*4+3));
indices.Store((i*6+4)<<2, asuint(i*4+2));
indices.Store((i*6+5)<<2, asuint(i*4));
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a8c6735ba651f4c808f068c3b99bca04
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,467 +0,0 @@
#pragma kernel SolveDiffuseContacts
#include "GridUtils.cginc"
#include "CollisionMaterial.cginc"
#include "ContactHandling.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
#include "Simplex.cginc"
#include "MathUtils.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#include "DistanceFunctions.cginc"
#define MAX_CONTACTS_PER_DIFFUSE 32
StructuredBuffer<float4> inputPositions;
StructuredBuffer<float4> inputAttributes;
RWStructuredBuffer<float4> inputVelocities;
StructuredBuffer<aabb> aabbs;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
StructuredBuffer<uint> sortedColliderIndices;
StructuredBuffer<uint> cellOffsets;
StructuredBuffer<uint> cellCounts;
// triangle mesh data:
StructuredBuffer<TriangleMeshHeader> triangleMeshHeaders;
StructuredBuffer<BIHNode> bihNodes;
StructuredBuffer<Triangle> triangles;
StructuredBuffer<float3> vertices;
// edge mesh data:
StructuredBuffer<EdgeMeshHeader> edgeMeshHeaders;
StructuredBuffer<BIHNode> edgeBihNodes;
StructuredBuffer<Edge> edges;
StructuredBuffer<float2> edgeVertices;
// heightfield data:
StructuredBuffer<HeightFieldHeader> heightFieldHeaders;
StructuredBuffer<float> heightFieldSamples;
// distance field data:
StructuredBuffer<DistanceFieldHeader> distanceFieldHeaders;
StructuredBuffer<DFNode> dfNodes;
StructuredBuffer<transform> solverToWorld;
StructuredBuffer<transform> worldToSolver;
StructuredBuffer<uint> dispatch;
float radiusScale;
uint colliderCount; // amount of colliders in the grid.
uint cellsPerCollider; // max amount of cells a collider can be inserted into. Typically this is 8.
int shapeTypeCount; // number of different collider shapes, ie: box, sphere, sdf, etc.
float deltaTime;
void CollideMesh(int colliderIndex, int threadIndex, aabb particleBounds, inout float4 pos, float radius)
{
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
TriangleMeshHeader header = triangleMeshHeaders[s.dataIndex];
TriangleMesh meshShape;
meshShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
meshShape.s = shapes[colliderIndex];
// invert a full matrix here to accurately represent collider bounds scale.
float4x4 solverToCollider = Inverse(TRS(meshShape.colliderToSolver.translation.xyz, meshShape.colliderToSolver.rotation, meshShape.colliderToSolver.scale.xyz));
aabb simplexBound = particleBounds.Transformed(solverToCollider); // TODO: this is wrong, passed bounds are in world space!
float4 marginCS = float4((s.contactOffset + collisionMargin) / meshShape.colliderToSolver.scale.xyz, 0);
int stack[12];
int stackTop = 0;
stack[stackTop++] = 0;
while (stackTop > 0)
{
// pop node index from the stack:
int nodeIndex = stack[--stackTop];
BIHNode node = bihNodes[header.firstNode + nodeIndex];
// leaf node:
if (node.firstChild < 0)
{
// check for contact against all triangles:
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
{
Triangle t = triangles[header.firstTriangle + dataOffset];
float4 v1 = float4(vertices[header.firstVertex + t.i1], 0);
float4 v2 = float4(vertices[header.firstVertex + t.i2], 0);
float4 v3 = float4(vertices[header.firstVertex + t.i3], 0);
aabb triangleBounds;
triangleBounds.FromTriangle(v1, v2, v3, marginCS);
if (triangleBounds.IntersectsAabb(simplexBound, s.is2D()))
{
meshShape.tri.Cache(v1 * meshShape.colliderToSolver.scale, v2 * meshShape.colliderToSolver.scale, v3 * meshShape.colliderToSolver.scale);
SurfacePoint surf;
meshShape.Evaluate(pos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(pos - surf.pos, surf.normal) - radius;
if (dist < 0)
pos = surf.pos + surf.normal * radius;
}
}
}
else // check min and/or max children:
{
// visit min node:
if (simplexBound.min_[node.axis] <= node.min_)
stack[stackTop++] = node.firstChild;
// visit max node:
if (simplexBound.max_[node.axis] >= node.max_)
stack[stackTop++] = node.firstChild + 1;
}
}
}
void CollideEdgeMesh(int colliderIndex, int threadIndex, aabb particleBounds, inout float4 pos, float radius)
{
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
EdgeMeshHeader header = edgeMeshHeaders[s.dataIndex];
EdgeMesh meshShape;
meshShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
meshShape.s = shapes[colliderIndex];
// invert a full matrix here to accurately represent collider bounds scale.
float4x4 solverToCollider = Inverse(TRS(meshShape.colliderToSolver.translation.xyz, meshShape.colliderToSolver.rotation, meshShape.colliderToSolver.scale.xyz));
aabb simplexBound = particleBounds.Transformed(solverToCollider); // TODO: this is wrong, passed bounds are in world space!
//simplexBound.Expand(0.02);
float4 marginCS = float4((s.contactOffset + collisionMargin) / meshShape.colliderToSolver.scale.xyz, 0);
int stack[12];
int stackTop = 0;
stack[stackTop++] = 0;
while (stackTop > 0)
{
// pop node index from the stack:
int nodeIndex = stack[--stackTop];
BIHNode node = edgeBihNodes[header.firstNode + nodeIndex];
// leaf node:
if (node.firstChild < 0)
{
// check for contact against all triangles:
for (int dataOffset = node.start; dataOffset < node.start + node.count; ++dataOffset)
{
Edge t = edges[header.firstEdge + dataOffset];
float4 v1 = float4(edgeVertices[header.firstVertex + t.i1],0,0) + s.center;
float4 v2 = float4(edgeVertices[header.firstVertex + t.i2],0,0) + s.center;
aabb edgeBounds;
edgeBounds.FromEdge(v1, v2, marginCS);
if (edgeBounds.IntersectsAabb(simplexBound, s.is2D()))
{
meshShape.edge.Cache(v1 * meshShape.colliderToSolver.scale, v2 * meshShape.colliderToSolver.scale);
SurfacePoint surf;
meshShape.Evaluate(pos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(pos - surf.pos, surf.normal) - radius;
if (dist < 0)
pos = surf.pos + surf.normal * radius;
}
}
}
else // check min and/or max children:
{
// visit min node:
if (simplexBound.min_[node.axis] <= node.min_)
stack[stackTop++] = node.firstChild;
// visit max node:
if (simplexBound.max_[node.axis] >= node.max_)
stack[stackTop++] = node.firstChild + 1;
}
}
}
void CollideHeightmap(int colliderIndex, int threadIndex, aabb particleBounds, inout float4 pos, float radius)
{
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
HeightFieldHeader header = heightFieldHeaders[s.dataIndex];
Heightfield fieldShape;
fieldShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
fieldShape.s = s;
// invert a full matrix here to accurately represent collider bounds scale.
float4x4 solverToCollider = Inverse(TRS(fieldShape.colliderToSolver.translation.xyz, fieldShape.colliderToSolver.rotation, fieldShape.colliderToSolver.scale.xyz));
aabb simplexBound = particleBounds.Transformed(solverToCollider);
int resolutionU = (int)s.center.x;
int resolutionV = (int)s.center.y;
// calculate terrain cell size:
float cellWidth = s.size.x / (resolutionU - 1);
float cellHeight = s.size.z / (resolutionV - 1);
// calculate particle bounds min/max cells:
int2 min_ = int2((int)floor(simplexBound.min_[0] / cellWidth), (int)floor(simplexBound.min_[2] / cellHeight));
int2 max_ = int2((int)floor(simplexBound.max_[0] / cellWidth), (int)floor(simplexBound.max_[2] / cellHeight));
for (int su = min_[0]; su <= max_[0]; ++su)
{
if (su >= 0 && su < resolutionU - 1)
{
for (int sv = min_[1]; sv <= max_[1]; ++sv)
{
if (sv >= 0 && sv < resolutionV - 1)
{
// calculate neighbor sample indices:
int csu1 = clamp(su + 1, 0, resolutionU - 1);
int csv1 = clamp(sv + 1, 0, resolutionV - 1);
// sample heights:
float h1 = heightFieldSamples[header.firstSample + sv * resolutionU + su] * s.size.y;
float h2 = heightFieldSamples[header.firstSample + sv * resolutionU + csu1] * s.size.y;
float h3 = heightFieldSamples[header.firstSample + csv1 * resolutionU + su] * s.size.y;
float h4 = heightFieldSamples[header.firstSample + csv1 * resolutionU + csu1] * s.size.y;
if (h1 < 0) continue;
h1 = abs(h1);
h2 = abs(h2);
h3 = abs(h3);
h4 = abs(h4);
float min_x = su * s.size.x / (resolutionU - 1);
float max_x = csu1 * s.size.x / (resolutionU - 1);
float min_z = sv * s.size.z / (resolutionV - 1);
float max_z = csv1 * s.size.z / (resolutionV - 1);
// ------contact against the first triangle------:
float4 v1 = float4(min_x, h3, max_z, 0);
float4 v2 = float4(max_x, h4, max_z, 0);
float4 v3 = float4(min_x, h1, min_z, 0);
fieldShape.tri.Cache(v1, v2, v3);
fieldShape.triNormal.xyz = normalizesafe(cross((v2 - v1).xyz, (v3 - v1).xyz));
SurfacePoint surf;
fieldShape.Evaluate(pos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(pos - surf.pos, surf.normal) - radius;
if (dist < 0)
pos = surf.pos + surf.normal * radius;
// ------contact against the second triangle------:
v1 = float4(min_x, h1, min_z, 0);
v2 = float4(max_x, h4, max_z, 0);
v3 = float4(max_x, h2, min_z, 0);
fieldShape.tri.Cache(v1, v2, v3);
fieldShape.triNormal.xyz = normalizesafe(cross((v2 - v1).xyz, (v3 - v1).xyz));
fieldShape.Evaluate(pos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
dist = dot(pos - surf.pos, surf.normal) - radius;
if (dist < 0)
pos = surf.pos + surf.normal * radius;
}
}
}
}
}
[numthreads(128, 1, 1)]
void SolveDiffuseContacts (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= dispatch[3]) return;
uint cellCount = colliderCount * cellsPerCollider;
int candidateCount = 0;
uint candidates[MAX_CONTACTS_PER_DIFFUSE];
float4 predPos = inputPositions[threadIndex] + inputVelocities[threadIndex] * deltaTime;
float radius = inputAttributes[threadIndex].z * radiusScale;
// max size of the particle bounds in cells:
int4 maxSize = int4(3,3,3,3);
aabb b;
b.FromEdge(inputPositions[threadIndex], predPos, radius);
b.Transform(solverToWorld[0]);
// build a list of candidate colliders:
for (uint m = 1; m <= levelPopulation[0]; ++m)
{
uint l = levelPopulation[m];
float cellSize = CellSizeOfLevel(l);
int4 minCell = floor(b.min_ / cellSize);
int4 maxCell = floor(b.max_ / cellSize);
maxCell = minCell + min(maxCell - minCell, maxSize);
for (int x = minCell[0]; x <= maxCell[0]; ++x)
{
for (int y = minCell[1]; y <= maxCell[1]; ++y)
{
// for 2D mode, project each cell at z == 0 and check them too. This way we ensure 2D colliders
// (which are inserted in cells with z == 0) are accounted for in the broadphase.
if (mode == 1)
{
uint flatCellIndex = GridHash(int4(x,y,0,l));
uint cellStart = cellOffsets[flatCellIndex];
uint cellCount = cellCounts[flatCellIndex];
// iterate through colliders in the neighbour cell
for (uint n = cellStart; n < cellStart + cellCount; ++n)
{
// sorted insert into the candidates list:
if (candidateCount < MAX_CONTACTS_PER_DIFFUSE)
candidates[candidateCount++] = sortedColliderIndices[n] / cellsPerCollider;
}
}
for (int z = minCell[2]; z <= maxCell[2]; ++z)
{
uint flatCellIndex = GridHash(int4(x,y,z,l));
uint cellStart = cellOffsets[flatCellIndex];
uint cellCount = cellCounts[flatCellIndex];
// iterate through colliders in the neighbour cell
for (uint n = cellStart; n < cellStart + cellCount; ++n)
{
if (candidateCount < MAX_CONTACTS_PER_DIFFUSE)
candidates[candidateCount++] = sortedColliderIndices[n] / cellsPerCollider;
}
}
}
}
}
//evaluate candidates and create contacts:
if (candidateCount > 0)
{
// insert sort:
for (int k = 1; k < candidateCount; ++k)
{
uint key = candidates[k];
int j = k - 1;
while (j >= 0 && candidates[j] > key)
candidates[j + 1] = candidates[j--];
candidates[j + 1] = key;
}
// make sure each candidate only shows up once in the list:
int first = 0, contactCount = 0;
while(++first != candidateCount)
{
if (candidates[contactCount] != candidates[first])
candidates[++contactCount] = candidates[first];
}
contactCount++;
// solve contacts:
for (int i = 0; i < contactCount; i++)
{
int c = candidates[i];
aabb colliderBoundsWS = aabbs[c];
if (b.IntersectsAabb(colliderBoundsWS, mode == 1))
{
switch(shapes[c].type)
{
case SPHERE_SHAPE:
{
SurfacePoint surf;
Sphere sphereShape;
sphereShape.colliderToSolver = worldToSolver[0].Multiply(transforms[c]);
sphereShape.s = shapes[c];
sphereShape.Evaluate(predPos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(predPos - surf.pos, surf.normal) - radius;
if (dist < 0) predPos = surf.pos + surf.normal * radius;
}
break;
case BOX_SHAPE:
{
SurfacePoint surf;
Box boxShape;
boxShape.colliderToSolver = worldToSolver[0].Multiply(transforms[c]);
boxShape.s = shapes[c];
boxShape.Evaluate(predPos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(predPos - surf.pos, surf.normal) - radius;
if (dist < 0) predPos = surf.pos + surf.normal * radius;
}
break;
case CAPSULE_SHAPE:
{
SurfacePoint surf;
Capsule capShape;
capShape.colliderToSolver = worldToSolver[0].Multiply(transforms[c]);
capShape.s = shapes[c];
capShape.Evaluate(predPos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(predPos - surf.pos, surf.normal) - radius;
if (dist < 0) predPos = surf.pos + surf.normal * radius;
}
break;
case TRIANGLE_MESH_SHAPE:
{
CollideMesh(c, threadIndex, b, predPos, radius);
}
break;
case EDGE_MESH_SHAPE:
{
CollideEdgeMesh(c, threadIndex, b, predPos, radius);
}
break;
case HEIGHTMAP_SHAPE:
{
CollideHeightmap(c, threadIndex, b, predPos, radius);
}
break;
case SDF_SHAPE:
{
SurfacePoint surf;
DistanceField dfShape;
dfShape.colliderToSolver = worldToSolver[0].Multiply(transforms[c]);
dfShape.s = shapes[c];
dfShape.distanceFieldHeaders = distanceFieldHeaders;
dfShape.dfNodes = dfNodes;
dfShape.Evaluate(predPos, float4(radius, radius, radius, 0), QUATERNION_IDENTITY, surf);
float dist = dot(predPos - surf.pos, surf.normal) - radius;
if (dist < 0) predPos = surf.pos + surf.normal * radius;
}
break;
}
}
}
}
inputVelocities[threadIndex].xyz = (predPos.xyz - inputPositions[threadIndex].xyz) / deltaTime;
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 62b650760f2294d0c9c68247f21c7009
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,199 +0,0 @@
#pragma kernel Clear
#pragma kernel InsertInGrid
#pragma kernel SortByGrid
#pragma kernel ComputeDensity
#pragma kernel ApplyDensity
#include "InterlockedUtils.cginc"
#include "MathUtils.cginc"
#include "GridUtils.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "FluidKernels.cginc"
RWStructuredBuffer<int> sortedToOriginal;
RWStructuredBuffer<uint> offsetInCell;
RWStructuredBuffer<uint> cellStart; // start of each cell in the sorted item array.
RWStructuredBuffer<uint> cellCounts; // number of item in each cell.
StructuredBuffer<aabb> solverBounds;
RWStructuredBuffer<float4> inputPositions;
RWStructuredBuffer<float4> inputVelocities;
RWStructuredBuffer<float4> inputColors;
RWStructuredBuffer<float4> sortedPositions;
RWStructuredBuffer<float4> sortedVelocities;
RWStructuredBuffer<float4> fluidData;
StructuredBuffer<uint> dispatch;
// each emitter has its own global radius, not possible to have foam emitters interact with each other.
float particleRadius;
float smoothingRadius;
float surfaceTension;
float pressure;
float viscosity;
float4 volumeLightDirection;
float deltaTime;
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxCells) return;
// clear all cell counts to zero, and cell offsets to invalid.
cellStart[i] = INVALID;
cellCounts[i] = 0;
}
[numthreads(128, 1, 1)]
void InsertInGrid (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
uint cellIndex = GridHash(floor(inputPositions[i] / smoothingRadius).xyz);
InterlockedAdd(cellCounts[cellIndex],1,offsetInCell[i]);
}
[numthreads(128, 1, 1)]
void SortByGrid (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
uint cellIndex = GridHash(floor(inputPositions[i] / smoothingRadius).xyz);
uint sortedIndex = cellStart[cellIndex] + offsetInCell[i];
sortedPositions[sortedIndex] = inputPositions[i];
sortedVelocities[sortedIndex] = inputVelocities[i];
sortedToOriginal[sortedIndex] = i;
}
[numthreads(128, 1, 1)]
void ComputeDensity (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
float4 positionA = inputPositions[i];
int3 cellCoords = floor(inputPositions[i] / smoothingRadius).xyz;
// self-contribution:
float avgKernel = Poly6(0,smoothingRadius);
float restVolume = pow(abs(particleRadius * 2),3-mode);
float grad = restVolume * Spiky(0,smoothingRadius);
float4 fluidDataA = float4(avgKernel,0,grad,grad*grad);
float4 positionB;
// iterate over neighborhood, calculate density and gradient.
for (int k = 0; k < 27; ++k)
{
int3 neighborCoords = cellCoords + cellNeighborhood[k].xyz;
uint cellIndex = GridHash(neighborCoords);
uint start = cellStart[cellIndex];
for (uint j = 0; j < cellCounts[cellIndex]; ++j)
{
positionB = sortedPositions[start + j];
float3 r = (positionA - positionB).xyz;
if (mode == 1)
r[2] = 0;
float dist = length(r);
if (dist > smoothingRadius || any(neighborCoords - floor(positionB / smoothingRadius).xyz)) continue;
float grad = restVolume * Spiky(dist,smoothingRadius);
fluidDataA += float4(Poly6(dist,smoothingRadius),0,grad,grad*grad);
}
}
// self particle contribution to density and gradient:
fluidDataA[3] += fluidDataA[2] * fluidDataA[2];
// usually, we'd weight density by mass (density contrast formulation) by dividing by invMass. Then, multiply by invMass when
// calculating the state equation (density / restDensity - 1, restDensity = mass / volume, so density * invMass * restVolume - 1
// We end up with density / invMass * invMass * restVolume - 1, invMass cancels out.
float constraint = max(0, fluidDataA[0] * restVolume - 1);
// calculate lambda:
fluidDataA[1] = -constraint / (fluidDataA[3] + EPSILON);
fluidData[i] = fluidDataA;
}
[numthreads(128, 1, 1)]
void ApplyDensity (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatch[3]) return;
int3 cellCoords = floor(inputPositions[i] / smoothingRadius).xyz;
float restVolume = pow(abs(particleRadius * 2),3-mode);
float neighborhoodVolume = pow(abs(smoothingRadius * 2),3-mode);
float4 positionA = inputPositions[i];
float4 velocityA = inputVelocities[i];
float4 fluidDataA = fluidData[i];
float4 fluidDataB;
float4 positionB;
float4 velocityB;
float4 pressureDelta = FLOAT4_ZERO;
float4 viscAccel = FLOAT4_ZERO;
float AO = 0;
for (int k = 0; k < 27; ++k)
{
int3 neighborCoords = cellCoords + cellNeighborhood[k].xyz;
uint cellIndex = GridHash(neighborCoords);
uint start = cellStart[cellIndex];
for (uint j = 0; j < cellCounts[cellIndex]; ++j)
{
positionB = sortedPositions[start + j];
velocityB = sortedVelocities[start + j];
fluidDataB = fluidData[sortedToOriginal[start + j]];
float4 r = float4((positionA - positionB).xyz,0);
if (mode == 1)
r[2] = 0;
float dist = length(r);
if (dist > smoothingRadius || any(neighborCoords - floor(positionB / smoothingRadius).xyz)) continue;
float kern = Poly6(dist,smoothingRadius);
float cAvg = Cohesion(dist,smoothingRadius);
// XSPH viscosity:
float4 relVel = float4((velocityB - velocityA).xyz,0);
viscAccel += viscosity * relVel * kern * restVolume;
float st = 0.2 * cAvg * surfaceTension;
float scorrA = - st / (fluidDataA[3] + EPSILON);
float scorrB = - st / (fluidDataB[3] + EPSILON);
pressureDelta += r / (dist + EPSILON) * Spiky(dist,smoothingRadius) * ((fluidDataA[1] + scorrA) + (fluidDataB[1] + scorrB)) * restVolume;
float4 v = r / (dist + EPSILON);
AO += max(0, dot (volumeLightDirection.xyz, v.xyz) ) / (1 + dist) * restVolume;
}
}
float4 delta = pressure * pressureDelta;
// modify position and velocity:
inputPositions[i] = float4((positionA + delta).rgb, 2*PI * AO.x / neighborhoodVolume);
inputVelocities[i] += float4(delta.xyz / deltaTime + viscAccel.xyz,0);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 2332ee3ce70e9447d8d13b5b67ac8e6c
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,40 +0,0 @@
#ifndef FLUIDKERNELS_INCLUDE
#define FLUIDKERNELS_INCLUDE
#include "SolverParameters.cginc"
float Poly6(float r, float h)
{
float h2 = h * h;
float h4 = h2 * h2;
float h8 = h4 * h4;
float rl = min(r, h);
float hr = h2 - rl * rl;
if (mode)
return 4.0f / PI / h8 * hr * hr * hr;
else
return 315.0f / (64.0 * PI) / (h8 * h) * hr * hr * hr;
}
float Spiky(float r, float h)
{
float h2 = h * h;
float h4 = h2 * h2;
float rl = min(r, h);
float hr = h - rl;
if (mode)
return -30.0f / PI / (h4 * h) * hr * hr;
else
return -45.0f / PI / (h4 * h2) * hr * hr;
}
float Cohesion(float r, float h)
{
return cos(min(r, h) * 3 * PI / (2 * h));
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 53c4eeee7bd2141deb8d1179555a1920
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,205 +0,0 @@
#pragma kernel ClearChunks
#pragma kernel ClearGrid
#pragma kernel InsertChunks
#pragma kernel SortParticles
#pragma kernel FindPopulatedLevels
#include "MathUtils.cginc"
#include "Bounds.cginc"
#include "GridUtils.cginc"
#include "FluidChunkDefs.cginc"
#include "SolverParameters.cginc"
// particle data:
StructuredBuffer<int> particleIndices;
StructuredBuffer<float4> positions;
StructuredBuffer<float4> velocities;
StructuredBuffer<float4> angularVelocities;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> fluidMaterial;
StructuredBuffer<float4> fluidData;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> colors;
StructuredBuffer<float4> normals;
RWStructuredBuffer<float4> sortedPositions;
RWStructuredBuffer<float4> sortedPrincipalRadii;
RWStructuredBuffer<float4> sortedVelocities;
RWStructuredBuffer<quaternion> sortedOrientations;
RWStructuredBuffer<float4> sortedColors;
// particle grid data:
StructuredBuffer<aabb> solverBounds;
StructuredBuffer<int> gridHashToSortedIndex;
RWStructuredBuffer<uint> cellOffsets; // start of each cell in the sorted item array.
RWStructuredBuffer<uint> cellCounts; // number of item in each cell.
RWStructuredBuffer<uint> offsetInCell; // for each item, offset within its the cell.
// voxel data:
RWStructuredBuffer<int3> chunkCoords; // for each chunk, spatial coordinates.
RWStructuredBuffer<keyvalue> hashtable; // size: maxChunks entries.
RWStructuredBuffer<uint> dispatchBuffer;
uint firstParticle;
uint particleCount;
float isosurface;
float smoothing;
uint AllocateChunk(uint3 coords)
{
uint key = VoxelID(coords);
uint slot = hash(key);
[allow_uav_condition]
for (uint i = 0; i < maxChunks; ++i) // at most, check the entire table.
{
uint prev;
InterlockedCompareExchange(hashtable[slot].key, INVALID, key, prev);
// allocate new chunk:
if (prev == INVALID)
{
InterlockedAdd(dispatchBuffer[4],1,hashtable[slot].handle);
chunkCoords[hashtable[slot].handle] = coords;
return hashtable[slot].handle;
}
// could not allocate chunk, since it already exists.
else if (prev == key)
{
return INVALID;
}
// collision, try next slot.
else
slot = (slot + 1) % maxChunks;
}
return INVALID; // could not allocate chunk, not enough space.
}
[numthreads(128, 1, 1)]
void ClearChunks (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxChunks)
return;
// clear all chunks:
keyvalue k;
k.key = 0xffffffff;
k.handle = 0xffffffff;
hashtable[i] = k;
}
[numthreads(128, 1, 1)]
void ClearGrid (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxCells)
return;
if (i == 0)
{
for (int l = 0; l <= GRID_LEVELS; ++l)
levelPopulation[l] = 0;
}
// clear all cell counts to zero, and cell offsets to invalid.
cellOffsets[i] = INVALID;
cellCounts[i] = 0;
}
[numthreads(128, 1, 1)]
void InsertChunks (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= particleCount) return;
int p = particleIndices[firstParticle + i];
// ignore inactive particles:
if (principalRadii[p].w <= 0.5f)
return;
// calculate particle cell index:
int level = GridLevelForSize(fluidMaterial[p].x);
float cellSize = CellSizeOfLevel(level);
int4 cellCoord = int4(floor((positions[p].xyz - solverBounds[0].min_.xyz) / cellSize),level);
// if the solver is 2D, project to the z = 0 cell.
if (mode == 1) cellCoord[2] = 0;
// since cell hashes are morton-sorted during collision detection, here we also get sorted cells
// as long as particle cellCoords are reasonably similar
// (won't be the exact same since we use renderable positions instead of positions)
uint cellIndex = gridHashToSortedIndex[GridHash(cellCoord)];
// insert simplex in cell:
InterlockedAdd(cellCounts[cellIndex],1, offsetInCell[p]);
// atomically increase this level's population by one:
InterlockedAdd(levelPopulation[1 + level],1);
//in 3D, only particles near the surface should spawn chunks:
//if (mode == 0 && dot(normals[p],normals[p]) < 0.0001f)
//return;
// expand aabb by voxel size, since boundary voxels (at a chunks' 0 X/Y/Z) can't be triangulated.
float radius = fluidMaterial[p].x + voxelSize;
// calculate particle chunk span.
float chunkSize = chunkResolution * voxelSize;
uint3 minCell = floor((positions[p].xyz - radius - chunkGridOrigin) / chunkSize);
uint3 maxCell = floor((positions[p].xyz + radius - chunkGridOrigin) / chunkSize);
if (mode == 1)
minCell[2] = maxCell[2] = 0;
for (uint x = minCell[0]; x <= maxCell[0]; ++x)
{
for (uint y = minCell[1]; y <= maxCell[1]; ++y)
{
for (uint z = minCell[2]; z <= maxCell[2]; ++z)
{
AllocateChunk(uint3(x, y, z));
}
}
}
}
[numthreads(128, 1, 1)]
void SortParticles (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= particleCount) return;
int p = particleIndices[firstParticle + i];
// ignore inactive particles:
if (principalRadii[p].w <= 0.5f)
return;
// calculate particle cell index:
int level = GridLevelForSize(fluidMaterial[p].x);
float cellSize = CellSizeOfLevel(level);
int4 cellCoord = int4(floor((positions[p].xyz - solverBounds[0].min_.xyz) / cellSize),level);
// if the solver is 2D, project to the z = 0 cell.
if (mode == 1) cellCoord[2] = 0;
uint cellIndex = gridHashToSortedIndex[GridHash(cellCoord)];
// find final sorted index:
uint gridIndex = cellOffsets[cellIndex] + offsetInCell[p];
// write particle data in sorted order:
sortedPositions[gridIndex] = float4(positions[p].xyz, 1 / fluidData[p].x);
sortedPrincipalRadii[gridIndex] = fluidMaterial[p].x * (principalRadii[p] / principalRadii[p].x);
sortedVelocities[gridIndex] = float4(velocities[p].xyz, (asuint(angularVelocities[p].w) & 0x0000ffff) / 65535.0);
sortedOrientations[gridIndex] = orientations[p];
sortedColors[gridIndex] = colors[p];
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 745eb95f8ca884ea6830b3ca14ee05a0
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,604 +0,0 @@
#pragma kernel SampleSDF
#pragma kernel CalculateSurface
#pragma kernel Triangulate
#pragma kernel Smoothing
#pragma kernel FixArgsBuffer
#pragma kernel FillIndirectDrawBuffer
#include "MathUtils.cginc"
#include "Bounds.cginc"
#include "GridUtils.cginc"
#include "FluidChunkDefs.cginc"
#include "FluidKernels.cginc"
#include "SolverParameters.cginc"
#include "NormalCompression.cginc"
/*
* y z
* ^ /
* |
* 6----7
* /| /|
* 2----3 |
* | 4--|-5
* |/ |/
* 0----1 --> x
*
*/
/*
* static Vector2Int[] cubeEdges =
* {
* new Vector2Int(7,3), 0
* new Vector2Int(7,5), 1
* new Vector2Int(7,6), 2
*
* new Vector2Int(6,4), 3 // x
* new Vector2Int(4,5), 4
* new Vector2Int(5,1), 5
*
* new Vector2Int(1,3), 6 // y
* new Vector2Int(3,2), 7
* new Vector2Int(2,6), 8
*
* new Vector2Int(4,0), 9 // z
* new Vector2Int(2,0), 10
* new Vector2Int(1,0) 11
* };
*/
static const int4 faceNeighborEdges[] =
{
int4(0,1,5,6),
int4(0,2,7,8),
int4(4,5,9,11),
int4(3,8,9,10),
int4(6,7,10,11),
int4(1,2,3,4)
};
static const float3 corners[] =
{
float3(0, 0, 0), // 0
float3(1, 0, 0), // 1
float3(0, 1, 0), // 2
float3(1, 1, 0), // 3
float3(0, 0, 1), // 4
float3(1, 0, 1), // 5
float3(0, 1, 1), // 6
float3(1, 1, 1) // 7
};
static const int3 quadNeighborIndices[] =
{
int3(1, 3, 2), // x
int3(4, 5, 1), // y
int3(2, 6, 4), // z
};
static const int oppositeFaces[] =
{
7, //0
6, //1
5, //2
4, //3
3, //4
2, //5
1 //6
};
static const int3 quadWindingOrder[] = {
int3(0, 1 ,2),
int3(2, 1 ,0)
};
struct indirectDrawIndexedArgs
{
uint indexCountPerInstance;
uint instanceCount;
uint startIndex;
uint baseVertexIndex;
uint startInstance;
};
// particle grid data:
StructuredBuffer<aabb> solverBounds;
StructuredBuffer<uint> cellOffsets; // start of each cell in the sorted item array.
StructuredBuffer<uint> cellCounts; // number of item in each cell.
StructuredBuffer<int> gridHashToSortedIndex;
StructuredBuffer<float4> sortedPositions;
StructuredBuffer<float4> sortedVelocities;
StructuredBuffer<float4> sortedPrincipalRadii;
StructuredBuffer<float4> sortedColors;
StructuredBuffer<quaternion> sortedOrientations;
// voxel data:
StructuredBuffer<int3> chunkCoords; // for each chunk, spatial coordinates.
StructuredBuffer<keyvalue> hashtable; // size: maxChunks entries.
RWStructuredBuffer<uint> voxelToVertex; // for each voxel, index into the vertices array in case the voxel spawns a vertex, INVALID otherwise.
RWStructuredBuffer<uint> vertexAdjacency; // indices of adjacent face voxels for each voxel.
RWStructuredBuffer<float4> voxelVelocities; // for each voxel, fluid velocity value. We are reusing the same ComputeBuffer used for vertexAdjacency.
// edge LUTs
StructuredBuffer<int2> edges;
StructuredBuffer<int> edgeTable;
// mesh data:
RWStructuredBuffer<float4> verts;
RWStructuredBuffer<float4> outputVerts;
RWStructuredBuffer<float4> colors;
RWStructuredBuffer<float4> velocities;
RWStructuredBuffer<int> quads;
RWStructuredBuffer<uint> dispatchBuffer;
RWStructuredBuffer<uint> dispatchBuffer2;
RWStructuredBuffer<indirectDrawIndexedArgs> indirectBuffer;
uint currentBatch;
float isosurface;
uint descentIterations;
float descentIsosurface;
float descentSpeed;
float smoothing;
float bevel;
uint dispatchMultiplier;
uint countMultiplier;
uint instanceCount;
uint voxelCoordToOffset(int3 coord)
{
if (mode == 1)
return (int)EncodeMorton2((uint2)coord.xy);
else return (int)EncodeMorton3((uint3)coord.xyz);
}
uint LookupChunk(int3 coords)
{
uint key = VoxelID(coords);
uint slot = hash(key);
for (uint i = 0; i < maxChunks; ++i) // at most, check the entire table.
{
if (hashtable[slot].key == key)
{
return hashtable[slot].handle;
}
if (hashtable[slot].key == INVALID)
{
return INVALID;
}
slot = (slot + 1) % maxChunks;
}
return INVALID;
}
uint GetVoxelIndex(int3 chunkCoords, int3 voxelCoords, int voxelsInChunk)
{
int3 mask = voxelCoords < 0 ? -1 : voxelCoords / chunkResolution;
uint chunk = LookupChunk(chunkCoords + mask);
return chunk == INVALID ? INVALID : chunk * voxelsInChunk + voxelCoordToOffset(nfmod(voxelCoords, chunkResolution));
}
[numthreads(128, 1, 1)]
void SampleSDF (uint3 id : SV_DispatchThreadID) // one thread per voxel,
{
uint i = id.x;
if (i >= dispatchBuffer[3]) return;
int voxelsInChunk = (int)pow(chunkResolution, 3 - mode); // 64 voxels in 3D, 16 in 2D.
// calculate chunk index:
int chunkIndex = i / voxelsInChunk;
// get offset of voxel within chunk:
int cornerOffset = i - chunkIndex * voxelsInChunk;
int3 voxelCoords;
if (mode == 1)
voxelCoords = (int3)DecodeMorton2((uint)cornerOffset);
else
voxelCoords = (int3)DecodeMorton3((uint)cornerOffset);
int3 cornerCoords= chunkCoords[chunkIndex] * chunkResolution + voxelCoords;
// calculate sampling position:
float3 samplePos = chunkGridOrigin + cornerCoords * voxelSize;
float dist = isosurface;
float4 color = FLOAT4_ZERO;
float4 velocity = FLOAT4_ZERO;
for (uint m = 1; m <= levelPopulation[0]; ++m)
{
uint l = levelPopulation[m];
float cellSize = CellSizeOfLevel(l);
int3 cell = floor((samplePos - solverBounds[0].min_.xyz) / cellSize); // TODO: not necessary to subtract solverBounds?
int3 minCell = cell - 1;
int3 maxCell = cell + 1;
if (mode == 1)
minCell[2] = maxCell[2] = 0;
for (int x = minCell[0]; x <= maxCell[0]; ++x)
{
for (int y = minCell[1]; y <= maxCell[1]; ++y)
{
for (int z = minCell[2]; z <= maxCell[2]; ++z)
{
int4 neighborCoord = int4(x, y, z, l);
int cellIndex = gridHashToSortedIndex[GridHash(neighborCoord)];
uint n = cellOffsets[cellIndex];
uint end = n + cellCounts[cellIndex];
for (;n < end; ++n)
{
float3 radii = sortedPrincipalRadii[n].xyz;
// due to hash collisions, two neighboring cells might map to the same
// hash bucket, and we'll add the same set of particles twice to the neighbors list.
// So we only consider particles that have the same spatial coordinates as the cell.
uint level = GridLevelForSize(radii.x);
float cellSize = CellSizeOfLevel(level);
int4 particleCoord = int4(floor((sortedPositions[n].xyz - solverBounds[0].min_.xyz)/ cellSize).xyz,level);
if (any (particleCoord - neighborCoord))
continue;
float3 normal = samplePos - sortedPositions[n].xyz;
if (mode == 1)
normal[2] = 0;
// only update distance if within anisotropic kernel radius:
float maxDistance = radii.x + voxelSize * 1.42f;
float r = dot(normal, normal);
if (r <= maxDistance * maxDistance)
{
normal = rotate_vector(q_conj(sortedOrientations[n]), normal.xyz) / radii;
float d = length(normal) * radii.x;
// sortedPositions.w is volume (1/normalized density):
float w = sortedPositions[n].w * Poly6(d,radii.x);
dist -= w;
// tigther smoothing kernel for color and velocities:
float w2 = 1-saturate(r / (radii.x * radii.x));
color += float4(sortedColors[n].xyz * w2, w2);
velocity += sortedVelocities[n] * w2; // 4th component is length(angularVel), vorticity intensity.
}
}
}
}
}
}
verts[i].x = dist;
verts[i].y = PackFloatRGBA(color/color.w);
voxelVelocities[i] = velocity / color.w;
}
float EvaluateSDF (float4 distancesA, float4 distancesB, in float3 nPos, out float3 normal)
{
// trilinear interpolation of distance:
float4 x = distancesA + (distancesB - distancesA) * nPos[0];
float2 y = x.xy + (x.zw - x.xy) * nPos[1];
// gradient estimation:
// x == 0
float2 a = distancesA.xy + (distancesA.zw - distancesA.xy) * nPos[1];
float x0 = a[0] + (a[1] - a[0]) * nPos[2];
// x == 1
a = distancesB.xy + (distancesB.zw - distancesB.xy) * nPos[1];
float x1 = a[0] + (a[1] - a[0]) * nPos[2];
// y == 0
float y0 = x[0] + (x[1] - x[0]) * nPos[2];
// y == 1
float y1 = x[2] + (x[3] - x[2]) * nPos[2];
normal = normalize(float3(x1 - x0, y1 - y0, y[1] - y[0]));
return y[0] + (y[1] - y[0]) * nPos[2];
}
[numthreads(128, 1, 1)]
void CalculateSurface (uint3 id : SV_DispatchThreadID) // once per voxel.
{
uint i = id.x;
if (i >= dispatchBuffer[3]) return;
int voxelsInChunk = (int)pow(chunkResolution, 3 - mode); // 64 voxels in 3D, 16 in 2D.
int verticesPerVoxel = mode == 1 ? 4 : 8; // 8 vertices in 3D, 4 in 2D.
float3 dimensionMask = mode == 1 ? float3(1, 1, 0) : float3(1, 1, 1);
int edgeCount = mode == 1 ? 4 : 12;
// initialize voxel with invalid vertex:
voxelToVertex[i] = INVALID;
// calculate chunk index:
int chunkIndex = i / voxelsInChunk;
// get offset of voxel within chunk:
int voxelOffset = i - chunkIndex * voxelsInChunk;
int3 voxelCoords;
if (mode == 1)
voxelCoords = (int3)DecodeMorton2((uint)voxelOffset);
else
voxelCoords = (int3)DecodeMorton3((uint)voxelOffset);
// get samples at voxel corners:
float samples[8];
float4 vcolor[8];
float4 vvelo[8];
uint cornerMask = 0;
// initialize all samples to zero (last 4 samples aren't written to in 2D).
int j = 0;
for (j = 0; j < 8; ++j)
samples[j] = 0;
for (j = 0; j < verticesPerVoxel; ++j)
{
uint v = GetVoxelIndex(chunkCoords[chunkIndex], voxelCoords + corners[j], voxelsInChunk);
if (v == INVALID)
return;
else
{
samples[j] = verts[v].x;
vcolor[j] = UnpackFloatRGBA(verts[v].y);
vvelo[j] = voxelVelocities[v];
cornerMask |= samples[j] >= 0 ? (1 << j) : 0;
}
}
// store cornerMask:
verts[i].z = asfloat(cornerMask);
int edgeMask = edgeTable[cornerMask];
// if the voxel does not intersect the surface, return:
if ((mode == 1 && cornerMask == 0xf) ||
(mode == 0 && edgeMask == 0))
return;
// calculate vertex position using edge crossings:
float3 normalizedPos = float3(0,0,0);
float4 velocity = FLOAT4_ZERO;
float4 color = FLOAT4_ZERO;
int intersections = 0;
for (j = 0; j < edgeCount; ++j)
{
if((edgeMask & (1 << j)) == 0)
continue;
int2 e = edges[j];
float t = -samples[e.x] / (samples[e.y] - samples[e.x]);
normalizedPos += lerp(corners[e.x],corners[e.y],t);
color += lerp(vcolor[e.x], vcolor[e.y], t);
velocity += lerp(vvelo[e.x], vvelo[e.y], t);
intersections++;
}
// intersections will always be > 0 in 3D:
if (intersections > 0)
{
normalizedPos /= intersections;
color /= intersections;
velocity /= intersections;
}
else
{
normalizedPos = float3(0.5f, 0.5f, -bevel);
color = (vcolor[0] + vcolor[1] + vcolor[2] + vcolor[3]) * 0.25f;
velocity = (vvelo[0] + vvelo[1] + vvelo[2] + vvelo[3]) * 0.25f;
}
float4 distancesA = float4(samples[0], samples[4], samples[2], samples[6]);
float4 distancesB = float4(samples[1], samples[5], samples[3], samples[7]);
// gradient descent:
float3 normal;
for(uint k = 0; k < descentIterations; ++k)
{
float d = EvaluateSDF(distancesA, distancesB, normalizedPos, normal);
normalizedPos -= descentSpeed * normal * (d + isosurface + descentIsosurface);
}
// final normal evaluation:
EvaluateSDF(distancesA, distancesB, normalizedPos, normal);
// modify normal in 2D mode:
if (mode)
normal = lerp(float3(0,0,-1),float3(normal.xy,-normal.z),bevel); // no bevel, flat normals
// Append vertex:
InterlockedAdd(dispatchBuffer[4],1,voxelToVertex[i]);
verts[voxelToVertex[i]].w = i;
float3 voxelCorner = chunkGridOrigin + (float3)(chunkCoords[chunkIndex] * chunkResolution + voxelCoords) * voxelSize;
outputVerts[voxelToVertex[i]] = float4(voxelCorner * dimensionMask + normalizedPos * voxelSize, encode(normal));
colors[voxelToVertex[i]] = color;
velocities[voxelToVertex[i]] = velocity;
}
[numthreads(128, 1, 1)]
void Triangulate (uint3 id : SV_DispatchThreadID)
{
uint v0 = id.x;
if (v0 >= dispatchBuffer[3]) return;
int voxelsInChunk = (int)pow(chunkResolution, 3 - mode); // 64 voxels in 3D, 16 in 2D.
int quadCount = 3 - mode * 2; // 3 quads in 3D, 1 in 2D.
int adjacentCount = 6 - mode * 2; // 6 adjacent voxels in 3D, 4 in 2D.
// get index of the voxel that spawned this vertex:
uint i = verts[v0].w;
// calculate chunk index and look up coordinates:
int chunkIndex = i / voxelsInChunk;
// get offset of voxel within chunk:
int voxelOffset = i - chunkIndex * voxelsInChunk;
int3 voxelCoords;
if (mode == 1)
voxelCoords = (int3)DecodeMorton2((uint)voxelOffset);
else
voxelCoords = (int3)DecodeMorton3((uint)voxelOffset);
uint cornerMask = asuint(verts[i].z);
int edgeMask = edgeTable[cornerMask];
// get winding order using last bit of cornermask, which indicates corner sign:
// in 2D, cornerMask >> 7 is always 0, so we get the second winding order.
int3 windingOrder = (cornerMask >> 7) ? quadWindingOrder[0] : quadWindingOrder[1];
// Retrieve adjacent voxels:
int j;
uint adjacent[6];
for (j = 0; j < adjacentCount; ++j)
adjacent[j] = GetVoxelIndex(chunkCoords[chunkIndex], voxelCoords + corners[j + 1], voxelsInChunk);
// Iterate over all potential quads, append those needed:
for (j = 0; j < quadCount; ++j)
{
// if the edge is not crossing the surface, skip it (3D only)
if (mode == 0 && (edgeMask & (1 << j)) == 0)
continue;
// calculate final neighbor indices:
uint3 neighbors = uint3(quadNeighborIndices[j][windingOrder[0]]-1,
quadNeighborIndices[j][windingOrder[1]]-1,
quadNeighborIndices[j][windingOrder[2]]-1);
// get vertex indices for all voxels involved:
uint v1 = voxelToVertex[adjacent[neighbors[0]]];
uint v2 = voxelToVertex[adjacent[neighbors[1]]];
uint v3 = voxelToVertex[adjacent[neighbors[2]]];
// if any of the vertices is invalid, skip the quad:
if (v1 == INVALID || v2 == INVALID || v3 == INVALID)
continue;
// append a new quad:
uint baseIndex;
InterlockedAdd(dispatchBuffer2[4],1,baseIndex);
baseIndex *= 6;
// flip edge if necessary, to always use the shortest diagonal:
float3 diag1 = outputVerts[v0].xyz - outputVerts[v2].xyz;
float3 diag2 = outputVerts[v1].xyz - outputVerts[v3].xyz;
if (dot(diag1,diag1) > dot(diag2,diag2) * 1.1)
{
quads[baseIndex] = v1;
quads[baseIndex+1] = v2;
quads[baseIndex+2] = v3;
quads[baseIndex+3] = v0;
quads[baseIndex+4] = v1;
quads[baseIndex+5] = v3;
}
else
{
quads[baseIndex] = v0;
quads[baseIndex+1] = v1;
quads[baseIndex+2] = v2;
quads[baseIndex+3] = v3;
quads[baseIndex+4] = v0;
quads[baseIndex+5] = v2;
}
}
// Move adjacent voxel in Z axis to last position, so that 2D adjacent voxels are the first 4.
adjacent[5] = adjacent[3];
adjacent[2] = GetVoxelIndex(chunkCoords[chunkIndex], voxelCoords + int3(0, -1, 0), voxelsInChunk);
adjacent[3] = GetVoxelIndex(chunkCoords[chunkIndex], voxelCoords + int3(-1, 0, 0), voxelsInChunk);
adjacent[4] = GetVoxelIndex(chunkCoords[chunkIndex], voxelCoords + int3(0, 0, -1), voxelsInChunk);
// initialize vertex adjacency to INVALID.
for (j = 0; j < 6; ++j)
vertexAdjacency[v0*6 + j] = INVALID;
// Determine adjacent surface voxels for smoothing:
bool isAdjacent;
for (j = 0; j < adjacentCount; ++j)
{
if (adjacent[j] != INVALID)
{
// adjacent if this does not intersect the surface or both intersect the surface.
isAdjacent = (edgeMask == 0 || edgeTable[asuint(verts[adjacent[j]].z)] != 0) &&
// in 3D mode, it should also intersect any of the face edges to be considered adjacent:
(mode == 1 || any(edgeMask & (1 << faceNeighborEdges[j])));
vertexAdjacency[v0 * 6 + j] = isAdjacent ? voxelToVertex[adjacent[j]] : INVALID;
}
}
}
[numthreads(128, 1, 1)]
void Smoothing (uint3 id : SV_DispatchThreadID)
{
uint thread = id.x;
if (thread >= dispatchBuffer[3]) return;
float3 n = decode(verts[thread].w);
float4 coord = float4(verts[thread].xyz,1);
float4 norm = float4(n,1);
for (int j = 0; j < 6; ++j)
{
uint v = vertexAdjacency[thread*6 + j];
if (v != INVALID)
{
coord += float4(verts[v].xyz,1);
norm += float4(decode(verts[v].w),1);
}
}
coord.xyz /= coord.w;
norm.xyz /= norm.w;
float3 v = lerp(verts[thread].xyz, coord.xyz, smoothing);
n = normalize(lerp(n,norm.xyz, smoothing));
outputVerts[thread] = float4(v, encode(n));
}
[numthreads(1, 1, 1)]
void FixArgsBuffer (uint3 id : SV_DispatchThreadID)
{
dispatchBuffer[3] = dispatchBuffer[4] * dispatchMultiplier;
dispatchBuffer[0] = dispatchBuffer[3] / 128 + 1;
dispatchBuffer[4] *= countMultiplier; // used to zero out fourth component if needed.
}
[numthreads(1, 1, 1)]
void FillIndirectDrawBuffer (uint3 id : SV_DispatchThreadID)
{
indirectDrawIndexedArgs a;
a.indexCountPerInstance = dispatchBuffer[3] * 6; // number of quads * 6
a.instanceCount = instanceCount;
a.startIndex = 0;
a.baseVertexIndex = 0;
a.startInstance = 0;
indirectBuffer[0] = a;
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 676e30b71ac3147969895b3f0ad6d2ec
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,166 +0,0 @@
#ifndef GRIDUTILS_INCLUDE
#define GRIDUTILS_INCLUDE
#define INVALID 0xFFFFFFFF
#define MIN_GRID_LEVEL -8 //-6 // minimum cell size is 0.004 meters, enough for very small colliders / particles (log(0.004) / log(2))
#define MAX_GRID_LEVEL 15 // maximum cell size is 32768 meters, enough for gargantuan objects.
#define GRID_LEVELS (MAX_GRID_LEVEL - MIN_GRID_LEVEL + 1)
RWStructuredBuffer<uint> levelPopulation;
uint maxCells; // maximum number of unique cells in the grid.
static const float4 cellNeighborhood[27] = {
float4(-1,-1,-1, 0),
float4(-1,-1, 0, 0),
float4(-1,-1, 1, 0),
float4(-1, 0,-1, 0),
float4(-1, 0, 0, 0),
float4(-1, 0, 1, 0),
float4(-1, 1,-1, 0),
float4(-1, 1, 0, 0),
float4(-1, 1, 1, 0),
float4( 0,-1,-1, 0),
float4( 0,-1, 0, 0),
float4( 0,-1, 1, 0),
float4( 0, 0,-1, 0),
float4( 0, 0, 0, 0),
float4( 0, 0, 1, 0),
float4( 0, 1,-1, 0),
float4( 0, 1, 0, 0),
float4( 0, 1, 1, 0),
float4( 1,-1,-1, 0),
float4( 1,-1, 0, 0),
float4( 1,-1, 1, 0),
float4( 1, 0,-1, 0),
float4( 1, 0, 0, 0),
float4( 1, 0, 1, 0),
float4( 1, 1,-1, 0),
float4( 1, 1, 0, 0),
float4( 1, 1, 1, 0),
};
static const float4 aheadCellNeighborhood[13] = {
float4(1,0,0,0), // + , 0 , 0 ( 1)
float4(0,1,0,0), // 0 , + , 0 ( 3)
float4(1,1,0,0), // + , + , 0 ( 4)
float4(0,0,1,0), // 0 , 0 , + ( 9)
float4(1,0,1,0), // + , 0 , + (10)
float4(0,1,1,0), // 0 , + , + (12)
float4(1,1,1,0), // + , + , + (13)
float4(-1,1,0,0), // - , + , 0 ( 2)
float4(-1,-1,1,0), // - , - , + ( 5)
float4(0,-1,1,0), // 0 , - , + ( 6)
float4(1,-1,1,0), // + , - , + ( 7)
float4(-1,0,1,0), // - , 0 , + ( 8)
float4(-1,1,1,0), // - , + , + (11)
};
[numthreads(1,1,1)]
void FindPopulatedLevels (uint3 id : SV_DispatchThreadID)
{
for (int l = 1; l <= GRID_LEVELS; ++l)
{
if (levelPopulation[l] > 0)
levelPopulation[1 + levelPopulation[0]++] = l - 1;
}
}
inline int GridLevelForSize(float size)
{
// the magic number is 1/log(2), used because log_a(x) = log_b(x) / log_b(a)
// level is clamped between MIN_LEVEL and MAX_LEVEL, then remapped to (0, MAX_LEVEL - MIN_LEVEL)
// this allows us to avoid InterlockedMax issues on GPU, since it doesn't work on negative numbers on some APIs.
return clamp((int)ceil(log(size) * 1.44269504089), MIN_GRID_LEVEL, MAX_GRID_LEVEL) - MIN_GRID_LEVEL;
}
inline float CellSizeOfLevel(int level)
{
return exp2(level + MIN_GRID_LEVEL);
}
inline int4 GetParentCellCoords(int4 cellCoords, uint level)
{
float decimation = exp2(level - cellCoords[3]);
int4 cell = (int4)floor((float4)cellCoords / decimation);
cell[3] = level;
return cell;
}
uint Part1By1(uint x)
{
x &= 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
return x;
}
// "Insert" two 0 bits after each of the 10 low bits of x
uint Part1By2(uint x)
{
x &= 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
return x;
}
uint Compact1By1(uint x)
{
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
return x;
}
uint Compact1By2(uint x)
{
x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
return x;
}
inline uint EncodeMorton2(uint2 coords)
{
return (Part1By1(coords.y) << 1) + Part1By1(coords.x);
}
inline uint EncodeMorton3(uint3 coords)
{
return (Part1By2(coords.z) << 2) + (Part1By2(coords.y) << 1) + Part1By2(coords.x);
}
inline uint3 DecodeMorton2(uint code)
{
return uint3(Compact1By1(code >> 0), Compact1By1(code >> 1), 0);
}
inline uint3 DecodeMorton3(uint code)
{
return uint3(Compact1By2(code >> 0), Compact1By2(code >> 1), Compact1By2(code >> 2));
}
inline uint GridHash(in int4 cellIndex)
{
return (73856093*cellIndex.x ^
19349663*cellIndex.y ^
83492791*cellIndex.z ^
10380569*cellIndex.w) % maxCells;
}
inline uint GridHash(in int3 cellIndex)
{
return (73856093*cellIndex.x ^
19349663*cellIndex.y ^
83492791*cellIndex.z) % maxCells;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: b4dbf43981a3b419fbc2eaf5f2c05a6c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,198 +0,0 @@
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "DistanceFunctions.cginc"
#include "Simplex.cginc"
#include "Bounds.cginc"
#include "SolverParameters.cginc"
#include "Optimization.cginc"
#pragma kernel GenerateContacts
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<int> simplices;
StructuredBuffer<aabb> simplexBounds; // bounding box of each simplex.
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
// heightfield data:
StructuredBuffer<HeightFieldHeader> heightFieldHeaders;
StructuredBuffer<float> heightFieldSamples;
StructuredBuffer<uint2> contactPairs;
StructuredBuffer<int> contactOffsetsPerType;
RWStructuredBuffer<contact> contacts;
RWStructuredBuffer<uint> dispatchBuffer;
StructuredBuffer<transform> worldToSolver;
uint maxContacts;
float deltaTime;
[numthreads(128, 1, 1)]
void GenerateContacts (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
// entry #11 in the dispatch buffer is the amount of pairs for the first shape type.
if (i >= dispatchBuffer[11 + 4 * HEIGHTMAP_SHAPE]) return;
int firstPair = contactOffsetsPerType[HEIGHTMAP_SHAPE];
int simplexIndex = contactPairs[firstPair + i].x;
int colliderIndex = contactPairs[firstPair + i].y;
shape s = shapes[colliderIndex];
if (s.dataIndex < 0) return;
HeightFieldHeader header = heightFieldHeaders[s.dataIndex];
Heightfield fieldShape;
fieldShape.colliderToSolver = worldToSolver[0].Multiply(transforms[colliderIndex]);
fieldShape.s = s;
// invert a full matrix here to accurately represent collider bounds scale.
float4x4 solverToCollider = Inverse(TRS(fieldShape.colliderToSolver.translation.xyz, fieldShape.colliderToSolver.rotation, fieldShape.colliderToSolver.scale.xyz));
aabb simplexBound = simplexBounds[simplexIndex].Transformed(solverToCollider);
int simplexSize;
int simplexStart = GetSimplexStartAndSize(simplexIndex, simplexSize);
int resolutionU = (int)s.center.x;
int resolutionV = (int)s.center.y;
// calculate terrain cell size:
float cellWidth = s.size.x / (resolutionU - 1);
float cellHeight = s.size.z / (resolutionV - 1);
// calculate particle bounds min/max cells:
int2 min_ = int2((int)floor(simplexBound.min_[0] / cellWidth), (int)floor(simplexBound.min_[2] / cellHeight));
int2 max_ = int2((int)floor(simplexBound.max_[0] / cellWidth), (int)floor(simplexBound.max_[2] / cellHeight));
for (int su = min_[0]; su <= max_[0]; ++su)
{
if (su >= 0 && su < resolutionU - 1)
{
for (int sv = min_[1]; sv <= max_[1]; ++sv)
{
if (sv >= 0 && sv < resolutionV - 1)
{
// calculate neighbor sample indices:
int csu1 = clamp(su + 1, 0, resolutionU - 1);
int csv1 = clamp(sv + 1, 0, resolutionV - 1);
// sample heights:
float h1 = heightFieldSamples[header.firstSample + sv * resolutionU + su] * s.size.y;
float h2 = heightFieldSamples[header.firstSample + sv * resolutionU + csu1] * s.size.y;
float h3 = heightFieldSamples[header.firstSample + csv1 * resolutionU + su] * s.size.y;
float h4 = heightFieldSamples[header.firstSample + csv1 * resolutionU + csu1] * s.size.y;
if (h1 < 0) continue;
h1 = abs(h1);
h2 = abs(h2);
h3 = abs(h3);
h4 = abs(h4);
float min_x = su * s.size.x / (resolutionU - 1);
float max_x = csu1 * s.size.x / (resolutionU - 1);
float min_z = sv * s.size.z / (resolutionV - 1);
float max_z = csv1 * s.size.z / (resolutionV - 1);
float4 convexPoint;
float4 simplexBary = BarycenterForSimplexOfSize(simplexSize);
// ------contact against the first triangle------:
float4 v1 = float4(min_x, h3, max_z, 0);
float4 v2 = float4(max_x, h4, max_z, 0);
float4 v3 = float4(min_x, h1, min_z, 0);
fieldShape.tri.Cache(v1, v2, v3);
fieldShape.triNormal.xyz = normalizesafe(cross((v2 - v1).xyz, (v3 - v1).xyz));
SurfacePoint colliderPoint = Optimize(fieldShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, convexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
float4 velocity = FLOAT4_ZERO;
float simplexRadius = 0;
int j;
for (j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexRadius += principalRadii[particleIndex].x * simplexBary[j];
velocity += velocities[particleIndex] * simplexBary[j];
}
float dAB = dot(convexPoint - colliderPoint.pos, colliderPoint.normal);
float vel = dot(velocity, colliderPoint.normal);
if (vel * deltaTime + dAB <= simplexRadius + s.contactOffset + collisionMargin)
{
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
contact c = (contact)0;
c.pointB = colliderPoint.pos;
c.normal = colliderPoint.normal * fieldShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
// ------contact against the second triangle------:
v1 = float4(min_x, h1, min_z, 0);
v2 = float4(max_x, h4, max_z, 0);
v3 = float4(max_x, h2, min_z, 0);
fieldShape.tri.Cache(v1, v2, v3);
fieldShape.triNormal.xyz = normalizesafe(cross((v2 - v1).xyz, (v3 - v1).xyz));
colliderPoint = Optimize(fieldShape, positions, orientations, principalRadii,
simplices, simplexStart, simplexSize, simplexBary, convexPoint, surfaceCollisionIterations, surfaceCollisionTolerance);
velocity = FLOAT4_ZERO;
simplexRadius = 0;
for (j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
simplexRadius += principalRadii[particleIndex].x * simplexBary[j];
velocity += velocities[particleIndex] * simplexBary[j];
}
dAB = dot(convexPoint - colliderPoint.pos, colliderPoint.normal);
vel = dot(velocity, colliderPoint.normal);
if (vel * deltaTime + dAB <= simplexRadius + s.contactOffset + collisionMargin)
{
uint count = contacts.IncrementCounter();
if (count < maxContacts)
{
contact c = (contact)0;
c.pointB = colliderPoint.pos;
c.normal = colliderPoint.normal * fieldShape.s.isInverted();
c.pointA = simplexBary;
c.bodyA = simplexIndex;
c.bodyB = colliderIndex;
contacts[count] = c;
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
}
}
}
}
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d9efd384bb82b4ec0b0adf1fc349a89b
ComputeShaderImporter:
externalObjects: {}
preprocessorOverride: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,23 +0,0 @@
#ifndef INERTIALFRAME_INCLUDE
#define INERTIALFRAME_INCLUDE
#include "Transform.cginc"
struct inertialFrame
{
transform frame;
transform prevFrame;
float4 velocity;
float4 angularVelocity;
float4 acceleration;
float4 angularAcceleration;
float4 velocityAtPoint(float4 pnt)
{
return velocity + float4(cross(angularVelocity.xyz, (pnt - prevFrame.translation).xyz), 0);
}
};
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 2d21eb1b3353a439eb5f6f6df05dd6d0
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,44 +0,0 @@
#pragma kernel UpdateParticleInstances
#include "PathFrame.cginc"
struct RendererData
{
float4 color;
float radiusScale;
};
StructuredBuffer<int> activeParticles;
StructuredBuffer<RendererData> rendererData;
StructuredBuffer<int> rendererIndex;
StructuredBuffer<float4> renderablePositions;
StructuredBuffer<quaternion> renderableOrientations;
StructuredBuffer<float4> renderableRadii;
StructuredBuffer<float4> colors;
float4x4 solverToWorld;
RWStructuredBuffer<float4x4> instanceTransforms;
RWStructuredBuffer<float4x4> invInstanceTransforms;
RWStructuredBuffer<float4> instanceColors;
uint particleCount;
[numthreads(128, 1, 1)]
void UpdateParticleInstances (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= particleCount) return;
int p = activeParticles[i];
float4x4 tfrm = TRS(renderablePositions[p].xyz,
renderableOrientations[p],
renderableRadii[p].xyz * renderableRadii[p][3] * rendererData[rendererIndex[i]].radiusScale);
instanceTransforms[i] = mul(solverToWorld, tfrm);
instanceColors[i] = colors[p] * rendererData[rendererIndex[i]].color;
invInstanceTransforms[i] = Inverse(instanceTransforms[i]);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d2b27c5c6c8154b6694d7e017468ccc0
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,38 +0,0 @@
#ifndef INTEGRATION_INCLUDE
#define INTEGRATION_INCLUDE
#include "Quaternion.cginc"
float4 IntegrateLinear(float4 position, float4 velocity, float dt)
{
return position + velocity * dt;
}
float4 DifferentiateLinear(float4 position, float4 prevPosition, float dt)
{
return (position - prevPosition) / dt;
}
quaternion AngularVelocityToSpinQuaternion(quaternion rotation, float4 angularVelocity, float dt)
{
quaternion delta = quaternion(angularVelocity.x,
angularVelocity.y,
angularVelocity.z, 0);
return quaternion(0.5f * qmul(delta,rotation) * dt);
}
quaternion IntegrateAngular(quaternion rotation, float4 angularVelocity, float dt)
{
rotation += AngularVelocityToSpinQuaternion(rotation,angularVelocity, dt);
return normalize(rotation);
}
float4 DifferentiateAngular(quaternion rotation, quaternion prevRotation, float dt)
{
quaternion deltaq = qmul(rotation, q_conj(prevRotation));
float s = deltaq.w >= 0 ? 1 : -1;
return float4(s * deltaq.xyz * 2.0f / dt, 0);
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: d436b0155e8f943d59a26290140664cd
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,46 +0,0 @@
#ifndef INTERLOCKEDUTILS_INCLUDE
#define INTERLOCKEDUTILS_INCLUDE
void InterlockedAddFloat(RWStructuredBuffer<uint4> buffer, int index, int axis, float value)
{
uint i_val = asuint(value);
uint tmp0 = 0;
uint tmp1;
[allow_uav_condition]
while (true)
{
InterlockedCompareExchange(buffer[index][axis], tmp0, i_val, tmp1);
if (tmp1 == tmp0)
break;
tmp0 = tmp1;
i_val = asuint(value + asfloat(tmp1));
}
return;
}
void InterlockedAddFloat(RWStructuredBuffer<uint> buffer, int index, float value)
{
uint i_val = asuint(value);
uint tmp0 = 0;
uint tmp1;
[allow_uav_condition]
while (true)
{
InterlockedCompareExchange(buffer[index], tmp0, i_val, tmp1);
if (tmp1 == tmp0)
break;
tmp0 = tmp1;
i_val = asuint(value + asfloat(tmp1));
}
return;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: bb532ce1395da4d13b47a4da3e1b4033
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,648 +0,0 @@
#ifndef MATHUTILS_INCLUDE
#define MATHUTILS_INCLUDE
#define PI 3.14159265359f
#define SQRT2 1.41421356237f
#define SQRT3 1.73205080757f
#define EPSILON 0.0000001f
#define FLT_MAX 3.402823466e+38
#define FLT_MIN 1.175494351e-38
#define FLOAT4_ZERO float4(0, 0, 0, 0)
#define FLOAT4_EPSILON float4(EPSILON, EPSILON, EPSILON, EPSILON)
#define zero 0
#define one 1
#define PHASE_SELFCOLLIDE (1 << 24)
#define PHASE_FLUID (1 << 25)
#define PHASE_ONESIDED (1 << 26)
#define PHASE_ISOLATED (1 << 27) // particles that are not part of persistent constraints and can be deleted without ill effects: fluids and granulars
#include "Quaternion.cginc"
#include "Matrix.cginc"
// Based on Kubelka-Munk theory: https://vanity-ibex.xyz/blog/kubelka_munk_colormixing/
float4 RGBToAbsorption(float4 rgb)
{
// S (scattering) is assumed to be 1 for all channels
float4 k;
k.r = pow(1 - rgb.r, 2) / (2 * rgb.r + EPSILON);
k.g = pow(1 - rgb.g, 2) / (2 * rgb.g + EPSILON);
k.b = pow(1 - rgb.b, 2) / (2 * rgb.b + EPSILON);
k.a = rgb.a;
return k;
}
// Based on Kubelka-Munk theory: https://vanity-ibex.xyz/blog/kubelka_munk_colormixing/
float4 AbsorptionToRGB(float4 k)
{
// Assuming S=1 for all channels
float4 rgb;
rgb.r = 1 + k.r - sqrt(k.r * (k.r + 2));
rgb.g = 1 + k.g - sqrt(k.g * (k.g + 2));
rgb.b = 1 + k.b - sqrt(k.b * (k.b + 2));
rgb.a = k.a;
return rgb;
}
//https://www.shadertoy.com/view/4djSRW
float3 hash33(float3 p3)
{
p3 = frac(p3 * float3(.1031, .1030, .0973));
p3 += dot(p3, p3.yxz+33.33);
return frac((p3.xxy + p3.yxx)*p3.zyx);
}
float hash13(float3 p3)
{
p3 = frac(p3 * .1031);
p3 += dot(p3, p3.zyx + 31.32);
return frac((p3.x + p3.y) * p3.z);
}
float2 hash21(float p)
{
float3 p3 = frac(float3(p,p,p) * float3(.1031, .1030, .0973));
p3 += dot(p3, p3.yzx + 33.33);
return frac((p3.xx+p3.yz)*p3.zy);
}
float3 hash31(float p)
{
float3 p3 = frac(float3(p,p,p) * float3(.1031f, .1030f, .0973f));
p3 += dot(p3, p3.yzx + 33.33f);
return frac((p3.xxy + p3.yzz) * p3.zyx);
}
float4 normalizesafe(in float4 v, float4 def = float4(0,0,0,0))
{
float len = length(v);
return (len < EPSILON) ? def : v/len;
}
float3 normalizesafe(in float3 v, float3 def = float3(0,0,0))
{
float len = length(v);
return (len < EPSILON) ? def : v/len;
}
inline float cmax( in float3 v)
{
return max(max(v.x,v.y),v.z);
}
inline float3 nfmod(float3 a, float3 b)
{
return a - b * floor(a / b);
}
inline float BaryScale(float4 coords)
{
return 1.0 / dot(coords, coords);
}
float Remap01(float value, float min_, float max_)
{
return (min(value, max_) - min(value, min_)) / (max_ - min_);
}
float Remap(float value, float min_, float max_, float newmin_, float newmax_)
{
return newmin_ + (value - min_) * (newmax_ - newmin_) / (max_ - min_);
}
float EllipsoidRadius(float4 normSolverDirection, quaternion orientation, float3 radii)
{
float3 localDir = rotate_vector(q_conj(orientation), normSolverDirection.xyz) / radii;
float sqrNorm = dot(localDir, localDir);
return sqrNorm > EPSILON ? sqrt(1 / sqrNorm) : radii.x;
}
float4 Project(float4 v, float4 onto)
{
float len = dot(onto,onto);
if (len < EPSILON)
return FLOAT4_ZERO;
return dot(onto, v) * onto / len;
}
float3 Project(float3 v, float3 onto)
{
float len = dot(onto,onto);
if (len < EPSILON)
return float3(0,0,0);
return dot(onto, v) * onto / len;
}
inline void OneSidedNormal(float4 forward, inout float4 normal)
{
float d = dot(normal.xyz, forward.xyz);
if (d < 0) normal -= 2 * d * forward;
}
quaternion ExtractRotation(float3x3 m, quaternion rotation, int iterations)
{
float4x4 R;
for (int i = 0; i < iterations; ++i)
{
R = q_toMatrix(rotation);
float3 omega = (cross(R._m00_m10_m20, m._m00_m10_m20) + cross(R._m01_m11_m21, m._m01_m11_m21) + cross(R._m02_m12_m22, m._m02_m12_m22)) /
(abs(dot(R._m00_m10_m20, m._m00_m10_m20) + dot(R._m01_m11_m21, m._m01_m11_m21) + dot(R._m02_m12_m22, m._m02_m12_m22)) + EPSILON);
float w = length(omega);
if (w < EPSILON)
break;
rotation = normalize(qmul(axis_angle((1.0f / w) * omega, w), rotation));
}
return rotation;
}
quaternion ExtractRotation(float4x4 m, quaternion rotation, int iterations)
{
return ExtractRotation((float3x3) m, rotation, iterations);
}
float4 GetParticleInertiaTensor(in float4 principalRadii, in float invRotationalMass)
{
float4 sqrRadii = principalRadii * principalRadii;
return 0.2f / (invRotationalMass + EPSILON) * float4(sqrRadii[1] + sqrRadii[2],
sqrRadii[0] + sqrRadii[2],
sqrRadii[0] + sqrRadii[1], 0);
}
float4x4 TransformInertiaTensor(float4 tensor, quaternion rotation)
{
float4x4 rotMatrix = q_toMatrix(rotation);
return mul(rotMatrix, mul(AsDiagonal(tensor), transpose(rotMatrix)));
}
float RotationalInvMass(float4x4 inverseInertiaTensor, float4 pos, float4 direction)
{
float4 cr = mul(inverseInertiaTensor, float4(cross(pos.xyz, direction.xyz), 0));
return dot(cross(cr.xyz, pos.xyz), direction.xyz);
}
float RaySphereIntersection(float3 rayOrigin, float3 rayDirection, float3 center, float radius)
{
float3 oc = rayOrigin - center;
float a = dot(rayDirection, rayDirection);
float b = 2.0 * dot(oc, rayDirection);
float c = dot(oc, oc) - radius * radius;
float discriminant = b * b - 4 * a * c;
if (discriminant < 0){
return -1.0f;
}
else{
return (-b - sqrt(discriminant)) / (2.0f * a);
}
}
struct CachedEdge
{
float4 vertex;
float4 edge0;
float data;
void Cache(in float4 v1,
in float4 v2)
{
vertex = v1;
vertex.w = 0;
edge0 = v2 - v1;
edge0.w = 0;
data = dot(edge0, edge0);
}
};
float4 NearestPointOnEdge(CachedEdge edge, float4 p, out float mu, bool clampToSegment = true)
{
float4 ap = p - edge.vertex;
ap.w = 0;
mu = dot(ap, edge.edge0) / (edge.data + EPSILON);
if (clampToSegment)
mu = saturate(mu);
float4 result = edge.vertex + edge.edge0 * mu;
result.w = 0;
return result;
}
float4 NearestPointOnEdge(float4 a, float4 b, float4 p, out float mu, bool clampToSegment = true)
{
float4 ap = p - a;
float4 ab = b - a;
ap.w = 0;
ab.w = 0;
mu = dot(ap, ab) / (dot(ab, ab) + EPSILON);
if (clampToSegment)
mu = saturate(mu);
float4 result = a + ab * mu;
result.w = 0;
return result;
}
float3 NearestPointOnEdge(float3 a, float3 b, float3 p, out float mu, bool clampToSegment = true)
{
float3 ap = p - a;
float3 ab = b - a;
mu = dot(ap, ab) / (dot(ab, ab) + EPSILON);
if (clampToSegment)
mu = saturate(mu);
float3 result = a + ab * mu;
return result;
}
struct CachedTri
{
float4 vertex;
float4 edge0;
float4 edge1;
float4 data;
void Cache(in float4 v1,
in float4 v2,
in float4 v3)
{
vertex = v1;
edge0 = v2 - v1;
edge1 = v3 - v1;
data = float4(0,0,0,0);
data[0] = dot(edge0, edge0);
data[1] = dot(edge0, edge1);
data[2] = dot(edge1, edge1);
data[3] = data[0] * data[2] - data[1] * data[1];
}
};
float4 NearestPointOnTri(in CachedTri tri,
in float4 p,
out float4 bary)
{
float4 v0 = tri.vertex - p;
float b0 = dot(tri.edge0, v0);
float b1 = dot(tri.edge1, v0);
float t0 = tri.data[1] * b1 - tri.data[2] * b0;
float t1 = tri.data[1] * b0 - tri.data[0] * b1;
if (t0 + t1 <= tri.data[3])
{
if (t0 < zero)
{
if (t1 < zero) // region 4
{
if (b0 < zero)
{
t1 = zero;
if (-b0 >= tri.data[0]) // V0
t0 = one;
else // E01
t0 = -b0 / tri.data[0];
}
else
{
t0 = zero;
if (b1 >= zero) // V0
t1 = zero;
else if (-b1 >= tri.data[2]) // V2
t1 = one;
else // E20
t1 = -b1 / tri.data[2];
}
}
else // region 3
{
t0 = zero;
if (b1 >= zero) // V0
t1 = zero;
else if (-b1 >= tri.data[2]) // V2
t1 = one;
else // E20
t1 = -b1 / tri.data[2];
}
}
else if (t1 < zero) // region 5
{
t1 = zero;
if (b0 >= zero) // V0
t0 = zero;
else if (-b0 >= tri.data[0]) // V1
t0 = one;
else // E01
t0 = -b0 / tri.data[0];
}
else // region 0, interior
{
float invDet = one / tri.data[3];
t0 *= invDet;
t1 *= invDet;
}
}
else
{
float tmp0, tmp1, numer, denom;
if (t0 < zero) // region 2
{
tmp0 = tri.data[1] + b0;
tmp1 = tri.data[2] + b1;
if (tmp1 > tmp0)
{
numer = tmp1 - tmp0;
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
if (numer >= denom) // V1
{
t0 = one;
t1 = zero;
}
else // E12
{
t0 = numer / denom;
t1 = one - t0;
}
}
else
{
t0 = zero;
if (tmp1 <= zero) // V2
t1 = one;
else if (b1 >= zero) // V0
t1 = zero;
else // E20
t1 = -b1 / tri.data[2];
}
}
else if (t1 < zero) // region 6
{
tmp0 = tri.data[1] + b1;
tmp1 = tri.data[0] + b0;
if (tmp1 > tmp0)
{
numer = tmp1 - tmp0;
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
if (numer >= denom) // V2
{
t1 = one;
t0 = zero;
}
else // E12
{
t1 = numer / denom;
t0 = one - t1;
}
}
else
{
t1 = zero;
if (tmp1 <= zero) // V1
t0 = one;
else if (b0 >= zero) // V0
t0 = zero;
else // E01
t0 = -b0 / tri.data[0];
}
}
else // region 1
{
numer = tri.data[2] + b1 - tri.data[1] - b0;
if (numer <= zero) // V2
{
t0 = zero;
t1 = one;
}
else
{
denom = tri.data[0] - 2 * tri.data[1] + tri.data[2];
if (numer >= denom) // V1
{
t0 = one;
t1 = zero;
}
else // 12
{
t0 = numer / denom;
t1 = one - t0;
}
}
}
}
bary = float4(1 - (t0 + t1), t0, t1,0);
return tri.vertex + t0 * tri.edge0 + t1 * tri.edge1;
}
float3 unitOrthogonal(float3 input)
{
// Find a vector to cross() the input with.
if (!(input.x < input.z * EPSILON)
|| !(input.y < input.z * EPSILON))
{
float invnm = 1 / length(input.xy);
return float3(-input.y * invnm, input.x * invnm, 0);
}
else
{
float invnm = 1 / length(input.yz);
return float3(0, -input.z * invnm, input.y * invnm);
}
}
// D is symmetric, S is an eigen value
float3 EigenVector(float3x3 D, float S)
{
// Compute a cofactor matrix of D - sI.
float3 c0 = D._m00_m10_m20; c0[0] -= S;
float3 c1 = D._m01_m11_m21; c1[1] -= S;
float3 c2 = D._m02_m12_m22; c2[2] -= S;
// Upper triangular matrix
float3 c0p = float3(c1[1] * c2[2] - c2[1] * c2[1], 0, 0);
float3 c1p = float3(c2[1] * c2[0] - c1[0] * c2[2], c0[0] * c2[2] - c2[0] * c2[0], 0);
float3 c2p = float3(c1[0] * c2[1] - c1[1] * c2[0], c1[0] * c2[0] - c0[0] * c2[1], c0[0] * c1[1] - c1[0] * c1[0]);
// Get a column vector with a largest norm (non-zero).
float C01s = c1p[0] * c1p[0];
float C02s = c2p[0] * c2p[0];
float C12s = c2p[1] * c2p[1];
float3 norm = float3(c0p[0] * c0p[0] + C01s + C02s,
C01s + c1p[1] * c1p[1] + C12s,
C02s + C12s + c2p[2] * c2p[2]);
// index of largest:
int index = 0;
if (norm[0] > norm[1] && norm[0] > norm[2])
index = 0;
else if (norm[1] > norm[0] && norm[1] > norm[2])
index = 1;
else
index = 2;
float3 V = float3(0,0,0);
// special case
if (norm[index] < EPSILON)
{
V[0] = 1; return V;
}
else if (index == 0)
{
V[0] = c0p[0]; V[1] = c1p[0]; V[2] = c2p[0];
return normalize(V);
}
else if (index == 1)
{
V[0] = c1p[0]; V[1] = c1p[1]; V[2] = c2p[1];
return normalize(V);
}
else
{
V = c2p;
return normalize(V);
}
}
static float3 EigenValues(float3x3 D)
{
float one_third = 1 / 3.0f;
float one_sixth = 1 / 6.0f;
float three_sqrt = sqrt(3.0f);
float3 c0 = D._m00_m10_m20;
float3 c1 = D._m01_m11_m21;
float3 c2 = D._m02_m12_m22;
float m = one_third * (c0[0] + c1[1] + c2[2]);
// K is D - I*diag(S)
float K00 = c0[0] - m;
float K11 = c1[1] - m;
float K22 = c2[2] - m;
float K01s = c1[0] * c1[0];
float K02s = c2[0] * c2[0];
float K12s = c2[1] * c2[1];
float q = 0.5f * (K00 * (K11 * K22 - K12s) - K22 * K01s - K11 * K02s) + c1[0] * c2[1] * c0[2];
float p = one_sixth * (K00 * K00 + K11 * K11 + K22 * K22 + 2 * (K01s + K02s + K12s));
float p_sqrt = sqrt(p);
float tmp = p * p * p - q * q;
float phi = one_third * atan2(sqrt(max(0, tmp)), q);
float phi_c = cos(phi);
float phi_s = sin(phi);
float sqrt_p_c_phi = p_sqrt * phi_c;
float sqrt_p_3_s_phi = p_sqrt * three_sqrt * phi_s;
float e0 = m + 2 * sqrt_p_c_phi;
float e1 = m - sqrt_p_c_phi - sqrt_p_3_s_phi;
float e2 = m - sqrt_p_c_phi + sqrt_p_3_s_phi;
float aux;
if (e0 > e1)
{
aux = e0;
e0 = e1;
e1 = aux;
}
if (e0 > e2)
{
aux = e0;
e0 = e2;
e2 = aux;
}
if (e1 > e2)
{
aux = e1;
e1 = e2;
e2 = aux;
}
return float3(e2, e1, e0);
}
void EigenSolve(float3x3 D, out float3 S, out float3x3 V)
{
// D is symmetric
// S is a vector whose elements are eigenvalues
// V is a matrix whose columns are eigenvectors
S = EigenValues(D);
float3 V0, V1, V2;
if (S[0] - S[1] > S[1] - S[2])
{
V0 = EigenVector(D, S[0]);
if (S[1] - S[2] < EPSILON)
{
V2 = unitOrthogonal(V0);
}
else
{
V2 = EigenVector(D, S[2]); V2 -= V0 * dot(V0, V2); V2 = normalize(V2);
}
V1 = cross(V2, V0);
}
else
{
V2 = EigenVector(D, S[2]);
if (S[0] - S[1] < EPSILON)
{
V1 = unitOrthogonal(V2);
}
else
{
V1 = EigenVector(D, S[1]); V1 -= V2 * dot(V2, V1); V1 = normalize(V1);
}
V0 = cross(V1, V2);
}
V._m00_m10_m20 = V0;
V._m01_m11_m21 = V1;
V._m02_m12_m22 = V2;
}
float4 UnpackFloatRGBA(float v)
{
uint rgba = asuint(v);
float r = ((rgba & 0xff000000) >> 24) / 255.0;
float g = ((rgba & 0x00ff0000) >> 16) / 255.0;
float b = ((rgba & 0x0000ff00) >> 8) / 255.0;
float a = (rgba & 0x000000ff) / 255.0;
return float4(r, g, b, a);
}
float PackFloatRGBA(float4 enc)
{
uint rgba = ((uint)(enc.x * 255.0) << 24) +
((uint)(enc.y * 255.0) << 16) +
((uint)(enc.z * 255.0) << 8) +
(uint)(enc.w * 255.0);
return asfloat(rgba);
}
float2 UnpackFloatRG(float v)
{
uint rgba = asuint(v);
float r = ((rgba & 0xffff0000) >> 16) / 65535.0;
float g = (rgba & 0x0000ffff) / 65535.0;
return float2(r, g);
}
float PackFloatRG(float2 enc)
{
uint rgba = ((uint)(enc.x * 65535.0) << 16) +
(uint)(enc.y * 65535.0);
return asfloat(rgba);
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 729e4f240dd344abfa1b0972b2ceb0ea
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,108 +0,0 @@
#ifndef MATRIX_INCLUDE
#define MATRIX_INCLUDE
#define FLOAT4X4_IDENTITY float4x4(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1)
#define FLOAT3X3_IDENTITY float3x3(1, 0, 0, 0, 1, 0, 0, 0, 1)
#define FLOAT4X4_ZERO float4x4(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
#define FLOAT3X3_ZERO float3x3(0, 0, 0, 0, 0, 0, 0, 0, 0)
#include "Quaternion.cginc"
float4x4 Inverse(float4x4 m)
{
float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0];
float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1];
float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2];
float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3];
float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
float det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;
float idet = 1.0f / det;
float4x4 ret;
ret[0][0] = t11 * idet;
ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet;
ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet;
ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet;
ret[1][0] = t12 * idet;
ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet;
ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet;
ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet;
ret[2][0] = t13 * idet;
ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet;
ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet;
ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet;
ret[3][0] = t14 * idet;
ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet;
ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet;
ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet;
return ret;
}
float4x4 m_scale(float4x4 m, float3 v)
{
float x = v.x, y = v.y, z = v.z;
m[0][0] *= x; m[1][0] *= y; m[2][0] *= z;
m[0][1] *= x; m[1][1] *= y; m[2][1] *= z;
m[0][2] *= x; m[1][2] *= y; m[2][2] *= z;
m[0][3] *= x; m[1][3] *= y; m[2][3] *= z;
return m;
}
float4x4 m_translate(float4x4 m, float3 v)
{
float x = v.x, y = v.y, z = v.z;
m[0][3] = x;
m[1][3] = y;
m[2][3] = z;
return m;
}
float4x4 TRS(float3 position, float4 quat, float3 scale)
{
float4x4 m = q_toMatrix(quat);
return float4x4(m[0][0] * scale.x, m[0][1] * scale.y, m[0][2] * scale.z, position.x,
m[1][0] * scale.x, m[1][1] * scale.y, m[1][2] * scale.z, position.y,
m[2][0] * scale.x, m[2][1] * scale.y, m[2][2] * scale.z, position.z,
0, 0, 0, 1);
}
float4x4 AsDiagonal(in float4 v)
{
return float4x4(v.x, 0, 0, 0,
0, v.y, 0, 0,
0, 0, v.z, 0,
0, 0, 0, v.w);
}
float3x3 multrnsp(in float4 column, in float4 row)
{
return float3x3(row.xyz * column[0],row.xyz * column[1],row.xyz * column[2]);
}
float4x4 multrnsp4(in float4 column, float4 row)
{
row[3] = 0;
return float4x4(row * column[0],row * column[1],row * column[2], float4(0,0,0,0));
}
float FrobeniusNorm(in float4x4 m)
{
return sqrt(dot(m._m00_m10_m20_m30,m._m00_m10_m20_m30) +
dot(m._m01_m11_m21_m31,m._m01_m11_m21_m31) +
dot(m._m02_m12_m22_m32,m._m02_m12_m22_m32) +
dot(m._m03_m13_m23_m33,m._m03_m13_m23_m33));
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 80cddbfae548f433a93a4e6e7a10b089
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,32 +0,0 @@
#ifndef NORMALCOMPRESSION_INCLUDE
#define NORMALCOMPRESSION_INCLUDE
float2 octWrap( float2 v )
{
return ( 1.0 - abs( v.yx ) ) * ( v.xy >= 0.0 ? 1.0 : -1.0 );
}
// use octahedral encoding to reduce to 2 coords, then pack them as two 16 bit values in a 32 bit float.
float encode( float3 n )
{
n /= ( abs( n.x ) + abs( n.y ) + abs( n.z ) );
n.xy = n.z >= 0.0 ? n.xy : octWrap( n.xy );
n.xy = n.xy * 0.5 + 0.5;
uint nx = (uint)(n.x * 0xffff);
uint ny = (uint)(n.y * 0xffff);
return asfloat((nx << 16) | (ny & 0xffff));
}
// unpack 32 bit float into two 16 bit ones, then use octahedral decoding.
float3 decode( float k )
{
uint d = asuint(k);
float2 f = float2((d >> 16) / 65535.0, (d & 0xffff) / 65535.0) * 2.0 - 1.0;
float3 n = float3( f.x, f.y, 1.0 - abs( f.x ) - abs( f.y ) );
float t = saturate( -n.z );
n.xy += n.xy >= 0.0 ? -t : t;
return normalize( n );
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: d05e07f20b7784bac93ad5818826c787
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,126 +0,0 @@
#ifndef OPTIMIZATION_INCLUDE
#define OPTIMIZATION_INCLUDE
#include "MathUtils.cginc"
#include "SurfacePoint.cginc"
void GetInterpolatedSimplexData(in int simplexStart,
in int simplexSize,
StructuredBuffer<int> simplices,
StructuredBuffer<float4> positions,
StructuredBuffer<quaternion> orientations,
StructuredBuffer<float4> radii,
float4 convexBary,
inout float4 convexPoint,
inout float4 convexRadii,
inout float4 convexOrientation)
{
convexPoint = FLOAT4_ZERO;
convexRadii = FLOAT4_ZERO;
convexOrientation = quaternion(0, 0, 0, 0);
for (int j = 0; j < simplexSize; ++j)
{
int particle = simplices[simplexStart + j];
convexPoint += positions[particle] * convexBary[j];
convexRadii += radii[particle] * convexBary[j];
convexOrientation += orientations[particle] * convexBary[j];
}
convexPoint.w = 0;
}
// Frank-Wolfe convex optimization algorithm. Returns closest point to a simplex in a signed distance function.
void FrankWolfe(in IDistanceFunction f,
in int simplexStart,
in int simplexSize,
StructuredBuffer<float4> positions,
StructuredBuffer<quaternion> orientations,
StructuredBuffer<float4> radii,
StructuredBuffer<int> simplices,
inout float4 convexPoint,
inout float4 convexThickness,
inout quaternion convexOrientation,
inout float4 convexBary,
inout SurfacePoint pointInFunction,
int maxIterations,
float tolerance)
{
for (int i = 0; i < maxIterations; ++i)
{
// sample target function:
f.Evaluate(convexPoint, convexThickness, convexOrientation, pointInFunction);
// find descent direction:
int descent = 0;
float gap = FLT_MIN;
for (int j = 0; j < simplexSize; ++j)
{
int particle = simplices[simplexStart + j];
float4 candidate = positions[particle] - convexPoint;
candidate.w = 0;
// here, we adjust the candidate by projecting it to the engrosed simplex's surface:
candidate -= pointInFunction.normal * (radii[particle].x - convexThickness.x);
float corr = dot(-pointInFunction.normal, candidate);
if (corr > gap)
{
descent = j;
gap = corr;
}
}
// if the duality gap is below tolerance threshold, stop iterating.
if (gap < tolerance)
break;
// update the barycentric coords using 2/(i+2) as the step factor
float stp = 0.3f * 2.0f / (i + 2);
convexBary *= 1 - stp;
switch(descent)
{
case 0: convexBary[0] += stp;break;
case 1: convexBary[1] += stp;break;
case 2: convexBary[2] += stp;break;
case 3: convexBary[3] += stp;break;
}
// get cartesian coordinates of current solution:
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, convexBary, convexPoint, convexThickness, convexOrientation);
}
}
SurfacePoint Optimize(in IDistanceFunction f,
StructuredBuffer<float4> positions,
StructuredBuffer<quaternion> orientations,
StructuredBuffer<float4> radii,
StructuredBuffer<int> simplices,
in int simplexStart,
in int simplexSize,
inout float4 convexBary,
out float4 convexPoint,
in int maxIterations = 16,
in float tolerance = 0.004f)
{
SurfacePoint pointInFunction;
// get cartesian coordinates of the initial guess:
float4 convexThickness;
quaternion convexOrientation;
GetInterpolatedSimplexData(simplexStart, simplexSize, simplices, positions, orientations, radii, convexBary, convexPoint, convexThickness, convexOrientation);
// for a 0-simplex (point), perform a single evaluation:
if (simplexSize == 1 || maxIterations < 1)
f.Evaluate(convexPoint, convexThickness, convexOrientation, pointInFunction);
// for a 1-simplex (edge), perform golden ratio search:
//else if (simplexSize == 2)
// GoldenSearch(ref function, simplexStart, simplexSize, positions, orientations, radii, simplices, ref convexPoint, ref convexThickness, ref convexOrientation, ref convexBary, ref pointInFunction, maxIterations, tolerance * 10);
// for higher-order simplices, use general Frank-Wolfe convex optimization:
else
FrankWolfe(f, simplexStart, simplexSize, positions, orientations, radii, simplices, convexPoint, convexThickness, convexOrientation, convexBary, pointInFunction, maxIterations, tolerance);
return pointInFunction;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: e10fb000e4ce24365adc488a45bbe664
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,214 +0,0 @@
#pragma kernel Initialize
#pragma kernel Project
#pragma kernel Apply
#include "SolverParameters.cginc"
#include "ContactHandling.cginc"
#include "ColliderDefinitions.cginc"
#include "CollisionMaterial.cginc"
#include "Simplex.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> simplices;
StructuredBuffer<float> invMasses;
StructuredBuffer<float> invRotationalMasses;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> velocities;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<float4> fluidInterface;
StructuredBuffer<quaternion> prevOrientations;
StructuredBuffer<quaternion> orientations;
RWStructuredBuffer<float4> positions;
RWStructuredBuffer<float4> deltas;
RWStructuredBuffer<float4> userData;
// Vulkan workaround: don't declare a RW array after a counter/append one (particleContacts) since the counter overlaps the first entry in the next array.
RWStructuredBuffer<contactMasses> effectiveMasses;
RWStructuredBuffer<contact> particleContacts;
StructuredBuffer<uint> dispatchBuffer;
// Variables set from the CPU
uint particleCount;
float substepTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Initialize (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int simplexSizeA;
int simplexSizeB;
int simplexStartA = GetSimplexStartAndSize(particleContacts[i].bodyA, simplexSizeA);
int simplexStartB = GetSimplexStartAndSize(particleContacts[i].bodyB, simplexSizeB);
float4 simplexVelocityA = float4(0,0,0,0);
float4 simplexPrevPositionA = float4(0,0,0,0);
quaternion simplexPrevOrientationA = quaternion(0, 0, 0, 0);
float simplexRadiusA = 0;
float simplexInvMassA = 0;
float simplexInvRotationalMassA = 0;
float4 simplexVelocityB = float4(0,0,0,0);
float4 simplexPrevPositionB = float4(0,0,0,0);
quaternion simplexPrevOrientationB = quaternion(0, 0, 0, 0);
float simplexRadiusB = 0;
float simplexInvMassB = 0;
float simplexInvRotationalMassB = 0;
int j = 0;
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
simplexVelocityA += velocities[particleIndex] * particleContacts[i].pointA[j];
simplexPrevPositionA += prevPositions[particleIndex] * particleContacts[i].pointA[j];
simplexPrevOrientationA += prevOrientations[particleIndex] * particleContacts[i].pointA[j];
simplexInvMassA += invMasses[particleIndex] * particleContacts[i].pointA[j];
simplexInvRotationalMassA += invRotationalMasses[particleIndex] * particleContacts[i].pointA[j];
simplexRadiusA += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointA[j];
}
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
simplexVelocityB += velocities[particleIndex] * particleContacts[i].pointB[j];
simplexPrevPositionB += prevPositions[particleIndex] * particleContacts[i].pointB[j];
simplexPrevOrientationB += prevOrientations[particleIndex] * particleContacts[i].pointB[j];
simplexInvMassB += invMasses[particleIndex] * particleContacts[i].pointB[j];
simplexInvRotationalMassB += invRotationalMasses[particleIndex] * particleContacts[i].pointB[j];
simplexRadiusB += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointB[j];
}
simplexPrevPositionA.w = 0;
simplexPrevPositionB.w = 0;
// update contact distance
float4 contactPointA = simplexPrevPositionA - particleContacts[i].normal * simplexRadiusA;
float4 contactPointB = simplexPrevPositionB + particleContacts[i].normal * simplexRadiusB;
particleContacts[i].dist = dot(contactPointA - contactPointB, particleContacts[i].normal);
// update contact basis:
CalculateBasis(simplexVelocityA - simplexVelocityB, particleContacts[i].normal,particleContacts[i].tangent);
// update contact masses:
int aMaterialIndex = collisionMaterialIndices[simplices[simplexStartA]];
int bMaterialIndex = collisionMaterialIndices[simplices[simplexStartB]];
bool rollingContacts = (aMaterialIndex >= 0 ? collisionMaterials[aMaterialIndex].rollingContacts > 0 : false) |
(bMaterialIndex >= 0 ? collisionMaterials[bMaterialIndex].rollingContacts > 0 : false);
float4 invInertiaTensorA = 1.0/(GetParticleInertiaTensor(simplexRadiusA, simplexInvRotationalMassA) + FLOAT4_EPSILON);
float4 invInertiaTensorB = 1.0/(GetParticleInertiaTensor(simplexRadiusB, simplexInvRotationalMassB) + FLOAT4_EPSILON);
float4 bitangent = GetBitangent(particleContacts[i]);
CalculateContactMassesA(simplexInvMassA, invInertiaTensorA, simplexPrevPositionA, simplexPrevOrientationA, contactPointA, rollingContacts, particleContacts[i].normal,particleContacts[i].tangent,bitangent, effectiveMasses[i].normalInvMassA, effectiveMasses[i].tangentInvMassA, effectiveMasses[i].bitangentInvMassA);
CalculateContactMassesB(simplexInvMassB, invInertiaTensorB, simplexPrevPositionB, simplexPrevOrientationB, contactPointB, rollingContacts, particleContacts[i].normal,particleContacts[i].tangent,bitangent, effectiveMasses[i].normalInvMassB, effectiveMasses[i].tangentInvMassB, effectiveMasses[i].bitangentInvMassB);
}
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int simplexSizeA;
int simplexSizeB;
int simplexStartA = GetSimplexStartAndSize(particleContacts[i].bodyA, simplexSizeA);
int simplexStartB = GetSimplexStartAndSize(particleContacts[i].bodyB, simplexSizeB);
// Combine collision materials (use material from first particle in simplex)
collisionMaterial material = CombineCollisionMaterials(collisionMaterialIndices[simplices[simplexStartA]], collisionMaterialIndices[simplices[simplexStartB]]);
float4 simplexPositionA = FLOAT4_ZERO, simplexPositionB = FLOAT4_ZERO;
float simplexRadiusA = 0, simplexRadiusB = 0;
float4 simplexUserDataA = FLOAT4_ZERO, simplexUserDataB = FLOAT4_ZERO;
float miscibility = 0;
int j = 0;
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
simplexPositionA += positions[particleIndex] * particleContacts[i].pointA[j];
simplexRadiusA += EllipsoidRadius(particleContacts[i].normal, orientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointA[j];
simplexUserDataA += userData[particleIndex] * particleContacts[i].pointA[j];
miscibility += fluidInterface[particleIndex].w * particleContacts[i].pointA[j];
}
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
simplexPositionB += positions[particleIndex] * particleContacts[i].pointB[j];
simplexRadiusB += EllipsoidRadius(particleContacts[i].normal, orientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointA[j];
simplexUserDataB += userData[particleIndex] * particleContacts[i].pointB[j];
miscibility += fluidInterface[particleIndex].w * particleContacts[i].pointB[j];
}
simplexPositionA.w = 0;
simplexPositionB.w = 0;
float4 posA = simplexPositionA - particleContacts[i].normal * simplexRadiusA;
float4 posB = simplexPositionB + particleContacts[i].normal * simplexRadiusB;
float normalInvMass = effectiveMasses[i].normalInvMassA + effectiveMasses[i].normalInvMassB;
// adhesion:
float lambda = SolveAdhesion(particleContacts[i], normalInvMass, posA, posB, material.stickDistance, material.stickiness, substepTime);
lambda += SolvePenetration(particleContacts[i], normalInvMass, posA, posB, maxDepenetration * substepTime);
if (abs(lambda) > EPSILON)
{
float shock = shockPropagation * dot(particleContacts[i].normal.xyz, normalizesafe(gravity));
float4 delta = lambda * particleContacts[i].normal;
float baryScale = BaryScale(particleContacts[i].pointA);
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
float4 delta1 = delta * invMasses[particleIndex] * particleContacts[i].pointA[j] * baryScale * (1 - shock);
AtomicAddPositionDelta(particleIndex, delta1);
}
baryScale = BaryScale(particleContacts[i].pointB);
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
float4 delta2 = -delta * invMasses[particleIndex] * particleContacts[i].pointB[j] * baryScale * (1 + shock);
AtomicAddPositionDelta(particleIndex, delta2);
}
}
// property diffusion:
if (particleContacts[i].dist < collisionMargin)
{
float diffusionSpeed = miscibility * 0.5 * substepTime;
float4 userDelta = (simplexUserDataB - simplexUserDataA) * diffusionMask * diffusionSpeed;
for (j = 0; j < simplexSizeA; ++j)
AtomicAddOrientationDelta(simplices[simplexStartA + j], userDelta * particleContacts[i].pointA[j]);
for (j = 0; j < simplexSizeB; ++j)
AtomicAddOrientationDelta(simplices[simplexStartB + j], -userDelta * particleContacts[i].pointB[j]);
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= particleCount) return;
int p = particleIndices[threadIndex];
ApplyPositionDelta(positions, p, sorFactor);
ApplyUserDataDelta(userData, p);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 770004596b21f49118a60c5ad181940c
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,187 +0,0 @@
#pragma kernel Project
#pragma kernel Apply
#include "ContactHandling.cginc"
#include "Integration.cginc"
#include "CollisionMaterial.cginc"
#include "Simplex.cginc"
#include "AtomicDeltas.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> simplices;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<quaternion> prevOrientations;
StructuredBuffer<float> invMasses;
StructuredBuffer<float> invRotationalMasses;
StructuredBuffer<float4> principalRadii;
RWStructuredBuffer<float4> positions;
RWStructuredBuffer<quaternion> orientations;
RWStructuredBuffer<float4> deltas;
RWStructuredBuffer<contact> particleContacts;
RWStructuredBuffer<contactMasses> effectiveMasses;
StructuredBuffer<uint> dispatchBuffer;
// Variables set from the CPU
uint particleCount;
float substepTime;
float stepTime;
float sorFactor;
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int simplexSizeA;
int simplexSizeB;
int simplexStartA = GetSimplexStartAndSize(particleContacts[i].bodyA, simplexSizeA);
int simplexStartB = GetSimplexStartAndSize(particleContacts[i].bodyB, simplexSizeB);
// Combine collision materials (use material from first particle in simplex)
collisionMaterial material = CombineCollisionMaterials(collisionMaterialIndices[simplices[simplexStartA]],
collisionMaterialIndices[simplices[simplexStartB]]);
float4 prevPositionA = float4(0,0,0,0);
float4 linearVelocityA = float4(0,0,0,0);
float4 angularVelocityA = float4(0,0,0,0);
float invRotationalMassA = 0;
quaternion orientationA = quaternion(0, 0, 0, 0);
float simplexRadiusA = 0;
float4 prevPositionB = float4(0,0,0,0);
float4 linearVelocityB = float4(0,0,0,0);
float4 angularVelocityB = float4(0,0,0,0);
float invRotationalMassB = 0;
quaternion orientationB = quaternion(0, 0, 0, 0);
float simplexRadiusB = 0;
int j = 0;
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
prevPositionA += prevPositions[particleIndex] * particleContacts[i].pointA[j];
linearVelocityA += DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * particleContacts[i].pointA[j];
angularVelocityA += DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * particleContacts[i].pointA[j];
invRotationalMassA += invRotationalMasses[particleIndex] * particleContacts[i].pointA[j];
orientationA += orientations[particleIndex] * particleContacts[i].pointA[j];
simplexRadiusA += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointA[j];
}
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
prevPositionB += prevPositions[particleIndex] * particleContacts[i].pointB[j];
linearVelocityB += DifferentiateLinear(positions[particleIndex], prevPositions[particleIndex], substepTime) * particleContacts[i].pointB[j];
angularVelocityB += DifferentiateAngular(orientations[particleIndex], prevOrientations[particleIndex], substepTime) * particleContacts[i].pointB[j];
invRotationalMassB += invRotationalMasses[particleIndex] * particleContacts[i].pointB[j];
orientationB += orientations[particleIndex] * particleContacts[i].pointB[j];
simplexRadiusB += EllipsoidRadius(particleContacts[i].normal, prevOrientations[particleIndex], principalRadii[particleIndex].xyz) * particleContacts[i].pointB[j];
}
float4 rA = FLOAT4_ZERO;
float4 rB = FLOAT4_ZERO;
// Consider angular velocities if rolling contacts are enabled:
if (material.rollingContacts > 0)
{
rA = -particleContacts[i].normal * simplexRadiusA;
rB = particleContacts[i].normal * simplexRadiusB;
linearVelocityA += float4(cross(angularVelocityA.xyz, rA.xyz), 0);
linearVelocityB += float4(cross(angularVelocityB.xyz, rB.xyz), 0);
}
// Calculate relative velocity:
float4 relativeVelocity = linearVelocityA - linearVelocityB;
// Determine impulse magnitude:
float tangentMass = effectiveMasses[i].tangentInvMassA + effectiveMasses[i].tangentInvMassB;
float bitangentMass = effectiveMasses[i].bitangentInvMassA + effectiveMasses[i].bitangentInvMassB;
float2 impulses = SolveFriction(particleContacts[i],tangentMass,bitangentMass,relativeVelocity, material.staticFriction, material.dynamicFriction, stepTime);
if (abs(impulses.x) > EPSILON || abs(impulses.y) > EPSILON)
{
float4 tangentImpulse = impulses.x * particleContacts[i].tangent;
float4 bitangentImpulse = impulses.y * GetBitangent(particleContacts[i]);
float4 totalImpulse = tangentImpulse + bitangentImpulse;
float baryScale = BaryScale(particleContacts[i].pointA);
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
float4 delta1 = (tangentImpulse * effectiveMasses[i].tangentInvMassA + bitangentImpulse * effectiveMasses[i].bitangentInvMassA) * substepTime * particleContacts[i].pointA[j] * baryScale;
AtomicAddPositionDelta(particleIndex, delta1);
}
baryScale = BaryScale(particleContacts[i].pointB);
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
float4 delta2 = -(tangentImpulse * effectiveMasses[i].tangentInvMassB + bitangentImpulse * effectiveMasses[i].bitangentInvMassB) * substepTime * particleContacts[i].pointB[j] * baryScale;
AtomicAddPositionDelta(particleIndex, delta2);
}
// Rolling contacts:
if (material.rollingContacts > 0)
{
float4 invInertiaTensorA = 1.0/(GetParticleInertiaTensor(simplexRadiusA, invRotationalMassA) + FLOAT4_EPSILON);
float4 invInertiaTensorB = 1.0/(GetParticleInertiaTensor(simplexRadiusB, invRotationalMassB) + FLOAT4_EPSILON);
// Calculate angular velocity deltas due to friction impulse:
float4x4 solverInertiaA = TransformInertiaTensor(invInertiaTensorA, orientationA);
float4x4 solverInertiaB = TransformInertiaTensor(invInertiaTensorB, orientationB);
float4 angVelDeltaA = mul(solverInertiaA, float4(cross(rA.xyz, totalImpulse.xyz), 0));
float4 angVelDeltaB = -mul(solverInertiaB, float4(cross(rB.xyz, totalImpulse.xyz), 0));
// Final angular velocities, after adding the deltas:
angularVelocityA += angVelDeltaA;
angularVelocityB += angVelDeltaB;
// Calculate weights (inverse masses):
float invMassA = length(mul(solverInertiaA, normalizesafe(angularVelocityA)));
float invMassB = length(mul(solverInertiaB, normalizesafe(angularVelocityB)));
// Calculate rolling axis and angular velocity deltas:
float4 rollAxis = FLOAT4_ZERO;
float rollingImpulse = SolveRollingFriction(particleContacts[i],angularVelocityA, angularVelocityB, material.rollingFriction, invMassA, invMassB, rollAxis);
angVelDeltaA += rollAxis * rollingImpulse * invMassA;
angVelDeltaB -= rollAxis * rollingImpulse * invMassB;
// Apply orientation deltas to particles:
quaternion orientationDeltaA = AngularVelocityToSpinQuaternion(orientationA, angVelDeltaA, substepTime);
quaternion orientationDeltaB = AngularVelocityToSpinQuaternion(orientationB, angVelDeltaB, substepTime);
for (j = 0; j < simplexSizeA; ++j)
{
int particleIndex = simplices[simplexStartA + j];
AtomicAddOrientationDelta(particleIndex, orientationDeltaA);
}
for (j = 0; j < simplexSizeB; ++j)
{
int particleIndex = simplices[simplexStartB + j];
AtomicAddOrientationDelta(particleIndex, orientationDeltaB);
}
}
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int threadIndex = id.x;
if (threadIndex >= particleCount) return;
int p = particleIndices[threadIndex];
ApplyPositionDelta(positions, p, sorFactor);
ApplyOrientationDelta(orientations, p, sorFactor);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: bd14011b19e9c47d1964f351a7ed4df7
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,626 +0,0 @@
#include "GridUtils.cginc"
#include "CollisionMaterial.cginc"
#include "ColliderDefinitions.cginc"
#include "ContactHandling.cginc"
#include "SolverParameters.cginc"
#include "Simplex.cginc"
#include "Phases.cginc"
#include "Bounds.cginc"
#include "Simplex.cginc"
#pragma kernel Clear
#pragma kernel InsertSimplices
#pragma kernel FindPopulatedLevels
#pragma kernel SortSimplices
#pragma kernel BuildFluidDispatch
#pragma kernel SortFluidSimplices
#pragma kernel BuildMortonIndices
#pragma kernel MortonSort
#pragma kernel FindFluidNeighborsInSameLevel
#pragma kernel FindFluidNeighborsInUpperLevels
#pragma kernel BuildContactList
#pragma kernel BuildFluidParticleIndexBuffer
StructuredBuffer<aabb> solverBounds;
StructuredBuffer<aabb> simplexBounds;
StructuredBuffer<int> simplices; // particle indices in each simplex.
StructuredBuffer<float4> positions;
StructuredBuffer<float4> restPositions;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> fluidMaterials;
StructuredBuffer<float4> fluidInterface;
StructuredBuffer<uint4> normals;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<quaternion> restOrientations;
StructuredBuffer<float4> velocities;
StructuredBuffer<float> invMasses;
StructuredBuffer<int> phases;
StructuredBuffer<int> filters;
StructuredBuffer<int4> R_cellCoords; // for each item, its cell coordinates.
StructuredBuffer<uint> R_offsetInCell; // for each item, its offset within the cell.
StructuredBuffer<uint> R_cellOffsets; // start of each cell in the sorted item array.
StructuredBuffer<uint> R_cellCounts; // number of item in each cell.
StructuredBuffer<uint> R_levelPopulation;
RWStructuredBuffer<int4> cellCoords; // for each item, its cell coordinates.
RWStructuredBuffer<uint> offsetInCell; // for each item, its offset within the cell.
RWStructuredBuffer<uint> cellOffsets; // start of each cell in the sorted item array.
RWStructuredBuffer<uint> cellCounts; // number of item in each cell.
RWStructuredBuffer<int> cellHashToMortonIndex;
RWStructuredBuffer<int> mortonSortedCellHashes;
RWStructuredBuffer<int> sortedSimplexToFluid; // fluidSimplices
RWStructuredBuffer<int> sortedFluidIndices;
RWStructuredBuffer<int> sortedSimplexIndices;
RWStructuredBuffer<float4> sortedPositions;
RWStructuredBuffer<float4> sortedFluidMaterials;
RWStructuredBuffer<float4> sortedFluidInterface;
RWStructuredBuffer<float4> sortedPrincipalRadii;
RWStructuredBuffer<uint> neighbors;
RWStructuredBuffer<uint> neighborCounts;
RWStructuredBuffer<contact> particleContacts;
RWStructuredBuffer<uint2> contactPairs;
RWStructuredBuffer<uint> fluidDispatchBuffer;
RWStructuredBuffer<uint> dispatchBuffer;
RWStructuredBuffer<float4> colors;
uint maxContacts;
uint maxNeighbors;
uint fluidParticleCount;
float deltaTime;
const uint groupWidth;
const uint groupHeight;
const uint stepIndex;
/**
For each cell, calculate coords and morton. This only works if theres no collisions, so use the coord of one random particle that maps to that cell:
For each simplex:
Determine cell coords (any particle in it will do) and hash, store in array per cell. Sort array by morton(coords), create array that maps from cell hash to morton index, then use as cellCounts[mortonIndex].
This way we have sorted particles and cells, and can use for fluid surface. Win win!!
*/
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxCells)
return;
if (i == 0)
{
for (int l = 0; l <= GRID_LEVELS; ++l)
levelPopulation[l] = 0;
}
// clear all cell counts to zero, and cell offsets to invalid.
cellOffsets[i] = INVALID;
cellCounts[i] = 0;
mortonSortedCellHashes[i] = i;
}
[numthreads(128, 1, 1)]
void InsertSimplices (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= pointCount + edgeCount + triangleCount) return;
// calculate simplex cell index:
int level = GridLevelForSize(simplexBounds[i].MaxAxisLength());
float cellSize = CellSizeOfLevel(level);
int4 cellCoord = int4(floor((simplexBounds[i].Center() - solverBounds[0].min_)/ cellSize).xyz,level);
// if the solver is 2D, project to the z = 0 cell.
if (mode == 1) cellCoord[2] = 0;
// insert simplex in cell:
uint cellIndex = GridHash(cellCoord);
cellCoords[i] = cellCoord;
InterlockedAdd(cellCounts[cellIndex],1,offsetInCell[i]);
// assign minimum morton code to cell
// (there may be hash collisions mapping two coordinates to the same cell, that's why we use atomic minimum)
float mortonCellSize = solverBounds[0].MaxAxisLength() / 1024.0;
uint morton = EncodeMorton3(floor((simplexBounds[i].Center() - solverBounds[0].min_).xyz / mortonCellSize));
InterlockedMin(cellOffsets[cellIndex], morton);
// clear neighbor count:
neighborCounts[i] = 0;
// atomically increase this level's population by one:
InterlockedAdd(levelPopulation[1 + level],1);
}
[numthreads(128,1,1)]
void MortonSort(uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
uint hIndex = i & (groupWidth - 1);
uint indexLeft = hIndex + (groupHeight + 1) * (i / groupWidth);
uint rightStepSize = stepIndex == 0 ? groupHeight - 2 * hIndex : (groupHeight + 1) / 2;
uint indexRight = indexLeft + rightStepSize;
// Exit if out of bounds
if (indexRight >= maxCells) return;
// get morton index for both cells:
uint mortonL = cellOffsets[indexLeft];
uint mortonR = cellOffsets[indexRight];
// get cell counts:
uint simplexIndexL = cellCounts[indexLeft];
uint simplexIndexR = cellCounts[indexRight];
uint orderL = mortonSortedCellHashes[indexLeft];
uint orderR = mortonSortedCellHashes[indexRight];
// Swap entries if order is incorrect
if (mortonL > mortonR)
{
cellCounts[indexLeft] = simplexIndexR;
cellCounts[indexRight] = simplexIndexL;
cellOffsets[indexLeft] = mortonR;
cellOffsets[indexRight] = mortonL;
mortonSortedCellHashes[indexLeft] = orderR;
mortonSortedCellHashes[indexRight] = orderL;
}
}
[numthreads(128,1,1)]
void BuildMortonIndices(uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= maxCells) return;
// build map from cell hash to index in morton-sorted cell data.
int index = mortonSortedCellHashes[i];
cellHashToMortonIndex[index] = i;
}
[numthreads(128, 1, 1)]
void SortSimplices (uint3 id : SV_DispatchThreadID)
{
uint i = id.x;
if (i >= pointCount + edgeCount + triangleCount) return;
// write simplex index to its index in the grid:
uint cellIndex = cellHashToMortonIndex[GridHash(R_cellCoords[i])];
uint gridIndex = R_cellOffsets[cellIndex] + R_offsetInCell[i];
sortedSimplexIndices[gridIndex] = i; // maps from index in grid to simplex index.
// flag fluid simplices with 1. we'll later do a prefix sum of this array
// to get a compact list of grid-sorted fluid indices.
int size;
int p = simplices[GetSimplexStartAndSize(i, size)];
sortedFluidIndices[gridIndex] = ((phases[p] & Fluid) != 0) ? 1:0;
}
[numthreads(1, 1, 1)]
void BuildFluidDispatch (uint3 id : SV_DispatchThreadID)
{
// since we are using *exclusive* prefix sum,
// we must add the last entries of both buffers together to get total count of fluid simplices.
int lastEntry = pointCount + edgeCount + triangleCount - 1;
fluidDispatchBuffer[3] = sortedSimplexToFluid[lastEntry] + sortedFluidIndices[lastEntry];
fluidDispatchBuffer[0] = fluidDispatchBuffer[3] / 128 + 1;
}
[numthreads(128, 1, 1)]
void SortFluidSimplices (uint3 id : SV_DispatchThreadID) //rename to sort fluid data.
{
// check all simplices.
uint i = id.x;
if (i >= pointCount + edgeCount + triangleCount) return;
uint cellIndex = cellHashToMortonIndex[GridHash(R_cellCoords[i])];
uint gridIndex = R_cellOffsets[cellIndex] + R_offsetInCell[i];
// copy the data of first particle in each fluid simplex to sorted arrays
// using prefix sum results: same as grid order, but contiguous.
int size;
int p = simplices[GetSimplexStartAndSize(i, size)];
if ((phases[p] & Fluid) != 0)
{
int fluidIndex = sortedSimplexToFluid[gridIndex];
sortedFluidIndices[fluidIndex] = i;
sortedPositions[fluidIndex] = positions[p];
sortedFluidMaterials[fluidIndex] = fluidMaterials[p];
sortedFluidInterface[fluidIndex] = fluidInterface[p];
sortedPrincipalRadii[fluidIndex] = principalRadii[p];
}
else
sortedSimplexToFluid[gridIndex] = -1;
}
int GetSimplexGroup(in int simplexStart,in int simplexSize, out int flags, out int category, out int mask, out bool restPositionsEnabled)
{
flags = 0;
int group = 0;
category = 0;
mask = 0;
restPositionsEnabled = false;
for (int j = 0; j < simplexSize; ++j)
{
int particleIndex = simplices[simplexStart + j];
group = max(group, phases[particleIndex] & GroupMask);
flags |= phases[particleIndex] & ~GroupMask; // get flags from phase
category |= filters[particleIndex] & CategoryMask; // get category from filter
mask |= (filters[particleIndex] & MaskMask) >> 16; // get mask from filter
restPositionsEnabled = restPositionsEnabled || (restPositions[particleIndex].w > 0.5f);
}
return group;
}
struct simplexData
{
int index;
int start;
int size;
int category;
int mask;
int flags;
int group;
bool restPosEnabled;
};
simplexData GetSimplexData(int indexInGrid)
{
simplexData s;
s.index = sortedSimplexIndices[indexInGrid];
s.start = GetSimplexStartAndSize(s.index, s.size);
s.group = GetSimplexGroup(s.start, s.size, s.flags, s.category, s.mask, s.restPosEnabled);
return s;
}
void InteractionTest(simplexData a, simplexData b)
{
if ((a.flags & Fluid) == 0 || (b.flags & Fluid) == 0)
{
// immediately reject simplex pairs that share particles:
int j = 0;
for (int i = 0; i < a.size; ++i)
for (j = 0; j < b.size; ++j)
if (simplices[a.start + i] == simplices[b.start + j])
return;
// if all particles are in the same group:
if (a.group == b.group)
{
// if none are self-colliding, reject the pair.
if ((a.flags & b.flags & SelfCollide) == 0)
return;
}
// category-based filtering:
else if ((a.mask & b.category) == 0 || (b.mask & a.category) == 0)
return;
// swap simplices (except for category) so that B is always the one-sided one.
int categoryA = a.category;
int categoryB = b.category;
if ((a.flags & OneSided) != 0 && categoryA < categoryB)
{
simplexData t = a;
a = b;
b = t;
}
float4 simplexBary = BarycenterForSimplexOfSize(a.size);
float4 simplexPoint;
Simplex simplexShape;
simplexShape.simplexStart = b.start;
simplexShape.simplexSize = b.size;
simplexShape.simplices = simplices;
simplexShape.positions = restPositions;
float simplexRadiusA = 0; float simplexRadiusB = 0;
// skip the contact if there's self-intersection at rest:
if (a.group == b.group && (a.restPosEnabled || b.restPosEnabled))
{
SurfacePoint restPoint = Optimize(simplexShape, restPositions, restOrientations, principalRadii,
simplices, a.start, a.size, simplexBary, simplexPoint, 4, 0);
for (j = 0; j < a.size; ++j)
simplexRadiusA += principalRadii[simplices[a.start + j]].x * simplexBary[j];
for (j = 0; j < b.size; ++j)
simplexRadiusB += principalRadii[simplices[b.start + j]].x * restPoint.bary[j];
// compare distance along contact normal with radius.
if (dot(simplexPoint - restPoint.pos, restPoint.normal) < simplexRadiusA + simplexRadiusB)
return;
}
simplexBary = BarycenterForSimplexOfSize(a.size);
simplexShape.positions = positions;
SurfacePoint surfacePoint = Optimize(simplexShape, positions, orientations, principalRadii,
simplices, a.start, a.size, simplexBary, simplexPoint);
simplexRadiusA = 0; simplexRadiusB = 0;
float4 velocityA = FLOAT4_ZERO, velocityB = FLOAT4_ZERO, normalA = FLOAT4_ZERO, normalB = FLOAT4_ZERO;
float invMassA = 0, invMassB = 0;
for (j = 0; j < a.size; ++j)
{
int particleIndex = simplices[a.start + j];
simplexRadiusA += principalRadii[particleIndex].x * simplexBary[j];
velocityA += velocities[particleIndex] * simplexBary[j];
float4 nrm = asfloat(normals[particleIndex]);
normalA += (nrm.w < 0 ? float4(rotate_vector(orientations[particleIndex],nrm.xyz), nrm.w) : nrm) * simplexBary[j];
invMassA += invMasses[particleIndex] * simplexBary[j];
}
for (j = 0; j < b.size; ++j)
{
int particleIndex = simplices[b.start + j];
simplexRadiusB += principalRadii[particleIndex].x * surfacePoint.bary[j];
velocityB += velocities[particleIndex] * surfacePoint.bary[j];
float4 nrm = asfloat(normals[particleIndex]);
normalB += (nrm.w < 0 ? float4(rotate_vector(orientations[particleIndex],nrm.xyz), nrm.w) : nrm) * surfacePoint.bary[j];
invMassB += invMasses[particleIndex] * simplexBary[j];
}
// no contact between fixed simplices: TODO: make optional
//if (!(invMassA > 0 || invMassB > 0))
// return;
float dAB = dot(simplexPoint - surfacePoint.pos, surfacePoint.normal);
float vel = dot(velocityA - velocityB, surfacePoint.normal);
// check if the projected velocity along the contact normal will get us within collision distance.
if (vel * deltaTime + dAB <= simplexRadiusA + simplexRadiusB + collisionMargin)
{
// adapt collision normal for one-sided simplices:
if ((b.flags & OneSided) != 0 && categoryA < categoryB)
OneSidedNormal(normalB, surfacePoint.normal);
// during inter-collision, if either particle contains SDF data and they overlap:
if (a.group != b.group && (normalB.w < 0 || normalA.w < 0) && dAB * 1.05f <= simplexRadiusA + simplexRadiusB)
{
// as normal, pick SDF gradient belonging to least penetration distance:
float4 nij = normalB;
if (normalB.w >= 0 || (normalA.w < 0 && normalB.w < normalA.w))
nij = float4(-normalA.xyz, normalA.w);
// for boundary particles, use one sided sphere normal:
if (abs(nij.w) <= max(simplexRadiusA, simplexRadiusB) * 1.5f)
OneSidedNormal(nij, surfacePoint.normal);
else
surfacePoint.normal = nij;
}
uint count = particleContacts.IncrementCounter();
if (count < maxContacts)
{
contact c = (contact)0;
c.normal = surfacePoint.normal;
c.pointA = simplexBary;
c.pointB = surfacePoint.bary;
c.bodyA = a.index;
c.bodyB = b.index;
particleContacts[count] = c;
contactPairs[count] = uint2(c.bodyA,c.bodyB);
InterlockedMax(dispatchBuffer[0],(count + 1) / 128 + 1);
InterlockedMax(dispatchBuffer[3], count + 1);
}
}
}
}
[numthreads(128, 1, 1)]
void BuildContactList (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= pointCount + edgeCount + triangleCount) return;
// current cell:
int4 cellCoord = R_cellCoords[i];
uint cellIndex = cellHashToMortonIndex[GridHash(cellCoord)];
uint n = R_cellOffsets[cellIndex];
uint end = n + R_cellCounts[cellIndex];
uint indexInGrid = n + R_offsetInCell[i];
simplexData data1 = GetSimplexData(indexInGrid);
// in current cell, only consider simplices that appear after this one:
for (++indexInGrid; indexInGrid < end; ++indexInGrid)
InteractionTest(data1,GetSimplexData(indexInGrid));
// neighbour cells ahead of the current one in the same level:
for(int j = 0; j < 13; ++j)
{
// get first simplex in neighbor cell:
cellIndex = cellHashToMortonIndex[GridHash(cellCoord + aheadCellNeighborhood[j])];
n = R_cellOffsets[cellIndex];
end = n + R_cellCounts[cellIndex];
// iterate through all simplices in neighbor cell:
for (; n < end; ++n)
InteractionTest(data1,GetSimplexData(n));
}
// higher grid levels:
for (uint m = 1; m <= R_levelPopulation[0]; ++m)
{
uint l = R_levelPopulation[m];
if (l <= (uint)cellCoord.w) continue;
int4 parentCellCoords = GetParentCellCoords(cellCoord, l);
for (int j = 0; j < 27; ++j)
{
// get first simplex in neighbor cell:
cellIndex = cellHashToMortonIndex[GridHash(parentCellCoords + cellNeighborhood[j])];
n = R_cellOffsets[cellIndex];
end = n + R_cellCounts[cellIndex];
// iterate through all simplices in neighbor cell:
for (; n < end; ++n)
InteractionTest(data1,GetSimplexData(n));
}
}
}
[numthreads(128, 1, 1)]
void FindFluidNeighborsInSameLevel (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
// current cell:
int4 cellCoord = R_cellCoords[sortedFluidIndices[i]];
int4 neighborCoord;
uint cellIndex, n, end;
float4 d;
float interactionRadius, cellSize;
int fluidB;
float4 posA = sortedPositions[i];
float radA = sortedFluidMaterials[i].x;
uint count = 0;
// neighbour cells in same level. We don't need atomics for this,
// and we can guarantee that the neighbors for each particle will
// appear in sorted order.
for(int j = 0; j < 27; ++j)
{
// get cell start/end
neighborCoord = cellCoord + cellNeighborhood[j];
cellIndex = cellHashToMortonIndex[GridHash(neighborCoord)];
n = R_cellOffsets[cellIndex];
end = n + R_cellCounts[cellIndex];
// iterate through all simplices in neighbor cell:
for (; n < end; ++n)
{
fluidB = sortedSimplexToFluid[n];
if (fluidB >= 0 && fluidB != (int)i)
{
// due to hash collisions, two neighboring cells might map to the same
// hash bucket, and we'll add the same set of particles twice to the neighbors list.
// So we only consider particles that have the same spatial coordinates as the cell.
if (any (R_cellCoords[sortedFluidIndices[fluidB]] - neighborCoord))
continue;
// calculate particle center distance:
d = posA - sortedPositions[fluidB]; d.w = 0;
interactionRadius = max(radA, sortedFluidMaterials[fluidB].x) + collisionMargin;
if (dot(d,d) <= interactionRadius * interactionRadius && count < maxNeighbors)
neighbors[maxNeighbors * i + (count++)] = fluidB;
}
}
}
neighborCounts[i] = count;
}
[numthreads(128, 1, 1)]
void FindFluidNeighborsInUpperLevels (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
int s = sortedFluidIndices[i];
int4 cellCoord = R_cellCoords[s];
int4 parentCellCoords, neighborCoord;
uint cellIndex, n, end;
float4 d;
float interactionRadius, cellSize;
int fluidB;
float4 posA = sortedPositions[i];
float radA = sortedFluidMaterials[i].x;
for (uint m = 1; m <= R_levelPopulation[0]; ++m)
{
uint l = R_levelPopulation[m];
// skip levels below this particle's level.
if (l <= (uint)cellCoord.w) continue;
parentCellCoords = GetParentCellCoords(cellCoord, l);
for (int j = 0; j < 27; ++j)
{
// get cell start/end
neighborCoord = parentCellCoords + cellNeighborhood[j];
cellIndex = cellHashToMortonIndex[GridHash(neighborCoord)];
n = R_cellOffsets[cellIndex];
end = n + R_cellCounts[cellIndex];
// iterate through all simplices in neighbor cell:
for (; n < end; ++n)
{
fluidB = sortedSimplexToFluid[n];
if (fluidB >= 0)
{
// due to hash collisions, two neighboring cells might map to the same
// hash bucket, and we'll add the same set of particles twice to the neighbors list.
// So we only consider particles that have the same spatial coordinates as the cell.
if (any (R_cellCoords[sortedFluidIndices[fluidB]] - neighborCoord))
continue;
// calculate particle center distance:
d = posA - sortedPositions[fluidB]; d.w = 0;
interactionRadius = max(radA, sortedFluidMaterials[fluidB].x) + collisionMargin;
if (dot(d,d) <= interactionRadius * interactionRadius)
{
uint entryA, entryB;
InterlockedAdd(neighborCounts[i], 1, entryA);
InterlockedAdd(neighborCounts[fluidB], 1, entryB);
if (entryA < maxNeighbors && entryB < maxNeighbors)
{
neighbors[maxNeighbors * i + entryA] = fluidB;
neighbors[maxNeighbors * fluidB + entryB] = i;
}
}
}
}
}
}
}
[numthreads(128, 1, 1)]
void BuildFluidParticleIndexBuffer (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= dispatchBuffer[3]) return;
// convert simplex index to start in indices array.
int o;
sortedFluidIndices[i] = simplices[GetSimplexStartAndSize(sortedFluidIndices[i], o)];
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 282fd6222e54d48fea7f2d1a74501fa9
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,72 +0,0 @@
#pragma kernel BuildMesh
#include "MathUtils.cginc"
struct RendererData
{
float4 color;
float radiusScale;
};
StructuredBuffer<int> particleIndices;
StructuredBuffer<float4> positions;
StructuredBuffer<float4> orientations;
StructuredBuffer<float4> radii;
StructuredBuffer<float4> colors;
StructuredBuffer<int> rendererIndices;
StructuredBuffer<RendererData> rendererData;
RWByteAddressBuffer vertices;
RWByteAddressBuffer indices;
uint firstParticle;
uint particleCount;
[numthreads(128, 1, 1)]
void BuildMesh (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= particleCount) return;
int p = particleIndices[firstParticle + i];
int r = rendererIndices[firstParticle + i];
// <<2 = multiply by 4 to get byte address, since a float/int is 4 bytes in size.
// particle data is the same for all 4 vertices:
for (uint v = i*4; v < i*4 + 4; ++v)
{
int base = v*23;
// pos
vertices.Store4(base<<2, asuint(float4(positions[p].xyz, 1)));
// color:
vertices.Store4((base+7)<<2, asuint(colors[p] * rendererData[r].color));
// b1, b2, b3:
vertices.Store4((base+11)<<2, asuint( float4(rotate_vector(orientations[p],float3(1,0,0)),radii[p].x * radii[p].w * rendererData[r].radiusScale) ));
vertices.Store4((base+15)<<2, asuint( float4(rotate_vector(orientations[p],float3(0,1,0)),radii[p].y * radii[p].w * rendererData[r].radiusScale) ));
vertices.Store4((base+19)<<2, asuint( float4(rotate_vector(orientations[p],float3(0,0,1)),radii[p].z * radii[p].w * rendererData[r].radiusScale) ));
}
//different offset for each vertex:
int base = i*4;
vertices.Store3((base*23 + 4)<<2, asuint(float3(1,1,0)));
vertices.Store3(((base+1)*23 + 4)<<2, asuint(float3(-1,1,0)));
vertices.Store3(((base+2)*23 + 4)<<2, asuint(float3(-1,-1,0)));
vertices.Store3(((base+3)*23 + 4)<<2, asuint(float3(1,-1,0)));
// indices:
indices.Store((i*6)<<2, asuint(i*4+2));
indices.Store((i*6+1)<<2, asuint(i*4+1));
indices.Store((i*6+2)<<2, asuint(i*4));
indices.Store((i*6+3)<<2, asuint(i*4+3));
indices.Store((i*6+4)<<2, asuint(i*4+2));
indices.Store((i*6+5)<<2, asuint(i*4));
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: a5989e4df4ce944ea96d566e7df4d68a
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,158 +0,0 @@
#ifndef PATHFRAME_INCLUDE
#define PATHFRAME_INCLUDE
#include "MathUtils.cginc"
struct pathFrame
{
float3 position;
float3 tangent;
float3 normal;
float3 binormal;
float4 color;
float thickness;
void Initialize(float3 position, float3 tangent, float3 normal, float3 binormal, float4 color, float thickness){
this.position = position;
this.normal = normal;
this.tangent = tangent;
this.binormal = binormal;
this.color = color;
this.thickness = thickness;
}
void Reset()
{
position = float3(0,0,0);
tangent = float3(0,0,1);
normal = float3(0,1,0);
binormal = float3(1,0,0);
color = float4(1,1,1,1);
thickness = 0;
}
void SetTwist(float twist)
{
quaternion twistQ = axis_angle(tangent, radians(twist));
normal = rotate_vector(twistQ, normal);
binormal = rotate_vector(twistQ, binormal);
}
void Transport(pathFrame frame, float twist)
{
// Calculate delta rotation:
quaternion rotQ = from_to_rotation(tangent, frame.tangent);
quaternion twistQ = axis_angle(frame.tangent, radians(twist));
quaternion finalQ = qmul(twistQ , rotQ);
// Rotate previous frame axes to obtain the new ones:
normal = rotate_vector(finalQ, normal);
binormal = rotate_vector(finalQ, binormal);
tangent = frame.tangent;
position = frame.position;
thickness = frame.thickness;
color = frame.color;
}
void Transport(float3 newPosition, float3 newTangent, float twist)
{
// Calculate delta rotation:
quaternion rotQ = from_to_rotation(tangent, newTangent);
quaternion twistQ = axis_angle(newTangent, radians(twist));
quaternion finalQ = qmul(twistQ, rotQ);
// Rotate previous frame axes to obtain the new ones:
normal = rotate_vector(finalQ, normal);
binormal = rotate_vector(finalQ, binormal);
tangent = newTangent;
position = newPosition;
}
// Transport, hinting the normal.
void Transport(float3 newPosition, float3 newTangent, float3 newNormal, float twist)
{
normal = rotate_vector(axis_angle(newTangent,radians(twist)), newNormal);
tangent = newTangent;
binormal = cross(normal, tangent);
position = newPosition;
}
float3x3 ToMatrix(int mainAxis)
{
float3x3 basis;
if (mainAxis == 0)
{
basis._m00_m10_m20 = tangent;
basis._m01_m11_m21 = binormal;
basis._m02_m12_m22 = normal;
}
else if (mainAxis == 1)
{
basis._m01_m11_m21 = tangent;
basis._m02_m12_m22 = binormal;
basis._m00_m10_m20 = normal;
}
else
{
basis._m02_m12_m22 = tangent;
basis._m00_m10_m20 = binormal;
basis._m01_m11_m21 = normal;
}
/*int xo = (mainAxis) % 3;
int yo = (mainAxis + 1) % 3;
int zo = (mainAxis + 2) % 3;
basis[xo] = tangent;
basis[yo] = binormal;
basis[zo] = normal;*/
return basis;
}
};
void WeightedSum(float w1, float w2, float w3, in pathFrame c1, in pathFrame c2, in pathFrame c3, out pathFrame sum)
{
sum.position.x = c1.position.x * w1 + c2.position.x * w2 + c3.position.x * w3;
sum.position.y = c1.position.y * w1 + c2.position.y * w2 + c3.position.y * w3;
sum.position.z = c1.position.z * w1 + c2.position.z * w2 + c3.position.z * w3;
sum.tangent.x = c1.tangent.x * w1 + c2.tangent.x * w2 + c3.tangent.x * w3;
sum.tangent.y = c1.tangent.y * w1 + c2.tangent.y * w2 + c3.tangent.y * w3;
sum.tangent.z = c1.tangent.z * w1 + c2.tangent.z * w2 + c3.tangent.z * w3;
sum.normal.x = c1.normal.x * w1 + c2.normal.x * w2 + c3.normal.x * w3;
sum.normal.y = c1.normal.y * w1 + c2.normal.y * w2 + c3.normal.y * w3;
sum.normal.z = c1.normal.z * w1 + c2.normal.z * w2 + c3.normal.z * w3;
sum.binormal.x = c1.binormal.x * w1 + c2.binormal.x * w2 + c3.binormal.x * w3;
sum.binormal.y = c1.binormal.y * w1 + c2.binormal.y * w2 + c3.binormal.y * w3;
sum.binormal.z = c1.binormal.z * w1 + c2.binormal.z * w2 + c3.binormal.z * w3;
sum.color.x = c1.color.x * w1 + c2.color.x * w2 + c3.color.x * w3;
sum.color.y = c1.color.y * w1 + c2.color.y * w2 + c3.color.y * w3;
sum.color.z = c1.color.z * w1 + c2.color.z * w2 + c3.color.z * w3;
sum.color.w = c1.color.w * w1 + c2.color.w * w2 + c3.color.w * w3;
sum.thickness = c1.thickness * w1 + c2.thickness * w2 + c3.thickness * w3;
}
pathFrame addFrames(pathFrame c1, pathFrame c2)
{
pathFrame r;
r.Initialize(c1.position + c2.position, c1.tangent + c2.tangent, c1.normal + c2.normal, c1.binormal + c2.binormal, c1.color + c2.color, c1.thickness + c2.thickness);
return r;
}
pathFrame multiplyFrame(float f, pathFrame c)
{
pathFrame r;
r.Initialize(c.position * f, c.tangent * f, c.normal * f, c.binormal * f, c.color * f, c.thickness * f);
return r;
}
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 10c2f01e614d74b30b6a489c96e8a0ce
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,235 +0,0 @@
#pragma kernel ParallelTransport
#pragma kernel Decimate
#pragma kernel ChaikinSmooth
#include "PathFrame.cginc"
struct smootherPathData
{
uint smoothing;
float decimation;
float twist;
float restLength;
float smoothLength;
bool usesOrientedParticles;
};
StructuredBuffer<float4> renderablePositions;
StructuredBuffer<quaternion> renderableOrientations;
StructuredBuffer<float4> principalRadii;
StructuredBuffer<float4> colors;
RWStructuredBuffer<smootherPathData> pathData;
StructuredBuffer<int> particleIndices;
RWStructuredBuffer<pathFrame> pathFrames;
StructuredBuffer<int> frameOffsets;
RWStructuredBuffer<int> decimatedFrameCounts;
RWStructuredBuffer<pathFrame> smoothFrames;
StructuredBuffer<int> smoothFrameOffsets;
RWStructuredBuffer<int> smoothFrameCounts;
// Variables set from the CPU
uint chunkCount;
void PathFrameFromParticle(inout pathFrame frame, int particleIndex, bool useOrientedParticles, bool interpolateOrientation = false)
{
// Update current frame values from particles:
frame.position = renderablePositions[particleIndex].xyz;
frame.thickness = principalRadii[particleIndex][0];
frame.color = colors[particleIndex];
// Use particle orientation if possible:
if (useOrientedParticles)
{
quaternion current = renderableOrientations[particleIndex];
quaternion previous = renderableOrientations[max(0, particleIndex - 1)];
float4x4 average = q_toMatrix(interpolateOrientation ? q_slerp(current, previous, 0.5f) : current);
frame.normal = average._m01_m11_m21;
frame.binormal = average._m00_m10_m20;
frame.tangent = average._m02_m12_m22;
}
}
[numthreads(128, 1, 1)]
void ParallelTransport (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= chunkCount) return;
pathFrame nextFrame;
pathFrame currFrame;
pathFrame prevFrame;
nextFrame.Reset();
currFrame.Reset();
prevFrame.Reset();
int firstIndex = i > 0 ? frameOffsets[i - 1] : 0;
int frameCount = frameOffsets[i] - firstIndex;
// initialize current and previous frame:
PathFrameFromParticle(currFrame, particleIndices[firstIndex], pathData[i].usesOrientedParticles, false);
prevFrame = currFrame;
// parallel transport:
for (int m = 1; m <= frameCount; ++m)
{
int index = firstIndex + min(m, frameCount - 1);
int pIndex = particleIndices[index];
// generate curve frame from particle:
PathFrameFromParticle(nextFrame, pIndex, pathData[i].usesOrientedParticles);
if (pathData[i].usesOrientedParticles)
{
// copy frame directly.
prevFrame = currFrame;
}
else
{
// perform parallel transport, using forward / backward average to calculate tangent.
// if the average is too small, reuse the previous frame tangent.
currFrame.tangent = normalizesafe((currFrame.position - prevFrame.position) +
(nextFrame.position - currFrame.position), prevFrame.tangent);
prevFrame.Transport(currFrame, pathData[i].twist);
}
// advance current frame:
currFrame = nextFrame;
pathFrames[firstIndex + m - 1] = prevFrame;
}
}
[numthreads(128, 1, 1)]
void Decimate (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= chunkCount) return;
int firstInputIndex = i > 0 ? frameOffsets[i - 1] : 0;
int inputFrameCount = frameOffsets[i] - firstInputIndex;
// no decimation, no work to do, just return:
if (pathData[i].decimation < 0.00001f || inputFrameCount < 3)
{
decimatedFrameCounts[i] = inputFrameCount;
return;
}
float scaledThreshold = pathData[i].decimation * pathData[i].decimation * 0.01f;
int start = 0;
int end = inputFrameCount - 1;
decimatedFrameCounts[i] = 0;
while (start < end)
{
// add starting point:
pathFrames[firstInputIndex + decimatedFrameCounts[i]++] = pathFrames[firstInputIndex + start];
int newEnd = end;
while (true)
{
int maxDistanceIndex = 0;
float maxDistance = 0;
float mu;
// find the point that's furthest away from the current segment:
for (int j = start + 1; j < newEnd; j++)
{
float3 nearest = NearestPointOnEdge(pathFrames[firstInputIndex + start].position,
pathFrames[firstInputIndex + newEnd].position,
pathFrames[firstInputIndex + j].position, mu);
float3 delta = nearest - pathFrames[firstInputIndex + j].position;
float d = dot(delta,delta);
if (d > maxDistance)
{
maxDistanceIndex = j;
maxDistance = d;
}
}
if (maxDistance <= scaledThreshold)
break;
newEnd = maxDistanceIndex;
}
start = newEnd;
}
// add the last point:
pathFrames[firstInputIndex + decimatedFrameCounts[i]++] = pathFrames[firstInputIndex + end];
}
[numthreads(128, 1, 1)]
void ChaikinSmooth (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= chunkCount) return;
int firstInputIndex = i > 0 ? frameOffsets[i - 1] : 0;
int inputFrameCount = decimatedFrameCounts[i];
int firstOutputIndex = smoothFrameOffsets[i];
int k = (int)pathData[i].smoothing;
// No work to do. just copy the input to the output:
if (k == 0)
{
smoothFrameCounts[i] = inputFrameCount;
for (int j = 0; j < inputFrameCount; ++j)
smoothFrames[firstOutputIndex + j] = pathFrames[firstInputIndex + j];
}
else
{
// precalculate some quantities:
int pCount = (int)pow(2, k);
int n0 = inputFrameCount - 1;
float twoRaisedToMinusKPlus1 = pow(2, -(k + 1));
float twoRaisedToMinusK = pow(2, -k);
float twoRaisedToMinus2K = pow(2, -2 * k);
float twoRaisedToMinus2KMinus1 = pow(2, -2 * k - 1);
smoothFrameCounts[i] = (inputFrameCount - 2) * pCount + 2;
// calculate initial curve points:
smoothFrames[firstOutputIndex] = addFrames(multiplyFrame(0.5f + twoRaisedToMinusKPlus1 , pathFrames[firstInputIndex]) , multiplyFrame(0.5f - twoRaisedToMinusKPlus1, pathFrames[firstInputIndex + 1]));
smoothFrames[firstOutputIndex + pCount * n0 - pCount + 1] = addFrames(multiplyFrame(0.5f - twoRaisedToMinusKPlus1, pathFrames[firstInputIndex + n0 - 1]) , multiplyFrame(0.5f + twoRaisedToMinusKPlus1, pathFrames[firstInputIndex + n0]));
// calculate internal points:
for (int j = 1; j <= pCount; ++j)
{
// precalculate coefficients:
float F = 0.5f - twoRaisedToMinusKPlus1 - (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2KMinus1);
float G = 0.5f + twoRaisedToMinusKPlus1 + (j - 1) * (twoRaisedToMinusK - j * twoRaisedToMinus2K);
float H = (j - 1) * j * twoRaisedToMinus2KMinus1;
for (int l = 1; l < n0; ++l)
{
WeightedSum(F, G, H,
pathFrames[firstInputIndex + l - 1],
pathFrames[firstInputIndex + l],
pathFrames[firstInputIndex + l + 1],
smoothFrames[firstOutputIndex + (l - 1) * pCount + j]);
}
}
// make first and last curve points coincide with original points:
smoothFrames[firstOutputIndex] = pathFrames[firstInputIndex];
smoothFrames[firstOutputIndex + smoothFrameCounts[i] - 1] = pathFrames[firstInputIndex + inputFrameCount - 1];
}
// calculate path lengths:
pathData[i].smoothLength = 0;
for (int j = firstOutputIndex + 1; j < firstOutputIndex + smoothFrameCounts[i]; ++j)
pathData[i].smoothLength += distance(smoothFrames[j-1].position, smoothFrames[j].position);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 91c36d3d171884541b751112dfac062c
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,13 +0,0 @@
#ifndef PHASES_INCLUDE
#define PHASES_INCLUDE
#define CategoryMask 0x0000ffff
#define MaskMask 0xffff0000
#define GroupMask 0x00ffffff
#define SelfCollide 1 << 24
#define Fluid 1 << 25
#define OneSided 1 << 26
#endif

View File

@@ -1,9 +0,0 @@
fileFormatVersion: 2
guid: 96d6b61b6caf54444b039bf6f29c9205
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,229 +0,0 @@
#pragma kernel Clear
#pragma kernel Initialize
#pragma kernel Project
#pragma kernel Apply
#pragma kernel ProjectRenderable
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
StructuredBuffer<int> particleIndices;
StructuredBuffer<int> colliderIndices;
StructuredBuffer<float4> offsets;
StructuredBuffer<quaternion> restDarboux;
StructuredBuffer<float2> stiffnesses;
RWStructuredBuffer<float4> lambdas;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
RWStructuredBuffer<rigidbody> RW_rigidbodies;
RWStructuredBuffer<float4> RW_positions;
RWStructuredBuffer<quaternion> RW_orientations;
StructuredBuffer<float4> positions;
StructuredBuffer<quaternion> orientations;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<float> invMasses;
StructuredBuffer<float> invRotationalMasses;
StructuredBuffer<inertialFrame> inertialSolverFrame;
// Variables set from the CPU
uint activeConstraintCount;
float stepTime;
float substepTime;
float timeLeft;
int steps;
float sorFactor;
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int colliderIndex = colliderIndices[i];
// no collider to pin to, so ignore the constraint.
if (colliderIndex < 0)
return;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
int orig;
InterlockedExchange(RW_rigidbodies[rigidbodyIndex].constraintCount, 0, orig);
}
}
[numthreads(128, 1, 1)]
void Initialize (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int colliderIndex = colliderIndices[i];
// no collider to pin to, so ignore the constraint.
if (colliderIndex < 0)
return;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
InterlockedAdd(RW_rigidbodies[rigidbodyIndex].constraintCount, 1);
}
}
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int particleIndex = particleIndices[i];
int colliderIndex = colliderIndices[i];
// no collider to pin to, so ignore the constraint.
if (colliderIndex < 0)
return;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
float frameEnd = stepTime * steps;
float substepsToEnd = timeLeft / substepTime;
// calculate time adjusted compliances
float2 compliances = stiffnesses[i].xy / (substepTime * substepTime);
// project particle position to the end of the full step:
float4 particlePosition = lerp(prevPositions[particleIndex], positions[particleIndex], substepsToEnd);
// express pin offset in world space:
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
float4 predictedPinOffset = worldPinOffset;
quaternion predictedRotation = transforms[colliderIndex].rotation;
float rigidbodyLinearW = 0;
float rigidbodyAngularW = 0;
if (rigidbodyIndex >= 0)
{
rigidbody rb = rigidbodies[rigidbodyIndex];
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
float4 velocityAtPoint = GetRigidbodyVelocityAtPoint(rigidbodies[rigidbodyIndex],inertialSolverFrame[0].frame.InverseTransformPoint(worldPinOffset),
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]);
predictedPinOffset = IntegrateLinear(predictedPinOffset, inertialSolverFrame[0].frame.TransformVector(velocityAtPoint), frameEnd);
// predict rotation at the end of the step:
predictedRotation = IntegrateAngular(predictedRotation, rb.angularVelocity + asfloat(angularDeltasAsInt[rigidbodyIndex]), frameEnd);
// calculate linear and angular rigidbody effective masses (mass splitting: multiply by constraint count)
rigidbodyLinearW = rb.inverseMass * rb.constraintCount;
rigidbodyAngularW = RotationalInvMass(rb.inverseInertiaTensor,
worldPinOffset - rb.com,
normalizesafe(inertialSolverFrame[0].frame.TransformPoint(particlePosition) - predictedPinOffset)) * rb.constraintCount;
}
// Transform pin position to solver space for constraint solving:
predictedPinOffset = inertialSolverFrame[0].frame.InverseTransformPoint(predictedPinOffset);
predictedRotation = qmul(q_conj(inertialSolverFrame[0].frame.rotation), predictedRotation);
float4 gradient = particlePosition - predictedPinOffset;
float constraint = length(gradient);
float4 gradientDir = gradient / (constraint + EPSILON);
float4 lambda = lambdas[i];
float linearDLambda = (-constraint - compliances.x * lambda.w) / (invMasses[particleIndex] + rigidbodyLinearW + rigidbodyAngularW + compliances.x + EPSILON);
lambda.w += linearDLambda;
float4 correction = linearDLambda * gradientDir;
AddPositionDelta(particleIndex, correction * invMasses[particleIndex] / substepsToEnd);
if (rigidbodyIndex >= 0)
{
ApplyImpulse(rigidbodyIndex,
-correction / frameEnd,
inertialSolverFrame[0].frame.InverseTransformPoint(worldPinOffset),
inertialSolverFrame[0].frame);
}
if (rigidbodyAngularW > 0 || invRotationalMasses[particleIndex] > 0)
{
// bend/twist constraint:
quaternion omega = qmul(q_conj(orientations[particleIndex]), predictedRotation); //darboux vector
quaternion omega_plus;
omega_plus = omega + restDarboux[i]; //delta Omega with - omega_0
omega -= restDarboux[i]; //delta Omega with + omega_0
if (dot(omega, omega) > dot(omega_plus, omega_plus))
omega = omega_plus;
float3 dlambda = (omega.xyz - compliances.y * lambda.xyz) / (compliances.y + invRotationalMasses[particleIndex] + rigidbodyAngularW + EPSILON);
lambda.xyz += dlambda;
//discrete Darboux vector does not have vanishing scalar part:
quaternion dlambdaQ = quaternion(dlambda[0], dlambda[1], dlambda[2], 0);
quaternion orientDelta = asfloat(orientationDeltasAsInt[particleIndex]);
orientDelta += qmul(predictedRotation, dlambdaQ) * invRotationalMasses[particleIndex] / substepsToEnd;
orientationDeltasAsInt[particleIndex] = asuint(orientDelta);
orientationConstraintCounts[particleIndex]++;
if (rigidbodyIndex >= 0)
{
ApplyDeltaQuaternion(rigidbodyIndex,
predictedRotation,
-qmul(orientations[particleIndex], dlambdaQ) * rigidbodyAngularW,
inertialSolverFrame[0].frame, stepTime);
}
}
lambdas[i] = lambda;
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int p = particleIndices[i];
ApplyPositionDelta(RW_positions, p, sorFactor);
ApplyOrientationDelta(RW_orientations, p, sorFactor);
}
[numthreads(128, 1, 1)]
void ProjectRenderable (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int particleIndex = particleIndices[i];
int colliderIndex = colliderIndices[i];
// no collider to pin to or projection deactivated, so ignore the constraint.
if (colliderIndex < 0 || offsets[i].w < 0.5f)
return;
transform attachmentMatrix = inertialSolverFrame[0].frame.Inverse().Multiply(transforms[colliderIndex]);
RW_positions[particleIndex] = attachmentMatrix.TransformPoint(offsets[i]);
if (stiffnesses[i].y < 10000)
RW_orientations[particleIndex] = qmul(attachmentMatrix.rotation, restDarboux[i]);
}

View File

@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d65f884fa36104c4493d2bc55441b89d
ComputeShaderImporter:
externalObjects: {}
currentAPIMask: 65536
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,306 +0,0 @@
#pragma kernel Clear
#pragma kernel Initialize
#pragma kernel Project
#pragma kernel Apply
#include "MathUtils.cginc"
#include "AtomicDeltas.cginc"
#include "ColliderDefinitions.cginc"
#include "Rigidbody.cginc"
RWStructuredBuffer<int> particleIndices;
StructuredBuffer<int> colliderIndices;
StructuredBuffer<float4> offsets;
RWStructuredBuffer<float> edgeMus;
StructuredBuffer<int2> edgeRanges;
StructuredBuffer<float2> edgeRangeMus;
StructuredBuffer<float> parameters;
RWStructuredBuffer<float> relativeVelocities;
RWStructuredBuffer<float> lambdas;
StructuredBuffer<transform> transforms;
StructuredBuffer<shape> shapes;
RWStructuredBuffer<rigidbody> RW_rigidbodies;
RWStructuredBuffer<float4> RW_positions;
StructuredBuffer<int> deformableEdges;
StructuredBuffer<float4> positions;
StructuredBuffer<float4> prevPositions;
StructuredBuffer<float> invMasses;
StructuredBuffer<inertialFrame> inertialSolverFrame;
// Variables set from the CPU
uint activeConstraintCount;
float stepTime;
float substepTime;
float timeLeft;
int steps;
float sorFactor;
[numthreads(128, 1, 1)]
void Clear (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int colliderIndex = colliderIndices[i];
// no collider to pin to, so ignore the constraint.
if (colliderIndex < 0)
return;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
int orig;
InterlockedExchange(RW_rigidbodies[rigidbodyIndex].constraintCount, 0, orig);
}
}
bool IsEdgeValid(int edgeIndex, int nextEdgeIndex, float mix)
{
return (mix < 0) ? deformableEdges[nextEdgeIndex * 2 + 1] == deformableEdges[edgeIndex * 2] :
deformableEdges[nextEdgeIndex * 2] == deformableEdges[edgeIndex * 2 + 1];
}
bool ClampToRange(int i, int edgeIndex, inout float mix)
{
bool clamped = false;
if (edgeIndex == edgeRanges[i].x && mix < edgeRangeMus[i].x)
{
mix = edgeRangeMus[i].x;
clamped = true;
}
if (edgeIndex == edgeRanges[i].y && mix > edgeRangeMus[i].y)
{
mix = edgeRangeMus[i].y;
clamped = true;
}
return clamped;
}
[numthreads(128, 1, 1)]
void Initialize (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int edgeIndex = particleIndices[i];
int colliderIndex = colliderIndices[i];
// if no collider or edge, ignore the constraint.
if (edgeIndex < 0 || colliderIndex < 0)
return;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
InterlockedAdd(RW_rigidbodies[rigidbodyIndex].constraintCount, 1);
}
float frameEnd = stepTime * steps;
float substepsToEnd = timeLeft / substepTime;
// calculate time adjusted compliances
float compliance = parameters[i * 5] / (substepTime * substepTime);
int p1 = deformableEdges[edgeIndex * 2];
int p2 = deformableEdges[edgeIndex * 2 + 1];
int edgeCount = max(0, edgeRanges[i].y - edgeRanges[i].x + 1);
// express pin offset in world space:
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
float4 predictedPinOffset = worldPinOffset;
if (rigidbodyIndex >= 0)
{
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
float4 velocityAtPoint = GetRigidbodyVelocityAtPoint(rigidbodies[rigidbodyIndex],inertialSolverFrame[0].frame.InverseTransformPoint(worldPinOffset),
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]);
predictedPinOffset = IntegrateLinear(predictedPinOffset, inertialSolverFrame[0].frame.TransformVector(velocityAtPoint), frameEnd);
}
// transform pinhole position to solver space for constraint solving:
float4 solverPredictedOffset = inertialSolverFrame[0].frame.InverseTransformPoint(predictedPinOffset);
// get current edge data:
float mix = 0;
float4 particlePosition1 = lerp(prevPositions[p1], positions[p1], substepsToEnd);
float4 particlePosition2 = lerp(prevPositions[p2], positions[p2], substepsToEnd);
float edgeLength = length(particlePosition1 - particlePosition2) + EPSILON;
NearestPointOnEdge(particlePosition1, particlePosition2, solverPredictedOffset, mix, false);
// calculate current relative velocity between rope and pinhole:
float velocity = (mix - edgeMus[i]) / substepTime * edgeLength; // vel = pos / time.
relativeVelocities[i] = velocity;
// apply motor force:
float targetAccel = (parameters[i * 5 + 2] - velocity) / substepTime; // accel = vel / time.
float maxAccel = parameters[i * 5 + 3] * max(lerp(invMasses[p1], invMasses[p2], mix), EPSILON); // accel = force / mass. Guard against inf*0
velocity += clamp(targetAccel, -maxAccel, maxAccel) * substepTime;
// calculate new position by adding motor acceleration:
float corrMix = edgeMus[i] + velocity * substepTime / edgeLength;
// apply artificial friction by interpolating predicted position and corrected position.
mix = lerp(mix, corrMix, parameters[i * 5 + 1]);
// move to an adjacent simplex if needed
if (!ClampToRange(i, edgeIndex, mix) && (mix < 0 || mix > 1))
{
bool clampOnEnd = parameters[i * 5 + 4] > 0.5f;
// calculate distance we need to travel along simplex chain:
float distToTravel = length(particlePosition1 - particlePosition2) * (mix < 0 ? -mix : mix - 1);
int nextEdgeIndex;
for (int k = 0; k < 10; ++k)
{
// calculate index of next edge:
nextEdgeIndex = edgeRanges[i].x + (int)nfmod((mix < 0 ? edgeIndex - 1 : edgeIndex + 1) - edgeRanges[i].x, edgeCount);
// see if it's valid
if (!IsEdgeValid(edgeIndex, nextEdgeIndex, mix))
{
// disable constraint if needed
if (!clampOnEnd) { particleIndices[i] = -1; return; }
// otherwise clamp to end:
mix = saturate(mix);
break;
}
// advance to next edge:
edgeIndex = nextEdgeIndex;
p1 = deformableEdges[edgeIndex*2];
p2 = deformableEdges[edgeIndex*2 + 1];
particlePosition1 = lerp(prevPositions[p1], positions[p1], substepsToEnd);
particlePosition2 = lerp(prevPositions[p2], positions[p2], substepsToEnd);
edgeLength = length(particlePosition1 - particlePosition2) + EPSILON;
// stop if we reached target edge:
if (distToTravel <= edgeLength)
{
mix = mix < 0 ? 1 - saturate(distToTravel / edgeLength) : saturate(distToTravel / edgeLength);
ClampToRange(i, edgeIndex, mix);
break;
}
// stop if we reached end of range:
if (ClampToRange(i, edgeIndex, mix))
break;
distToTravel -= edgeLength;
}
}
// store new position along edge:
edgeMus[i] = mix;
particleIndices[i] = edgeIndex;
}
[numthreads(128, 1, 1)]
void Project (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int edgeIndex = particleIndices[i];
int colliderIndex = colliderIndices[i];
// if no collider or edge, ignore the constraint.
if (edgeIndex < 0 || colliderIndex < 0)
return;
float frameEnd = stepTime * steps;
float substepsToEnd = timeLeft / substepTime;
// calculate time adjusted compliances
float compliance = parameters[i * 5] / (substepTime * substepTime);
int p1 = deformableEdges[edgeIndex * 2];
int p2 = deformableEdges[edgeIndex * 2 + 1];
// get current edge data:
float mix = edgeMus[i];
float4 particlePosition1 = lerp(prevPositions[p1], positions[p1], substepsToEnd);
float4 particlePosition2 = lerp(prevPositions[p2], positions[p2], substepsToEnd);
float4 projection = lerp(particlePosition1, particlePosition2, mix);
// express pin offset in world space:
float4 worldPinOffset = transforms[colliderIndex].TransformPoint(offsets[i]);
float4 predictedPinOffset = worldPinOffset;
float rigidbodyLinearW = 0;
float rigidbodyAngularW = 0;
int rigidbodyIndex = shapes[colliderIndex].rigidbodyIndex;
if (rigidbodyIndex >= 0)
{
rigidbody rb = rigidbodies[rigidbodyIndex];
// predict offset point position using rb velocity at that point (can't integrate transform since position != center of mass)
float4 velocityAtPoint = GetRigidbodyVelocityAtPoint(rigidbodies[rigidbodyIndex],inertialSolverFrame[0].frame.InverseTransformPoint(worldPinOffset),
asfloat(linearDeltasAsInt[rigidbodyIndex]),
asfloat(angularDeltasAsInt[rigidbodyIndex]), inertialSolverFrame[0]);
predictedPinOffset = IntegrateLinear(predictedPinOffset, inertialSolverFrame[0].frame.TransformVector(velocityAtPoint), frameEnd);
// calculate linear and angular rigidbody effective masses (mass splitting: multiply by constraint count)
rigidbodyLinearW = rb.inverseMass * rb.constraintCount;
rigidbodyAngularW = RotationalInvMass(rb.inverseInertiaTensor,
worldPinOffset - rb.com,
normalizesafe(inertialSolverFrame[0].frame.TransformPoint(projection) - predictedPinOffset)) * rb.constraintCount;
}
// transform pinhole position to solver space for constraint solving:
predictedPinOffset = inertialSolverFrame[0].frame.InverseTransformPoint(predictedPinOffset);
float4 gradient = projection - predictedPinOffset;
float constraint = length(gradient);
float4 gradientDir = gradient / (constraint + EPSILON);
float lambda = (-constraint - compliance * lambdas[i]) / (lerp(invMasses[p1], invMasses[p2], mix) + rigidbodyLinearW + rigidbodyAngularW + compliance + EPSILON);
lambdas[i] += lambda;
float4 correction = lambda * gradientDir;
float baryScale = BaryScale(float4(1 - mix, mix, 0, 0));
AddPositionDelta(p1, correction * baryScale * invMasses[p1] * (1 - mix) / substepsToEnd);
AddPositionDelta(p2, correction * baryScale * invMasses[p2] * mix / substepsToEnd);
if (rigidbodyIndex >= 0)
{
ApplyImpulse(rigidbodyIndex,
-correction / frameEnd,
inertialSolverFrame[0].frame.InverseTransformPoint(worldPinOffset),
inertialSolverFrame[0].frame);
}
}
[numthreads(128, 1, 1)]
void Apply (uint3 id : SV_DispatchThreadID)
{
unsigned int i = id.x;
if (i >= activeConstraintCount) return;
int edgeIndex = particleIndices[i];
if (edgeIndex < 0) return;
int p1 = deformableEdges[edgeIndex * 2];
int p2 = deformableEdges[edgeIndex * 2 + 1];
ApplyPositionDelta(RW_positions, p1, sorFactor);
ApplyPositionDelta(RW_positions, p2, sorFactor);
}

Some files were not shown because too many files have changed in this diff Show More