You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
404 lines
12 KiB
HLSL
404 lines
12 KiB
HLSL
struct StrandGroomStruct
|
|
{
|
|
float2 flowDirectionRoot;
|
|
float2 flowDirectionBend;
|
|
float2 flowDirectionOrientation;
|
|
float2 scale;
|
|
float raise;
|
|
float isErased;
|
|
float windContribution;
|
|
float clumpMask;
|
|
float4 twist;
|
|
float4 overrideColor;
|
|
};
|
|
|
|
|
|
uniform float worldScale = 1;
|
|
|
|
#define SIZEOF_FLOAT3 12
|
|
uniform RWByteAddressBuffer sourceMeshData;
|
|
uniform uint vertexBufferStride;
|
|
|
|
static int DIRECTION_ROOT = 0;
|
|
static int HEIGHT = 1;
|
|
static int RAISE = 2;
|
|
static int MASK = 3;
|
|
static int ADD_FUR = 4;
|
|
static int DELETE_FUR = 5;
|
|
static int WIND_MAX_DISTANCE = 6;
|
|
static int CLUMPING_MASK = 7;
|
|
static int DIRECTION_BEND = 8;
|
|
static int DIRECTION_ORIENTATION = 9;
|
|
static int TWIST = 10;
|
|
static int RESET = 11;
|
|
static int ATTRACT = 12;
|
|
static int SMOOTH = 13;
|
|
static int WIDTH = 14;
|
|
static int COLOR_OVERRIDE = 15;
|
|
|
|
uniform int brushMenuType;
|
|
uniform float3 mouseHitPoint;
|
|
uniform float3 previousMouseHitPoint;
|
|
uniform float3 mouseHitNormal;
|
|
uniform float brushSize;
|
|
uniform float brushFalloff;
|
|
uniform float brushOpacity;
|
|
uniform float maskErase;
|
|
uniform bool ignoreNormal;
|
|
uniform float brushIntensity;
|
|
uniform bool isClumpTwistSelected;
|
|
uniform float twistSpread;
|
|
uniform float twistAmount;
|
|
uniform float overrideIntensity;
|
|
uniform float4 overrideColor;
|
|
uniform float3 clickStartHitPoint;
|
|
uniform float4 resetValues;
|
|
|
|
static float OPACITY_SENSITIVITY = 0.1;
|
|
static float FALLOFF_MASK_TRESHOLD = 0.01;
|
|
static float CARD_MESH_HEIGHT = 0.12;
|
|
|
|
uniform float4x4 localToWorldMatrix;
|
|
uniform float4x4 localToWorldRotationMatrix;
|
|
uniform float4x4 worldToLocalMatrix;
|
|
|
|
float linearFalloff(float distance, float brushRadius)
|
|
{
|
|
return saturate(1 - distance / brushRadius);
|
|
}
|
|
|
|
float normalFalloff(float3 normal, float3 hitNormal)
|
|
{
|
|
float falloff = 1;
|
|
if (!ignoreNormal)
|
|
{
|
|
falloff = saturate(dot(normalize(hitNormal), normalize(normal)) - 0.75f) * 4.0;
|
|
}
|
|
return falloff;
|
|
}
|
|
|
|
float calculateFalloff(float mag, float3 normal, float3 hitNormal)
|
|
{
|
|
float falloff = linearFalloff(mag, brushSize);
|
|
falloff = pow(falloff, saturate(1.0 - brushFalloff / brushSize)) * (brushOpacity * OPACITY_SENSITIVITY);
|
|
falloff *= normalFalloff(normal, hitNormal);
|
|
return falloff;
|
|
}
|
|
|
|
StrandGroomStruct lerpScale(StrandGroomStruct fpsp, float intensity, float value, bool scaleWidth, bool scaleHeight)
|
|
{
|
|
if (value > 1)
|
|
{
|
|
if (scaleWidth) fpsp.scale = float2(intensity, fpsp.scale.y);
|
|
if (scaleHeight) fpsp.scale = float2(fpsp.scale.x, intensity);
|
|
}
|
|
if (scaleWidth) fpsp.scale = float2(fpsp.scale.x + (intensity - fpsp.scale.x) * value, fpsp.scale.y);
|
|
if (scaleHeight) fpsp.scale = float2(fpsp.scale.x, fpsp.scale.y + (intensity - fpsp.scale.y) * value);
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpRaise(StrandGroomStruct fpsp, float intensity, float value)
|
|
{
|
|
if (value > 1)
|
|
{
|
|
fpsp.raise = intensity;
|
|
}
|
|
else
|
|
{
|
|
fpsp.raise = fpsp.raise + (intensity - fpsp.raise) * value;
|
|
}
|
|
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpClumpingMask(StrandGroomStruct fpsp, float intensity, float value)
|
|
{
|
|
if (value > 1)
|
|
{
|
|
fpsp.clumpMask = intensity;
|
|
}
|
|
else
|
|
{
|
|
fpsp.clumpMask = fpsp.clumpMask + (intensity - fpsp.clumpMask) * value;
|
|
}
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpTwist(StrandGroomStruct fpsp, float twistAmount, float value, bool isClumpTwist,
|
|
float twistSpread)
|
|
{
|
|
if (value > 1)
|
|
{
|
|
float4 twist = fpsp.twist;
|
|
if (isClumpTwist)
|
|
{
|
|
twist.y = twistAmount;
|
|
twist.w = twistSpread;
|
|
}
|
|
else
|
|
{
|
|
twist.x = twistAmount;
|
|
twist.z = twistSpread;
|
|
}
|
|
fpsp.twist = twist;
|
|
}
|
|
else
|
|
{
|
|
float4 twist = fpsp.twist;
|
|
if (isClumpTwist)
|
|
{
|
|
twist.y = twist.y + (twistAmount - twist.y) * value;
|
|
twist.w = twist.w + (twistSpread - twist.w) * value;
|
|
}
|
|
else
|
|
{
|
|
twist.x = twist.x + (twistAmount - twist.x) * value;
|
|
twist.z = twist.z + (twistSpread - twist.z) * value;
|
|
}
|
|
fpsp.twist = twist;
|
|
}
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpColorOverride(StrandGroomStruct fpsp, float intensity, float value, float4 color)
|
|
{
|
|
intensity = 1 - intensity;
|
|
float4 overrideColor = fpsp.overrideColor;
|
|
overrideColor.x = saturate(lerp(overrideColor.x, color.x, value));
|
|
overrideColor.y = saturate(lerp(overrideColor.y, color.y, value));
|
|
overrideColor.z = saturate(lerp(overrideColor.z, color.z, value));
|
|
if (value > 1)
|
|
{
|
|
overrideColor.w = intensity;
|
|
fpsp.overrideColor = overrideColor;
|
|
}
|
|
else
|
|
{
|
|
overrideColor.w = saturate(lerp(overrideColor.w, intensity, value));
|
|
fpsp.overrideColor = overrideColor;
|
|
}
|
|
return fpsp;
|
|
}
|
|
|
|
|
|
float3 calculateFlowDirection(
|
|
float3 normal,
|
|
float4 tangent,
|
|
float3 currentHitPont,
|
|
float3 previousHitPoint
|
|
)
|
|
{
|
|
float3 biNormal = cross(normal, tangent.xyz);
|
|
float4x4 mat = float4x4(
|
|
tangent.x, tangent.y, tangent.z, 0,
|
|
biNormal.x, biNormal.y, biNormal.z, 0,
|
|
normal.x, normal.y, normal.z, 0,
|
|
0, 0, 0, 1);
|
|
|
|
float3 currentPoint = mul(worldToLocalMatrix, float4(currentHitPont, 1.0)).xyz;
|
|
float3 previousPoint = mul(worldToLocalMatrix, float4(previousHitPoint, 1.0)).xyz;
|
|
float3 textureDirection = mul(mat, float4((currentPoint - previousPoint) / worldScale, 1.0)).xyz;
|
|
textureDirection.z = -textureDirection.y / worldScale;
|
|
textureDirection.y = 0;
|
|
textureDirection = normalize(textureDirection);
|
|
textureDirection = (textureDirection + float3(1, 1, 1)) / 2;
|
|
return textureDirection;
|
|
}
|
|
|
|
StrandGroomStruct lerpDirectionBend(StrandGroomStruct fpsp, float dirX, float dirY, float value)
|
|
{
|
|
fpsp.flowDirectionBend = float2(
|
|
fpsp.flowDirectionBend.x + (dirX - fpsp.flowDirectionBend.x) * value,
|
|
fpsp.flowDirectionBend.y + (dirY - fpsp.flowDirectionBend.y) * value
|
|
);
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpDirectionOrientation(StrandGroomStruct fpsp, float dirX, float dirY, float value)
|
|
{
|
|
fpsp.flowDirectionOrientation = float2(
|
|
fpsp.flowDirectionOrientation.x + (dirX - fpsp.flowDirectionOrientation.x) * value,
|
|
fpsp.flowDirectionOrientation.y + (dirY - fpsp.flowDirectionOrientation.y) * value
|
|
);
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpDirectionRoot(StrandGroomStruct fpsp, float dirX, float dirY, float value)
|
|
{
|
|
fpsp.flowDirectionRoot = float2(
|
|
fpsp.flowDirectionRoot.x + (dirX - fpsp.flowDirectionRoot.x) * value,
|
|
fpsp.flowDirectionRoot.y + (dirY - fpsp.flowDirectionRoot.y) * value
|
|
);
|
|
return fpsp;
|
|
}
|
|
|
|
float getCardHeight(StrandGroomStruct sgs)
|
|
{
|
|
return CARD_MESH_HEIGHT * sgs.scale.y * 3.5;
|
|
//Todo: WTF MAGIC NUMBER? The number should probably be a slider in the UI. The number says when the strand start standing up.
|
|
}
|
|
|
|
StrandGroomStruct blendSmootie(StrandGroomStruct fpsp, float intensity, float falloff,
|
|
float resetLengthAmount, float resetWidthAmount, float resetOrientAmount,
|
|
float resetBendAmount, float2 bendAverage,
|
|
float2 orientationAverage, float2 scaleAverage)
|
|
{
|
|
float brushIntensity = intensity * falloff;
|
|
|
|
fpsp.flowDirectionBend = lerp(fpsp.flowDirectionBend, bendAverage, resetBendAmount * brushIntensity);
|
|
fpsp.flowDirectionOrientation = lerp(fpsp.flowDirectionOrientation, orientationAverage,
|
|
resetOrientAmount * brushIntensity);
|
|
fpsp.scale.y = lerp(fpsp.scale.y, scaleAverage.y, resetLengthAmount * brushIntensity);
|
|
fpsp.scale.x = lerp(fpsp.scale.x, scaleAverage.x, resetWidthAmount * brushIntensity);
|
|
return fpsp;
|
|
}
|
|
|
|
StrandGroomStruct lerpReset(StrandGroomStruct fpsp, float intensity, float falloff,
|
|
float resetLengthAmount, float resetWidthAmount, float resetOrientAmount,
|
|
float resetBendAmount)
|
|
{
|
|
float2 centered = float2(0.5, 0.5);
|
|
return blendSmootie(fpsp, intensity, falloff, resetLengthAmount, resetWidthAmount, resetOrientAmount,
|
|
resetBendAmount, centered, centered, centered);
|
|
}
|
|
|
|
StrandGroomStruct paintAttract(
|
|
StrandGroomStruct fpsp,
|
|
float falloff,
|
|
float3 normal,
|
|
float4 tangent,
|
|
float3 vertPos
|
|
)
|
|
{
|
|
fpsp = lerpReset(
|
|
fpsp,
|
|
brushIntensity,
|
|
falloff,
|
|
0, 0, 0, 1);
|
|
|
|
float3 texDir = calculateFlowDirection(normal, tangent, clickStartHitPoint, vertPos);
|
|
float distanceToVertFomClickStart = distance(clickStartHitPoint, vertPos);
|
|
float cardHeight = getCardHeight(fpsp);
|
|
float standUpAtEndAmount = 1.0 - saturate(distanceToVertFomClickStart / cardHeight);
|
|
texDir = lerp(texDir, float3(1, 1, 1) / 2.0, standUpAtEndAmount);
|
|
return lerpDirectionOrientation(fpsp, texDir.x, texDir.z, falloff);
|
|
}
|
|
|
|
StrandGroomStruct lerpWindContribution(StrandGroomStruct fpsp, float intensity, float value)
|
|
{
|
|
if (value > 1)
|
|
{
|
|
fpsp.windContribution = intensity;
|
|
}
|
|
else
|
|
{
|
|
fpsp.windContribution = fpsp.windContribution + (intensity - fpsp.windContribution) * value;
|
|
}
|
|
return fpsp;
|
|
}
|
|
|
|
AppendStructuredBuffer<float2> smoothIndicesAndFalloffs;
|
|
StructuredBuffer<float2> smoothIndicesAndFalloffsRead;
|
|
RWStructuredBuffer<int> smoothSumBuffer;
|
|
|
|
|
|
static float PI = 3.14159265358979;
|
|
|
|
float CalculateAngle(float2 arrival)
|
|
{
|
|
float value = (float)((atan2(arrival.x, arrival.y) / PI) * 180.0);
|
|
return value;
|
|
}
|
|
|
|
float4x4 getScaleMatrix(float3 scale)
|
|
{
|
|
if (scale.y == 0 || isnan(scale.y) || isinf(scale.y))scale.y = 0.0001;
|
|
if (scale.x == 0 || isnan(scale.x) || isinf(scale.x))scale.x = 0.0001;
|
|
return float4x4(
|
|
scale.x, 0, 0, 0,
|
|
0, scale.y, 0, 0,
|
|
0, 0, scale.z, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
}
|
|
|
|
float4x4 getQuaternionMatrix(float3 rotationEuler)
|
|
{
|
|
float radX = radians(rotationEuler.x);
|
|
float radY = radians(rotationEuler.y);
|
|
float radZ = radians(rotationEuler.z);
|
|
|
|
float sinX, cosX;
|
|
sincos(radX, sinX, cosX);
|
|
|
|
float sinY, cosY;
|
|
sincos(radY, sinY, cosY);
|
|
|
|
float sinZ, cosZ;
|
|
sincos(radZ, sinZ, cosZ);
|
|
|
|
return float4x4(
|
|
cosY * cosZ, -cosY * sinZ, sinY, 0,
|
|
cosX * sinZ + sinX * sinY * cosZ, cosX * cosZ - sinX * sinY * sinZ, -sinX * cosY, 0,
|
|
sinX * sinZ - cosX * sinY * cosZ, sinX * cosZ + cosX * sinY * sinZ, cosX * cosY, 0,
|
|
0, 0, 0, 1
|
|
);
|
|
}
|
|
|
|
float getRaiseFactor(float raiseAmount)
|
|
{
|
|
return lerp(90.0, 0.0, raiseAmount);
|
|
}
|
|
|
|
float maxScaleHeight;
|
|
float minScaleHeight;
|
|
float maxScaleWidth;
|
|
float minScaleWidth;
|
|
|
|
float2 getScaleFactor(float2 scale)
|
|
{
|
|
scale.x = lerp(minScaleWidth, maxScaleWidth, scale.x);
|
|
scale.y = lerp(minScaleHeight, maxScaleHeight, scale.y);
|
|
return scale;
|
|
}
|
|
|
|
float4 getOrientationRotationValues(float2 flowDirection, float rootDirectionAngle)
|
|
{
|
|
float thisDirectionAngle = CalculateAngle(float2(flowDirection.x - 0.5f, flowDirection.y - 0.5f));
|
|
float bendAngle = thisDirectionAngle - rootDirectionAngle;
|
|
float bendAngleInaRadians = bendAngle * PI / 180;
|
|
float2 rootDir = normalize(float2(sin(rootDirectionAngle * PI / 180),
|
|
cos(rootDirectionAngle * PI / 180)));
|
|
float2 thisDir = normalize(float2(sin(thisDirectionAngle * PI / 180),
|
|
cos(thisDirectionAngle * PI / 180)));
|
|
float dotProd = abs(dot(rootDir, thisDir));
|
|
float yRotation = 90 - dotProd * 90;
|
|
float bendAmount = distance(flowDirection, float2(0.5, 0.5)) * 2;
|
|
return float4(sin(bendAngleInaRadians), yRotation, cos(bendAngleInaRadians), bendAmount);
|
|
}
|
|
|
|
float4 Interpolate4(float4 v1, float4 v2, float4 v3, float3 barycentricCoord)
|
|
{
|
|
return v1 * barycentricCoord.x + v2 * barycentricCoord.y + v3 * barycentricCoord.z;
|
|
}
|
|
|
|
float3 Interpolate3(float3 v1, float3 v2, float3 v3, float3 barycentricCoord)
|
|
{
|
|
return v1 * barycentricCoord.x + v2 * barycentricCoord.y + v3 * barycentricCoord.z;
|
|
}
|
|
|
|
float2 Interpolate2(float2 v1, float2 v2, float2 v3, float3 barycentricCoord)
|
|
{
|
|
return v1 * barycentricCoord.x + v2 * barycentricCoord.y + v3 * barycentricCoord.z;
|
|
}
|
|
|
|
float Interpolate(float v1, float v2, float v3, float3 barycentricCoord)
|
|
{
|
|
return v1 * barycentricCoord.x + v2 * barycentricCoord.y + v3 * barycentricCoord.z;
|
|
}
|
|
|
|
float rand(float n)
|
|
{
|
|
return frac(sin(dot(float2(n, n * 2), float2(12.9898, 4.1414))) * 43758.5453) - 0.5;
|
|
}
|