You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
7.1 KiB
179 lines
7.1 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.VFX;
|
|
|
|
namespace UnityEditor.VFX.Block
|
|
{
|
|
class CollisionCone : CollisionShapeBase
|
|
{
|
|
public class InputProperties
|
|
{
|
|
[Tooltip("Sets the cone with which particles can collide.")]
|
|
public TCone cone = TCone.defaultValue;
|
|
}
|
|
|
|
public override IEnumerable<VFXNamedExpression> GetParameters(CollisionBase collisionBase, IEnumerable<VFXNamedExpression> collisionBaseParameters)
|
|
{
|
|
VFXExpression transform = null;
|
|
VFXExpression height = null;
|
|
VFXExpression baseRadius = null;
|
|
VFXExpression topRadius = null;
|
|
|
|
foreach (var param in collisionBaseParameters)
|
|
{
|
|
if (param.name.StartsWith("cone"))
|
|
{
|
|
if (param.name == "cone_" + nameof(TCone.transform))
|
|
transform = param.exp;
|
|
if (param.name == "cone_" + nameof(TCone.height))
|
|
height = param.exp;
|
|
if (param.name == "cone_" + nameof(TCone.baseRadius))
|
|
baseRadius = param.exp;
|
|
if (param.name == "cone_" + nameof(TCone.topRadius))
|
|
topRadius = param.exp;
|
|
|
|
continue; //exclude all automatic cone inputs
|
|
}
|
|
|
|
yield return param;
|
|
}
|
|
|
|
var finalTransform = transform;
|
|
|
|
var isZeroScaled = VFXOperatorUtility.IsTRSMatrixZeroScaled(finalTransform);
|
|
yield return new VFXNamedExpression(isZeroScaled, "isZeroScaled");
|
|
|
|
yield return new VFXNamedExpression(finalTransform, "fieldTransform");
|
|
yield return new VFXNamedExpression(new VFXExpressionInverseTRSMatrix(finalTransform), "invFieldTransform");
|
|
if (collisionBase.radiusMode != CollisionBase.RadiusMode.None)
|
|
{
|
|
var scale = new VFXExpressionExtractScaleFromMatrix(finalTransform);
|
|
yield return new VFXNamedExpression(VFXOperatorUtility.Reciprocal(scale), "invFieldScale");
|
|
}
|
|
|
|
yield return new VFXNamedExpression(baseRadius, "cone_baseRadius");
|
|
yield return new VFXNamedExpression(topRadius, "cone_topRadius");
|
|
yield return new VFXNamedExpression(height, "cone_height");
|
|
}
|
|
|
|
public override string GetSource(CollisionBase collisionBase)
|
|
{
|
|
string Source = @"
|
|
if (!isZeroScaled)
|
|
{
|
|
float3 nextPos = position + velocity * deltaTime;
|
|
float3 tNextPos = mul(invFieldTransform, float4(nextPos, 1.0f)).xyz;
|
|
float cone_radius = lerp(cone_baseRadius, cone_topRadius, saturate(tNextPos.y/cone_height));
|
|
float cone_halfHeight = cone_height * 0.5f;
|
|
float relativePosY = abs(tNextPos.y - cone_halfHeight);
|
|
";
|
|
|
|
if (collisionBase.radiusMode == CollisionBase.RadiusMode.None)
|
|
{
|
|
Source += @"
|
|
float sqrLength = dot(tNextPos.xz, tNextPos.xz);";
|
|
if (collisionBase.mode == CollisionBase.Mode.Solid)
|
|
Source += @"
|
|
hit = relativePosY < cone_halfHeight && sqrLength < cone_radius * cone_radius;";
|
|
else
|
|
Source += @"
|
|
hit = relativePosY > cone_halfHeight || sqrLength > cone_radius * cone_radius;";
|
|
|
|
Source += @"
|
|
if (hit)
|
|
{
|
|
float dist = sqrt(sqrLength);
|
|
float radiusCorrectionXZ = 0.0f;
|
|
float radiusCorrectionY = 0.0f;";
|
|
}
|
|
else
|
|
{
|
|
Source += @"
|
|
float dist = max(length(tNextPos.xz), VFX_EPSILON);
|
|
|
|
float2 relativeScaleXZ = (tNextPos.xz/dist) * invFieldScale.xz;
|
|
float radiusCorrectionXZ = radius * length(relativeScaleXZ);
|
|
dist -= radiusCorrectionXZ * colliderSign;
|
|
|
|
float radiusCorrectionY = radius * invFieldScale.y;
|
|
relativePosY -= radiusCorrectionY * colliderSign;";
|
|
if (collisionBase.mode == CollisionBase.Mode.Solid)
|
|
Source += @"
|
|
hit = relativePosY < cone_halfHeight && dist < cone_radius;";
|
|
else
|
|
Source += @"
|
|
hit = relativePosY > cone_halfHeight || dist > cone_radius;";
|
|
|
|
Source += @"
|
|
if (hit)
|
|
{";
|
|
}
|
|
|
|
Source += @"
|
|
float distToCap = colliderSign * (cone_halfHeight - relativePosY);
|
|
float distToSide = colliderSign * (cone_radius - dist);
|
|
float3 tPos = mul(invFieldTransform, float4(position, 1.0f)).xyz;
|
|
|
|
tNextPos.xz /= dist;
|
|
float3 sideNormal = normalize(float3(tNextPos.x * cone_height, cone_baseRadius - cone_topRadius, tNextPos.z * cone_height));
|
|
float3 capNormal = float3(0, tNextPos.y < cone_halfHeight ? -1.0f : 1.0f, 0);";
|
|
|
|
//Position/Normal correction, the reason behind float3(1,0,1) is the distToSide which is actually in 2D Plane (XZ)
|
|
Source += @"
|
|
if (colliderSign * distToSide < colliderSign * distToCap)
|
|
{
|
|
hitNormal = colliderSign * sideNormal;
|
|
tPos += hitNormal * float3(1,0,1) * distToSide;
|
|
}
|
|
else
|
|
{
|
|
hitNormal = colliderSign * capNormal;
|
|
tPos += hitNormal * distToCap;
|
|
}";
|
|
|
|
//Clamp outside/inside cone afterwards (could optional, only relevant with teleport cases)
|
|
//Alternatively, we can apply several time Position & Normal correction
|
|
bool applyClamp = true;
|
|
if (applyClamp)
|
|
{
|
|
Source += @"
|
|
dist = max(length(tPos.xz), VFX_EPSILON);
|
|
cone_radius = lerp(cone_baseRadius, cone_topRadius, saturate(tPos.y/cone_height));";
|
|
if (collisionBase.mode == CollisionBase.Mode.Solid)
|
|
{
|
|
Source += @"
|
|
if ( tPos.y > -radiusCorrectionY
|
|
&& tPos.y < cone_height + radiusCorrectionY
|
|
&& dist < cone_radius + radiusCorrectionXZ)
|
|
{
|
|
float3 candidateA = tPos;
|
|
float3 candidateB = tPos;
|
|
candidateA.y = tPos.y > 0.5f ? -radiusCorrectionY : cone_height + radiusCorrectionY;
|
|
candidateB.xz = tPos.xz / dist * (cone_radius + radiusCorrectionXZ);
|
|
if (Length2(candidateA - tPos.xyz) < Length2(candidateB - tPos.xyz))
|
|
tPos = candidateA;
|
|
else
|
|
tPos = candidateB;
|
|
}";
|
|
}
|
|
else
|
|
{
|
|
//Cone Radius is recomputed after y correction in case of high slope of the cone (no-op with cylinder)
|
|
Source += @"
|
|
tPos.y = clamp(tPos.y, radiusCorrectionY, cone_height - radiusCorrectionY);
|
|
cone_radius = lerp(cone_baseRadius, cone_topRadius, tPos.y/cone_height);
|
|
tPos.xz = tPos.xz/dist * min(dist, cone_radius - radiusCorrectionXZ);";
|
|
}
|
|
}
|
|
|
|
//Back to the initial space
|
|
Source += @"
|
|
hitPos = mul(fieldTransform, float4(tPos.xyz, 1.0f)).xyz;
|
|
hitNormal = VFXSafeNormalize(mul(float4(hitNormal, 0.0f), invFieldTransform).xyz);
|
|
}
|
|
}";
|
|
return Source;
|
|
}
|
|
}
|
|
}
|