// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= PhysicsFieldEval.ush =============================================================================*/ #pragma once #include "Common.ush" /* ----------------------------------------------------------------- * Field System constants and context * ----------------------------------------------------------------- */ Buffer NodesParams; Buffer NodesOffsets; Buffer TargetsOffsets; float TimeSeconds; /* ----------------------------------------------------------------- * Shared datas to store field evaluation * ----------------------------------------------------------------- */ #define MAX_DATAS 16 groupshared float SharedDatas[64][MAX_DATAS]; static int GFieldGroupThreadId; /* ----------------------------------------------------------------- * Reandom Vector + Perlin Noise * ----------------------------------------------------------------- */ static const int p[512] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 }; float FadeValue(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } float LerpValue(float t, float a, float b) { return a + t * (b - a); } float GradientDirection(int hash, float x, float y, float z) { // CONVERT LO 4 BITS OF HASH CODE INTO 12 GRADIENT DIRECTIONS. int h = hash & 15; float u = h < 8 ? x : y; float v = h < 4 ? y : h == 12 || h == 14 ? x : z; return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v); } float PerlinNoise(float3 pos) { // FIND UNIT CUBE THAT CONTAINS POINT. int X = (int) floor(pos.x) & 255; int Y = (int) floor(pos.y) & 255; int Z = (int) floor(pos.z) & 255; // FIND RELATIVE X,Y,Z OF POINT IN CUBE. float x = pos.x - floor(pos.x); float y = pos.y - floor(pos.y); float z = pos.z - floor(pos.z); // COMPUTE FADE CURVES FOR EACH OF X,Y,Z. float u = FadeValue(x); float v = FadeValue(y); float w = FadeValue(z); // HASH COORDINATES OF THE 8 CUBE CORNERS int A = p[X] + Y; int AA = p[A] + Z; int AB = p[A + 1] + Z; int B = p[X + 1] + Y; int BA = p[B] + Z; int BB = p[B + 1] + Z; // AND ADD BLENDED RESULTS FROM 8 CORNERS OF CUBE return clamp(0.97 * LerpValue(w, LerpValue(v, LerpValue(u, GradientDirection(p[AA], x, y, z), // CORNER 0 GradientDirection(p[BA], x - 1, y, z)), // CORNER 1 LerpValue(u, GradientDirection(p[AB], x, y - 1, z), // CORNER 2 GradientDirection(p[BB], x - 1, y - 1, z))), // CORNER 3 LerpValue(v, LerpValue(u, GradientDirection(p[AA + 1], x, y, z - 1), // CORNER 4 GradientDirection(p[BA + 1], x - 1, y, z - 1)), // CORNER 5 LerpValue(u, GradientDirection(p[AB + 1], x, y - 1, z - 1), // CORNER 6 GradientDirection(p[BB + 1], x - 1, y - 1, z - 1)))), -1.0, 1.0); // CORNER 7 } uint3 RandomVector(int3 p) { // taking a signed int then reinterpreting as unsigned gives good behavior for negatives uint3 v = uint3(p); // Linear congruential step. These LCG constants are from Numerical Recipies // For additional #'s, PCG would do multiple LCG steps and scramble each on output // So v here is the RNG state v = v * 1664525u + 1013904223u; // PCG uses xorshift for the final shuffle, but it is expensive (and cheap // versions of xorshift have visible artifacts). Instead, use simple MAD Feistel steps // // Feistel ciphers divide the state into separate parts (usually by bits) // then apply a series of permutation steps one part at a time. The permutations // use a reversible operation (usually ^) to part being updated with the result of // a permutation function on the other parts and the key. // // In this case, I'm using v.x, v.y and v.z as the parts, using + instead of ^ for // the combination function, and just multiplying the other two parts (no key) for // the permutation function. // // That gives a simple mad per round. v.x += v.y * v.z; v.y += v.z * v.x; v.z += v.x * v.y; v.x += v.y * v.z; v.y += v.z * v.x; v.z += v.x * v.y; // only top 16 bits are well shuffled return v >> 16u; } /* ----------------------------------------------------------------- * Field System defines * ----------------------------------------------------------------- */ #define NONE_TYPE 0 #define RESULTS_TYPE 1 #define INTEGER_TYPE 2 #define SCALAR_TYPE 3 #define VECTOR_TYPE 4 #define NONE_TARGET 0 #define DYNAMIC_STATE 1 #define LINEAR_FORCE 2 #define EXTERNAL_STRAIN 3 #define KILL_PARTICLES 4 #define LINEAR_VELOCITY 5 #define ANGULAR_VELOCITY 6 #define ANGULAR_TORQUE 7 #define INTERNAL_STRAIN 8 #define DISABLED_THRESHOLD 9 #define SLEEPING_THRESHOLD 10 #define POSITION_STATIC 11 #define POSITION_ANIMATED 12 #define POSITION_TARGET 13 #define DYNAMIC_CONSTRAINT 14 #define COLLISION_GROUP 15 #define ACTIVATE_DISABLED 16 #define NONE_NODE 0 #define UNIFORM_INTEGER 1 #define RADIAL_MASK_INTEGER 2 #define UNIFORM_SCALAR 3 #define RADIAL_FALLOFF_SCALAR 4 #define PLANE_FALLOFF_SCALAR 5 #define BOX_FALLOFF_SCALAR 6 #define NOISE_SCALAR 7 #define UNIFORM_VECTOR 8 #define RADIAL_VECTOR 9 #define RANDOM_VECTOR 10 #define SUM_SCALAR 11 #define SUM_VECTOR 12 #define CONVERSION_FIELD 13 #define CULLING_FIELD 14 #define WAVE_SCALAR 15 #define SET_ALWAYS 0 #define SET_IFF_NOT_INTERIOR 1 #define SET_IFF_NOT_EXTERIOR 2 #define NONE_FALLOFF 0 #define FALLOFF_LINEAR 1 #define FALLOFF_INVERSE 2 #define FALLOFF_SQUARED 3 #define FALLOFF_LOGARITHMIC 4 #define MULTIPLY_OP 0 #define DIVIDE_OP 1 #define ADD_OP 2 #define SUBTRACT_OP 3 #define CULLING_INSIDE 0 #define CULLING_OUTSIDE 1 #define WAVE_COSINE 0 #define WAVE_GAUSSIAN 1 #define WAVE_FALLOFF 2 #define WAVE_DECAY 3 /* ----------------------------------------------------------------- * Quat utils * ----------------------------------------------------------------- */ float3 FieldRotateVectorByQuat(in float3 Vector, in float4 Quat) { float3 T = 2.0 * cross(Quat.xyz, Vector); return Vector + Quat.w * T + cross(Quat.xyz, T); } float3 FieldUnRotateVectorByQuat(in float3 Vector, in float4 Quat) { float3 T = 2.0 * cross(Quat.xyz, Vector); return Vector - Quat.w * T + cross(Quat.xyz, T); } /* ----------------------------------------------------------------- * Uniform Integer Field * ----------------------------------------------------------------- */ #define UNIFORM_INTEGER_MAGNITUDE 0 void EvaluateUniformInteger(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { SharedDatas[GFieldGroupThreadId][LocalIndex++] = NodesParams[NodeOffset + UNIFORM_INTEGER_MAGNITUDE]; } /* ----------------------------------------------------------------- * Radial Int Mask Field * ----------------------------------------------------------------- */ #define RADIAL_MASK_INTEGER_RADIUS 0 #define RADIAL_MASK_INTEGER_POSITIONX 1 #define RADIAL_MASK_INTEGER_POSITIONY 2 #define RADIAL_MASK_INTEGER_POSITIONZ 3 #define RADIAL_MASK_INTEGER_INTERIOR_VALUE 4 #define RADIAL_MASK_INTEGER_EXTERIOR_VALUE 5 #define RADIAL_MASK_INTEGER_SET_MASK_CONDITION 6 void EvaluateRadialMaskInteger(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const float3 DeltaPosition = float3(NodesParams[NodeOffset + RADIAL_MASK_INTEGER_POSITIONX], NodesParams[NodeOffset + RADIAL_MASK_INTEGER_POSITIONY], NodesParams[NodeOffset + RADIAL_MASK_INTEGER_POSITIONZ]) - SamplePosition; const float DistanceSquared = dot(DeltaPosition, DeltaPosition); const int DeltaResult = (DistanceSquared < NodesParams[NodeOffset + RADIAL_MASK_INTEGER_RADIUS] * NodesParams[NodeOffset + RADIAL_MASK_INTEGER_RADIUS]) ? (int)NodesParams[NodeOffset + RADIAL_MASK_INTEGER_INTERIOR_VALUE]: (int)NodesParams[NodeOffset + RADIAL_MASK_INTEGER_EXTERIOR_VALUE]; const int MaskCondition = (int)NodesParams[NodeOffset + RADIAL_MASK_INTEGER_SET_MASK_CONDITION]; SharedDatas[GFieldGroupThreadId][LocalIndex++] = (MaskCondition == SET_ALWAYS) ? DeltaResult : ((MaskCondition == SET_IFF_NOT_INTERIOR) && (SharedDatas[GFieldGroupThreadId][LocalIndex] != NodesParams[NodeOffset + RADIAL_MASK_INTEGER_INTERIOR_VALUE])) ? DeltaResult : ((MaskCondition == SET_IFF_NOT_EXTERIOR) && (SharedDatas[GFieldGroupThreadId][LocalIndex] != NodesParams[NodeOffset + RADIAL_MASK_INTEGER_EXTERIOR_VALUE])) ? DeltaResult : SharedDatas[GFieldGroupThreadId][LocalIndex]; } /* ----------------------------------------------------------------- * Uniform Scalar Field * ----------------------------------------------------------------- */ #define UNIFORM_SCALAR_MAGNITUDE 0 void EvaluateUniformScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { SharedDatas[GFieldGroupThreadId][LocalIndex++] = NodesParams[NodeOffset + UNIFORM_SCALAR_MAGNITUDE]; } /* ----------------------------------------------------------------- * Common Falloff functions * ----------------------------------------------------------------- */ void SetFalloffValue(in int FieldFalloff, in float FieldMagnitude, in float DeltaDistance, out float OutNodeResult) { if (FieldFalloff == NONE_FALLOFF) { OutNodeResult = FieldMagnitude; } else if (FieldFalloff == FALLOFF_LINEAR) { OutNodeResult = FieldMagnitude * DeltaDistance; } else if (FieldFalloff == FALLOFF_SQUARED) { OutNodeResult = FieldMagnitude * DeltaDistance * DeltaDistance; } else if (FieldFalloff == FALLOFF_INVERSE) { OutNodeResult = FieldMagnitude * 2.0 * (1.0 - 1.0 / (DeltaDistance+1.0)); } else if (FieldFalloff == FALLOFF_LOGARITHMIC) { OutNodeResult = FieldMagnitude * log(DeltaDistance + 1.0) / log(2.0); } else { OutNodeResult = 0.0; } } /* ----------------------------------------------------------------- * Wave Scalar Field * ----------------------------------------------------------------- */ #define WAVE_SCALAR_MAGNITUDE 0 #define WAVE_SCALAR_POSITIONX 1 #define WAVE_SCALAR_POSITIONY 2 #define WAVE_SCALAR_POSITIONZ 3 #define WAVE_SCALAR_WAVELENGTH 4 #define WAVE_SCALAR_PERIOD 5 #define WAVE_SCALAR_TIME 6 #define WAVE_SCALAR_FUNCTION 7 #define WAVE_SCALAR_FALLOFF 8 static const float WAVE_PI = 3.14159265f; void EvaluateWaveScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex, in float MinLength) { const float Distance = length(float3(NodesParams[NodeOffset + WAVE_SCALAR_POSITIONX], NodesParams[NodeOffset + WAVE_SCALAR_POSITIONY], NodesParams[NodeOffset + WAVE_SCALAR_POSITIONZ]) - SamplePosition); const float Wavelength = NodesParams[NodeOffset + WAVE_SCALAR_WAVELENGTH]; const float Velocity = Wavelength / NodesParams[NodeOffset + WAVE_SCALAR_PERIOD]; const float Wavenumber = 2.0 * WAVE_PI / max(Wavelength, MinLength); const float DeltaTime = max(TimeSeconds - NodesParams[NodeOffset + WAVE_SCALAR_TIME], 0.0); const float Radius = Velocity * DeltaTime; const float Phase = Wavenumber * (Distance - Radius); float Result = 0.0; const int Function = (int) NodesParams[NodeOffset + WAVE_SCALAR_FUNCTION]; const float Magnitude = NodesParams[NodeOffset + WAVE_SCALAR_MAGNITUDE]; [branch] if (Function == WAVE_GAUSSIAN) { Result = Magnitude * exp(-Phase * Phase); } else if (Function == WAVE_COSINE) { Result = Magnitude * cos(Phase); } else if (Function == WAVE_FALLOFF) { const int Falloff = (int) NodesParams[NodeOffset + WAVE_SCALAR_FALLOFF]; if ((Distance < Radius) && (Radius > 0.0)) { const float Fraction = (1.0 - Distance / Radius); SetFalloffValue(Falloff, Magnitude, Fraction, Result); } } else if (Function == WAVE_DECAY) { const float Decay = DeltaTime / NodesParams[NodeOffset + WAVE_SCALAR_PERIOD]; Result = Magnitude * exp(-Decay * Decay); } SharedDatas[GFieldGroupThreadId][LocalIndex++] = Result; } /* ----------------------------------------------------------------- * Radial Falloff Field * ----------------------------------------------------------------- */ #define RADIAL_FALLOFF_MAGNITUDE 0 #define RADIAL_FALLOFF_MIN_RANGE 1 #define RADIAL_FALLOFF_MAX_RANGE 2 #define RADIAL_FALLOFF_DEFAULT 3 #define RADIAL_FALLOFF_RADIUS 4 #define RADIAL_FALLOFF_POSITIONX 5 #define RADIAL_FALLOFF_POSITIONY 6 #define RADIAL_FALLOFF_POSITIONZ 7 #define RADIAL_FALLOFF_FALLOFF 8 void EvaluateRadialFalloffScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { float ScalarDatas = NodesParams[NodeOffset + RADIAL_FALLOFF_DEFAULT]; const float DeltaRange = NodesParams[NodeOffset + RADIAL_FALLOFF_MAX_RANGE] - NodesParams[NodeOffset + RADIAL_FALLOFF_MIN_RANGE]; const float3 DeltaPosition = float3(NodesParams[NodeOffset + RADIAL_FALLOFF_POSITIONX], NodesParams[NodeOffset + RADIAL_FALLOFF_POSITIONY], NodesParams[NodeOffset + RADIAL_FALLOFF_POSITIONZ]) - SamplePosition; const float LocalDistance = length(DeltaPosition); const float FieldRadius = NodesParams[NodeOffset + RADIAL_FALLOFF_RADIUS]; if (FieldRadius > 0.0 && LocalDistance < FieldRadius) { const float DeltaDistance = 1.0 - LocalDistance / FieldRadius; SetFalloffValue((int)NodesParams[NodeOffset + RADIAL_FALLOFF_FALLOFF], 1.0, DeltaDistance, ScalarDatas); ScalarDatas = NodesParams[NodeOffset + RADIAL_FALLOFF_MAGNITUDE] * (NodesParams[NodeOffset + RADIAL_FALLOFF_MIN_RANGE] + ScalarDatas * DeltaRange); } SharedDatas[GFieldGroupThreadId][LocalIndex++] = ScalarDatas; } /* ----------------------------------------------------------------- * Plane Falloff Field * ----------------------------------------------------------------- */ #define PLANE_FALLOFF_MAGNITUDE 0 #define PLANE_FALLOFF_MIN_RANGE 1 #define PLANE_FALLOFF_MAX_RANGE 2 #define PLANE_FALLOFF_DEFAULT 3 #define PLANE_FALLOFF_DISTANCE 4 #define PLANE_FALLOFF_POSITIONX 5 #define PLANE_FALLOFF_POSITIONY 6 #define PLANE_FALLOFF_POSITIONZ 7 #define PLANE_FALLOFF_NORMALX 8 #define PLANE_FALLOFF_NORMALY 9 #define PLANE_FALLOFF_NORMALZ 10 #define PLANE_FALLOFF_FALLOFF 11 void EvaluatePlaneFalloffScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { float ScalarDatas = NodesParams[NodeOffset + PLANE_FALLOFF_DEFAULT]; const float DeltaRange = NodesParams[NodeOffset + PLANE_FALLOFF_MAX_RANGE] - NodesParams[NodeOffset + PLANE_FALLOFF_MIN_RANGE]; const float3 PlaneBase = float3(NodesParams[NodeOffset + PLANE_FALLOFF_POSITIONX], NodesParams[NodeOffset + PLANE_FALLOFF_POSITIONY], NodesParams[NodeOffset + PLANE_FALLOFF_POSITIONZ]); const float3 PlaneNormal = float3(NodesParams[NodeOffset + PLANE_FALLOFF_NORMALX], NodesParams[NodeOffset + PLANE_FALLOFF_NORMALY], NodesParams[NodeOffset + PLANE_FALLOFF_NORMALZ]); const float PlaneOffset = dot(PlaneBase, PlaneNormal); const float LocalDistance = dot(PlaneNormal, SamplePosition) - PlaneOffset; const float PlaneDistance = NodesParams[NodeOffset + PLANE_FALLOFF_DISTANCE]; if (PlaneDistance > 0.0 && LocalDistance > -PlaneDistance && LocalDistance < 0.0) { const float DeltaDistance = 1.0 + LocalDistance / PlaneDistance; SetFalloffValue((int)NodesParams[NodeOffset + PLANE_FALLOFF_FALLOFF], 1.0, DeltaDistance, ScalarDatas); ScalarDatas = NodesParams[NodeOffset + PLANE_FALLOFF_MAGNITUDE] * (NodesParams[NodeOffset + PLANE_FALLOFF_MIN_RANGE] + ScalarDatas * DeltaRange); } SharedDatas[GFieldGroupThreadId][LocalIndex++] = ScalarDatas; } /* ----------------------------------------------------------------- * Box Falloff Field * ----------------------------------------------------------------- */ #define BOX_FALLOFF_MAGNITUDE 0 #define BOX_FALLOFF_MIN_RANGE 1 #define BOX_FALLOFF_MAX_RANGE 2 #define BOX_FALLOFF_DEFAULT 3 #define BOX_FALLOFF_ROTATIONX 4 #define BOX_FALLOFF_ROTATIONY 5 #define BOX_FALLOFF_ROTATIONZ 6 #define BOX_FALLOFF_ROTATIONW 7 #define BOX_FALLOFF_TRANSLATIONX 8 #define BOX_FALLOFF_TRANSLATIONY 9 #define BOX_FALLOFF_TRANSLATIONZ 10 #define BOX_FALLOFF_SCALEX 11 #define BOX_FALLOFF_SCALEY 12 #define BOX_FALLOFF_SCALEZ 13 #define BOX_FALLOFF_FALLOFF 14 void EvaluateBoxFalloffScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { float ScalarDatas = NodesParams[NodeOffset + BOX_FALLOFF_DEFAULT]; const float HalfBox = 50.0; const float DeltaRange = NodesParams[NodeOffset + BOX_FALLOFF_MAX_RANGE] - NodesParams[NodeOffset + BOX_FALLOFF_MIN_RANGE]; const float4 BoxRotation = float4(NodesParams[NodeOffset + BOX_FALLOFF_ROTATIONX], NodesParams[NodeOffset + BOX_FALLOFF_ROTATIONY], NodesParams[NodeOffset + BOX_FALLOFF_ROTATIONZ], NodesParams[NodeOffset + BOX_FALLOFF_ROTATIONW]); const float3 BoxTranslation = float3(NodesParams[NodeOffset + BOX_FALLOFF_TRANSLATIONX], NodesParams[NodeOffset + BOX_FALLOFF_TRANSLATIONY], NodesParams[NodeOffset + BOX_FALLOFF_TRANSLATIONZ]); const float3 BoxScale = float3(NodesParams[NodeOffset + BOX_FALLOFF_SCALEX], NodesParams[NodeOffset + BOX_FALLOFF_SCALEY], NodesParams[NodeOffset + BOX_FALLOFF_SCALEZ]); const float3 InverseScale = float3(BoxScale.x != 0.0 ? 1.0 / BoxScale.x : 0.0, BoxScale.y != 0.0 ? 1.0 / BoxScale.y : 0.0, BoxScale.z != 0.0 ? 1.0 / BoxScale.z : 0.0); const float3 LocalPosition = FieldUnRotateVectorByQuat(SamplePosition - BoxTranslation, BoxRotation) * InverseScale; const float3 DeltaPosition = abs(LocalPosition) - float3(HalfBox, HalfBox, HalfBox); const int ClosestAxis = ((DeltaPosition.x > DeltaPosition.y) && (DeltaPosition.x > DeltaPosition.z)) ? 0 : (DeltaPosition.y > DeltaPosition.z) ? 1 : 2; const float OutsideDistance = length(max(DeltaPosition, 0.0)); const float LocalDistance = OutsideDistance + min(DeltaPosition[ClosestAxis], 0.0); if (LocalDistance < 0.0) { const float DeltaDistance = -LocalDistance / HalfBox; SetFalloffValue((int)NodesParams[NodeOffset + BOX_FALLOFF_FALLOFF], 1.0,DeltaDistance, ScalarDatas); ScalarDatas = NodesParams[NodeOffset + BOX_FALLOFF_MAGNITUDE] * (NodesParams[NodeOffset + BOX_FALLOFF_MIN_RANGE] + ScalarDatas * DeltaRange); } SharedDatas[GFieldGroupThreadId][LocalIndex++] = ScalarDatas; } /* ----------------------------------------------------------------- * Noise Field * ----------------------------------------------------------------- */ #define NOISE_MIN_RANGE 0 #define NOISE_MAX_RANGE 1 #define NOISE_ROTATIONX 2 #define NOISE_ROTATIONY 3 #define NOISE_ROTATIONZ 4 #define NOISE_ROTATIONW 5 #define NOISE_TRANSLATIONX 6 #define NOISE_TRANSLATIONY 7 #define NOISE_TRANSLATIONZ 8 #define NOISE_SCALEX 9 #define NOISE_SCALEY 10 #define NOISE_SCALEZ 11 void EvaluateNoiseScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex, in float MinScale) { const float4 NoiseRotation = float4(NodesParams[NodeOffset + NOISE_ROTATIONX], NodesParams[NodeOffset + NOISE_ROTATIONY], NodesParams[NodeOffset + NOISE_ROTATIONZ], NodesParams[NodeOffset + NOISE_ROTATIONW]); const float3 NoiseTranslation = float3(NodesParams[NodeOffset + NOISE_TRANSLATIONX], NodesParams[NodeOffset + NOISE_TRANSLATIONY], NodesParams[NodeOffset + NOISE_TRANSLATIONZ]); float3 NoiseScale = float3(NodesParams[NodeOffset + NOISE_SCALEX], NodesParams[NodeOffset + NOISE_SCALEY], NodesParams[NodeOffset + NOISE_SCALEZ]); NoiseScale = 0.5 * max(NoiseScale, float3(MinScale, MinScale, MinScale)); const float3 InverseScale = float3(NoiseScale.x != 0.0 ? 1.0 / NoiseScale.x : 0.0, NoiseScale.y != 0.0 ? 1.0 / NoiseScale.y : 0.0, NoiseScale.z != 0.0 ? 1.0 / NoiseScale.z : 0.0); float3 LocalPosition = fmod(FieldUnRotateVectorByQuat(SamplePosition - NoiseTranslation, NoiseRotation), NoiseScale) * InverseScale; LocalPosition = (clamp(LocalPosition, float3(-1.0,-1.0,-1.0), float3(1.0,1.0,1.0)) * 0.5 + float3(0.5,0.5,0.5)) * 255; const float NoiseValue = PerlinNoise(LocalPosition) * 0.5 + 0.5; SharedDatas[GFieldGroupThreadId][LocalIndex++] = NoiseValue * (NodesParams[NodeOffset + NOISE_MAX_RANGE] - NodesParams[NodeOffset + NOISE_MIN_RANGE]) + NodesParams[NodeOffset + NOISE_MIN_RANGE]; } /* ----------------------------------------------------------------- * Uniform Vector Field * ----------------------------------------------------------------- */ #define UNIFORM_VECTOR_MAGNITUDE 0 #define UNIFORM_VECTOR_DIRECTIONX 1 #define UNIFORM_VECTOR_DIRECTIONY 2 #define UNIFORM_VECTOR_DIRECTIONZ 3 void EvaluateUniformVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const float3 VectorDatas = NodesParams[NodeOffset + UNIFORM_VECTOR_MAGNITUDE] * float3( NodesParams[NodeOffset + UNIFORM_VECTOR_DIRECTIONX], NodesParams[NodeOffset + UNIFORM_VECTOR_DIRECTIONY], NodesParams[NodeOffset + UNIFORM_VECTOR_DIRECTIONZ]); SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.x; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.y; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.z; } /* ----------------------------------------------------------------- * Radial Vector Field * ----------------------------------------------------------------- */ #define RADIAL_VECTOR_MAGNITUDE 0 #define RADIAL_VECTOR_POSITIONX 1 #define RADIAL_VECTOR_POSITIONY 2 #define RADIAL_VECTOR_POSITIONZ 3 void EvaluateRadialVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const float3 RadialDirection = SamplePosition - float3( NodesParams[NodeOffset + RADIAL_VECTOR_POSITIONX], NodesParams[NodeOffset + RADIAL_VECTOR_POSITIONY], NodesParams[NodeOffset + RADIAL_VECTOR_POSITIONZ]); const float DirectionLength = length(RadialDirection); const float3 VectorDatas = (DirectionLength != 0.0) ? NodesParams[NodeOffset + RADIAL_VECTOR_MAGNITUDE] * RadialDirection / DirectionLength : float3(0, 0, 0); SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.x; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.y; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.z; } /* ----------------------------------------------------------------- * Random Vector Field * ----------------------------------------------------------------- */ #define RANDOM_VECTOR_MAGNITUDE 0 void EvaluateRandomVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const float3 RadialDirection = float3(RandomVector(int3(SamplePosition))) / 0xffff - 0.5;//float3(Rand3DPCG16(int3(SamplePosition))) / 0xffff - 0.5; const float DirectionLength = length(RadialDirection); const float3 VectorDatas = (DirectionLength != 0.0) ? NodesParams[NodeOffset + RANDOM_VECTOR_MAGNITUDE] * RadialDirection / DirectionLength : float3(0, 0, 0); SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.x; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.y; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.z; } /* ----------------------------------------------------------------- * Sum Scalar Field * ----------------------------------------------------------------- */ #define SUM_SCALAR_MAGNITUDE 0 #define SUM_SCALAR_RIGHT 1 #define SUM_SCALAR_LEFT 2 #define SUM_SCALAR_OPERATION 3 void EvaluateSumScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int HasRight = (int)NodesParams[NodeOffset + SUM_SCALAR_RIGHT]; const int HasLeft = (int)NodesParams[NodeOffset + SUM_SCALAR_LEFT]; LocalIndex -= HasRight + HasLeft; const int FieldOperation = (int)NodesParams[NodeOffset + SUM_SCALAR_OPERATION]; const bool UnitOperation = (FieldOperation == MULTIPLY_OP) || (FieldOperation == DIVIDE_OP); int OffsetIndex = LocalIndex; const float ScalarRight = HasRight ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : UnitOperation ? 1.0 : 0.0; OffsetIndex += HasRight; const float ScalarLeft = HasLeft ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : UnitOperation ? 1.0 : 0.0; float ScalarDatas = 0.0; if (FieldOperation == MULTIPLY_OP) { ScalarDatas = ScalarRight * ScalarLeft; } else if (FieldOperation == DIVIDE_OP) { ScalarDatas = ScalarLeft / ScalarRight; } else if (FieldOperation == ADD_OP) { ScalarDatas = ScalarRight + ScalarLeft; } else if (FieldOperation == SUBTRACT_OP) { ScalarDatas = ScalarLeft - ScalarRight; } SharedDatas[GFieldGroupThreadId][LocalIndex++] = ScalarDatas * NodesParams[NodeOffset + SUM_SCALAR_MAGNITUDE]; } /* ----------------------------------------------------------------- * Sum Vector Field * ----------------------------------------------------------------- */ #define SUM_VECTOR_MAGNITUDE 0 #define SUM_VECTOR_SCALAR 1 #define SUM_VECTOR_RIGHT 2 #define SUM_VECTOR_LEFT 3 #define SUM_VECTOR_OPERATION 4 void EvaluateSumVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int HasScalar = (int)NodesParams[NodeOffset + SUM_VECTOR_SCALAR]; const int HasRight = (int)NodesParams[NodeOffset + SUM_VECTOR_RIGHT]; const int HasLeft = (int)NodesParams[NodeOffset + SUM_VECTOR_LEFT]; LocalIndex -= HasScalar + HasRight * 3 + HasLeft * 3; const int FieldOperation = (int)NodesParams[NodeOffset + SUM_VECTOR_OPERATION]; const bool UnitOperation = (FieldOperation == MULTIPLY_OP) || (FieldOperation == DIVIDE_OP); int OffsetIndex = LocalIndex; const float ScalarMagnitude = HasScalar ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : 1.0; OffsetIndex += HasScalar; const float3 VectorRight = HasRight ? float3(SharedDatas[GFieldGroupThreadId][OffsetIndex], SharedDatas[GFieldGroupThreadId][OffsetIndex + 1], SharedDatas[GFieldGroupThreadId][OffsetIndex + 2]) : UnitOperation ? float3(1, 1, 1) : float3(0, 0, 0); OffsetIndex += HasRight * 3; const float3 VectorLeft = HasLeft ? float3(SharedDatas[GFieldGroupThreadId][OffsetIndex], SharedDatas[GFieldGroupThreadId][OffsetIndex + 1], SharedDatas[GFieldGroupThreadId][OffsetIndex + 2]) : UnitOperation ? float3(1, 1, 1) : float3(0, 0, 0); float3 VectorDatas = float3(0.0, 0.0, 0.0); if (FieldOperation == MULTIPLY_OP) { VectorDatas = VectorRight * VectorLeft; } else if (FieldOperation == DIVIDE_OP) { VectorDatas = VectorLeft / VectorRight; } else if (FieldOperation == ADD_OP) { VectorDatas = VectorRight + VectorLeft; } else if (FieldOperation == SUBTRACT_OP) { VectorDatas = VectorLeft - VectorRight; } VectorDatas *= ScalarMagnitude * NodesParams[NodeOffset + SUM_VECTOR_MAGNITUDE]; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.x; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.y; SharedDatas[GFieldGroupThreadId][LocalIndex++] = VectorDatas.z; } /* ----------------------------------------------------------------- * Conversion Scalar * ----------------------------------------------------------------- */ #define CONVERSION_INPUT 0 void EvaluateConversionScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { // conversion in the same LocalIndex } /* ----------------------------------------------------------------- * Conversion Integer * ----------------------------------------------------------------- */ void EvaluateConversionInteger(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { // conversion in the same LocalIndex } /* ----------------------------------------------------------------- * Culling Integer * ----------------------------------------------------------------- */ #define CULLING_SCALAR 0 #define CULLING_INPUT 1 #define CULLING_OPERATION 2 void EvaluateCullingInteger(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int HasScalar = (int)NodesParams[NodeOffset + CULLING_SCALAR]; const int HasInput = (int)NodesParams[NodeOffset + CULLING_INPUT]; LocalIndex -= HasScalar + HasInput; int OffsetIndex = LocalIndex; const float CullingScalar = HasScalar ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : 0.0; OffsetIndex += (int)CullingScalar; const int InputInteger = HasInput ? (int) SharedDatas[GFieldGroupThreadId][OffsetIndex] : 0; const int CullingOperation = (int)NodesParams[NodeOffset + CULLING_OPERATION]; const bool ValidSample = ((CullingOperation == CULLING_OUTSIDE) && (CullingScalar != 0.0)) || ((CullingOperation == CULLING_INSIDE) && (CullingScalar == 0.0)); SharedDatas[GFieldGroupThreadId][LocalIndex++] = (float) (ValidSample ? InputInteger : 0); } /* ----------------------------------------------------------------- * Culling Scalar * ----------------------------------------------------------------- */ void EvaluateCullingScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int HasScalar = (int)NodesParams[NodeOffset + CULLING_SCALAR]; const int HasInput = (int) NodesParams[NodeOffset + CULLING_INPUT]; LocalIndex -= HasScalar + HasInput; int OffsetIndex = LocalIndex; const float CullingScalar = HasScalar ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : 0.0; OffsetIndex += (int)CullingScalar; const float InputScalar = HasInput ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : 0.0; const int CullingOperation = (int)NodesParams[NodeOffset + CULLING_OPERATION]; const bool ValidSample = ((CullingOperation == CULLING_OUTSIDE) && (CullingScalar != 0.0)) || ((CullingOperation == CULLING_INSIDE) && (CullingScalar == 0.0)); SharedDatas[GFieldGroupThreadId][LocalIndex++] = ValidSample ? InputScalar : 0.0; } /* ----------------------------------------------------------------- * Culling Vector * ----------------------------------------------------------------- */ void EvaluateCullingVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int HasScalar = (int)NodesParams[NodeOffset + CULLING_SCALAR]; const int HasInput = (int)NodesParams[NodeOffset + CULLING_INPUT]; LocalIndex -= HasScalar + HasInput * 3; int OffsetIndex = LocalIndex; const float CullingScalar = HasScalar ? SharedDatas[GFieldGroupThreadId][OffsetIndex] : 0.0; OffsetIndex += (int)CullingScalar; const float3 InputVector = HasInput ? float3(SharedDatas[GFieldGroupThreadId][OffsetIndex], SharedDatas[GFieldGroupThreadId][OffsetIndex + 1], SharedDatas[GFieldGroupThreadId][OffsetIndex + 2]) : float3(0, 0, 0); const int CullingOperation = (int)NodesParams[NodeOffset + CULLING_OPERATION]; const bool ValidSample = ((CullingOperation == CULLING_OUTSIDE) && (CullingScalar != 0.0)) || ((CullingOperation == CULLING_INSIDE) && (CullingScalar == 0.0)); SharedDatas[GFieldGroupThreadId][LocalIndex++] = ValidSample ? InputVector.x : 0.0; SharedDatas[GFieldGroupThreadId][LocalIndex++] = ValidSample ? InputVector.y : 0.0; SharedDatas[GFieldGroupThreadId][LocalIndex++] = ValidSample ? InputVector.z : 0.0; } /* ----------------------------------------------------------------- * Nodes evaluation * ----------------------------------------------------------------- */ void EvaluateFieldNodeInteger(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int NodeType = (int)NodesParams[NodeOffset]; [branch] if (NodeType == UNIFORM_INTEGER) { EvaluateUniformInteger(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == RADIAL_MASK_INTEGER) { EvaluateRadialMaskInteger(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == CONVERSION_FIELD) { EvaluateConversionInteger(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == CULLING_FIELD) { EvaluateCullingInteger(SamplePosition, NodeOffset + 1, LocalIndex); } } void EvaluateFieldNodeScalar(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex, in float MinScale, in float MinLength) { const int NodeType = (int)NodesParams[NodeOffset]; [branch] if (NodeType == UNIFORM_SCALAR) { EvaluateUniformScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == WAVE_SCALAR) { EvaluateWaveScalar(SamplePosition, NodeOffset + 1, LocalIndex, MinLength); } else if (NodeType == RADIAL_FALLOFF_SCALAR) { EvaluateRadialFalloffScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == PLANE_FALLOFF_SCALAR) { EvaluatePlaneFalloffScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == BOX_FALLOFF_SCALAR) { EvaluateBoxFalloffScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == NOISE_SCALAR) { EvaluateNoiseScalar(SamplePosition, NodeOffset + 1, LocalIndex, MinScale); } else if (NodeType == SUM_SCALAR) { EvaluateSumScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == CONVERSION_FIELD) { EvaluateConversionScalar(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == CULLING_FIELD) { EvaluateCullingScalar(SamplePosition, NodeOffset + 1, LocalIndex); } } void EvaluateFieldNodeVector(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex) { const int NodeType = (int)NodesParams[NodeOffset]; [branch] if (NodeType == UNIFORM_VECTOR) { EvaluateUniformVector(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == RADIAL_VECTOR) { EvaluateRadialVector(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == RANDOM_VECTOR) { EvaluateRandomVector(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == SUM_VECTOR) { EvaluateSumVector(SamplePosition, NodeOffset + 1, LocalIndex); } else if (NodeType == CULLING_FIELD) { EvaluateCullingVector(SamplePosition, NodeOffset + 1, LocalIndex); } } void EvaluateFieldNodeDatas(in float3 SamplePosition, in int NodeOffset, inout int LocalIndex, in float MinScale, in float MinLength) { const int DatasType = (int)NodesParams[NodeOffset]; [branch] if (DatasType == SCALAR_TYPE) { EvaluateFieldNodeScalar(SamplePosition, NodeOffset + 1, LocalIndex, MinScale, MinLength); } else if (DatasType == INTEGER_TYPE) { EvaluateFieldNodeInteger(SamplePosition, NodeOffset + 1, LocalIndex); } else if (DatasType == VECTOR_TYPE) { EvaluateFieldNodeVector(SamplePosition, NodeOffset + 1, LocalIndex); } } void SampleFieldDatas(in float3 SamplePosition, inout int LocalIndex, in int TargetType, in float MinScale, in float MinLength) { const int NodesBegin = TargetsOffsets[TargetType]; const int NodesEnd = TargetsOffsets[TargetType + 1]; const int NumNodes = NodesBegin - NodesEnd; [unroll] for (int DatasIndex = 0; DatasIndex < MAX_DATAS; ++DatasIndex) { SharedDatas[GFieldGroupThreadId][DatasIndex] = 0.0; } [branch] if (NumNodes != 0) { for (int NodeIndex = NodesBegin; NodeIndex < NodesEnd; ++NodeIndex) { const int NodeOffset = NodesOffsets[NodeIndex]; EvaluateFieldNodeDatas(SamplePosition, NodeOffset, LocalIndex, MinScale, MinLength); } } } float3 PhysicsField_EvalPhysicsVectorField(in float3 SamplePosition, in int TargetIndex) { float3 VectorField = float3(0,0,0); int LocalIndex = 0; SampleFieldDatas(SamplePosition, LocalIndex, TargetIndex, 0.0, 0.0); if(LocalIndex == 3) { VectorField.x = SharedDatas[GFieldGroupThreadId][0]; VectorField.y = SharedDatas[GFieldGroupThreadId][1]; VectorField.z = SharedDatas[GFieldGroupThreadId][2]; } return VectorField; } float PhysicsField_EvalPhysicsScalarField(in float3 SamplePosition, in int TargetIndex) { float ScalarField = 0.0; int LocalIndex = 0; SampleFieldDatas(SamplePosition, LocalIndex, TargetIndex, 0.0, 0.0); if(LocalIndex == 1) { ScalarField = SharedDatas[GFieldGroupThreadId][0]; } return ScalarField; } int PhysicsField_EvalPhysicsIntegerField(in float3 SamplePosition, in int TargetIndex) { int IntegerField = 0; int LocalIndex = 0; SampleFieldDatas(SamplePosition, LocalIndex, TargetIndex, 0.0, 0.0); if(LocalIndex == 1) { IntegerField = SharedDatas[GFieldGroupThreadId][0]; } return IntegerField; }