Search the source of every open source package.
3025 results
using System.Runtime.CompilerServices;
public static class OpenSimplex2S
{
private const long PRIME_X = 0x5205402B9270C86FL;
private const long PRIME_Y = 0x598CD327003817B5L;
private const long PRIME_Z = 0x5BCC226E9FA0BACBL;
private const long PRIME_W = 0x56CC5227E58F554BL;
private const long HASH_MULTIPLIER = 0x53A3F72DEEC546F5L;
private const long SEED_FLIP_3D = -0x52D547B2E96ED629L;
private const double ROOT2OVER2 = 0.7071067811865476;
private const double SKEW_2D = 0.366025403784439;
private const double UNSKEW_2D = -0.21132486540518713;
private const double ROOT3OVER3 = 0.577350269189626;
private const double FALLBACK_ROTATE3 = 2.0 / 3.0;
private const double ROTATE3_ORTHOGONALIZER = UNSKEW_2D;
private const float SKEW_4D = 0.309016994374947f;
private const float UNSKEW_4D = -0.138196601125011f;
private const int N_GRADS_2D_EXPONENT = 7;
private const int N_GRADS_3D_EXPONENT = 8;
private const int N_GRADS_4D_EXPONENT = 9;
private const int N_GRADS_2D = 1 << N_GRADS_2D_EXPONENT;
private const int N_GRADS_3D = 1 << N_GRADS_3D_EXPONENT;
private const int N_GRADS_4D = 1 << N_GRADS_4D_EXPONENT;
private const double NORMALIZER_2D = 0.05481866495625118;
private const double NORMALIZER_3D = 0.2781926117527186;
private const double NORMALIZER_4D = 0.11127401889945551;
private const float RSQUARED_2D = 2.0f / 3.0f;
private const float RSQUARED_3D = 3.0f / 4.0f;
private const float RSQUARED_4D = 4.0f / 5.0f;
/*
* Noise Evaluators
*/
/**
* 2D OpenSimplex2S/SuperSimplex noise, standard lattice orientation.
*/
public static float Noise2( long seed, double x, double y )
{
// Get points for A2* lattice
double s = SKEW_2D * (x + y);
double xs = x + s, ys = y + s;
return Noise2_UnskewedBase( seed, xs, ys );
}
/**
* 2D OpenSimplex2S/SuperSimplex noise, with Y pointing down the main diagonal.
* Might be better for a 2D sandbox style game, where Y is vertical.
* Probably slightly less optimal for heightmaps or continent maps,
* unless your map is centered around an equator. It's a slight
* difference, but the option is here to make it easy.
*/
public static float Noise2_ImproveX( long seed, double x, double y )
{
// Skew transform and rotation baked into one.
double xx = x * ROOT2OVER2;
double yy = y * (ROOT2OVER2 * (1 + 2 * SKEW_2D));
return Noise2_UnskewedBase( seed, yy + xx, yy - xx );
}
/**
* 2D OpenSimplex2S/SuperSimplex noise base.
*/
private static float Noise2_UnskewedBase( long seed, double xs, double ys )
{
// Get base points and offsets.
int xsb = FastFloor( xs ), ysb = FastFloor( ys );
float xi = (float)(xs - xsb), yi = (float)(ys - ysb);
// Prime pre-multiplication for hash.
long xsbp = xsb * PRIME_X, ysbp = ysb * PRIME_Y;
// Unskew.
float t = (xi + yi) * (float)UNSKEW_2D;
float dx0 = xi + t, dy0 = yi + t;
// First vertex.
float a0 = RSQUARED_2D - dx0 * dx0 - dy0 * dy0;
float value = (a0 * a0) * (a0 * a0) * Grad( seed, xsbp, ysbp, dx0, dy0 );
// Second vertex.
float a1 = (float)(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + ((float)(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0);
float dx1 = dx0 - (float)(1 + 2 * UNSKEW_2D);
float dy1 = dy0 - (float)(1 + 2 * UNSKEW_2D);
value += (a1 * a1) * (a1 * a1) * Grad( seed, xsbp + PRIME_X, ysbp + PRIME_Y, dx1, dy1 );
// Third and fourth vertices.
// Nested conditionals were faster than compact bit logic/arithmetic.
float xmyi = xi - yi;
if ( t < UNSKEW_2D )
{
if ( xi + xmyi > 1 )
{
float dx2 = dx0 - (float)(3 * UNSKEW_2D + 2);
float dy2 = dy0 - (float)(3 * UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp + (PRIME_X << 1), ysbp + PRIME_Y, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)UNSKEW_2D;
float dy2 = dy0 - (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp + PRIME_Y, dx2, dy2 );
}
}
if ( yi - xmyi > 1 )
{
float dx3 = dx0 - (float)(3 * UNSKEW_2D + 1);
float dy3 = dy0 - (float)(3 * UNSKEW_2D + 2);
float a3 = RSQUARED_2D - dx3 * dx3 - dy3 * dy3;
if ( a3 > 0 )
{
value += (a3 * a3) * (a3 * a3) * Grad( seed, xsbp + PRIME_X, ysbp + (PRIME_Y << 1), dx3, dy3 );
}
}
else
{
float dx3 = dx0 - (float)(UNSKEW_2D + 1);
float dy3 = dy0 - (float)UNSKEW_2D;
float a3 = RSQUARED_2D - dx3 * dx3 - dy3 * dy3;
if ( a3 > 0 )
{
value += (a3 * a3) * (a3 * a3) * Grad( seed, xsbp + PRIME_X, ysbp, dx3, dy3 );
}
}
}
else
{
if ( xi + xmyi < 0 )
{
float dx2 = dx0 + (float)(1 + UNSKEW_2D);
float dy2 = dy0 + (float)UNSKEW_2D;
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp - PRIME_X, ysbp, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)(UNSKEW_2D + 1);
float dy2 = dy0 - (float)UNSKEW_2D;
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp + PRIME_X, ysbp, dx2, dy2 );
}
}
if ( yi < xmyi )
{
float dx2 = dx0 + (float)UNSKEW_2D;
float dy2 = dy0 + (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp - PRIME_Y, dx2, dy2 );
}
}
else
{
float dx2 = dx0 - (float)UNSKEW_2D;
float dy2 = dy0 - (float)(UNSKEW_2D + 1);
float a2 = RSQUARED_2D - dx2 * dx2 - dy2 * dy2;
if ( a2 > 0 )
{
value += (a2 * a2) * (a2 * a2) * Grad( seed, xsbp, ysbp + PRIME_Y, dx2, dy2 );
}
}
}
return value;
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, with better visual isotropy in (X, Y).
* Recommended for 3D terrain and time-varied animations.
* The Z coordinate should always be the "different" coordinate in whatever your use case is.
* If Y is vertical in world coordinates, call Noise3_ImproveXZ(x, z, Y) or use Noise3_XZBeforeY.
* If Z is vertical in world coordinates, call Noise3_ImproveXZ(x, y, Z).
* For a time varied animation, call Noise3_ImproveXY(x, y, T).
*/
public static float Noise3_ImproveXY( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
// and the planes formed by XY are moved far out of alignment with the cube faces.
// Orthonormal rotation. Not a skew transform.
double xy = x + y;
double s2 = xy * ROTATE3_ORTHOGONALIZER;
double zz = z * ROOT3OVER3;
double xr = x + s2 + zz;
double yr = y + s2 + zz;
double zr = xy * -ROOT3OVER3 + zz;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, with better visual isotropy in (X, Z).
* Recommended for 3D terrain and time-varied animations.
* The Y coordinate should always be the "different" coordinate in whatever your use case is.
* If Y is vertical in world coordinates, call Noise3_ImproveXZ(x, Y, z).
* If Z is vertical in world coordinates, call Noise3_ImproveXZ(x, Z, y) or use Noise3_ImproveXY.
* For a time varied animation, call Noise3_ImproveXZ(x, T, y) or use Noise3_ImproveXY.
*/
public static float Noise3_ImproveXZ( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
// and the planes formed by XZ are moved far out of alignment with the cube faces.
// Orthonormal rotation. Not a skew transform.
double xz = x + z;
double s2 = xz * -0.211324865405187;
double yy = y * ROOT3OVER3;
double xr = x + s2 + yy;
double zr = z + s2 + yy;
double yr = xz * -ROOT3OVER3 + yy;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* 3D OpenSimplex2S/SuperSimplex noise, fallback rotation option
* Use Noise3_ImproveXY or Noise3_ImproveXZ instead, wherever appropriate.
* They have less diagonal bias. This function's best use is as a fallback.
*/
public static float Noise3_Fallback( long seed, double x, double y, double z )
{
// Re-orient the cubic lattices via rotation, to produce a familiar look.
// Orthonormal rotation. Not a skew transform.
double r = FALLBACK_ROTATE3 * (x + y + z);
double xr = r - x, yr = r - y, zr = r - z;
// Evaluate both lattices to form a BCC lattice.
return Noise3_UnrotatedBase( seed, xr, yr, zr );
}
/**
* Generate overlapping cubic lattices for 3D Re-oriented BCC noise.
* Lookup table implementation inspired by DigitalShadow.
* It was actually faster to narrow down the points in the loop itself,
* than to build up the index with enough info to isolate 8 points.
*/
private static float Noise3_UnrotatedBase( long seed, double xr, double yr, double zr )
{
// Get base points and offsets.
int xrb = FastFloor( xr ), yrb = FastFloor( yr ), zrb = FastFloor( zr );
float xi = (float)(xr - xrb), yi = (float)(yr - yrb), zi = (float)(zr - zrb);
// Prime pre-multiplication for hash. Also flip seed for second lattice copy.
long xrbp = xrb * PRIME_X, yrbp = yrb * PRIME_Y, zrbp = zrb * PRIME_Z;
long seed2 = seed ^ -0x52D547B2E96ED629L;
// -1 if positive, 0 if negative.
int xNMask = (int)(-0.5f - xi), yNMask = (int)(-0.5f - yi), zNMask = (int)(-0.5f - zi);
// First vertex.
float x0 = xi + xNMask;
float y0 = yi + yNMask;
float z0 = zi + zNMask;
float a0 = RSQUARED_3D - x0 * x0 - y0 * y0 - z0 * z0;
float value = (a0 * a0) * (a0 * a0) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x0, y0, z0 );
// Second vertex.
float x1 = xi - 0.5f;
float y1 = yi - 0.5f;
float z1 = zi - 0.5f;
float a1 = RSQUARED_3D - x1 * x1 - y1 * y1 - z1 * z1;
value += (a1 * a1) * (a1 * a1) * Grad( seed2,
xrbp + PRIME_X, yrbp + PRIME_Y, zrbp + PRIME_Z, x1, y1, z1 );
// Shortcuts for building the remaining falloffs.
// Derived by subtracting the polynomials with the offsets plugged in.
float xAFlipMask0 = ((xNMask | 1) << 1) * x1;
float yAFlipMask0 = ((yNMask | 1) << 1) * y1;
float zAFlipMask0 = ((zNMask | 1) << 1) * z1;
float xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0f;
float yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0f;
float zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0f;
bool skip5 = false;
float a2 = xAFlipMask0 + a0;
if ( a2 > 0 )
{
float x2 = x0 - (xNMask | 1);
float y2 = y0;
float z2 = z0;
value += (a2 * a2) * (a2 * a2) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x2, y2, z2 );
}
else
{
float a3 = yAFlipMask0 + zAFlipMask0 + a0;
if ( a3 > 0 )
{
float x3 = x0;
float y3 = y0 - (yNMask | 1);
float z3 = z0 - (zNMask | 1);
value += (a3 * a3) * (a3 * a3) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), x3, y3, z3 );
}
float a4 = xAFlipMask1 + a1;
if ( a4 > 0 )
{
float x4 = (xNMask | 1) + x1;
float y4 = y1;
float z4 = z1;
value += (a4 * a4) * (a4 * a4) * Grad( seed2,
xrbp + (xNMask & unchecked(PRIME_X * 2)), yrbp + PRIME_Y, zrbp + PRIME_Z, x4, y4, z4 );
skip5 = true;
}
}
bool skip9 = false;
float a6 = yAFlipMask0 + a0;
if ( a6 > 0 )
{
float x6 = x0;
float y6 = y0 - (yNMask | 1);
float z6 = z0;
value += (a6 * a6) * (a6 * a6) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), x6, y6, z6 );
}
else
{
float a7 = xAFlipMask0 + zAFlipMask0 + a0;
if ( a7 > 0 )
{
float x7 = x0 - (xNMask | 1);
float y7 = y0;
float z7 = z0 - (zNMask | 1);
value += (a7 * a7) * (a7 * a7) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), x7, y7, z7 );
}
float a8 = yAFlipMask1 + a1;
if ( a8 > 0 )
{
float x8 = x1;
float y8 = (yNMask | 1) + y1;
float z8 = z1;
value += (a8 * a8) * (a8 * a8) * Grad( seed2,
xrbp + PRIME_X, yrbp + (yNMask & (PRIME_Y << 1)), zrbp + PRIME_Z, x8, y8, z8 );
skip9 = true;
}
}
bool skipD = false;
float aA = zAFlipMask0 + a0;
if ( aA > 0 )
{
float xA = x0;
float yA = y0;
float zA = z0 - (zNMask | 1);
value += (aA * aA) * (aA * aA) * Grad( seed,
xrbp + (xNMask & PRIME_X), yrbp + (yNMask & PRIME_Y), zrbp + (~zNMask & PRIME_Z), xA, yA, zA );
}
else
{
float aB = xAFlipMask0 + yAFlipMask0 + a0;
if ( aB > 0 )
{
float xB = x0 - (xNMask | 1);
float yB = y0 - (yNMask | 1);
float zB = z0;
value += (aB * aB) * (aB * aB) * Grad( seed,
xrbp + (~xNMask & PRIME_X), yrbp + (~yNMask & PRIME_Y), zrbp + (zNMask & PRIME_Z), xB, yB, zB );
}
float aC = zAFlipMask1 + a1;
if ( aC > 0 )
{
float xC = x1;
float yC = y1;
float zC = (zNMask | 1) + z1;
value += (aC * aC) * (aC * aC) * Grad( seed2,
xrbp + PRIME_X, yrbp + PRIME_Y, zrbp + (zNMask & (PRIME_Z << 1)), xC, yC, zC );
skipD = true;
}
}
if ( !skip5 )
{
float a5 = yAFlipMask1 + zAFlipMask1 + a1;
if ( a5 > 0 )
{
float x5 = x1;
float y5 = (yNMask | 1) + y1;
float z5 = (zNMask | 1) + z1;
value += (a5 * a5) * (a5 * a5) * Grad( seed2,
xrbp + PRIME_X, yrbp + (yNMask & (PRIME_Y << 1)), zrbp + (zNMask & (PRIME_Z << 1)), x5, y5, z5 );
}
}
if ( !skip9 )
{
float a9 = xAFlipMask1 + zAFlipMask1 + a1;
if ( a9 > 0 )
{
float x9 = (xNMask | 1) + x1;
float y9 = y1;
float z9 = (zNMask | 1) + z1;
value += (a9 * a9) * (a9 * a9) * Grad( seed2,
xrbp + (xNMask & unchecked(PRIME_X * 2)), yrbp + PRIME_Y, zrbp + (zNMask & (PRIME_Z << 1)), x9, y9, z9 );
}
}
if ( !skipD )
{
float aD = xAFlipMask1 + yAFlipMask1 + a1;
if ( aD > 0 )
{
float xD = (xNMask | 1) + x1;
float yD = (yNMask | 1) + y1;
float zD = z1;
value += (aD * aD) * (aD * aD) * Grad( seed2,
xrbp + (xNMask & (PRIME_X << 1)), yrbp + (yNMask & (PRIME_Y << 1)), zrbp + PRIME_Z, xD, yD, zD );
}
}
return value;
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_ImproveXY
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* in a space where Z is vertical
*/
public static float Noise4_ImproveXYZ_ImproveXY( long seed, double x, double y, double z, double w )
{
double xy = x + y;
double s2 = xy * -0.21132486540518699998;
double zz = z * 0.28867513459481294226;
double ww = w * 1.118033988749894;
double xr = x + (zz + ww + s2), yr = y + (zz + ww + s2);
double zr = xy * -0.57735026918962599998 + (zz + ww);
double wr = z * -0.866025403784439 + ww;
return Noise4_UnskewedBase( seed, xr, yr, zr, wr );
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_ImproveXZ
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* in a space where Y is vertical
*/
public static float Noise4_ImproveXYZ_ImproveXZ( long seed, double x, double y, double z, double w )
{
double xz = x + z;
double s2 = xz * -0.21132486540518699998;
double yy = y * 0.28867513459481294226;
double ww = w * 1.118033988749894;
double xr = x + (yy + ww + s2), zr = z + (yy + ww + s2);
double yr = xz * -0.57735026918962599998 + (yy + ww);
double wr = y * -0.866025403784439 + ww;
return Noise4_UnskewedBase( seed, xr, yr, zr, wr );
}
/**
* 4D SuperSimplex noise, with XYZ oriented like Noise3_Fallback
* and W for an extra degree of freedom. W repeats eventually.
* Recommended for time-varied animations which texture a 3D object (W=time)
* where there isn't a clear distinction between horizontal and vertical
*/
public static float Noise4_ImproveXYZ( long seed, double x, double y, double z, double w )
{
double xyz = x + y + z;
double ww = w * 1.118033988749894;
double s2 = xyz * -0.16666666666666666 + ww;
double xs = x + s2, ys = y + s2, zs = z + s2, ws = -0.5 * xyz + ww;
return Noise4_UnskewedBase( seed, xs, ys, zs, ws );
}
/**
* 4D SuperSimplex noise, fallback lattice orientation.
*/
public static float Noise4_Fallback( long seed, double x, double y, double z, double w )
{
// Get points for A4 lattice
double s = SKEW_4D * (x + y + z + w);
double xs = x + s, ys = y + s, zs = z + s, ws = w + s;
return Noise4_UnskewedBase( seed, xs, ys, zs, ws );
}
/**
* 4D SuperSimplex noise base.
* Using ultra-simple 4x4x4x4 lookup partitioning.
* This isn't as elegant or SIMD/GPU/etc. portable as other approaches,
* but it competes performance-wise with optimized 2014 OpenSimplex.
*/
private static float Noise4_UnskewedBase( long seed, double xs, double ys, double zs, double ws )
{
// Get base points and offsets
int xsb = FastFloor( xs ), ysb = FastFloor( ys ), zsb = FastFloor( zs ), wsb = FastFloor( ws );
float xsi = (float)(xs - xsb), ysi = (float)(ys - ysb), zsi = (float)(zs - zsb), wsi = (float)(ws - wsb);
// Unskewed offsets
float ssi = (xsi + ysi + zsi + wsi) * UNSKEW_4D;
float xi = xsi + ssi, yi = ysi + ssi, zi = zsi + ssi, wi = wsi + ssi;
// Prime pre-multiplication for hash.
long xsvp = xsb * PRIME_X, ysvp = ysb * PRIME_Y, zsvp = zsb * PRIME_Z, wsvp = wsb * PRIME_W;
// Index into initial table.
int index = ((FastFloor( xs * 4 ) & 3) << 0)
| ((FastFloor( ys * 4 ) & 3) << 2)
| ((FastFloor( zs * 4 ) & 3) << 4)
| ((FastFloor( ws * 4 ) & 3) << 6);
// Point contributions
float value = 0;
(int secondaryIndexStart, int secondaryIndexStop) = LOOKUP_4D_A[index];
for ( int i = secondaryIndexStart; i < secondaryIndexStop; i++ )
{
LatticeVertex4D c = LOOKUP_4D_B[i];
float dx = xi + c.dx, dy = yi + c.dy, dz = zi + c.dz, dw = wi + c.dw;
float a = (dx * dx + dy * dy) + (dz * dz + dw * dw);
if ( a < RSQUARED_4D )
{
a -= RSQUARED_4D;
a *= a;
value += a * a * Grad( seed, xsvp + c.xsvp, ysvp + c.ysvp, zsvp + c.zsvp, wsvp + c.wsvp, dx, dy, dz, dw );
}
}
return value;
}
/*
* Utility
*/
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xsvp, long ysvp, float dx, float dy )
{
long hash = seed ^ xsvp ^ ysvp;
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_2D_EXPONENT + 1);
int gi = (int)hash & ((N_GRADS_2D - 1) << 1);
return GRADIENTS_2D[gi | 0] * dx + GRADIENTS_2D[gi | 1] * dy;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xrvp, long yrvp, long zrvp, float dx, float dy, float dz )
{
long hash = (seed ^ xrvp) ^ (yrvp ^ zrvp);
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_3D_EXPONENT + 2);
int gi = (int)hash & ((N_GRADS_3D - 1) << 2);
return GRADIENTS_3D[gi | 0] * dx + GRADIENTS_3D[gi | 1] * dy + GRADIENTS_3D[gi | 2] * dz;
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static float Grad( long seed, long xsvp, long ysvp, long zsvp, long wsvp, float dx, float dy, float dz, float dw )
{
long hash = seed ^ (xsvp ^ ysvp) ^ (zsvp ^ wsvp);
hash *= HASH_MULTIPLIER;
hash ^= hash >> (64 - N_GRADS_4D_EXPONENT + 2);
int gi = (int)hash & ((N_GRADS_4D - 1) << 2);
return (GRADIENTS_4D[gi | 0] * dx + GRADIENTS_4D[gi | 1] * dy) + (GRADIENTS_4D[gi | 2] * dz + GRADIENTS_4D[gi | 3] * dw);
}
[MethodImpl( MethodImplOptions.AggressiveInlining )]
private static int FastFloor( double x )
{
int xi = (int)x;
return x < xi ? xi - 1 : xi;
}
/*
* Lookup Tables & Gradients
*/
private static readonly float[] GRADIENTS_2D;
private static readonly float[] GRADIENTS_3D;
private static readonly float[] GRADIENTS_4D;
private static readonly (short SecondaryIndexStart, short SecondaryIndexStop)[] LOOKUP_4D_A;
private static readonly LatticeVertex4D[] LOOKUP_4D_B;
static OpenSimplex2S()
{
GRADIENTS_2D = new float[N_GRADS_2D * 2];
float[] grad2 = {
0.38268343236509f, 0.923879532511287f,
0.923879532511287f, 0.38268343236509f,
0.923879532511287f, -0.38268343236509f,
0.38268343236509f, -0.923879532511287f,
-0.38268343236509f, -0.923879532511287f,
-0.923879532511287f, -0.38268343236509f,
-0.923879532511287f, 0.38268343236509f,
-0.38268343236509f, 0.923879532511287f,
//-------------------------------------//
0.130526192220052f, 0.99144486137381f,
0.608761429008721f, 0.793353340291235f,
0.793353340291235f, 0.608761429008721f,
0.99144486137381f, 0.130526192220051f,
0.99144486137381f, -0.130526192220051f,
0.793353340291235f, -0.60876142900872f,
0.608761429008721f, -0.793353340291235f,
0.130526192220052f, -0.99144486137381f,
-0.130526192220052f, -0.99144486137381f,
-0.608761429008721f, -0.793353340291235f,
-0.793353340291235f, -0.608761429008721f,
-0.99144486137381f, -0.130526192220052f,
-0.99144486137381f, 0.130526192220051f,
-0.793353340291235f, 0.608761429008721f,
-0.608761429008721f, 0.793353340291235f,
-0.130526192220052f, 0.99144486137381f,
};
for ( int i = 0; i < grad2.Length; i++ )
{
grad2[i] = (float)(grad2[i] / NORMALIZER_2D);
}
for ( int i = 0, j = 0; i < GRADIENTS_2D.Length; i++, j++ )
{
if ( j == grad2.Length ) j = 0;
GRADIENTS_2D[i] = grad2[j];
}
GRADIENTS_3D = new float[N_GRADS_3D * 4];
float[] grad3 = {
2.22474487139f, 2.22474487139f, -1.0f, 0.0f,
2.22474487139f, 2.22474487139f, 1.0f, 0.0f,
3.0862664687972017f, 1.1721513422464978f, 0.0f, 0.0f,
1.1721513422464978f, 3.0862664687972017f, 0.0f, 0.0f,
-2.22474487139f, 2.22474487139f, -1.0f, 0.0f,
-2.22474487139f, 2.22474487139f, 1.0f, 0.0f,
-1.1721513422464978f, 3.0862664687972017f, 0.0f, 0.0f,
-3.0862664687972017f, 1.1721513422464978f, 0.0f, 0.0f,
-1.0f, -2.22474487139f, -2.22474487139f, 0.0f,
1.0f, -2.22474487139f, -2.22474487139f, 0.0f,
0.0f, -3.0862664687972017f, -1.1721513422464978f, 0.0f,
0.0f, -1.1721513422464978f, -3.0862664687972017f, 0.0f,
-1.0f, -2.22474487139f, 2.22474487139f, 0.0f,
1.0f, -2.22474487139f, 2.22474487139f, 0.0f,
0.0f, -1.1721513422464978f, 3.0862664687972017f, 0.0f,
0.0f, -3.0862664687972017f, 1.1721513422464978f, 0.0f,
//--------------------------------------------------------------------//
-2.22474487139f, -2.22474487139f, -1.0f, 0.0f,
-2.22474487139f, -2.22474487139f, 1.0f, 0.0f,
-3.0862664687972017f, -1.1721513422464978f, 0.0f, 0.0f,
-1.1721513422464978f, -3.0862664687972017f, 0.0f, 0.0f,
-2.22474487139f, -1.0f, -2.22474487139f, 0.0f,
-2.22474487139f, 1.0f, -2.22474487139f, 0.0f,
-1.1721513422464978f, 0.0f, -3.0862664687972017f, 0.0f,
-3.0862664687972017f, 0.0f, -1.1721513422464978f, 0.0f,
-2.22474487139f, -1.0f, 2.22474487139f, 0.0f,
-2.22474487139f, 1.0f, 2.22474487139f, 0.0f,
-3.0862664687972017f, 0.0f, 1.1721513422464978f, 0.0f,
-1.1721513422464978f, 0.0f, 3.0862664687972017f, 0.0f,
-1.0f, 2.22474487139f, -2.22474487139f, 0.0f,
1.0f, 2.22474487139f, -2.22474487139f, 0.0f,
0.0f, 1.1721513422464978f, -3.0862664687972017f, 0.0f,
0.0f, 3.0862664687972017f, -1.1721513422464978f, 0.0f,
-1.0f, 2.22474487139f, 2.22474487139f, 0.0f,
1.0f, 2.22474487139f, 2.22474487139f, 0.0f,
0.0f, 3.0862664687972017f, 1.1721513422464978f, 0.0f,
0.0f, 1.1721513422464978f, 3.0862664687972017f, 0.0f,
2.22474487139f, -2.22474487139f, -1.0f, 0.0f,
2.22474487139f, -2.22474487139f, 1.0f, 0.0f,
1.1721513422464978f, -3.0862664687972017f, 0.0f, 0.0f,
3.0862664687972017f, -1.1721513422464978f, 0.0f, 0.0f,
2.22474487139f, -1.0f, -2.22474487139f, 0.0f,
2.22474487139f, 1.0f, -2.22474487139f, 0.0f,
3.0862664687972017f, 0.0f, -1.1721513422464978f, 0.0f,
1.1721513422464978f, 0.0f, -3.0862664687972017f, 0.0f,
2.22474487139f, -1.0f, 2.22474487139f, 0.0f,
2.22474487139f, 1.0f, 2.22474487139f, 0.0f,
1.1721513422464978f, 0.0f, 3.0862664687972017f, 0.0f,
3.0862664687972017f, 0.0f, 1.1721513422464978f, 0.0f,
};
for ( int i = 0; i < grad3.Length; i++ )
{
grad3[i] = (float)(grad3[i] / NORMALIZER_3D);
}
for ( int i = 0, j = 0; i < GRADIENTS_3D.Length; i++, j++ )
{
if ( j == grad3.Length ) j = 0;
GRADIENTS_3D[i] = grad3[j];
}
GRADIENTS_4D = new float[N_GRADS_4D * 4];
float[] grad4 = {
-0.6740059517812944f, -0.3239847771997537f, -0.3239847771997537f, 0.5794684678643381f,
-0.7504883828755602f, -0.4004672082940195f, 0.15296486218853164f, 0.5029860367700724f,
-0.7504883828755602f, 0.15296486218853164f, -0.4004672082940195f, 0.5029860367700724f,
-0.8828161875373585f, 0.08164729285680945f, 0.08164729285680945f, 0.4553054119602712f,
-0.4553054119602712f, -0.08164729285680945f, -0.08164729285680945f, 0.8828161875373585f,
-0.5029860367700724f, -0.15296486218853164f, 0.4004672082940195f, 0.7504883828755602f,
-0.5029860367700724f, 0.4004672082940195f, -0.15296486218853164f, 0.7504883828755602f,
-0.5794684678643381f, 0.3239847771997537f, 0.3239847771997537f, 0.6740059517812944f,
-0.6740059517812944f, -0.3239847771997537f, 0.5794684678643381f, -0.3239847771997537f,
-0.7504883828755602f, -0.4004672082940195f, 0.5029860367700724f, 0.15296486218853164f,
-0.7504883828755602f, 0.15296486218853164f, 0.5029860367700724f, -0.4004672082940195f,
-0.8828161875373585f, 0.08164729285680945f, 0.4553054119602712f, 0.08164729285680945f,
-0.4553054119602712f, -0.08164729285680945f, 0.8828161875373585f, -0.08164729285680945f,
-0.5029860367700724f, -0.15296486218853164f, 0.7504883828755602f, 0.4004672082940195f,
-0.5029860367700724f, 0.4004672082940195f, 0.7504883828755602f, -0.15296486218853164f,
-0.5794684678643381f, 0.3239847771997537f, 0.6740059517812944f, 0.3239847771997537f,
-0.6740059517812944f, 0.5794684678643381f, -0.3239847771997537f, -0.3239847771997537f,
-0.7504883828755602f, 0.5029860367700724f, -0.4004672082940195f, 0.15296486218853164f,
-0.7504883828755602f, 0.5029860367700724f, 0.15296486218853164f, -0.4004672082940195f,
-0.8828161875373585f, 0.4553054119602712f, 0.08164729285680945f, 0.08164729285680945f,
-0.4553054119602712f, 0.8828161875373585f, -0.08164729285680945f, -0.08164729285680945f,
-0.5029860367700724f, 0.7504883828755602f, -0.15296486218853164f, 0.4004672082940195f,
-0.5029860367700724f, 0.7504883828755602f, 0.4004672082940195f, -0.15296486218853164f,
-0.5794684678643381f, 0.6740059517812944f, 0.3239847771997537f, 0.3239847771997537f,
0.5794684678643381f, -0.6740059517812944f, -0.3239847771997537f, -0.3239847771997537f,
0.5029860367700724f, -0.7504883828755602f, -0.4004672082940195f, 0.15296486218853164f,
0.5029860367700724f, -0.7504883828755602f, 0.15296486218853164f, -0.4004672082940195f,
0.4553054119602712f, -0.8828161875373585f, 0.08164729285680945f, 0.08164729285680945f,
0.8828161875373585f, -0.4553054119602712f, -0.08164729285680945f, -0.08164729285680945f,
0.7504883828755602f, -0.5029860367700724f, -0.15296486218853164f, 0.4004672082940195f,
0.7504883828755602f, -0.5029860367700724f, 0.4004672082940195f, -0.15296486218853164f,
0.6740059517812944f, -0.5794684678643381f, 0.3239847771997537f, 0.3239847771997537f,
//------------------------------------------------------------------------------------------//
-0.753341017856078f, -0.37968289875261624f, -0.37968289875261624f, -0.37968289875261624f,
-0.7821684431180708f, -0.4321472685365301f, -0.4321472685365301f, 0.12128480194602098f,
-0.7821684431180708f, -0.4321472685365301f, 0.12128480194602098f, -0.4321472685365301f,
-0.7821684431180708f, 0.12128480194602098f, -0.4321472685365301f, -0.4321472685365301f,
-0.8586508742123365f, -0.508629699630796f, 0.044802370851755174f, 0.044802370851755174f,
-0.8586508742123365f, 0.044802370851755174f, -0.508629699630796f, 0.044802370851755174f,
-0.8586508742123365f, 0.044802370851755174f, 0.044802370851755174f, -0.508629699630796f,
-0.9982828964265062f, -0.03381941603233842f, -0.03381941603233842f, -0.03381941603233842f,
-0.37968289875261624f, -0.753341017856078f, -0.37968289875261624f, -0.37968289875261624f,
-0.4321472685365301f, -0.7821684431180708f, -0.4321472685365301f, 0.12128480194602098f,
-0.4321472685365301f, -0.7821684431180708f, 0.12128480194602098f, -0.4321472685365301f,
0.12128480194602098f, -0.7821684431180708f, -0.4321472685365301f, -0.4321472685365301f,
-0.508629699630796f, -0.8586508742123365f, 0.044802370851755174f, 0.044802370851755174f,
0.044802370851755174f, -0.8586508742123365f, -0.508629699630796f, 0.044802370851755174f,
0.044802370851755174f, -0.8586508742123365f, 0.044802370851755174f, -0.508629699630796f,
-0.03381941603233842f, -0.9982828964265062f, -0.03381941603233842f, -0.03381941603233842f,
-0.37968289875261624f, -0.37968289875261624f, -0.753341017856078f, -0.37968289875261624f,
-0.4321472685365301f, -0.4321472685365301f, -0.7821684431180708f, 0.12128480194602098f,
-0.4321472685365301f, 0.12128480194602098f, -0.7821684431180708f, -0.4321472685365301f,
0.12128480194602098f, -0.4321472685365301f, -0.7821684431180708f, -0.4321472685365301f,
-0.508629699630796f, 0.044802370851755174f, -0.8586508742123365f, 0.044802370851755174f,
0.044802370851755174f, -0.508629699630796f, -0.8586508742123365f, 0.044802370851755174f,
0.044802370851755174f, 0.044802370851755174f, -0.8586508742123365f, -0.508629699630796f,
-0.03381941603233842f, -0.03381941603233842f, -0.9982828964265062f, -0.03381941603233842f,
-0.37968289875261624f, -0.37968289875261624f, -0.37968289875261624f, -0.753341017856078f,
-0.4321472685365301f, -0.4321472685365301f, 0.12128480194602098f, -0.7821684431180708f,
-0.4321472685365301f, 0.12128480194602098f, -0.4321472685365301f, -0.7821684431180708f,
0.12128480194602098f, -0.4321472685365301f, -0.4321472685365301f, -0.7821684431180708f,
-0.508629699630796f, 0.044802370851755174f, 0.044802370851755174f, -0.8586508742123365f,
0.044802370851755174f, -0.508629699630796f, 0.044802370851755174f, -0.8586508742123365f,
0.044802370851755174f, 0.044802370851755174f, -0.508629699630796f, -0.8586508742123365f,
-0.03381941603233842f, -0.03381941603233842f, -0.03381941603233842f, -0.9982828964265062f,
-0.3239847771997537f, -0.6740059517812944f, -0.3239847771997537f, 0.5794684678643381f,
-0.4004672082940195f, -0.7504883828755602f, 0.15296486218853164f, 0.5029860367700724f,
0.15296486218853164f, -0.7504883828755602f, -0.4004672082940195f, 0.5029860367700724f,
0.08164729285680945f, -0.8828161875373585f, 0.08164729285680945f, 0.4553054119602712f,
-0.08164729285680945f, -0.4553054119602712f, -0.08164729285680945f, 0.8828161875373585f,
-0.15296486218853164f, -0.5029860367700724f, 0.4004672082940195f, 0.7504883828755602f,
0.4004672082940195f, -0.5029860367700724f, -0.15296486218853164f, 0.7504883828755602f,
0.3239847771997537f, -0.5794684678643381f, 0.3239847771997537f, 0.6740059517812944f,
-0.3239847771997537f, -0.3239847771997537f, -0.6740059517812944f, 0.5794684678643381f,
-0.4004672082940195f, 0.15296486218853164f, -0.7504883828755602f, 0.5029860367700724f,
0.15296486218853164f, -0.4004672082940195f, -0.7504883828755602f, 0.5029860367700724f,
0.08164729285680945f, 0.08164729285680945f, -0.8828161875373585f, 0.4553054119602712f,
-0.08164729285680945f, -0.08164729285680945f, -0.4553054119602712f, 0.8828161875373585f,
-0.15296486218853164f, 0.4004672082940195f, -0.5029860367700724f, 0.7504883828755602f,
0.4004672082940195f, -0.15296486218853164f, -0.5029860367700724f, 0.7504883828755602f,
0.3239847771997537f, 0.3239847771997537f, -0.5794684678643381f, 0.6740059517812944f,
-0.3239847771997537f, -0.6740059517812944f, 0.5794684678643381f, -0.3239847771997537f,
-0.4004672082940195f, -0.7504883828755602f, 0.5029860367700724f, 0.15296486218853164f,
0.15296486218853164f, -0.7504883828755602f, 0.5029860367700724f, -0.4004672082940195f,
0.08164729285680945f, -0.8828161875373585f, 0.4553054119602712f, 0.08164729285680945f,
-0.08164729285680945f, -0.4553054119602712f, 0.8828161875373585f, -0.08164729285680945f,
-0.15296486218853164f, -0.5029860367700724f, 0.7504883828755602f, 0.4004672082940195f,
0.4004672082940195f, -0.5029860367700724f, 0.7504883828755602f, -0.15296486218853164f,
0.3239847771997537f, -0.5794684678643381f, 0.6740059517812944f, 0.3239847771997537f,
-0.3239847771997537f, -0.3239847771997537f, 0.5794684678643381f, -0.6740059517812944f,
-0.4004672082940195f, 0.15296486218853164f, 0.5029860367700724f, -0.7504883828755602f,
0.15296486218853164f, -0.4004672082940195f, 0.5029860367700724f, -0.7504883828755602f,
0.08164729285680945f, 0.08164729285680945f, 0.4553054119602712f, -0.8828161875373585f,
-0.08164729285680945f, -0.08164729285680945f, 0.8828161875373585f, -0.4553054119602712f,
-0.15296486218853164f, 0.4004672082940195f, 0.7504883828755602f, -0.5029860367700724f,
0.4004672082940195f, -0.15296486218853164f, 0.7504883828755602f, -0.5029860367700724f,
0.3239847771997537f, 0.3239847771997537f, 0.6740059517812944f, -0.5794684678643381f,
-0.3239847771997537f, 0.5794684678643381f, -0.6740059517812944f, -0.3239847771997537f,
-0.4004672082940195f, 0.5029860367700724f, -0.7504883828755602f, 0.15296486218853164f,
0.15296486218853164f, 0.5029860367700724f, -0.7504883828755602f, -0.4004672082940195f,
0.08164729285680945f, 0.4553054119602712f, -0.8828161875373585f, 0.08164729285680945f,
-0.08164729285680945f, 0.8828161875373585f, -0.4553054119602712f, -0.08164729285680945f,
-0.15296486218853164f, 0.7504883828755602f, -0.5029860367700724f, 0.4004672082940195f,
0.4004672082940195f, 0.7504883828755602f, -0.5029860367700724f, -0.15296486218853164f,
0.3239847771997537f, 0.6740059517812944f, -0.5794684678643381f, 0.3239847771997537f,
-0.3239847771997537f, 0.5794684678643381f, -0.3239847771997537f, -0.6740059517812944f,
-0.4004672082940195f, 0.5029860367700724f, 0.15296486218853164f, -0.7504883828755602f,
0.15296486218853164f, 0.5029860367700724f, -0.4004672082940195f, -0.7504883828755602f,
0.08164729285680945f, 0.4553054119602712f, 0.08164729285680945f, -0.8828161875373585f,
-0.08164729285680945f, 0.8828161875373585f, -0.08164729285680945f, -0.4553054119602712f,
-0.15296486218853164f, 0.7504883828755602f, 0.4004672082940195f, -0.5029860367700724f,
0.4004672082940195f, 0.7504883828755602f, -0.15296486218853164f, -0.5029860367700724f,
0.3239847771997537f, 0.6740059517812944f, 0.3239847771997537f, -0.5794684678643381f,
0.5794684678643381f, -0.3239847771997537f, -0.6740059517812944f, -0.3239847771997537f,
0.5029860367700724f, -0.4004672082940195f, -0.7504883828755602f, 0.15296486218853164f,
0.5029860367700724f, 0.15296486218853164f, -0.7504883828755602f, -0.4004672082940195f,
0.4553054119602712f, 0.08164729285680945f, -0.8828161875373585f, 0.08164729285680945f,
0.8828161875373585f, -0.08164729285680945f, -0.4553054119602712f, -0.08164729285680945f,
0.7504883828755602f, -0.15296486218853164f, -0.5029860367700724f, 0.4004672082940195f,
0.7504883828755602f, 0.4004672082940195f, -0.5029860367700724f, -0.15296486218853164f,
0.6740059517812944f, 0.3239847771997537f, -0.5794684678643381f, 0.3239847771997537f,
0.5794684678643381f, -0.3239847771997537f, -0.3239847771997537f, -0.6740059517812944f,
0.5029860367700724f, -0.4004672082940195f, 0.15296486218853164f, -0.7504883828755602f,
0.5029860367700724f, 0.15296486218853164f, -0.4004672082940195f, -0.7504883828755602f,
0.4553054119602712f, 0.08164729285680945f, 0.08164729285680945f, -0.8828161875373585f,
0.8828161875373585f, -0.08164729285680945f, -0.08164729285680945f, -0.4553054119602712f,
0.7504883828755602f, -0.15296486218853164f, 0.4004672082940195f, -0.5029860367700724f,
0.7504883828755602f, 0.4004672082940195f, -0.15296486218853164f, -0.5029860367700724f,
0.6740059517812944f, 0.3239847771997537f, 0.3239847771997537f, -0.5794684678643381f,
0.03381941603233842f, 0.03381941603233842f, 0.03381941603233842f, 0.9982828964265062f,
-0.044802370851755174f, -0.044802370851755174f, 0.508629699630796f, 0.8586508742123365f,
-0.044802370851755174f, 0.508629699630796f, -0.044802370851755174f, 0.8586508742123365f,
-0.12128480194602098f, 0.4321472685365301f, 0.4321472685365301f, 0.7821684431180708f,
0.508629699630796f, -0.044802370851755174f, -0.044802370851755174f, 0.8586508742123365f,
0.4321472685365301f, -0.12128480194602098f, 0.4321472685365301f, 0.7821684431180708f,
0.4321472685365301f, 0.4321472685365301f, -0.12128480194602098f, 0.7821684431180708f,
0.37968289875261624f, 0.37968289875261624f, 0.37968289875261624f, 0.753341017856078f,
0.03381941603233842f, 0.03381941603233842f, 0.9982828964265062f, 0.03381941603233842f,
-0.044802370851755174f, 0.044802370851755174f, 0.8586508742123365f, 0.508629699630796f,
-0.044802370851755174f, 0.508629699630796f, 0.8586508742123365f, -0.044802370851755174f,
-0.12128480194602098f, 0.4321472685365301f, 0.7821684431180708f, 0.4321472685365301f,
0.508629699630796f, -0.044802370851755174f, 0.8586508742123365f, -0.044802370851755174f,
0.4321472685365301f, -0.12128480194602098f, 0.7821684431180708f, 0.4321472685365301f,
0.4321472685365301f, 0.4321472685365301f, 0.7821684431180708f, -0.12128480194602098f,
0.37968289875261624f, 0.37968289875261624f, 0.753341017856078f, 0.37968289875261624f,
0.03381941603233842f, 0.9982828964265062f, 0.03381941603233842f, 0.03381941603233842f,
-0.044802370851755174f, 0.8586508742123365f, -0.044802370851755174f, 0.508629699630796f,
-0.044802370851755174f, 0.8586508742123365f, 0.508629699630796f, -0.044802370851755174f,
-0.12128480194602098f, 0.7821684431180708f, 0.4321472685365301f, 0.4321472685365301f,
0.508629699630796f, 0.8586508742123365f, -0.044802370851755174f, -0.044802370851755174f,
0.4321472685365301f, 0.7821684431180708f, -0.12128480194602098f, 0.4321472685365301f,
0.4321472685365301f, 0.7821684431180708f, 0.4321472685365301f, -0.12128480194602098f,
0.37968289875261624f, 0.753341017856078f, 0.37968289875261624f, 0.37968289875261624f,
0.9982828964265062f, 0.03381941603233842f, 0.03381941603233842f, 0.03381941603233842f,
0.8586508742123365f, -0.044802370851755174f, -0.044802370851755174f, 0.508629699630796f,
0.8586508742123365f, -0.044802370851755174f, 0.508629699630796f, -0.044802370851755174f,
0.7821684431180708f, -0.12128480194602098f, 0.4321472685365301f, 0.4321472685365301f,
0.8586508742123365f, 0.508629699630796f, -0.044802370851755174f, -0.044802370851755174f,
0.7821684431180708f, 0.4321472685365301f, -0.12128480194602098f, 0.4321472685365301f,
0.7821684431180708f, 0.4321472685365301f, 0.4321472685365301f, -0.12128480194602098f,
0.753341017856078f, 0.37968289875261624f, 0.37968289875261624f, 0.37968289875261624f,
};
for ( int i = 0; i < grad4.Length; i++ )
{
grad4[i] = (float)(grad4[i] / NORMALIZER_4D);
}
for ( int i = 0, j = 0; i < GRADIENTS_4D.Length; i++, j++ )
{
if ( j == grad4.Length ) j = 0;
GRADIENTS_4D[i] = grad4[j];
}
int[][] lookup4DVertexCodes = {
new int[] { 0x15, 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x01, 0x05, 0x11, 0x15, 0x41, 0x45, 0x51, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x01, 0x15, 0x16, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x15, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x16, 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xAA, 0xAB },
new int[] { 0x04, 0x05, 0x14, 0x15, 0x44, 0x45, 0x54, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0xAA },
new int[] { 0x05, 0x15, 0x16, 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x5B, 0x6A, 0x9A, 0xAA, 0xAB },
new int[] { 0x04, 0x15, 0x19, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x19, 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x19, 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x5E, 0x6A, 0x9A, 0xAA, 0xAE },
new int[] { 0x05, 0x15, 0x1A, 0x45, 0x4A, 0x55, 0x56, 0x59, 0x5A, 0x5B, 0x5E, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x15, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x16, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x6B, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x6B, 0x9A, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x19, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x6E, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x6E, 0x9A, 0xAA, 0xAE },
new int[] { 0x15, 0x1A, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x6B, 0x6E, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x10, 0x11, 0x14, 0x15, 0x50, 0x51, 0x54, 0x55, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0xA6, 0xAA },
new int[] { 0x11, 0x15, 0x16, 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x67, 0x6A, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA6, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x6B, 0xA6, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xAA },
new int[] { 0x15, 0x16, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0xAA, 0xAB },
new int[] { 0x14, 0x15, 0x19, 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x6A, 0x6D, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x6E, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x19, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6E, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x6B, 0x6E, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x10, 0x15, 0x25, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x25, 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x25, 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x6A, 0x76, 0xA6, 0xAA, 0xBA },
new int[] { 0x11, 0x15, 0x26, 0x51, 0x55, 0x56, 0x62, 0x65, 0x66, 0x67, 0x6A, 0x76, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x25, 0x54, 0x55, 0x59, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA6, 0xAA, 0xBA },
new int[] { 0x15, 0x26, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x6B, 0x7A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x25, 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x6A, 0x79, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x25, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x7A, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0x7A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x14, 0x15, 0x29, 0x54, 0x55, 0x59, 0x65, 0x68, 0x69, 0x6A, 0x6D, 0x79, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x29, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x6E, 0x7A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6E, 0x7A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x6B, 0x6E, 0x7A, 0xAA, 0xAB, 0xAE, 0xBA, 0xBF },
new int[] { 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x5A, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x5A, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x9A, 0x9B, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x99, 0x9A, 0x9E, 0xAA, 0xAE },
new int[] { 0x45, 0x4A, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x59, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x56, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x45, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x66, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA },
new int[] { 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x15, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x66, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x61, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x6A, 0xA5, 0xA6, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x62, 0x65, 0x66, 0x6A, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x64, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x6A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x6A, 0xA5, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x15, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x68, 0x69, 0x6A, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xBE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x6A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x15, 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0xAA, 0xAB, 0xAE, 0xBA, 0xBF },
new int[] { 0x40, 0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x95, 0x96, 0x9A, 0xA6, 0xAA },
new int[] { 0x41, 0x45, 0x46, 0x51, 0x52, 0x55, 0x56, 0x95, 0x96, 0x97, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x95, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xAA },
new int[] { 0x45, 0x46, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0xAA, 0xAB },
new int[] { 0x44, 0x45, 0x49, 0x54, 0x55, 0x58, 0x59, 0x95, 0x99, 0x9A, 0x9D, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x59, 0x5A, 0x95, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x49, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9E, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x99, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x51, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xEA },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA },
new int[] { 0x45, 0x54, 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x96, 0x99, 0x9A, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA },
new int[] { 0x51, 0x52, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x54, 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x51, 0x55, 0x56, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB },
new int[] { 0x54, 0x55, 0x58, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE },
new int[] { 0x54, 0x55, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x66, 0x69, 0x6A, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xAF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x61, 0x64, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB5, 0xBA },
new int[] { 0x51, 0x55, 0x61, 0x65, 0x66, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x61, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xB6, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x64, 0x65, 0x69, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x6A, 0x96, 0xA5, 0xA6, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x64, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xB9, 0xBA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x59, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA },
new int[] { 0x55, 0x56, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA, 0xBB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xBE },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x6A, 0x99, 0xA5, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA, 0xBE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xBA },
new int[] { 0x40, 0x45, 0x51, 0x54, 0x55, 0x85, 0x91, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x85, 0x91, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xAA, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x85, 0x91, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xD6, 0xEA },
new int[] { 0x41, 0x45, 0x51, 0x55, 0x56, 0x86, 0x92, 0x95, 0x96, 0x97, 0x9A, 0xA6, 0xAA, 0xAB, 0xD6, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x85, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x86, 0x95, 0x96, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB, 0xDA, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x85, 0x94, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xD9, 0xEA },
new int[] { 0x45, 0x55, 0x59, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x85, 0x95, 0x96, 0x99, 0x9A, 0xAA, 0xDA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0xA6, 0xAA, 0xAB, 0xDA, 0xEA, 0xEB },
new int[] { 0x44, 0x45, 0x54, 0x55, 0x59, 0x89, 0x95, 0x98, 0x99, 0x9A, 0x9D, 0xA9, 0xAA, 0xAE, 0xD9, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x89, 0x95, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE, 0xDA, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9E, 0xA9, 0xAA, 0xAE, 0xDA, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0x9B, 0x9E, 0xAA, 0xAB, 0xAE, 0xDA, 0xEA, 0xEF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x91, 0x94, 0x95, 0x96, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x91, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x91, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x92, 0x95, 0x96, 0x9A, 0xA6, 0xA7, 0xAA, 0xAB, 0xE6, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x94, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x95, 0x96, 0x9A, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x94, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x59, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x45, 0x55, 0x56, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x95, 0x98, 0x99, 0x9A, 0xA9, 0xAA, 0xAD, 0xAE, 0xE9, 0xEA, 0xEE },
new int[] { 0x55, 0x59, 0x95, 0x99, 0x9A, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x45, 0x55, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x95, 0x96, 0x99, 0x9A, 0xAA, 0xAB, 0xAE, 0xEA, 0xEF },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x91, 0x94, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xE5, 0xEA },
new int[] { 0x51, 0x55, 0x65, 0x91, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x91, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xE6, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xE6, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x65, 0x94, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x55, 0x65, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x51, 0x55, 0x56, 0x66, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x94, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xE9, 0xEA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x59, 0x65, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xEA },
new int[] { 0x55, 0x56, 0x5A, 0x66, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xEA, 0xEB },
new int[] { 0x54, 0x55, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xE9, 0xEA, 0xEE },
new int[] { 0x54, 0x55, 0x59, 0x69, 0x95, 0x99, 0x9A, 0xA5, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x59, 0x5A, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xEA, 0xEE },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xEA },
new int[] { 0x50, 0x51, 0x54, 0x55, 0x65, 0x95, 0xA1, 0xA4, 0xA5, 0xA6, 0xA9, 0xAA, 0xB5, 0xBA, 0xE5, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x95, 0xA1, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA, 0xE6, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xB6, 0xBA, 0xE6, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA7, 0xAA, 0xAB, 0xB6, 0xBA, 0xE6, 0xEA, 0xFB },
new int[] { 0x54, 0x55, 0x65, 0x95, 0xA4, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA, 0xE9, 0xEA, 0xFA },
new int[] { 0x55, 0x65, 0x95, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x51, 0x55, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x95, 0x96, 0xA5, 0xA6, 0xAA, 0xAB, 0xBA, 0xEA, 0xFB },
new int[] { 0x54, 0x55, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xB9, 0xBA, 0xE9, 0xEA, 0xFA },
new int[] { 0x54, 0x55, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xBA, 0xEA, 0xFA },
new int[] { 0x55, 0x56, 0x65, 0x66, 0x6A, 0x95, 0x96, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xBA, 0xEA },
new int[] { 0x54, 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAD, 0xAE, 0xB9, 0xBA, 0xE9, 0xEA, 0xFE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x95, 0x99, 0xA5, 0xA9, 0xAA, 0xAE, 0xBA, 0xEA, 0xFE },
new int[] { 0x55, 0x59, 0x65, 0x69, 0x6A, 0x95, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAE, 0xBA, 0xEA },
new int[] { 0x55, 0x56, 0x59, 0x5A, 0x65, 0x66, 0x69, 0x6A, 0x95, 0x96, 0x99, 0x9A, 0xA5, 0xA6, 0xA9, 0xAA, 0xAB, 0xAE, 0xBA, 0xEA },
};
LatticeVertex4D[] latticeVerticesByCode = new LatticeVertex4D[256];
for ( int i = 0; i < 256; i++ )
{
int cx = ((i >> 0) & 3) - 1;
int cy = ((i >> 2) & 3) - 1;
int cz = ((i >> 4) & 3) - 1;
int cw = ((i >> 6) & 3) - 1;
latticeVerticesByCode[i] = new LatticeVertex4D( cx, cy, cz, cw );
}
int nLatticeVerticesTotal = 0;
for ( int i = 0; i < 256; i++ )
{
nLatticeVerticesTotal += lookup4DVertexCodes[i].Length;
}
LOOKUP_4D_A = new (short SecondaryIndexStart, short SecondaryIndexStop)[256];
LOOKUP_4D_B = new LatticeVertex4D[nLatticeVerticesTotal];
for ( int i = 0, j = 0; i < 256; i++ )
{
LOOKUP_4D_A[i] = ((short)j, (short)(j + lookup4DVertexCodes[i].Length));
for ( int k = 0; k < lookup4DVertexCodes[i].Length; k++ )
{
LOOKUP_4D_B[j++] = latticeVerticesByCode[lookup4DVertexCodes[i][k]];
}
}
}
private class LatticeVertex4D
{
public readonly float dx, dy, dz, dw;
public readonly long xsvp, ysvp, zsvp, wsvp;
public LatticeVertex4D( int xsv, int ysv, int zsv, int wsv )
{
this.xsvp = xsv * PRIME_X; this.ysvp = ysv * PRIME_Y;
this.zsvp = zsv * PRIME_Z; this.wsvp = wsv * PRIME_W;
float ssv = (xsv + ysv + zsv + wsv) * UNSKEW_4D;
this.dx = -xsv - ssv;
this.dy = -ysv - ssv;
this.dz = -zsv - ssv;
this.dw = -wsv - ssv;
}
}
}
using Editor;
using Sandbox;
using System;
namespace Sturnus.TerrainGenerationTool;
public static class Islands
{
public static float Default( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize = 0.1f, float warpStrength = 0.5f )
{
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
float warpX;
float warpY;
float warpedNx;
float warpedNy;
float noise;
if ( warp )
{
// Generate warp offsets using additional noise
warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
// Apply domain warping
warpedNx = nx + warpX;
warpedNy = ny + warpY;
}
else
{
warpedNx = nx;
warpedNy = ny;
}
// Radial distance from the center
float distance = (float)Math.Sqrt( nx * nx + ny * ny );
float falloff = 1.0f - Math.Clamp( distance, 0.25f, 1 ); // Smooth taper from center to edge
// Central mountain shape (parabolic for smooth curvature)
float centralMountain = (1.0f - distance * distance) * falloff;
// Beach-style taper near the edges
float beachStart = 0.5f; // Start of the beach region (distance normalized)
float beachEnd = 0.98f; // End of the beach region (ocean level)
float beachFalloff = Math.Clamp( (distance - beachStart) / (beachEnd - beachStart), 0.1f, 1 );
float beachTaper = (1.0f - beachFalloff) * 0.25f; // Smooth transition to flat region
// Add subtle noise for terrain variation
if ( warp )
{
noise = OpenSimplex2S.Noise2( seed, warpedNx * 2, warpedNy * 2 ) * 0.4f;
}
else
{
noise = OpenSimplex2S.Noise2( seed, nx * 6, ny * 6 ) * 0.05f; // Low-frequency noise
}
// Combine components: central mountain, beach taper, and noise
float output = centralMountain * (1.0f - beachFalloff) + beachTaper + noise;
// Combine all effects
float heightValue = output;
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Archipelagos( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Use noise layers to create clusters of small islands
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.5f, ny * 1.5f );
float secondaryNoise = OpenSimplex2S.Noise2( seed + 1, nx * 3.0f, ny * 3.0f ) * 0.5f;
float archipelagoHeight = baseNoise + secondaryNoise;
// Apply radial falloff to form rounded island clusters
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.2f, 0, 1 );
float heightValue = Math.Clamp( archipelagoHeight * falloff, 0, 1 );
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Atoll( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 20, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 21, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Calculate distance from the center
float distance = MathF.Sqrt( nx * nx + ny * ny );
// Define parameters for the single ring
float ringCenter = 0.6f; // Center of the ring
float ringWidth = 0.1f; // Width of the ring
// Create a single ring using a Gaussian-like function
float ring = MathF.Exp( -MathF.Pow( (distance - ringCenter) / ringWidth, 2 ) );
// Add some noise for variation
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.0f, ny * 1.0f ) * 0.4f;
// Introduce a beach-like area (reduce noise and height for one side of the map)
float beachEffect = Math.Clamp( (1 - nx) * 0.5f, 0.2f, 1.0f ); // Reduces height on one side of the map
float beachNoise = OpenSimplex2S.Noise2( seed + 30, nx * 2.0f, ny * 2.0f ) * 0.2f;
// Combine the ring, noise, and beach effect
float heightValue = (ring + baseNoise * beachEffect + beachNoise) * beachEffect;
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Islets( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 30, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 31, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate scattered small islands
float scatterNoise = OpenSimplex2S.Noise2( seed, nx * 6.0f, ny * 6.0f );
float baseNoise = OpenSimplex2S.Noise2( seed + 1, nx * 3.0f, ny * 3.0f ) * 0.5f;
float isletHeight = scatterNoise + baseNoise;
// Apply distance falloff to create isolated islets
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.5f, 0, 1 );
float heightValue = Math.Clamp( isletHeight * falloff, 0, 1 );
// Add a baseline value to ensure no flat zero areas
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Oceanic( int x, int y, int width, int height, long seed, float minHeight, bool warp, float warpSize, float warpStrength )
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1;
float ny = (y / (float)height) * 2 - 1;
// Apply domain warping
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 40, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 41, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate large, continuous landmass with a few scattered features
float baseNoise = OpenSimplex2S.Noise2( seed, nx * 1.0f, ny * 1.0f );
float featureNoise = OpenSimplex2S.Noise2( seed + 1, nx * 2.0f, ny * 2.0f ) * 0.5f;
float oceanicHeight = baseNoise + featureNoise;
// Apply radial falloff for a natural ocean/land mix
float distance = MathF.Sqrt( nx * nx + ny * ny );
float falloff = Math.Clamp( 1 - distance * 1.0f, 0, 1 );
float heightValue = Math.Clamp( oceanicHeight * falloff, 0, 1 );
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
}
using Editor;
using Sandbox;
using System;
using System.Collections.Generic;
namespace Sturnus.TerrainGenerationTool;
public static class Planetary
{
public static float Sharded(
int x,
int y,
int width,
int height,
long seed,
float minHeight,
bool warp, // Apply domain warping
float warpSize, // Warp scale
float warpStrength // Warp strength
)
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
float shardHeight = 10f;
float crackDepth = 1f;
float noiseStrength = 0.05f;
int cellCount = 5;
// Apply domain warping for irregularity
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Generate cracks
float minDist = float.MaxValue;
float secondaryDist = float.MaxValue;
float cellSize = 2.0f / cellCount; // Normalize the cell size
for ( int i = 0; i < cellCount; i++ )
{
for ( int j = 0; j < cellCount; j++ )
{
float cellX = -1 + i * cellSize + OpenSimplex2S.Noise2( seed + 20, i, j ) * cellSize * 0.5f;
float cellY = -1 + j * cellSize + OpenSimplex2S.Noise2( seed + 21, i, j ) * cellSize * 0.5f;
float distance = MathF.Sqrt( (nx - cellX) * (nx - cellX) + (ny - cellY) * (ny - cellY) );
if ( distance < minDist )
{
secondaryDist = minDist;
minDist = distance;
}
else if ( distance < secondaryDist )
{
secondaryDist = distance;
}
}
}
// Compute shard height based on the secondary distance
float shardNoise = OpenSimplex2S.Noise2( seed + 30, nx, ny ) * noiseStrength;
float heightValue = MathF.Max( secondaryDist - minDist, 0f ) * shardHeight + shardNoise;
// Apply crack depth at borders between shards
if ( secondaryDist - minDist < 0.03f ) // Control the width of cracks
{
heightValue -= crackDepth;
}
// Add a baseline value to ensure no flat zero areas
//heightValue = MathF.Max( heightValue, minHeight );
// Clamp height to avoid negative values
heightValue = Math.Clamp( heightValue, 0, 1 );
float baseline = minHeight; // Minimum height
float heightValueCombined = MathF.Max( heightValue, baseline );
// Clamp the final height to valid range
return heightValueCombined;
}
public static float Craters(
int x,
int y,
int width,
int height,
long seed,
float minHeight,
bool warp, // Apply domain warping for irregularity
float warpSize, // Warp scale
float warpStrength // Warp strength
)
{
Random random = new Random( (int)(seed & 0xFFFFFFFF) );
float nx = (x / (float)width) * 2 - 1; // Normalize x to range [-1, 1]
float ny = (y / (float)height) * 2 - 1; // Normalize y to range [-1, 1]
int craterCount = 100;
float minCraterSize = 0.1f;
float maxCraterSize = 0.3f;
float craterDepth = 0.05f;
float rimHeight = 0.05f;
float rimWidthRatio = 0.2f;
float noiseStrength = 0.05f;
float largeCraterRatio = 0.2f;
float slopeFalloff = 0.9f; // Controls smoothness of ramps
// Apply domain warping for irregularity
if ( warp )
{
float warpX = OpenSimplex2S.Noise2( seed + 10, nx * warpSize, ny * warpSize ) * warpStrength;
float warpY = OpenSimplex2S.Noise2( seed + 11, nx * warpSize, ny * warpSize ) * warpStrength;
nx += warpX;
ny += warpY;
}
// Base terrain noise
float baseTerrain = OpenSimplex2S.Noise2( seed + 1, nx * 2.0f, ny * 2.0f ) * noiseStrength;
baseTerrain = (baseTerrain + 1) * 0.5f; // Normalize to [0, 1]
float heightValue = baseTerrain;
// Iterate through craters in reverse order to overwrite previous craters
for ( int i = craterCount - 1; i >= 0; i-- )
{
// Randomize crater properties
float craterX = random.Next( -100000, 100000 ) / 50000.0f;
float craterY = random.Next( -100000, 100000 ) / 50000.0f;
float craterRadius = (i < craterCount * largeCraterRatio)
? random.Next( (int)(maxCraterSize * 500), (int)(maxCraterSize * 1000) ) / 1000.0f // Large craters
: random.Next( (int)(minCraterSize * 500), (int)(minCraterSize * 1000) ) / 1000.0f; // Small craters
// Distance from the current point to the crater center
float distance = MathF.Sqrt( (nx - craterX) * (nx - craterX) + (ny - craterY) * (ny - craterY) );
if ( distance < craterRadius )
{
float rimStart = craterRadius * (1f - rimWidthRatio);
float rimEnd = craterRadius;
// Inside the pit
if ( distance < rimStart )
{
float pitFalloff = Math.Clamp( 1f - (distance / rimStart), 0f, 1f );
heightValue = baseTerrain - MathF.Pow( pitFalloff, slopeFalloff ) * craterDepth; // Smooth ramp to the center
}
// Raised rim
else if ( distance >= rimStart && distance < rimEnd )
{
float rimFalloff = Math.Clamp( (distance - rimStart) / (rimEnd - rimStart), 0f, 1f );
heightValue = baseTerrain + MathF.Pow( 1f - rimFalloff, slopeFalloff ) * rimHeight; // Rounded rim
}
// Reset terrain below the rim to prevent intersecting ridges
if ( distance >= rimEnd )
{
heightValue = baseTerrain;
}
// Exit the loop once the current crater is applied
break;
}
}
// Ensure height values are clamped to [0, 1]
heightValue = Math.Clamp( heightValue, 0, 1 );
return heightValue;
}
}
using Editor;
using Sandbox;
using Sandbox.UI;
namespace Panelize;
public class Properties : Widget
{
public static object SelectedObject { get; set; }
private static object currentSelectedObject { get; set; }
Layout editor;
public Properties(MainWindow mainWindow) : base(mainWindow)
{
WindowTitle = "Properties";
Layout = Layout.Column();
editor = Layout.AddRow( 1 );
SelectedObject = null;
currentSelectedObject = null;
}
[EditorEvent.Frame]
public void Frame()
{
//Log.Info( $"Select {SelectedObject}" );
if ( SelectedObject != currentSelectedObject )
{
Select( SelectedObject );
}
}
private void Select(object obj )
{
currentSelectedObject = obj;
editor.Clear(true);
var so = obj.GetSerialized();
Widget inspector = null;
if(obj is Panel p )
{
inspector = new PanelInspector( this, p, PanelEditorSession.Current );
}
else
{
inspector = InspectorWidget.Create( so );
}
if ( inspector.IsValid() )
{
editor.Add( inspector, 1 );
}
else
{
// Try CanEdit still..
// todo: Everything that should be an inspector should be an InspectorWidget
inspector = CanEditAttribute.CreateEditorForObject( obj );
if ( inspector.IsValid() )
{
editor.Add( inspector, 1 );
}
else
{
try
{
var sheet = new ControlSheet();
sheet.AddObject( so );
var scroller = new ScrollArea( this );
scroller.Canvas = new Widget();
scroller.Canvas.Layout = Layout.Column();
scroller.Canvas.VerticalSizeMode = SizeMode.CanGrow;
scroller.Canvas.HorizontalSizeMode = SizeMode.Flexible;
scroller.Canvas.Layout.Add( sheet );
scroller.Canvas.Layout.AddStretchCell();
editor.Add( scroller );
}
catch { }
}
}
}
}
using Sandbox;
using Sandbox.Diagnostics;
using Sandbox.UI;
using System.Linq;
namespace Panelize;
public class PanelBrowser : Widget
{
public float EntrySize { get; set; } = 200f;
public int Columns { get; set; } = 2;
GridLayout grid;
public PanelBrowser()
{
WindowTitle = "Panel List";
Layout = Layout.Column();
grid = Layout.AddLayout(Layout.Grid(), 10);
grid.Spacing = 5f;
BuildList();
}
private void BuildList()
{
grid.Clear( true );
var builderTypes = EditorTypeLibrary.GetTypes<PanelBuilder>();
int x = 0;
int y = 0;
foreach (var type in builderTypes)
{
if ( type.IsAbstract || type.IsGenericType ) continue;
if (x > Columns)
{
x = 0;
y++;
}
PanelPreviewWidget preview = new PanelPreviewWidget( type.Create<PanelBuilder>() )
{
FixedWidth = EntrySize,
FixedHeight = EntrySize
};
grid.AddCell(x, y, preview);
x++;
}
}
}
public class PanelPreviewWidget : Widget
{
PanelBuilder builder;
Color bgColor = Color.Gray;
//Drag drag;
public PanelPreviewWidget( PanelBuilder builder )
{
Assert.NotNull( builder );
this.builder = builder;
IsDraggable = true;
}
protected override void OnPaint()
{
Rect size = LocalRect;
Paint.SetBrushAndPen( bgColor );
Paint.DrawRect( size.Shrink(5f), 1f );
Paint.SetPen( Color.White );
Paint.SetFont( "Roboto", 24f, 800 );
Paint.DrawText( size, builder.Title );
}
bool dragging = false;
protected override void OnDragStart()
{
if ( dragging ) return;
dragging = true;
Log.Info( $"Drag Start!" );
}
protected override void OnMouseReleased( MouseEvent e )
{
if(e.LeftMouseButton && dragging )
{
Vector2 pos = e.WindowPosition;
Log.Info( $"Drag stop!" );
PanelEditorSession.Current.EditorWidget?.OnBuilderDrop( builder, e );
dragging = false;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Panelize;
public class Checkbox : Widget
{
public bool Value { get; set; }
public string IconEnabled { get; set; }
public string IconDisabled { get; set; }
public Action<bool> OnEdited;
public Checkbox( Widget parent = null, bool value = false, string iconEnabled = null, string iconDisabled = null, float? size = null) : base(parent)
{
Value = value;
IconEnabled = iconEnabled;
IconDisabled = iconDisabled;
Cursor = CursorShape.Finger;
MinimumWidth = size ?? ControlWidget.ControlRowHeight;
MinimumHeight = size ?? ControlWidget.ControlRowHeight;
HorizontalSizeMode = SizeMode.CanShrink;
VerticalSizeMode = SizeMode.CanShrink;
}
protected override void OnMouseClick( MouseEvent e )
{
if(e.LeftMouseButton)
{
Value = !Value;
OnEdited?.Invoke(Value);
}
}
protected override void OnPaint()
{
Paint.Antialiasing = true;
Paint.TextAntialiasing = true;
float alpha = (ReadOnly ? 0.5f : 1f);
Rect localRect = LocalRect;
Color color = Theme.Blue;
Rect rect = localRect.Shrink( 2 );
Paint.ClearPen();
Paint.SetBrush( ControlWidget.ControlColor.Lighten( ReadOnly ? 0.5f : 0f ).WithAlphaMultiplied( alpha ) );
Paint.DrawRect( rect, 2f );
if(Value)
{
Paint.SetPen( color.WithAlpha( 0.3f * alpha ), 1f );
Paint.SetBrush( color.WithAlpha( 0.2f * alpha ) );
Paint.DrawRect( rect, 2f );
Paint.SetPen( color.WithAlphaMultiplied( 0.5f ) );
Paint.DrawIcon( rect, IconEnabled ?? "done", 13f );
}
else if ( IconDisabled != null )
{
Paint.SetPen( Theme.Grey.WithAlphaMultiplied( 0.5f ) );
Paint.DrawIcon( rect, IconDisabled, 13f );
}
if ( IsUnderMouse && !ReadOnly )
{
Paint.SetPen( color.WithAlpha(0.5f * alpha), 1);
Paint.ClearBrush();
Paint.DrawRect( in rect, 1f );
}
}
}
namespace MANIFOLD.Camera {
/// <summary>
/// Locks the <see cref="VirtualCamera"/> to it's <see cref="VirtualCamera.TrackingTarget"/>.
/// </summary>
[Title(LibraryData.TITLE_SPLIT + "Hard Lock"), Category(LibraryData.CATEGORY), Icon("lock")]
public sealed class CameraHardLock : CameraExtension {
public enum OrientMode {
/// <summary>
/// The target's orientation is not taken into account.
/// </summary>
None,
/// <summary>
/// The target's orientation only affects the position.
/// </summary>
[Title("Position Only")]
NoRotation,
/// <summary>
/// The target's orientation affects both position and rotation.
/// </summary>
[Title("Position and Rotation")]
Full
}
/// <summary>
/// How should the Target's orientation be handled?
/// </summary>
[Property, Title("Orientation Handling")]
public OrientMode Orient { get; set; } = OrientMode.Full;
private GameObject lastTarget;
private Vector3 targetBindRelativePos;
private Vector3 targetBindLocalPos;
private Rotation targetBindLocalRot;
protected internal override void OnCameraInitialize() {
if (!Camera.TrackingTarget.IsValid()) return;
BindValues();
}
protected internal override void OnCameraUpdate(ref Vector3 localPosition, ref Rotation localRotation) {
if (!Camera.TrackingTarget.IsValid()) return;
if (Camera.TrackingTarget != lastTarget) {
BindValues();
}
GameObject target = Camera.TrackingTarget;
switch (Orient) {
case OrientMode.None: {
Camera.WorldPosition = target.WorldPosition + targetBindRelativePos;
break;
}
case OrientMode.NoRotation: {
Camera.WorldPosition = target.WorldPosition + (targetBindLocalPos * target.WorldRotation);
break;
}
case OrientMode.Full: {
Camera.WorldPosition = target.WorldPosition + (targetBindLocalPos * target.WorldRotation);
Camera.WorldRotation = target.WorldRotation * targetBindLocalRot;
break;
}
}
}
private void BindValues() {
lastTarget = Camera.TrackingTarget;
targetBindRelativePos = WorldPosition - Camera.TrackingTarget.WorldPosition;
Rotation inverse = Camera.TrackingTarget.WorldRotation.Inverse;
targetBindLocalRot = WorldRotation * inverse;
targetBindLocalPos = (WorldPosition - Camera.TrackingTarget.WorldPosition) * inverse;
}
}
}
using System.Collections.Generic;
using Sandbox;
using Sandbox.Utility;
namespace MANIFOLD.Camera {
/// <summary>
/// The actual brain of the camera system.
/// </summary>
public sealed class CameraSystem : GameObjectSystem {
private CameraBrain mainCameraBrain;
private bool cameraStackDirty;
private LinkedList<VirtualCamera> cameraStack;
private VirtualCamera transitionFrom;
private VirtualCamera transitionTo;
private bool inTransition;
private bool reverseTransition;
private float currentTransitionTimer;
private float currentTransitionElapsed;
private TransitionData currentTransitionData;
public CameraBrain Brain => mainCameraBrain;
public VirtualCamera LastCamera => transitionFrom;
public VirtualCamera CurrentCamera => transitionTo;
public CameraSystem(Scene scene) : base(scene) {
cameraStack = new LinkedList<VirtualCamera>();
Listen(Stage.FinishUpdate, 100, CameraUpdate, "camera.update");
}
private void CameraUpdate() {
if (!FindBrain()) return;
if (Scene.IsEditor) {
if (!mainCameraBrain.UpdateInEditor) {
return;
}
}
if (cameraStackDirty) {
OnStackEdit();
}
if (!transitionTo.IsValid()) return;
GetCameraTransform(transitionTo, out Vector3 toPos, out Rotation toRot);
if (!Scene.IsEditor && inTransition) {
float linearFactor = currentTransitionTimer / currentTransitionData.Duration;
float easedFactor = 0f;
{
float offset = currentTransitionElapsed / currentTransitionData.Duration;
float evalTime = linearFactor.Remap(offset, 1);
if (currentTransitionData.Mode == TransitionMode.Predefined) {
var func = Easing.GetFunction(currentTransitionData.EaseFunction.ToString());
easedFactor = func(evalTime) + currentTransitionElapsed;
} else if (currentTransitionData.Mode == TransitionMode.Curve) {
easedFactor = currentTransitionData.EaseCurve.Evaluate(evalTime);
}
easedFactor = easedFactor.Remap(0, 1, offset);
}
GetCameraTransform(transitionFrom, out Vector3 fromPos, out Rotation fromRot);
mainCameraBrain.WorldPosition = Vector3.Lerp(fromPos, toPos, easedFactor);
mainCameraBrain.WorldRotation = Rotation.Slerp(fromRot, toRot, easedFactor);
mainCameraBrain.Camera.FieldOfView = MathX.Lerp(GetCameraFOV(transitionFrom), GetCameraFOV(transitionTo), easedFactor);
currentTransitionTimer += mainCameraBrain.UseRealTime ? RealTime.Delta : Time.Delta;
if (currentTransitionTimer >= currentTransitionData.Duration) {
currentTransitionTimer = currentTransitionData.Duration;
inTransition = false;
}
} else {
mainCameraBrain.WorldPosition = toPos;
mainCameraBrain.WorldRotation = toRot;
mainCameraBrain.Camera.FieldOfView = GetCameraFOV(transitionTo);
}
}
private void GetCameraTransform(VirtualCamera cam, out Vector3 pos, out Rotation rot) {
cam.DoExtensionUpdate(out Vector3 localPos, out Rotation localRot);
pos = cam.WorldPosition + (localPos * cam.WorldRotation);
rot = cam.WorldRotation * localRot;
}
private float GetCameraFOV(VirtualCamera cam) {
if (cam.FOVMode == FOVMode.Vertical) return (2 * float.Atan(float.Tan(cam.FieldOfView.DegreeToRadian() / 2) * Screen.Aspect)).RadianToDegree();
return cam.FieldOfView;
}
public void ActivateCamera(VirtualCamera newCamera, bool updateNow = false) {
if (!mainCameraBrain.IsValid()) {
Log.Warning($"Tried to activate virtual camera '${newCamera.GameObject.Name}' but there is no brain in the scene.");
}
VirtualCamera highestCamera = null;
foreach (VirtualCamera camera in cameraStack) {
if (camera.Priority <= newCamera.Priority) {
highestCamera = camera;
}
}
if (highestCamera != null) {
cameraStack.AddAfter(cameraStack.Find(highestCamera), newCamera);
} else {
cameraStack.AddLast(newCamera);
}
cameraStackDirty = true;
if (updateNow) {
OnStackEdit();
}
}
public void DeactivateCamera(VirtualCamera camera, bool updateNow = false) {
cameraStack.Remove(camera);
cameraStackDirty = true;
if (updateNow) {
OnStackEdit();
}
}
private bool FindBrain() {
if (mainCameraBrain.IsValid()) return true;
mainCameraBrain = Scene.Components.GetInDescendants<CameraBrain>();
return mainCameraBrain.IsValid();
}
private void OnStackEdit() {
var lastNode = cameraStack.Last;
if (lastNode == null) return;
// var previousNode = lastNode.Previous;
// if (previousNode != null) {
// transitionFrom = previousNode.Value;
// }
VirtualCamera newTo = lastNode.Value;
if (newTo != transitionTo && transitionTo.IsValid()) {
TransitionData newData = newTo.UseCustomTransition ? newTo.TransitionData : mainCameraBrain.TransitionData;
if (newData.Mode != TransitionMode.Cut) {
inTransition = true;
if (newTo == transitionFrom) {
float elapsedNorm = 1 - (currentTransitionTimer / currentTransitionData.Duration);
currentTransitionTimer = newData.Duration * elapsedNorm;
currentTransitionElapsed = newData.AbsoluteEase ? 0f : newData.Duration * elapsedNorm;
} else {
currentTransitionTimer = 0;
currentTransitionElapsed = 0;
}
}
currentTransitionData = newData;
transitionFrom = transitionTo;
}
transitionTo = newTo;
cameraStackDirty = false;
}
}
}
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
using System.Threading.Tasks;
using Sandbox.Utility;
namespace MANIFOLD.Camera {
public enum FOVMode { Vertical, Horizontal }
/// <summary>
/// Imagine a <see cref="CameraComponent"/> but it isn't real.
/// </summary>
[EditorHandle("materials/gizmo/virtual_cam.png")]
[Title(LibraryData.TITLE_SPLIT + "Virtual Camera"), Icon("videocam"), Category(LibraryData.CATEGORY)]
public sealed class VirtualCamera : Component, Component.ExecuteInEditor {
/// <summary>
/// This camera's priority in the stack.
/// </summary>
[Property]
public int Priority { get; set; }
/// <summary>
/// Which type of field of view to use.
/// <see cref="FOVMode.Vertical"/> is recommended.
/// </summary>
[Property, Header("Lens"), Title("FOV Type")]
public FOVMode FOVMode { get; set; }
/// <summary>
/// Camera's field of view in degrees.
/// </summary>
[Property, Range(0, 180)]
public float FieldOfView { get; set; } = 90f;
/// <summary>
/// Does this camera have an look target?
/// If not, <see cref="TrackingTarget"/> is used.
/// </summary>
[Property, Header("Targets")]
public bool UseLookTarget { get; set; }
/// <summary>
/// Target to track. Used as the <see cref="LookTarget"/> as well by default.
/// </summary>
[Property]
public GameObject TrackingTarget { get; set; }
/// <summary>
/// Target to look at.
/// </summary>
[Property, ShowIf(nameof(UseLookTarget), true)]
public GameObject LookTarget { get; set; }
/// <summary>
/// Should switching to this camera use a special transition?
/// </summary>
[Property, Header("Transition")]
public bool UseCustomTransition { get; set; } = false;
[ShowIf(nameof(UseCustomTransition), true)]
[Property]
public TransitionData TransitionData { get; set; }
private CameraSystem internalSystem;
private List<CameraExtension> extensions;
private int lastComponentCount;
public CameraSystem System {
get {
if (internalSystem == null) {
internalSystem = Scene.GetSystem<CameraSystem>();
}
return internalSystem;
}
}
public bool IsActive => System.CurrentCamera == this;
internal void OnSystemInit(CameraSystem system) {
internalSystem = system;
extensions = Components.GetAll<CameraExtension>(FindMode.EverythingInSelf).ToList();
foreach (CameraExtension ext in extensions) {
ext.Camera = this;
ext.OnCameraInitialize();
}
}
internal void DoExtensionUpdate(out Vector3 localPos, out Rotation localRot) {
localPos = Vector3.Zero;
localRot = Rotation.Identity;
foreach (CameraExtension ext in extensions) {
ext.OnCameraUpdate(ref localPos, ref localRot);
}
}
protected override void OnStart() {
if (internalSystem == null) {
OnSystemInit(Scene.GetSystem<CameraSystem>());
}
}
protected override void OnEnabled() {
if (internalSystem == null) {
OnSystemInit(Scene.GetSystem<CameraSystem>());
}
System.ActivateCamera(this);
}
protected override void OnDisabled() {
System.DeactivateCamera(this);
}
protected override void OnUpdate() {
if (!Scene.IsEditor) return; // we probably dont need to check this at runtime right?
if (lastComponentCount != Components.Count) {
extensions = Components.GetAll<CameraExtension>(FindMode.EverythingInSelf).ToList();
Log.Info("component count has changed");
lastComponentCount = Components.Count;
}
}
protected override void DrawGizmos() {
if (IsActive) return;
if (!System.Brain.IsValid()) return;
CameraComponent camera = System.Brain.Camera;
float horizontalAngle = FieldOfView;
// THIS IS WRONG BUT GOOD ENOUGH
float verticalAngle = horizontalAngle * (camera.ScreenRect.Height / camera.ScreenRect.Width); // cam fov is horizontal
Vector3 origin = Vector3.Zero;
Vector3 forward = Vector3.Forward;
Ray tl = new Ray(origin, forward * new Angles(verticalAngle * 0.5f, horizontalAngle * 0.5f, 0f));
Ray tr = new Ray(origin, forward * new Angles(verticalAngle * 0.5f, -horizontalAngle * 0.5f, 0f));
Ray bl = new Ray(origin, forward * new Angles(-verticalAngle * 0.5f, horizontalAngle * 0.5f, 0f));
Ray br = new Ray(origin, forward * new Angles(-verticalAngle * 0.5f, -horizontalAngle * 0.5f, 0f));
Frustum frustum = Frustum.FromCorners(tl, tr, br, bl, camera.ZNear, camera.ZFar);
Gizmo.Draw.Color = new Color(0.5f, 0f, 0f);
Gizmo.Draw.LineFrustum(frustum);
}
}
}
global using static Sandbox.Internal.GlobalGameNamespace;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "BetterUI" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "betterui" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "umblestudio" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "umblestudio.betterui" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "1/20/2025 1:22:55 PM" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "17" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]
[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.293.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.293.0")]/// <summary>
/// Animates a component by scaling it between a minimum and maximum scale value.
/// </summary>
using System;
namespace BetterUI.Animations;
public class ScaleAnimation : AnimationBase
{
/// <summary>
/// Gets or sets the minimum scale factor.
/// </summary>
[Property]
public float MinScale { get; set; } = 0.8f;
/// <summary>
/// Gets or sets the maximum scale factor.
/// </summary>
[Property]
public float MaxScale { get; set; } = 1.2f;
protected override void OnAnimate( float t )
{
var easedTime = ApplyEasing( NormalizedTime, Easing );
var scale = MinScale + (MaxScale - MinScale) * (0.5f + 0.5f * MathF.Sin( easedTime * MathF.PI * 2 ));
WorldScale = new Vector3(
Axis.x > 0 ? scale : 1f,
Axis.y > 0 ? scale : 1f,
Axis.z > 0 ? scale : 1f
);
}
}
using System;
namespace BetterUI.Extensions;
/// <summary>
/// Extensions for property descriptions.
/// </summary>
internal static class PropertyDescriptionExtensions
{
/// <summary>
/// Checks if a property is a cascading property with the given name and type.
/// </summary>
/// <param name="prop">The property to check.</param>
/// <param name="name">The name of the cascading property.</param>
/// <param name="type">The type of the cascading property.</param>
/// <returns>True if the property is a cascading property with the given name and type. False otherwise.</returns>
public static bool IsCascadingProperty( this PropertyDescription prop, string name, Type type )
{
var attr = prop.GetCustomAttribute<CascadingPropertyAttribute>();
if ( attr is null ) return false;
return (attr.Name == name && type == prop.PropertyType) || type == prop.PropertyType;
}
}
namespace BetterUI;
/// <summary>
/// An event that can be listened to in order to receive notifications
/// </summary>
public interface INotificationEvent : ISceneEvent<INotificationEvent>
{
/// <summary>
/// Called when a new notification is added to the system.
/// </summary>
/// <param name="notification">The notification that was added.</param>
void OnNotification( INotification notification );
/// <summary>
/// Called when the notification system is changed.
/// </summary>
/// <param name="system">The new notification system.</param>
void OnNotificationSystemChanged( NotificationSystem system );
}
@namespace BetterUI
@inherits Panel
@ChildContent
@namespace BetterUI
@inherits Panel
using System;
namespace BetterUI;
/// <summary>
/// An attribute that can be applied to a type to specify a notification view for a <see cref="Notification"/>.
/// </summary>
/// <remarks>
/// The type that this attribute is applied to must be a panel.
/// </remarks>
[AttributeUsage( AttributeTargets.Class | AttributeTargets.Struct )]
public sealed class NotificationViewAttribute( Type type ) : Attribute
{
/// <summary>
/// The type of the notification view.
/// </summary>
public Type Type => type;
}
using System;
namespace BetterUI.Animations;
/// <summary>
/// Animates a component rotating around a central point in a pendulum-like motion.
/// </summary>
public class PendulumAnimation : AnimationBase
{
/// <summary>
/// The minimum angle of rotation (in degrees) that the component will reach.
/// </summary>
[Property]
public float MinAngle { get; set; } = -45f;
/// <summary>
/// The maximum angle of rotation (in degrees) that the component will reach.
/// </summary>
[Property]
public float MaxAngle { get; set; } = 45f;
protected override void OnAnimate( float t )
{
var easedTime = ApplyEasing( NormalizedTime, Easing );
var angle = MinAngle + (MaxAngle - MinAngle) * (0.5f + 0.5f * MathF.Sin( easedTime * MathF.PI * 2 ));
WorldRotation = Rotation.FromAxis( Vector3.Up, angle );
}
}
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
using Sandbox;
[TestClass]
public partial class LibraryTests
{
[TestMethod]
public void SceneTest()
{
var scene = new Scene();
using ( scene.Push() )
{
var go = new GameObject();
Assert.AreEqual( 1, scene.Directory.GameObjectCount );
}
}
}
namespace SmallFishUtils;
// Originally taken from Facepunch source code, thank you!
public abstract class Singleton<T> : Component, IHotloadManaged where T : Singleton<T>
{
public static T Instance { get; private set; }
protected override void OnAwake()
{
if ( Instance.IsValid() )
{
Log.Warning( $"Multiple Singletons found of type: {TypeLibrary.GetType<T>().Name}" );
Destroy();
return;
}
if ( Active )
{
Instance = (T)this;
}
}
void IHotloadManaged.Destroyed( Dictionary<string, object> state )
{
state["IsActive"] = Instance == this;
}
void IHotloadManaged.Created( IReadOnlyDictionary<string, object> state )
{
if ( state.GetValueOrDefault( "IsActive" ) is true )
{
Instance = (T)this;
}
}
protected override void OnDestroy()
{
if ( Instance == this )
{
Instance = null;
}
}
}
using Sandbox.UI;
using Sandbox.UI.Construct;
public class ToastPanel : Panel
{
public ToastPanel( Toast toast )
{
AddClass( toast.Status.ToString() );
AddClass( toast.Position.ToString() );
Add.Icon( toast.Status switch
{
ToastStatus.Info => "info",
ToastStatus.Warning => "warning_amber",
ToastStatus.Success => "check_circle_outline",
ToastStatus.Error => "error_outline",
_ => "",
} );
Add.Label( toast.Text, "text" );
AddEventListener( "onclick", ( PanelEvent _ ) => Delete() );
Invoke( toast.Duration, () => Delete() );
}
protected override int BuildHash() => HashCode.Combine( 1 );
}
using System;
using static TestClasses;
namespace SandbankDatabase;
internal static class TestData
{
public static ReadmeExample TestData1 = new ReadmeExample()
{
UID = "",
Health = 100,
Name = "TestPlayer1",
Level = 10,
LastPlayTime = DateTime.UtcNow,
Items = new() { "gun", "frog", "banana" }
};
public static ReadmeExample TestData2 = new ReadmeExample()
{
UID = "",
Health = 90,
Name = "TestPlayer2",
Level = 15,
LastPlayTime = DateTime.UtcNow,
Items = new() { "apple", "box" }
};
}
global using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class TestInit
{
[AssemblyInitialize]
public static void ClassInitialize( TestContext context )
{
Sandbox.Application.InitUnitTest();
}
}
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
using Sandbox;
using Sandbox.Diagnostics;
namespace DataTables;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class JsonTypeAnnotateAttribute : Attribute
{
}
internal static class Json
{
public static JsonSerializerOptions Options()
{
return new JsonSerializerOptions() { WriteIndented = true };
}
public static JsonNode Serialize( object target, bool typeAnnotate, Type typeOverride = null )
{
var type = target.GetType();
var typeDesc = TypeLibrary.GetType( type );
if ( typeDesc.IsValueType || type.IsAssignableTo( typeof(Resource) ) ||
type.IsAssignableTo( typeof(string) ) )
return Sandbox.Json.ToNode( target );
if ( type.IsAssignableTo( typeof(IList) ) )
return SerializeList( (IList)target, typeAnnotate );
if ( type.IsAssignableTo( typeof(IDictionary) ) )
return SerializeDictionary( (IDictionary)target, typeAnnotate );
var node = SerializeObject( target, true, typeOverride );
if ( typeAnnotate )
node["__type"] = typeDesc.FullName;
return node;
}
public static JsonNode SerializeDictionary( IDictionary target, bool typeAnnotate )
{
JsonObject jdict = new();
Type keyArg = TypeLibrary.GetGenericArguments( target.GetType() )[0];
bool isInteger = keyArg == typeof(int);
bool isString = keyArg == typeof(string);
bool isReal = keyArg == typeof(float) || keyArg == typeof(double);
if ( !(isInteger || isString || isReal) )
{
Log.Error(
$"The type '{keyArg.FullName}' is not a supported dictionary key! If you really need this to be supported, please submit an issue @ https://github.com/tzainten/DataTables" );
return jdict;
}
foreach ( var key in target.Keys )
{
if ( key is null )
continue;
var value = target[key];
if ( value is null )
continue;
jdict.Add( key.ToString(), Serialize( value, typeAnnotate ) );
}
return jdict;
}
public static JsonArray SerializeList( IList target, bool typeAnnotate )
{
JsonArray jarray = new();
foreach ( var elem in target )
{
if ( elem is null )
continue;
jarray.Add( Serialize( elem, typeAnnotate ) );
}
return jarray;
}
public static JsonObject SerializeObject( object target, bool typeAnnotate, Type typeOverride = null )
{
JsonObject jobj = new();
var type = typeOverride ?? target.GetType();
var typeDesc = TypeLibrary.GetType( type );
if ( typeDesc.IsValueType || type.IsAssignableTo( typeof(Resource) ) ||
type.IsAssignableTo( typeof(string) ) )
return Sandbox.Json.ToNode( target ).AsObject();
var members = TypeLibrary.GetFieldsAndProperties( typeDesc );
foreach ( var member in members )
{
object value = null;
bool shouldAnnotate = false;
if ( member.IsField )
{
FieldDescription field = (FieldDescription)member;
value = field.GetValue( target );
if ( value is null )
continue;
shouldAnnotate = field.HasAttribute( typeof(JsonTypeAnnotateAttribute) );
jobj[field.Name] = Serialize( value, shouldAnnotate, !shouldAnnotate ? field.FieldType : null );
continue;
}
PropertyDescription property = (PropertyDescription)member;
value = property.GetValue( target );
if ( value is null )
continue;
shouldAnnotate = property.HasAttribute( typeof(JsonTypeAnnotateAttribute) );
jobj[property.Name] = Serialize( value, shouldAnnotate, !shouldAnnotate ? property.PropertyType : null );
}
return jobj;
}
public static T Deserialize<T>( string json )
{
JsonNode node = JsonNode.Parse( json );
if ( node is null )
return default;
return (T)DeserializeInternal( node, typeof(T) );
}
public static object DeserializeInternal( JsonNode node, Type type )
{
TypeDescription typeDesc = TypeLibrary.GetType( type );
if ( typeDesc is not null && (typeDesc.IsValueType || type.IsAssignableTo( typeof(Resource) ) ||
type.IsAssignableTo( typeof(string) )) )
{
try
{
return Sandbox.Json.FromNode( node, type );
}
catch ( Exception e )
{
return null;
}
}
if ( type.IsAssignableTo( typeof(IDictionary) ) )
return DeserializeDictionary( node.AsObject(), type );
switch ( node.GetValueKind() )
{
case JsonValueKind.Object:
return DeserializeObject( node.AsObject(), type );
case JsonValueKind.Array:
return DeserializeList( node.AsArray(), type );
default:
try
{
return Sandbox.Json.FromNode( node, type );
}
catch ( Exception e )
{
return null;
}
}
}
public static IList DeserializeList( JsonArray jarray, Type type )
{
IList list = TypeLibrary.Create<IList>( type );
using var enumerator = jarray.GetEnumerator();
while ( enumerator.MoveNext() )
{
var node = enumerator.Current;
Type genericArg = TypeLibrary.GetGenericArguments( type ).First();
var elem = DeserializeInternal( node, genericArg );
if ( elem is null )
continue;
if ( elem.GetType().IsAssignableTo( genericArg ) )
list.Add( elem );
}
return list;
}
public static IDictionary DeserializeDictionary( JsonObject jobj, Type type )
{
IDictionary dict = TypeLibrary.Create<IDictionary>( type );
using var enumerator = jobj.GetEnumerator();
while ( enumerator.MoveNext() )
{
var pair = enumerator.Current;
Type[] genericArgs = TypeLibrary.GetGenericArguments( type );
var keyType = genericArgs[0];
var key = pair.Key;
object parsedKey = key;
if ( keyType == typeof(int) )
{
if ( int.TryParse( key, out int num ) )
parsedKey = num;
}
else if ( keyType == typeof(double) )
{
if ( double.TryParse( key, out double num ) )
parsedKey = num;
}
else if ( keyType == typeof(float) )
{
if ( float.TryParse( key, out float num ) )
parsedKey = num;
}
var node = pair.Value;
var elem = DeserializeInternal( node, genericArgs[1] );
if ( elem is null )
continue;
Type keyArg = TypeLibrary.GetGenericArguments( type )[0];
Type valueArg = TypeLibrary.GetGenericArguments( type )[1];
bool isCorrectKeyType = parsedKey.GetType().IsAssignableTo( keyArg );
bool isCorrectValueType = elem.GetType().IsAssignableTo( valueArg );
if ( !isCorrectKeyType || !isCorrectValueType )
continue;
if ( elem.GetType().IsAssignableTo( genericArgs[1] ) )
dict.Add( parsedKey, elem );
}
return dict;
}
public static object DeserializeObject( JsonObject jobj, Type type )
{
jobj.TryGetPropertyValue( "__type", out JsonNode __type );
TypeDescription typeDesc = null;
if ( __type is not null )
{
typeDesc = TypeLibrary.GetType( __type.GetValue<string>() );
}
else
{
typeDesc = TypeLibrary.GetType( type );
}
if ( typeDesc is null )
return null;
object instance = TypeLibrary.Create<object>( typeDesc.TargetType );
using var enumerator = jobj.GetEnumerator();
while ( enumerator.MoveNext() )
{
var node = enumerator.Current;
var property = typeDesc.Properties.FirstOrDefault( x =>
x.IsPublic && !x.IsStatic && x.IsNamed( node.Key ) && x.CanWrite && x.CanRead );
bool isValidProperty = property is not null;
var field = typeDesc.Fields.FirstOrDefault( x => x.IsPublic && !x.IsStatic && x.IsNamed( node.Key ) );
bool isValidField = field is not null;
if ( !isValidProperty && !isValidField )
continue;
var deserializeType = isValidProperty ? property.PropertyType : field.FieldType;
var value = DeserializeInternal( node.Value, deserializeType );
if ( value is null )
continue;
if ( value.GetType().IsAssignableTo( deserializeType ) )
{
if ( isValidProperty )
property.SetValue( instance, value );
else
field.SetValue( instance, value );
}
}
return instance;
}
}
global using Microsoft.VisualStudio.TestTools.UnitTesting;
[TestClass]
public class TestInit
{
[AssemblyInitialize]
public static void ClassInitialize( TestContext context )
{
Sandbox.Application.InitUnitTest();
}
}
global using static Sandbox.Internal.GlobalGameNamespace;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Second Order Dynamics" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "secondorderdynamics" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "andicraft" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "andicraft.secondorderdynamics" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "1/17/2025 7:48:34 PM" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "17" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]
[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.140.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.140.0")]using Andicraft.SecondOrderDynamics;
using Sandbox;
namespace Andicraft.SecondOrderDynamics.Components;
[Title( "Second Order Dynamics - Follow Target" ), Category("Second Order Dynamics")]
public class FollowTargetComponent : Component
{
[Property] public GameObject FollowTarget { get; set; }
/// <summary>
/// Reset the dynamics if the object moves further than this distance in one frame
/// </summary>
[Property] public float TeleportDistanceThreshold { get; set; } = 32 * 10;
[Property] public DynamicsParameters PositionParameters { get; set; } = new();
[Property] public DynamicsParameters RotationParameters { get; set; } = new();
private Vector3Dynamics _positionDynamics;
private RotationDynamics _rotationDynamics;
private Vector3 _previousFollowPos = default;
protected override void OnStart()
{
_positionDynamics = new Vector3Dynamics( PositionParameters, FollowTarget.WorldPosition );
_rotationDynamics = new RotationDynamics( RotationParameters, FollowTarget.WorldRotation );
_previousFollowPos = FollowTarget.WorldPosition;
}
protected override void OnUpdate()
{
if ( FollowTarget == null ) return;
if ( FollowTarget.WorldPosition.DistanceSquared( _previousFollowPos ) >
TeleportDistanceThreshold * TeleportDistanceThreshold )
{
Warp();
_previousFollowPos = FollowTarget.WorldPosition;
return;
}
WorldPosition = _positionDynamics.Update( Time.Delta, FollowTarget.WorldPosition );
WorldRotation = _rotationDynamics.Update( Time.Delta, FollowTarget.WorldRotation );
_previousFollowPos = FollowTarget.WorldPosition;
}
/// <summary>
/// Resets the following target, useful for when you teleport or jump somewhere
/// </summary>
public void Warp()
{
_positionDynamics.Reset( FollowTarget.WorldPosition );
_rotationDynamics.Reset( FollowTarget.WorldRotation );
WorldPosition = FollowTarget.WorldPosition;
WorldRotation = FollowTarget.WorldRotation;
}
public void UpdatePositionParameters( DynamicsParameters p )
{
PositionParameters = p;
_positionDynamics.UpdateParameters( p );
}
public void UpdateRotationParameters( DynamicsParameters p )
{
RotationParameters = p;
_rotationDynamics.UpdateParameters( p );
}
}
using Sandbox;
[Title("[DEMO] Follow Mouse On Plane"), Category("Second Order Dynamics (Demo)")]
public sealed class DemoFollowMouse : Component
{
[Property] private CameraComponent Camera { get; set; }
[Property] private float PlaneDistance { get; set; } = 100;
[Property] private GameObject TargetObject { get; set; }
protected override void OnUpdate()
{
var ray = Camera.ScreenPixelToRay( Mouse.Position );
var p = new Plane( Camera.WorldPosition + Camera.WorldTransform.Forward * PlaneDistance,
Camera.WorldTransform.Backward );
var pt = p.Trace( ray );
if (pt.HasValue)
TargetObject.WorldPosition = pt.Value;
}
}
using System;
using Sandbox;
namespace Andicraft.SecondOrderDynamics;
/// <summary>
/// Helper class that lets you run dynamics on a Color.
/// </summary>
public class ColorDynamics
{
private FloatDynamics _h;
private Vector2Dynamics _sv;
private FloatDynamics _alpha;
private Vector3 _currentHsv;
private Color _currentColor;
public Color CurrentValue => _currentColor;
public float CurrentAlpha => _currentColor.a;
public ColorDynamics(float frequency, float damping, float response, Color startingValue)
{
_currentHsv = startingValue.ToOkHsv();
_h = new(frequency, damping, response, _currentHsv.x)
{
MinWrapValue = 0f,
MaxWrapValue = 1f
};
_sv = new Vector2Dynamics(frequency, damping, response, new Vector2(_currentHsv.z, _currentHsv.y));
_alpha = new FloatDynamics(frequency, damping, response, startingValue.a);
}
public void SetFrequency(float f)
{
_h.SetFrequency(f);
_sv.SetFrequency(f);
_alpha.SetFrequency(f);
}
public void SetDamping(float d)
{
_h.SetDamping(d);
_sv.SetDamping(d);
_alpha.SetDamping(d);
}
public void SetResponse(float r)
{
_h.SetResponse(r);
_sv.SetResponse(r);
_alpha.SetResponse(r);
}
public void Reset(Color value)
{
var targetColor = value.ToOkHsv();
_currentColor = value;
_currentHsv = targetColor;
_h.Reset(_currentHsv.x);
_sv.Reset(new Vector2(_currentHsv.y, _currentHsv.z));
_alpha.Reset(value.a);
}
public Color Update(float deltaTime, Color target, bool setVelocity = false, Vector4 velocity = default)
{
var targetHsv = target.ToOkHsv();
var h = _h.Update(deltaTime, targetHsv.x, setVelocity, velocity.x);
var sv = _sv.Update(deltaTime, new Vector2(targetHsv.y, targetHsv.z), setVelocity,
new Vector2(velocity.y, velocity.z));
var a = _alpha.Update(deltaTime, target.a, setVelocity, velocity.w);
_currentHsv = new Vector3(h, sv.x.Saturate(), sv.y.Saturate());
var col = OkColor.OkHsvToColor(_currentHsv);
col.a = a.Saturate();
_currentColor = col;
return _currentColor;
}
}
// Copyright(c) 2021 Bjorn Ottosson
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files(the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
using System;
namespace Andicraft.SecondOrderDynamics;
// Straight up port of OKColor to allow ColorDynamics to work nicely
public static class OkColor
{
// Finds the maximum saturation possible for a given hue that fits in sRGB
// Saturation here is defined as S = C/L
// a and b must be normalized so a^2 + b^2 == 1
private static double ComputeMaxSaturation (in double a, in double b)
{
// Max saturation will be when one of r, g or b goes below zero.
// Select different coefficients depending on which component goes below zero first
double k0, k1, k2, k3, k4, wl, wm, ws;
if (-1.88170328d * a - 0.80936493d * b > 1.0d)
{
// Re component
k0 = 1.19086277d;
k1 = 1.76576728d;
k2 = 0.59662641d;
k3 = 0.75515197d;
k4 = 0.56771245d;
wl = 4.0767416621d;
wm = -3.3077115913d;
ws = 0.2309699292d;
}
else if (1.81444104d * a - 1.19445276d * b > 1.0d)
{
// Green component
k0 = 0.73956515d;
k1 = -0.45954404d;
k2 = 0.08285427d;
k3 = 0.1254107d;
k4 = 0.14503204d;
wl = -1.2684380046d;
wm = 2.6097574011d;
ws = -0.3413193965d;
}
else
{
// Blue component
k0 = 1.35733652d;
k1 = -0.00915799d;
k2 = -1.1513021d;
k3 = -0.50559606d;
k4 = +0.00692167d;
wl = -0.0041960863d;
wm = -0.7034186147d;
ws = 1.707614701d;
}
// Approximate max saturation using a polynomial:
double S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b;
// Do one step Halley's method to get closer
// this gives an error less than 10e6, except for some blue hues where the dS/dh is close to infinite
// this should be sufficient for most applications, otherwise do two/three steps
double k_l = 0.3963377774d * a + 0.2158037573d * b;
double k_m = -0.1055613458d * a - 0.0638541728d * b;
double k_s = -0.0894841775d * a - 1.291485548d * b;
{
double l_ = 1.0d + S * k_l;
double m_ = 1.0d + S * k_m;
double s_ = 1.0d + S * k_s;
double l = l_ * l_ * l_;
double m = m_ * m_ * m_;
double s = s_ * s_ * s_;
double l_dS = 3.0d * k_l * l_ * l_;
double m_dS = 3.0d * k_m * m_ * m_;
double s_dS = 3.0d * k_s * s_ * s_;
double l_dS2 = 6.0d * k_l * k_l * l_;
double m_dS2 = 6.0d * k_m * k_m * m_;
double s_dS2 = 6.0d * k_s * k_s * s_;
double f = wl * l + wm * m + ws * s;
double f1 = wl * l_dS + wm * m_dS + ws * s_dS;
double f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2;
double sDenom = (f1 * f1 - 0.5d * f * f2);
if (sDenom != 0.0d) { S = S - f * f1 / sDenom; }
}
return S;
}
private static (double L, double C) FindCusp (in double a, in double b)
{
// First, find the maximum saturation (saturation S = C/L)
double S_cusp = OkColor.ComputeMaxSaturation (a, b);
// Convert to linear sRGB to find the first point where at least one of r,g or b >= 1:
var rgb_at_max = OkColor.OkLabToLinearSrgb ((L: 1.0d, a: S_cusp * a, b: S_cusp * b));
double L_cusp = Math.Pow (1.0d / Math.Max (Math.Max (rgb_at_max.r, rgb_at_max.g), rgb_at_max.b), 1.0d / 3.0d);
double C_cusp = L_cusp * S_cusp;
return (L: L_cusp, C: C_cusp);
}
// Finds intersection of the line defined by
// L = L0 * (1 - t) + t * L1;
// C = t * C1;
// a and b must be normalized so a^2 + b^2 == 1
private static double FindGamutIntersection (in double a, in double b, in double L1, in double C1, in double L0, (double L, double C) cusp)
{
// Find the intersection for upper and lower half seprately
double t = 0.0d;
if (((L1 - L0) * cusp.C - (cusp.L - L0) * C1) <= 0.0d)
{
// Lower half
double tDenom = (C1 * cusp.L + cusp.C * (L0 - L1));
if (tDenom != 0.0d) { t = cusp.C * L0 / tDenom; }
}
else
{
// Upper half
// First intersect with triangle
double tDenom = C1 * (cusp.L - 1.0d) + cusp.C * (L0 - L1);
if (tDenom != 0.0d) { t = cusp.C * (L0 - 1.0d) / tDenom; }
// Then one step Halley's method
{
double dL = L1 - L0;
double dC = C1;
double k_l = 0.3963377774d * a + 0.2158037573d * b;
double k_m = -0.1055613458d * a - 0.0638541728d * b;
double k_s = -0.0894841775d * a - 1.2914855480d * b;
double l_dt = dL + dC * k_l;
double m_dt = dL + dC * k_m;
double s_dt = dL + dC * k_s;
// If higher accuracy is required, 2 or 3 iterations of the following block can be used:
{
double L = L0 * (1.0d - t) + t * L1;
double C = t * C1;
double l_ = L + C * k_l;
double m_ = L + C * k_m;
double s_ = L + C * k_s;
double l = l_ * l_ * l_;
double m = m_ * m_ * m_;
double s = s_ * s_ * s_;
double ldt = 3.0d * l_dt * l_ * l_;
double mdt = 3.0d * m_dt * m_ * m_;
double sdt = 3.0d * s_dt * s_ * s_;
double ldt2 = 6.0d * l_dt * l_dt * l_;
double mdt2 = 6.0d * m_dt * m_dt * m_;
double sdt2 = 6.0d * s_dt * s_dt * s_;
double r0 = 4.0767416621d * l - 3.3077115913d * m + 0.2309699292d * s - 1.0d;
double r1 = 4.0767416621d * ldt - 3.3077115913d * mdt + 0.2309699292d * sdt;
double r2 = 4.0767416621d * ldt2 - 3.3077115913d * mdt2 + 0.2309699292d * sdt2;
double rDenom = r1 * r1 - 0.5d * r0 * r2;
double ur = (rDenom != 0.0d) ? r1 / rDenom : 0.0d;
double tr = -r0 * ur;
double g0 = -1.2684380046d * l + 2.6097574011d * m - 0.3413193965d * s - 1.0d;
double g1 = -1.2684380046d * ldt + 2.6097574011d * mdt - 0.3413193965d * sdt;
double g2 = -1.2684380046d * ldt2 + 2.6097574011d * mdt2 - 0.3413193965d * sdt2;
double gDenom = g1 * g1 - 0.5d * g0 * g2;
double ug = (gDenom != 0.0d) ? g1 / gDenom : 0.0d;
double tg = -g0 * ug;
double b0 = -0.0041960863d * l - 0.7034186147d * m + 1.7076147010d * s - 1.0d;
double b1 = -0.0041960863d * ldt - 0.7034186147d * mdt + 1.7076147010d * sdt;
double b2 = -0.0041960863d * ldt2 - 0.7034186147d * mdt2 + 1.7076147010d * sdt2;
double bDenom = b1 * b1 - 0.5d * b0 * b2;
double ub = (bDenom != 0.0d) ? b1 / bDenom : 0.0d;
double tb = -b0 * ub;
tr = ur >= 0.0d ? tr : Single.MaxValue;
tg = ug >= 0.0d ? tg : Single.MaxValue;
tb = ub >= 0.0d ? tb : Single.MaxValue;
t = t + Math.Min (tr, Math.Min (tg, tb));
}
}
}
return t;
}
private static (double C_0, double C_mid, double C_max) GetCs (in double L, in double a_, in double b_)
{
var cusp = OkColor.FindCusp (a_, b_);
double C_max = OkColor.FindGamutIntersection (a_, b_, L, 1.0d, L, cusp);
var ST_max = OkColor.ToSt (cusp);
// Scale factor to compensate for the curved part of gamut shape:
double k = C_max / Math.Min ((L * ST_max.S), (1.0d - L) * ST_max.T);
double C_mid = 0.0d;
{
var ST_mid = OkColor.GetSTMid (a_, b_);
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
double C_a = L * ST_mid.S;
double C_b = (1.0d - L) * ST_mid.T;
double cae4 = C_a * C_a * C_a * C_a;
double cbe4 = C_b * C_b * C_b * C_b;
C_mid = 0.9d * k * Math.Sqrt (Math.Sqrt (1.0d / (1.0d / cae4 + 1.0d / cbe4)));
}
double C_0 = 0.0d;
{
// for C_0, the shape is independent of hue, so ST are constant. Values picked to roughly be the average values of ST.
double C_a = L * 0.4d;
double C_b = (1.0d - L) * 0.8d;
// Use a soft minimum function, instead of a sharp triangle shape to get a smooth value for chroma.
C_0 = Math.Sqrt (1.0d / (1.0d / (C_a * C_a) + 1.0d / (C_b * C_b)));
}
return (C_0: C_0, C_mid: C_mid, C_max: C_max);
}
// Returns a smooth approximation of the location of the cusp
// This polynomial was created by an optimization process
// It has been designed so that S_mid < S_max and T_mid < T_max
private static (double S, double T) GetSTMid (in double a_, in double b_)
{
double S = 0.11516993d;
double sDenom = 7.4477897d + 4.1590124d * b_ +
a_ * (-2.19557347d + 1.75198401d * b_ +
a_ * (-2.13704948d - 10.02301043d * b_ +
a_ * (-4.24894561d + 5.38770819d * b_ +
4.69891013d * a_)));
if (sDenom != 0.0d) { S += 1.0d / sDenom; }
double T = 0.11239642d;
double tDenom = 1.6132032d - 0.68124379d * b_ +
a_ * (0.40370612d + 0.90148123d * b_ +
a_ * (-0.27087943d + 0.6122399d * b_ +
a_ * (0.00299215d - 0.45399568d * b_ - 0.14661872d * a_)));
if (tDenom != 0.0d) { T += 1.0d / tDenom; }
return (S: S, T: T);
}
private static (double L, double a, double b) LinearSrgbToOkLab (in (double r, double g, double b) c)
{
double cr = c.r;
double cg = c.g;
double cb = c.b;
double l = 0.4122214708d * cr + 0.5363325363d * cg + 0.0514459929d * cb;
double m = 0.2119034982d * cr + 0.6806995451d * cg + 0.1073969566d * cb;
double s = 0.0883024619d * cr + 0.2817188376d * cg + 0.6299787005d * cb;
double lCbrt = Math.Pow (l, 1.0d / 3.0d);
double mCbrt = Math.Pow (m, 1.0d / 3.0d);
double sCbrt = Math.Pow (s, 1.0d / 3.0d);
return (
L: 0.2104542553d * lCbrt + 0.793617785d * mCbrt - 0.0040720468d * sCbrt,
a: 1.9779984951d * lCbrt - 2.428592205d * mCbrt + 0.4505937099d * sCbrt,
b: 0.0259040371d * lCbrt + 0.7827717662d * mCbrt - 0.808675766d * sCbrt);
}
public static (double L, double a, double b) OkHslToOkLab (in (double h, double s, double l) hsl)
{
// With single-precision numbers, this can generate invalid values, NaNs, infinities, etc.
double l = hsl.l;
if (l >= 1.0d) { return (L: 1.0d, a: 0.0d, b: 0.0d); }
if (l <= 0.0d) { return (L: 0.0d, a: 0.0d, b: 0.0d); }
double s = hsl.s;
if (s < 0.0d) { s = 0.0d; }
if (s > 1.0d) { s = 1.0d; }
double hRad = hsl.h * 6.283185307179586d;
double a_ = Math.Cos (hRad);
double b_ = Math.Sin (hRad);
double L = OkColor.ToeInv (l);
var cs = OkColor.GetCs (L, a_, b_);
double c0 = cs.C_0;
double cMid = cs.C_mid;
double cMax = cs.C_max;
double mid = 0.8d;
double midInv = 1.25d;
double C = 0.0d;
double t = 0.0d;
double k0 = 0.0d;
double k1 = 0.0d;
double k2 = 1.0d;
if (s < mid)
{
t = midInv * s;
k1 = mid * c0;
if (cMid != 0.0d) { k2 = (1.0d - k1 / cMid); }
double kDenom = 1.0d - k2 * t;
if (kDenom != 0.0d) { C = t * k1 / kDenom; }
}
else
{
double tDenom = 1.0d - mid;
if (tDenom != 0.0d) { t = (s - mid) / tDenom; }
k0 = cMid;
if (c0 != 0.0d) { k1 = (1.0d - mid) * cMid * cMid * midInv * midInv / c0; }
double cDenom = cMax - cMid;
k2 = 1.0d;
if (cDenom != 0.0d) { k2 = 1.0d - k1 / cDenom; }
double kDenom = 1.0d - k2 * t;
if (kDenom != 0.0d) { C = k0 + t * k1 / kDenom; }
}
return (L: L, a: C * a_, b: C * b_);
}
public static (double r, double g, double b) OkHslToSrgb (in (double h, double s, double l) hsl)
{
return OkColor.OkLabToSrgb (OkColor.OkHslToOkLab (hsl));
}
public static (double L, double a, double b) OkHsvToOkLab (in (double h, double s, double v) hsv)
{
double v = hsv.v;
if (v <= 0.0d) { return (L: 0.0d, a: 0.0d, b: 0.0d); }
if (v > 1.0d) { v = 1.0d; }
double s = hsv.s;
if (s < 0.0d) { s = 0.0d; }
if (s > 1.0d) { s = 1.0d; }
double hRad = hsv.h * 6.283185307179586d;
double a_ = Math.Cos (hRad);
double b_ = Math.Sin (hRad);
var cusp = OkColor.FindCusp (a_, b_);
var stMax = OkColor.ToSt (cusp);
double sMax = stMax.S;
double tMax = stMax.T;
double s0 = 0.5d;
double k = 1.0d;
if (sMax != 0.0d) { k = 1.0d - s0 / sMax; }
// first we compute L and V as if the gamut is a perfect triangle:
// L, C when v==1:
double vDenom = s0 + tMax - tMax * k * s;
double lv = 1.0d;
double cv = 0.0d;
if (vDenom != 0.0d)
{
lv = 1.0d - s * s0 / vDenom;
cv = s * tMax * s0 / vDenom;
}
double L = v * lv;
double C = v * cv;
// then we compensate for both toe and the curved top part of the triangle:
double lvt = OkColor.ToeInv (lv);
double cvt = 0.0d;
if (lv != 0.0d) { cvt = cv * lvt / lv; }
double lNew = OkColor.ToeInv (L);
if (L != 0.0d) { C = C * lNew / L; }
L = lNew;
var rgbScale = OkColor.OkLabToLinearSrgb ((L: lvt, a: a_ * cvt, b: b_ * cvt));
double maxComp = Math.Max (rgbScale.r, Math.Max (rgbScale.g, Math.Max (rgbScale.b, 0.0d)));
double lScale = 0.0d;
if (maxComp != 0.0d)
{
lScale = Math.Pow (1.0d / maxComp, 1.0d / 3.0d);
}
C = C * lScale;
return (
L: L * lScale,
a: C * a_,
b: C * b_);
}
public static (double r, double g, double b) OkHsvToSrgb (in (double h, double s, double v) hsv)
{
return OkColor.OkLabToSrgb (OkColor.OkHsvToOkLab (hsv));
}
static (double r, double g, double b) OkLabToLinearSrgb (in (double L, double a, double b) c)
{
double cl = c.L;
double ca = c.a;
double cb = c.b;
double lCbrt = cl + 0.3963377774d * ca + 0.2158037573d * cb;
double mCbrt = cl - 0.1055613458d * ca - 0.0638541728d * cb;
double sCbrt = cl - 0.0894841775d * ca - 1.291485548d * cb;
double l = lCbrt * lCbrt * lCbrt;
double m = mCbrt * mCbrt * mCbrt;
double s = sCbrt * sCbrt * sCbrt;
return (
r: 4.0767416621d * l - 3.3077115913d * m + 0.2309699292d * s,
g: -1.2684380046d * l + 2.6097574011d * m - 0.3413193965d * s,
b: -0.0041960863d * l - 0.7034186147d * m + 1.707614701d * s);
}
public static (double h, double s, double l) OkLabToOkHsl (in (double L, double a, double b) lab)
{
double L = lab.L;
if (L > 1.0d - Single.Epsilon) { return (h: 0.0d, s: 0.0d, l: 1.0d); }
if (L < Single.Epsilon) { return (h: 0.0d, s: 0.0d, l: 0.0d); }
// This has to be gt epsilon, not gt zero to avoid glitches.
double Csq = lab.a * lab.a + lab.b * lab.b;
if (Csq > Single.Epsilon)
{
double C = Math.Sqrt (Csq);
double a_ = lab.a / C;
double b_ = lab.b / C;
// 1.0 / math.pi = 0.3183098861837907
double h = 0.5d + 0.5d * (Math.Atan2 (-lab.b, -lab.a) * 0.3183098861837907d);
var cs = OkColor.GetCs (L, a_, b_);
double c0 = cs.C_0;
double cMid = cs.C_mid;
double cMax = cs.C_max;
// Inverse of the interpolation in okhsl_to_srgb:
double mid = 0.8d;
double midInv = 1.25d;
double s = 0.0d;
if (C < cMid)
{
double k1 = mid * c0;
double k2 = 1.0d;
if (cMid != 0.0d) { k2 = (1.0d - k1 / cMid); }
double tDenom = k1 + k2 * C;
double t = 0.0d;
if (tDenom != 0.0d) { t = C / tDenom; }
s = t * mid;
}
else
{
double k0 = cMid;
double k1 = 0.0d;
if (c0 != 0.0d)
{
k1 = (1.0d - mid) * cMid * cMid * midInv * midInv / c0;
}
double cDenom = cMax - cMid;
double k2 = 1.0d;
if (cDenom != 0.0d) { k2 = 1.0d - k1 / cDenom; }
double tDenom = k1 + k2 * (C - k0);
double t = 0.0d;
if (tDenom != 0.0d) { t = (C - k0) / tDenom; }
s = mid + (1.0d - mid) * t;
}
return (h: h, s: s, l: OkColor.Toe (L));
}
else
{
return (h: 0.0d, s: 0.0d, l: L);
}
}
public static (double h, double s, double v) OkLabToOkHsv (in (double L, double a, double b) lab)
{
double L = lab.L;
if (L > 1.0d - Single.Epsilon) { return (h: 0.0d, s: 0.0d, v: 1.0d); }
if (L < Single.Epsilon) { return (h: 0.0d, s: 0.0d, v: 0.0d); }
// This has to be gt epsilon, not gt zero to avoid glitches.
double csq = lab.a * lab.a + lab.b * lab.b;
if (csq > Single.Epsilon)
{
double C = Math.Sqrt (csq);
double a_ = lab.a / C;
double b_ = lab.b / C;
// 1.0 / math.pi = 0.3183098861837907
double h = 0.5d + 0.5d * (Math.Atan2 (-lab.b, -lab.a) * 0.3183098861837907d);
var cusp = OkColor.FindCusp (a_, b_);
var stMax = OkColor.ToSt (cusp);
double sMax = stMax.S;
double tMax = stMax.T;
double s0 = 0.5d;
double k = 1.0d;
if (sMax != 0.0d) { k = 1.0d - s0 / sMax; }
// first we find L_v, C_v, L_vt and C_vt
double tDenom = C + L * tMax;
double t = 0.0d;
if (tDenom != 0.0d) { t = tMax / tDenom; }
double lv = t * L;
double cv = t * C;
double lvt = OkColor.ToeInv (lv);
double cvt = 0.0d;
if (lv != 0.0d) { cvt = cv * lvt / lv; }
// we can then use these to invert the step that compensates for the toe and the curved top part of the triangle:
var rgbScale = OkColor.OkLabToLinearSrgb (
(L: lvt,
a: a_ * cvt,
b: b_ * cvt));
double scaleDenom = Math.Max (rgbScale.r, Math.Max (rgbScale.g, Math.Max (rgbScale.b, 0.0d)));
double lScale = 0.0d;
if (scaleDenom != 0.0d)
{
lScale = Math.Pow (1.0d / scaleDenom, 1.0d / 3.0d);
L = L / lScale;
C = C / lScale;
}
double toel = OkColor.Toe (L);
C = C * toel / L;
L = toel;
// we can now compute v and s:
double v = 0.0d;
if (lv != 0.0d) { v = L / lv; }
double s = 0.0d;
double sDenom = ((tMax * s0) + tMax * k * cv);
if (sDenom != 0.0d) { s = (s0 + tMax) * cv / sDenom; }
return (h: h, s: s, v: v);
}
else
{
return (h: 0.0d, s: 0.0d, v: L);
}
}
public static (double r, double g, double b) OkLabToSrgb (in (double L, double a, double b) lab)
{
var lrgb = OkColor.OkLabToLinearSrgb (lab);
return (
r: OkColor.SrgbTransferFunction (lrgb.r),
g: OkColor.SrgbTransferFunction (lrgb.g),
b: OkColor.SrgbTransferFunction (lrgb.b));
}
public static (double h, double s, double l) SrgbToOkHsl (in (double r, double g, double b) srgb)
{
return OkColor.OkLabToOkHsl (OkColor.SrgbToOkLab (srgb));
}
public static (double h, double s, double v) SrgbToOkHsv (in (double r, double g, double b) srgb)
{
return OkColor.OkLabToOkHsv (OkColor.SrgbToOkLab (srgb));
}
public static (double L, double a, double b) SrgbToOkLab (in (double r, double g, double b) srgb)
{
return OkColor.LinearSrgbToOkLab ((
r: OkColor.SrgbTransferFunctionInv (srgb.r),
g: OkColor.SrgbTransferFunctionInv (srgb.g),
b: OkColor.SrgbTransferFunctionInv (srgb.b)));
}
static double SrgbTransferFunction (in double a)
{
return 0.0031308d >= a ? 12.92d * a : 1.055d * Math.Pow (a, 1.0d / 2.4d) - 0.055d;
}
static double SrgbTransferFunctionInv (in double a)
{
return 0.04045d < a ? Math.Pow ((a + 0.055d) / 1.055d, 2.4d) : a / 12.92d;
}
static (double S, double T) ToSt (in (double L, double C) cusp)
{
double L = cusp.L;
double C = cusp.C;
if (L != 0.0d && L != 1.0d)
{
return (S: C / L, T: C / (1.0d - L));
}
else if (L != 0.0d)
{
return (S: C / L, T: 0.0d);
}
else if (L != 1.0d)
{
return (S: 0.0d, T: C / (1.0d - L));
}
else
{
return (S: 0.0d, T: 0.0d);
}
}
static double Toe (in double x)
{
double y = 1.170873786407767d * x - 0.206d;
return 0.5d * (y + Math.Sqrt (y * y + 0.14050485436893204d * x));
}
static double ToeInv (in double x)
{
double denom = 1.170873786407767d * (x + 0.03d);
return (denom != 0.0) ? (x * x + 0.206d * x) / denom : 0.0d;
}
public static Vector3 ToOkHsv(this Color c)
{
(double, double, double) col = (c.r, c.g, c.b);
(double, double, double) hsv = SrgbToOkHsv(col);
return new Vector3((float)hsv.Item1, (float)hsv.Item2, (float)hsv.Item3);
}
public static Color OkHsvToColor(Vector3 hsv)
{
(double, double, double) col = (hsv.x, hsv.y, hsv.z);
(double, double, double) rgb = OkHsvToSrgb(col);
return new Color((float)rgb.Item1, (float)rgb.Item2, (float)rgb.Item3);
}
}
using System;
namespace Andicraft.SecondOrderDynamics;
public class Vector4Dynamics : SecondOrderDynamics<Vector4>
{
/// <inheritdoc/>
public Vector4Dynamics(Vector4 startingValue) : base(startingValue)
{
}
/// <inheritdoc/>
public Vector4Dynamics(Vector3 parameters, Vector4 startingValue) : base(parameters, startingValue)
{
}
/// <inheritdoc/>
public Vector4Dynamics(float frequency, float damping, float response, Vector4 startingValue) : base(frequency, damping, response, startingValue)
{
}
/// <inheritdoc/>
public override void Reset(Vector4 value)
{
_previousValue = value;
_currentValue = value;
_velocity = Vector4.Zero;
}
/// <inheritdoc/>
public override Vector4 Update(float deltaTime, Vector4 target, bool setVelocity = false, Vector4 velocity = default)
{
if (setVelocity == false)
{
velocity = (target - _previousValue) / deltaTime;
_previousValue = target;
}
var k2Stable = MathF.Max(k2, MathF.Max(deltaTime * deltaTime / 2 + deltaTime * k1 / 2, deltaTime * k1));
_currentValue += deltaTime * _velocity;
_velocity += deltaTime * (target + k3 * velocity - _currentValue - k1 * _velocity) / k2Stable;
return _currentValue;
}
}
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
namespace RbxlReader.DataTypes;
public class NumberSequenceKeypoint {
public float Time;
public float Value;
public float Envelope;
}
public class NumberSequence {
public NumberSequenceKeypoint[] Keypoints;
public NumberSequence(NumberSequenceKeypoint[] points) {
Keypoints = points;
}
}namespace RbxlReader.DataTypes;
public class UDim {
public float Scale;
public int Offset;
public UDim() {}
public UDim(float scale, int offset) {
Scale = scale;
Offset = offset;
}
}using System.Collections.Generic;
using Sandbox;
[Group("Noblox")]
public class RbxlRoot : Component {
public InstanceComponent[] IdToInstance {get; set;}
[Property, ReadOnly]
public int InstanceCount {get; set;}
[Property, ReadOnly]
public int ClassCount {get; set;}
[Property, ReadOnly]
public int ImportedObjectCount {get; set;}
private List<GameObject> debris = new();
/// <summary>
/// add to cleanup list, so it will be deleted. Do not use by itself
/// </summary>
public void AddToCleanup(GameObject obj) => debris.Add(obj);
/// <summary>
/// Cleans up a debris list.
/// </summary>
/// <returns>Instances removed total</returns>
public int Cleanup() {
int i = 0;
foreach(var obj in debris) {
obj.Destroy();
i++;
}
debris = new List<GameObject>();
return i;
}
}using Sandbox;
[Group("Noblox Instances")]
public class RopeComponent : ConstraintComponent {
[Property, ReadOnly]
public float Length {get; set;}
public override void ConstraintSetup(GameObject part0, GameObject part1) {
if (part0.GetComponent<InstanceComponent>().ClassName == "Attachment") {
part0 = part0.Parent;
}
if (part1.GetComponent<InstanceComponent>().ClassName == "Attachment") {
part1 = part1.Parent;
}
var joint = part0.Components.GetOrCreate<SpringJoint>();
joint.Frequency = 10;
joint.Body = part1;
}
}using System.Threading.Tasks;
using Sandbox;
using System.IO;
using Sandbox.Utility;
public sealed class LocalFile : Component
{
public void Save( string FileName, Curve HeightCurve, int Noise1Type, int Noise1Seed, Curve Noise1Weight, float Noise1Frequency, int Noise2Type, int Noise2Seed, Curve Noise2Weight, float Noise2Frequency, int Noise3Type, int Noise3Seed, Curve Noise3Weight, float Noise3Frequency, int FalloffValue, int Resolution, Vector2 TerrainOffset, Vector2 FalloffCenter, int SampleRes, int Noise1Octave, int Noise2Octave, int Noise3Octave, float Noise1gain, float Noise2gain, float Noise3gain, float Noise1Lac, float Noise2Lac, float Noise3Lac)
{
//Makes filename string
string FloatsName = FileName + " Floats" + ".json";
string CurvesName = FileName + " Curves" + ".json";
//Writes data to json
FileSystem.Data.WriteJson( FloatsName, new float[25] { Noise1Type, Noise1Seed, Noise1Frequency, Noise2Type, Noise2Seed, Noise2Frequency, Noise3Type, Noise3Seed, Noise3Frequency, FalloffValue, Resolution, TerrainOffset.x, TerrainOffset.y, FalloffCenter.x, FalloffCenter.y, SampleRes, Noise1Octave, Noise2Octave, Noise3Octave, Noise1gain, Noise2gain, Noise3gain, Noise1Lac, Noise2Lac, Noise3Lac } );
FileSystem.Data.WriteJson( CurvesName, new Curve[4] { HeightCurve, Noise1Weight, Noise2Weight, Noise3Weight } );
Log.Info( "Saved " + FileName.ToString() + " as " + FloatsName.ToString() + " and " + CurvesName.ToString());
Log.Info( "Saved at: " + FileSystem.Data.GetFullPath( FloatsName ).ToString() );
}
public void Load( string FileName, out float[] LoadedFloatValues, out Curve[] LoadedCurveValues )
{
//Makes filename string
string FloatsName = FileName + " Floats" + ".json";
string CurvesName = FileName + " Curves" + ".json";
LoadedCurveValues = new Curve[4];
LoadedFloatValues = new float[25];
if ( FileSystem.Data.ReadJson<float[]>( FloatsName ) != null && FileSystem.Data.ReadJson<Curve[]>( CurvesName ) != null ) //Checks for file
{
//Loads File
LoadedFloatValues = FileSystem.Data.ReadJson<float[]>( FloatsName );
LoadedCurveValues = FileSystem.Data.ReadJson<Curve[]>( CurvesName );
}
else
{
Log.Error( "FILE NOT FOUND" );
}
}
}
using Sandbox;
using System.Numerics;
public sealed class SpawnTrigger : Component, Component.ITriggerListener
{
public async void OnTriggerEnter( Collider other )
{
var player = other.Components.Get<GudeMovement>();
if ( player != null )
{
var gravity = player.Gravity;
var directionalInfluence = player.DirectionalInfluence;
var spawn = player.Spawned;
Log.Info( "Get Ready!!!" );
if ( spawn == true )
{
player.Rigidbody.Velocity = Vector3.Zero;
player.Gravity = 0;
player.DirectionalInfluence = 0;
await Task.DelaySeconds( 2 );
player.DirectionalInfluence = directionalInfluence;
player.Gravity = gravity;
}
}
}
public void OnTriggerExit( Collider other )
{
var player = other.Components.Get<GudeMovement>();
if ( player != null )
{
var spawn = player.Spawned;
Log.Info( "Left Trigger" );
if ( spawn == true )
{
player.Spawned = false;
}
}
}
}
using System;
using System.Threading.Tasks;
using Editor;
using Sandbox;
namespace YAAI;
public sealed class YaaiWindow : Window
{
public Task RunningTask;
private bool IsRunning = false;
public readonly int MinimumSpeed = 10;
public readonly int MaximumSpeed = 30;
private Random random = new Random();
private float RandomSpeed => random.Float( MinimumSpeed, MaximumSpeed );
public YaaiWindow()
{
WindowTitle = "YAAI";
Width = 250;
Height = 200;
HasMaximizeButton = false;
DeleteOnClose = true;
WindowFlags = WindowFlags.WithFlag( WindowFlags.WindowStaysOnTopHint, true );
WindowFlags = WindowFlags.WithFlag( WindowFlags.MinimizeButton, false);
new YaaiWidget(this );
Show();
RunningTask = Move();
}
protected override bool OnClose()
{
// Dispose of Task
IsRunning = false;
RunningTask.Wait();
RunningTask.Dispose();
YaaiManager.OnWindowClose( this );
return base.OnClose();
}
async Task Move()
{
IsRunning = true;
bool moveDown = random.Int( 0,1 ) == 1;
bool moveRight = random.Int( 0,1 ) == 1;
float Speed = RandomSpeed;
while ( IsWindow && IsRunning )
{
float nx = moveRight ? Position.x + Speed/2 : Position.x - Speed/2;
float ny = moveDown ? Position.y + Speed : Position.y - Speed;
bool shouldChangeSpeed = false;
if ( Position.x + Width > ScreenGeometry.Width )
{
shouldChangeSpeed = true;
moveRight = false;
}
else if ( Position.x < 0)
{
shouldChangeSpeed = true;
moveRight = true;
}
if ( Position.y + Height > ScreenGeometry.Height )
{
shouldChangeSpeed = true;
moveDown = false;
}
else if ( Position.y < 0)
{
shouldChangeSpeed = true;
moveDown = true;
}
if ( shouldChangeSpeed )
Speed = RandomSpeed;
Position = Position.WithX( nx ).WithY( ny );
await Task.Delay( 25 );
}
}
}
global using static Sandbox.Internal.GlobalGameNamespace;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Editor Themes" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "editorthemes" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "katewoz" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "katewoz.editorthemes" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "8/4/2025 1:34:00 AM" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "20" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]
[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.118.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.118.0")]using Sandbox;
using System;
public class EditorTheme
{
[Group( "Window" )] public Color TabBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color TabBarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color TabInactiveBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color SurfaceBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color SurfaceLightBackground { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color SidebarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color WindowBackground { get; set; } = new Color( 0.094f, 0.094f, 0.094f );
[Group( "Window" )] public Color WidgetBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color ControlBackground { get; set; } = new Color( 0.094f, 0.094f, 0.094f );
[Group( "Window" )] public Color ButtonBackground { get; set; } = new Color( 0.231f, 0.231f, 0.231f );
[Group( "Window" )] public Color SelectedBackground { get; set; } = new Color( 0.502f, 0.502f, 0.502f );
[Group( "Window" )] public Color StatusBarBackground { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Text" )] public Color Text { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextControl { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextLight { get; set; } = new Color( 0.62f, 0.62f, 0.62f );
[Group( "Text" )] public Color TextWidget { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextButton { get; set; } = new Color( 1.0f, 1.0f, 1.0f );
[Group( "Text" )] public Color TextSelected { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextLink { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextHighlight { get; set; } = new Color( 0.4f, 0.639f, 1.0f );
[Group( "Text" )] public Color TextDisabled { get; set; } = new Color( 1.0f, 1.0f, 1.0f, 0.47f );
[Group( "Text" )] public Color TextDark { get; set; } = new Color( 0.0f, 0.0f, 0.0f );
[Group( "Window" )] public Color Border { get; set; } = new Color( 0.322f, 0.322f, 0.322f );
[Group( "Window" )] public Color BorderLight { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color BorderButton { get; set; } = new Color( 0.412f, 0.412f, 0.412f );
[Group( "Window" )] public Color Shadow { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color Primary { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Window" )] public Color Overlay { get; set; } = new Color( 0.141f, 0.141f, 0.141f );
[Group( "Window" )] public Color MultipleValues { get; set; } = new Color( 0.502f, 0.502f, 0.502f );
[Group( "Window" )] public Color Highlight { get; set; } = new Color( 0.298f, 0.478f, 0.749f );
[Group( "Checkbox" )] public Color ToggleEnabled { get; set; } = new Color( 0.353f, 0.922f, 0.361f );
[Group( "Checkbox" )] public Color ToggleDisabled { get; set; } = new Color( 0.337f, 0.431f, 0.337f );
[Group( "Window" )] public Color Base { get; set; } = new Color(0.125f, 0.125f, 0.125f);
[Group( "Window" )] public Color BaseAlt { get; set; } = new Color(0.164f,0.164f,0.164f);
[Group( "Colors?" )] public Color Blue { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Colors?" )] public Color Green { get; set; } = new Color( 0.690f, 0.89f, 0.302f );
[Group( "Colors?" )] public Color Red { get; set; } = new Color( 0.984f, 0.353f, 0.353f );
[Group( "Colors?" )] public Color Yellow { get; set; } = new Color( 0.902f, 0.859f, 0.455f );
[Group( "Colors?" )] public Color Pink { get; set; } = new Color( 0.874f, 0.569f, 0.580f );
[Group( "Files" )] public Color Prefab { get; set; } = new Color( 0.353f, 0.553f, 0.922f );
[Group( "Files" )] public Color Folder { get; set; } = new Color( 0.902f, 0.859f, 0.455f );
[Group( "Layout" )] public int RowHeight { get; set; } = 22;
[Group( "Layout" )] public int ControlHeight { get; set; } = 22;
[Group( "Layout" )] public int ControlRadius { get; set; } = 4;
[FontName][Group( "Text" )] public string HeadingFont { get; set; } = "Inter";
[FontName][Group( "Text" )] public string DefaultFont { get; set; } = "Inter";
public EditorTheme()
{ }
public static EditorTheme ShallowCopy( EditorTheme source )
{
if ( source == null )
throw new ArgumentNullException( nameof( source ) );
EditorTheme copy = new EditorTheme();
var properties = typeof( EditorTheme ).GetProperties();
foreach ( var prop in properties )
{
if ( prop.CanRead && prop.CanWrite )
{
var value = prop.GetValue( source, null );
prop.SetValue( copy, value, null );
}
}
return copy;
}
}
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConchplexEmitters;
public sealed class ConchplexDonutEmitter : ConchplexEmitter
{
/// <summary>
/// 1 = spawns on edge, 0 = spawns in centre
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(0f, 1f), Step(0.01f)]
public float DistanceBias { get; set; } = 0.5f;
/// <summary>
/// Direction + intensity to bias particles to spawn towards
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 PositionBias { get; set; } = 0f;
/// <summary>
/// Bias partcles inwards or outwards on a each axis
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 AxisBias { get; set; } = 0f;
/// <summary>
/// Particles only spawn on the axes marked
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
public AxisClampFlags AxisClamp { get; set; }
[Property]
public float MainRadius { get; set; } = 20f;
[Property]
public float RingRadius { get; set; } = 7.5f;
[Property]
[Range(0f, 1f), Step(0.01f)]
public float Thickness { get; set; } = 0f;
[Property, Group("Initial Velocity")]
public ParticleFloat VelocityFromDonut { get; set; } = 0f;
protected override void DrawGizmos()
{
if (!Gizmo.IsSelected) return;
Gizmo.Draw.Color = Color.White.WithAlpha(0.5f);
Gizmo.GizmoDraw draw = Gizmo.Draw;
// draw shape
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius + RingRadius, 16);
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius - RingRadius, 16);
draw.LineCircle(Vector3.Zero.WithZ(RingRadius), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithZ(-RingRadius), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithX(MainRadius), Vector3.Left, RingRadius, 8);
draw.LineCircle(Vector3.Zero.WithX(-MainRadius), Vector3.Left, RingRadius, 8);
draw.LineCircle(Vector3.Zero.WithY(MainRadius), Vector3.Forward, RingRadius, 8);
draw.LineCircle(Vector3.Zero.WithY(-MainRadius), Vector3.Forward, RingRadius, 8);
// draw thickness
if (Thickness > 0f && Thickness < 1f)
{
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius + (RingRadius * Thickness), 16);
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius - (RingRadius * Thickness), 16);
draw.LineCircle(Vector3.Zero.WithZ(RingRadius), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithZ(-RingRadius * Thickness), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithX(MainRadius), Vector3.Left, RingRadius * Thickness, 8);
draw.LineCircle(Vector3.Zero.WithX(-MainRadius), Vector3.Left, RingRadius * Thickness, 8);
draw.LineCircle(Vector3.Zero.WithY(MainRadius), Vector3.Forward, RingRadius * Thickness, 8);
draw.LineCircle(Vector3.Zero.WithY(-MainRadius), Vector3.Forward, RingRadius * Thickness, 8);
}
// draw Distance Bias
if (DistanceBias > 0f && DistanceBias < 1f)
{
Gizmo.Draw.Color = Color.Red.WithAlpha(0.4f);
var biasRadius = MathX.Lerp(RingRadius * Thickness, RingRadius, DistanceBias);
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius + biasRadius, 16);
draw.LineCircle(Vector3.Zero, Vector3.Up, MainRadius - biasRadius, 16);
draw.LineCircle(Vector3.Zero.WithZ(biasRadius), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithZ(-biasRadius), Vector3.Up, MainRadius, 16);
draw.LineCircle(Vector3.Zero.WithX(MainRadius), Vector3.Left, biasRadius, 8);
draw.LineCircle(Vector3.Zero.WithX(-MainRadius), Vector3.Left, biasRadius, 8);
draw.LineCircle(Vector3.Zero.WithY(MainRadius), Vector3.Forward, biasRadius, 8);
draw.LineCircle(Vector3.Zero.WithY(-MainRadius), Vector3.Forward, biasRadius, 8);
}
if (PositionBias.IsNearlyZero()) return;
Gizmo.Draw.Color = Color.Red.WithAlpha(0.8f);
Vector3 biasClamped = Vector3.Clamp(PositionBias, new Vector3(-1f), new Vector3(1f)) * (MainRadius + RingRadius);
draw.Arrow(Vector3.Zero, biasClamped, biasClamped.Length * 0.1f, biasClamped.Length * 0.1f);
}
public override Particle Emit(ParticleEffect target)
{
// radius point.
var mainRadius = Random.Shared.VectorInCircle();
// radius pos bias
mainRadius += new Vector2(PositionBias.x, PositionBias.y);
mainRadius = mainRadius.Normal;
// Random point around the edge of a unit circle
Vector3 random = new Vector3(mainRadius.x, mainRadius.y, 0f);
random *= MainRadius;
// get random point on the ring
var randomRing = Random.Shared.VectorInCircle();
var biasedRing = randomRing;
biasedRing.y += PositionBias.z;
var ringPosBias = new Vector2(PositionBias.x, PositionBias.y).Length;
biasedRing.x += ringPosBias;
var outterRing = biasedRing.Normal * RingRadius;
var innerRing = outterRing * Thickness;
var betweenRandomRing = Vector2.Lerp(innerRing, outterRing, randomRing.Length);
var ringAdjustment = Vector2.Zero;
// apply distance bias
if (DistanceBias <= 0.5f)
{
float scaledBias = DistanceBias / 0.5f;
ringAdjustment = Vector2.Lerp(innerRing, betweenRandomRing, scaledBias);
}
else
{
float scaledBias = (DistanceBias - 0.5f) / 0.5f;
ringAdjustment = Vector2.Lerp(betweenRandomRing, outterRing, scaledBias);
}
var randomXY = new Vector2(random.x, random.y);
random.x += randomXY.Normal.x * ringAdjustment.x;
random.y += randomXY.Normal.y * ringAdjustment.x;
random.z += ringAdjustment.y;
// AXIS BIAS
random = ApplyAxisBias(random, AxisBias);
// TODO - we should clamp position and velocity before calculating world position/rotation position/velocity
var donutVelocityDir = new Vector3(randomXY.Normal.x * ringAdjustment.x, randomXY.Normal.y * ringAdjustment.x, ringAdjustment.y).Normal;
var clampedParticlePoint = ProcessClampPosition(random, donutVelocityDir, AxisClamp);
var finalPos = (clampedParticlePoint.Position * LocalScale * LocalRotation) + WorldPosition;
var finalParticleVel = clampedParticlePoint.Velocity * LocalRotation;
Particle particle = target.Emit(finalPos, base.Delta);
particle.Velocity += finalParticleVel * VelocityFromDonut.Evaluate(Delta,Random.Shared.Float());
return particle;
}
}
using Sandbox;
using Sandbox.Internal;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using static Sandbox.PhysicsContact;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ConchplexEmitters;
/// <summary>
/// Defines the shape of an emitter, optionally you can emit dirrectly from this shape
/// </summary>
public abstract class ConchplexEmitter : Sandbox.Component, Sandbox.Component.ExecuteInEditor, Sandbox.Component.ITemporaryEffect
{
public float time;
public int emitted;
private int loops;
private bool suspended;
[Property, Group("Initial Velocity"), Order(0)]
public ParticleVector3 InitialVelocity { get; set; } = Vector3.Zero;
[Property, Group("Initial Velocity"), Order(0)]
public float VelocityFromCentre { get; set; } = 0f;
// If we are emitting what effect should we target
//[Property, Feature("Emitter")]
public ParticleEffect target;
[Property, Feature("Emitter"), Order(2)]
public bool Loop { get; set; } = false;
/// <summary>
/// How many times to loop - 0 = Infinite
/// </summary>
[Property, Feature("Emitter"), Order(2), ShowIf(nameof(Loop), true)]
public int LoopAmmount { get; set; } = 0;
/// <summary>
/// How long should the emitter go for
/// </summary>
[Property, Feature("Emitter"), Order(2)]
public float Duration { get; set; } = 10f;
[Property, Feature("Emitter"), Order(2)]
public float Delay { get; set; }
[Property, Feature("Emitter"), Order(2)]
public bool DestroyOnEnd { get; set; }
// EMISSIONS
[Property, Feature("Emitter"), Category("Emissions"), Order(2)]
public ParticleFloat RateOverTime { get; set; }
[Property, Feature("Emitter"), Category("Emissions"), Order(2)]
public int RateOverDistance { get; set; }
[Hide] private Vector3? lastPos;
[Hide] private float distanceTravelled;
[Property, Feature("Emitter"), Category("Emissions"), WideMode, Order(2)]
public List<ConchplexBurstEmission> Bursts { get; set; } = new List<ConchplexBurstEmission>();
public float Delta { get; private set; }
public float EmitRandom { get; private set; }
public bool IsStarted => time - Delay >= 0f;
private bool IsFinished => time > Duration + Delay;
bool ITemporaryEffect.IsActive
{
get
{
if (suspended)
{
return false;
}
if (Loop)
{
return true;
}
foreach (var burst in Bursts)
{
if (burst.IsActive()) return true;
}
if (!IsStarted)
{
return true;
}
if (IsFinished)
{
return false;
}
target.IsValid();
return false;
}
}
protected override void OnEnabled()
{
suspended = false;
ResetEmitter();
loops = 0;
// If there is no overrided target set, then try get target from the object
if ( target == null) target = base.Components.GetInAncestorsOrSelf<ParticleEffect>();
target = base.Components.GetInAncestorsOrSelf<ParticleEffect>();
if (target != null)
{
ParticleEffect particleEffect = target;
particleEffect.OnPreStep = (Action<float>)Delegate.Combine(particleEffect.OnPreStep, new Action<float>(OnParticleStep));
}
else
{
GlobalSystemNamespace.Log.Warning($"No particle effect found for {this}");
}
foreach (var burst in Bursts) burst.ResetEmission();
lastPos = null;
}
protected override void OnDisabled()
{
base.OnDisabled();
if (target != null)
{
ParticleEffect particleEffect = target;
particleEffect.OnPreStep = (Action<float>)Delegate.Remove(particleEffect.OnPreStep, new Action<float>(OnParticleStep));
}
target = null;
}
private void DestroyEmitter()
{
// Try destroy the effect, if its no longer needed
var allEmitters = GameObject.GetComponentsInChildren<ParticleEffect>(includeSelf: false);
bool stillHasChildEffects = allEmitters.Count() > 0;
// there are still child effects in progress / this effect is still running, so return
if (stillHasChildEffects || target.Particles.Count != 0) return;
bool isSoloEmitter = GameObject.Components.GetAll<ConchplexEmitter>().Count() == 1;
if (isSoloEmitter) GameObject.Destroy();
else Destroy();
}
public void ResetEmitter()
{
loops++;
emitted = 0;
time = 0f;
EmitRandom = Random.Shared.Float(0f, 1f);
foreach (var burst in Bursts) burst.ResetEmission();
}
private void OnParticleStep(float delta)
{
if (!target.IsValid() || !target.Active || suspended )
{
return;
}
// Set current time we have been emitting since we started
time += delta;
// Get time minus dely, so its only positive after the delay is over
float postDelayTime = time - Delay;
Delta = 0f;
if (!IsStarted)
{
return;
}
// Emitter has reached final duration
if (IsFinished)
{
if (Loop)
{
if (LoopAmmount == 0 || loops <= LoopAmmount)
ResetEmitter();
else
return;
}
else
{
if (base.Scene.IsEditor && !base.GameObject.Flags.HasFlag(GameObjectFlags.NotSaved))
{
ResetEmitter();
}
if (DestroyOnEnd && !base.Scene.IsEditor)
{
DestroyEmitter();
}
}
return;
}
Delta = time.Remap(Delay, Duration + Delay);
while (!target.IsFull && emitted < GetRateCount() * (postDelayTime))
{
emitted += 1;
EmitParticle(target);
}
if (RateOverDistance > 0) DistanceEmit(target, RateOverDistance);
foreach (var burst in Bursts)
{
burst.TryEmit(target ,this, postDelayTime);
}
}
void ITemporaryEffect.DisableLooping()
{
suspended = true;
}
protected virtual int GetRateCount()
{
var rate = RateOverTime;
float delta = Delta;
float randomFixed = 1f;
return (int)rate.Evaluate(delta, in randomFixed);
}
public abstract Particle Emit(ParticleEffect target);
public void DistanceEmit(ParticleEffect target, int distance)
{
// Set last world position
Vector3 worldPosition = WorldPosition;
if (!lastPos.HasValue)
{
lastPos = worldPosition;
return;
}
float length = (lastPos.Value - worldPosition).Length;
float num = 100f / distance;
lastPos = worldPosition;
distanceTravelled += length;
while (distanceTravelled > num)
{
distanceTravelled -= num;
if (!target.IsFull)
{
EmitParticle(target);
}
}
}
public void EmitParticle(ParticleEffect target)
{
var particle = Emit(target);
var randomSeed = Random.Shared.Float(0f, 1f);
particle.Velocity += (InitialVelocity.Evaluate(Delta, randomSeed, randomSeed, randomSeed) * WorldRotation);
var particlePos = particle.Position - WorldPosition;
particle.Velocity += particlePos.Normal * VelocityFromCentre;
}
public Vector3 ApplyAxisBias(Vector3 random, Vector3 axisBias)
{
Vector3 result = random;
// Helper to apply bias to a Vector2 slice
Vector2 ApplyBias(Vector2 vec2, float bias)
{
if (bias == 0f) return vec2; // no change
Vector2 min = Vector2.Zero;
Vector2 max = vec2.Normal;
return bias < 0f
? Vector2.Lerp(vec2, min, -bias) // pull inward
: Vector2.Lerp(vec2, max, bias); // push outward
}
// X bias affects YZ
Vector2 yz = new Vector2(result.y, result.z);
yz = ApplyBias(yz, axisBias.x);
result.y = yz.x;
result.z = yz.y;
// Y bias affects XZ
Vector2 xz = new Vector2(result.x, result.z);
xz = ApplyBias(xz, axisBias.y);
result.x = xz.x;
result.z = xz.y;
// Z bias affects XY
Vector2 xy = new Vector2(result.x, result.y);
xy = ApplyBias(xy, axisBias.z);
result.x = xy.x;
result.y = xy.y;
return result;
}
/// <summary>
/// Which axis to clamp to / in between
/// </summary>
[Flags]
public enum AxisClampFlags
{
None = 0,
XPositive = 1 << 0,
XNegative = 1 << 1,
YPositive = 1 << 2,
YNegative = 1 << 3,
ZPositive = 1 << 4,
ZNegative = 1 << 5
}
public Vector3 ProcessClampPosition(Vector3 randomPosition, AxisClampFlags flags)
{
Vector3 resultPos = randomPosition;
// Process X axis
resultPos.x = ProcessAxis(
randomPosition.x,
(flags & AxisClampFlags.XPositive) != 0,
(flags & AxisClampFlags.XNegative) != 0);
// Process Y axis
resultPos.y = ProcessAxis(
randomPosition.y,
(flags & AxisClampFlags.YPositive) != 0,
(flags & AxisClampFlags.YNegative) != 0);
// Process Z axis
resultPos.z = ProcessAxis(
randomPosition.z,
(flags & AxisClampFlags.ZPositive) != 0,
(flags & AxisClampFlags.ZNegative) != 0);
return resultPos;
}
public (Vector3 Position, Vector3 Velocity) ProcessClampPosition(Vector3 randomPosition, Vector3 velocity, AxisClampFlags flags)
{
Vector3 resultPos = randomPosition;
Vector3 resultVelocity = velocity;
// Process X axis
(resultPos.x, resultVelocity.x) = ProcessAxis(
resultPos.x, resultVelocity.x,
(flags & AxisClampFlags.XPositive) != 0,
(flags & AxisClampFlags.XNegative) != 0);
// Process Y axis
(resultPos.y, resultVelocity.y) = ProcessAxis(
resultPos.y, resultVelocity.y,
(flags & AxisClampFlags.YPositive) != 0,
(flags & AxisClampFlags.YNegative) != 0);
// Process Z axis
(resultPos.z, resultVelocity.z) = ProcessAxis(
resultPos.z, resultVelocity.z,
(flags & AxisClampFlags.ZPositive) != 0,
(flags & AxisClampFlags.ZNegative) != 0);
return (resultPos, resultVelocity);
}
private (float pos, float vel) ProcessAxis(float pos, float vel, bool positive, bool negative)
{
float originalPos = pos;
if (positive && negative)
{
pos = 0;
vel = 0;
}
else if (positive)
{
pos = MathF.Abs(pos); // Flip negative to positive
}
else if (negative)
{
pos = -MathF.Abs(pos); // Flip positive to negative
}
// If position was changed, flip velocity
if (pos != originalPos)
{
vel *= -1;
}
return (pos, vel);
}
private float ProcessAxis(float pos, bool positive, bool negative)
{
if (positive && negative)
{
pos = 0;
}
else if (positive)
{
pos = MathF.Abs(pos); // Flip negative to positive
}
else if (negative)
{
pos = -MathF.Abs(pos); // Flip positive to negative
}
return (pos);
}
}
using Sandbox;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConchplexEmitters;
/// <summary>
/// Emits particles within a ring
/// </summary>
[Title( "Conchplex Sphere Emitter" ), Category( "Particles" ), Icon( "panorama_photosphere" )]
public sealed class ConchplexSphereEmitter : ConchplexEmitter
{
/// <summary>
/// 1 = spawns on edge, 0 = spawns in centre
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(0f, 1f), Step(0.01f)]
public float DistanceBias { get; set; } = 0.5f;
/// <summary>
/// Direction + intensity to bias particles to spawn towards
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 PositionBias { get; set; } = 0f;
[Property, Group("Spawn Bias"), Order(0)]
[Range(-1, 1)]
public Vector3 AxisBias { get; set; } = 0f;
/// <summary>
/// Particles only spawn on the axes marked
/// </summary>
[Property, Group("Spawn Bias"), Order(0)]
public AxisClampFlags AxisClamp { get; set; }
[Property]
[Range( 0f, 100f), Step(0.01f)]
public float Radius { get; set; } = 25f;
[Property]
[Range(0f, 1f), Step(0.01f)]
public float Thickness { get; set; } = 0f;
protected override void DrawGizmos()
{
if (Gizmo.IsSelected)
{
Gizmo.Draw.Color = Color.White.WithAlpha(0.5f);
Gizmo.GizmoDraw draw = Gizmo.Draw;
// Draw max bounds
Vector3 point = Vector3.Zero;
float radius = Radius;
int rings = 8;
draw.LineSphere(in point, in radius, in rings);
// Draw min bounds
Gizmo.Draw.Color = Color.White.WithAlpha(0.6f);
float minRadius = Radius * Thickness;
draw.LineSphere(point, minRadius, rings);
// Draw Distance Bias
Gizmo.Draw.Color = Color.Red.WithAlpha(0.5f);
float biasRadius = MathX.Lerp(minRadius, radius, DistanceBias);
draw.LineSphere(point, biasRadius, rings);
// draw Position Bias
if (PositionBias.IsNearlyZero()) return;
Gizmo.Draw.Color = Color.Red.WithAlpha(0.8f);
Vector3 biasClamped = Vector3.Clamp(PositionBias, new Vector3(-1f), new Vector3(1f)) * radius * 1.1f;
draw.Arrow(point, biasClamped, biasClamped.Length*0.1f, biasClamped.Length * 0.1f);
}
}
public override Particle Emit(ParticleEffect target )
{
Vector3 random = Vector3.Random;
Vector3 radius = Radius;
Vector3 radiusMin = Radius * Thickness;
// POSITION BIAS
var posBias = PositionBias.ClampLength(1);
var randomFlipChance = Random.Shared.Float();
random = new Vector3(
MathF.Abs(posBias.x) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.x : random.x,
MathF.Abs(posBias.y) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.y : random.y,
MathF.Abs(posBias.z) > randomFlipChance & Vector3.Dot(random, posBias) < 0f ? -random.z : random.z
);
random = Vector3.Lerp( random, posBias + random, posBias.Length);
// AXIS BIAS
random = ApplyAxisBias(random, AxisBias);
// MIN / MAX / TRUE RANDOM
var targetRandomLenth = MathX.Lerp( Radius * Thickness, Radius, random.Length );
random = random.Normal * targetRandomLenth;
var maxEdgePos = random.Normal * radius;
var minEdgePos = random.Normal * radiusMin;
// DISTANCE BIAS
if ( DistanceBias <= 0.5f )
{
float scaledBias = DistanceBias / 0.5f;
random = Vector3.Lerp( minEdgePos, random, scaledBias );
}
else
{
float scaledBias = (DistanceBias - 0.5f) / 0.5f;
random = Vector3.Lerp( random, maxEdgePos, scaledBias );
}
// APPLY SCALE / POSITION / ROTATION
var finalPos = ProcessClampPosition( random, AxisClamp);
finalPos = (finalPos * LocalScale * LocalRotation) + WorldPosition;
return target.Emit(finalPos, base.Delta );
}
}
global using static Sandbox.Internal.GlobalGameNamespace;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "Timelines" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "timelines" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "stellawisps" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "stellawisps.timelines" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "8/15/2025 1:26:03 PM" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "20" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]
[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.132.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.132.0")]using Sandbox;
using System;
/// <summary>
/// Do I look like I know what a JAY-PEG is? (not fully working)
/// </summary>
[Title( "JPEG Compression" )]
[Category( "Post Processing" )]
[Icon( "gradient" )]
//ok im at my limit with this one, can't figure out how to get it to actually work right.
//still makes an interesting effect even when broken though.
public sealed class CCSJpeg : Component, Component.ExecuteInEditor
{
/// <summary>
/// JPEG Compression level. Lower = worse.
/// </summary>
[Header("Currently kinda broken")]
[Property, Title("Compression Level"), Range( 1, 99, 1, true )]
public int levels { get; set; } = 15;
/// <summary>
/// Changes the number of reconstruction steps, higher = more weird.
/// </summary>
[Property, Title("Reconstruction Frequency") , Range( 1, 20, 1, true )]
public int freq { get; set; } = 8;
/// <summary>
/// Display the quanitized DCT, which is some weird math thing that looks kinda cool.
/// </summary>
[Property, Title("Show DCT") , Range( 1, 20, 1, true )]
public bool bypass { get; set; }
IDisposable renderHook;
protected override void OnEnabled()
{
renderHook?.Dispose();
var cc = Components.Get<CameraComponent>( true );
renderHook = cc.AddHookBeforeOverlay( "CCSJpeg", 3001, RenderEffect );
}
protected override void OnDisabled()
{
renderHook?.Dispose();
renderHook = null;
}
RenderAttributes attributes = new RenderAttributes();
public void RenderEffect( SceneCamera camera )
{
if ( !camera.EnablePostProcessing )
return;
attributes.Set( "levels", levels );
attributes.Set( "freq", freq );
attributes.Set( "bypass", bypass );
Graphics.GrabFrameTexture( "ColorBuffer", attributes );
// Graphics.GrabDepthTexture( "DepthBuffer", attributes );
Graphics.Blit( Material.Load( "materials/postprocess/ccs_jpeg_dct.vmat" ), attributes );
Graphics.GrabFrameTexture( "DCTBuffer", attributes );
Graphics.Blit( Material.Load( "materials/postprocess/ccs_jpeg.vmat" ), attributes );
}
}
using Sandbox;
using System;
/// <summary>
/// Make everything look horrid (VERY FLASHY COLORS!)
/// </summary>
[Title( "Wacky Screen" )]
[Category( "Post Processing" )]
[Icon( "coronavirus" )]
public sealed class CCSJGlitch_bad : Component, Component.ExecuteInEditor
{
/// <summary>
/// Higher = more messed up.
/// </summary>
[Property, Title("Goofyness"), Range( 1, 99, 1, true )]
public int levels { get; set; } = 75;
/// <summary>
/// Sorta zooms in on the image.
/// </summary>
[Property, Title("Zoomyness") , Range( 1, 20, 1, true )]
public int freq { get; set; } = 3;
IDisposable renderHook;
protected override void OnEnabled()
{
renderHook?.Dispose();
var cc = Components.Get<CameraComponent>( true );
renderHook = cc.AddHookBeforeOverlay( "CCSJGlitch_bad", 5001, RenderEffect );
}
protected override void OnDisabled()
{
renderHook?.Dispose();
renderHook = null;
}
RenderAttributes attributes = new RenderAttributes();
public void RenderEffect( SceneCamera camera )
{
if ( !camera.EnablePostProcessing )
return;
attributes.Set( "levels", levels );
attributes.Set( "freq", freq );
Graphics.GrabFrameTexture( "ColorBuffer", attributes );
// Graphics.GrabDepthTexture( "DepthBuffer", attributes );
Graphics.Blit( Material.Load( "materials/postprocess/ccs_jpeg_bad.vmat" ), attributes );
}
}
global using System.Collections.Generic;
global using System.Linq;
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo( "moviemaker.editor" )]
[assembly: InternalsVisibleTo( "moviemaker.unittest" )]
using System;
using Editor.MovieMaker;
namespace Sandbox.MovieMaker.Test;
[TestClass]
public sealed class EditingTests
{
private static PropertySignal<float> CreateCrossFadeWithRange( MovieTimeRange fadeRange )
{
var from = 1f.AsSignal();
var to = 2f.AsSignal();
return from.CrossFade( to, fadeRange );
}
[TestMethod]
public void SimpleCrossFade()
{
var fade = CreateCrossFadeWithRange( (0d, 1d) );
Assert.AreEqual( 1f, fade.GetValue( 0 ) );
Assert.AreEqual( 1.5f, fade.GetValue( 0.5 ) );
Assert.AreEqual( 2f, fade.GetValue( 1 ) );
}
[TestMethod]
public void CrossFadeWithInnerRange()
{
var fade = CreateCrossFadeWithRange( (0.25d, 0.75d) );
Assert.AreEqual( 1f, fade.GetValue( 0 ) );
Assert.AreEqual( 1f, fade.GetValue( 0.25 ) );
Assert.AreEqual( 1.5f, fade.GetValue( 0.5 ) );
Assert.AreEqual( 2f, fade.GetValue( 0.75 ) );
Assert.AreEqual( 2f, fade.GetValue( 1 ) );
}
[TestMethod]
public void OverlappingCrossFade()
{
var fade1 = CreateCrossFadeWithRange( (0d, 0.75d) );
var fade2 = CreateCrossFadeWithRange( (0.25d, 1d) );
var fade = fade1.CrossFade( fade2, (0d, 1d) );
Console.WriteLine( fade.ToString() );
Assert.AreEqual( 1f, fade.GetValue( 0d ) );
Assert.AreEqual( 2f, fade.GetValue( 1d ) );
}
[TestMethod]
public void ReduceHardCut()
{
var signal = 1f.AsSignal()
.HardCut( 2f, 2d )
.HardCut( 1f, 1d )
.Reduce();
Assert.AreEqual( 1f, signal );
}
[TestMethod]
public void ReduceHardCut2()
{
var signal1 = 1f.AsSignal()
.HardCut( 2f, 2d );
var signal2 = 2f.AsSignal()
.HardCut( 1f, 2d );
var reduced1 = signal1
.HardCut( signal2, 2d )
.Reduce();
Console.WriteLine( reduced1 );
Assert.AreEqual( 1f, reduced1 );
var reduced2 = signal2
.HardCut( signal1, 2d )
.Reduce();
Console.WriteLine( reduced2 );
Assert.AreEqual( 2f, reduced2 );
}
[TestMethod]
public void ReduceCrossFade()
{
var signal = 1f.AsSignal()
.CrossFade( 2f, (3d, 4d) )
.CrossFade( 1f, (1d, 2d) )
.Reduce();
Assert.AreEqual( 1f, signal );
}
[TestMethod]
public void ReduceCrossFadeHardCut()
{
var signal1 = 1f.AsSignal();
var signal2 = 1f.AsSignal()
.CrossFade( 2f, (2d, 3d) )
.CrossFade( 1f, (4d, 5d) );
var reduced = signal1.HardCut( signal2, 1d )
.Reduce();
Console.WriteLine( reduced );
Assert.AreEqual( signal2, reduced );
}
[TestMethod]
public void ReduceShift()
{
var signal = (1f.AsSignal() + 1d)
.Reduce();
Assert.AreEqual( 1f, signal );
}
[TestMethod]
public void HardCut()
{
var signal = 1f.AsSignal().HardCut( 2f, 1d );
Console.WriteLine( signal.ToString() );
Assert.AreEqual( 1f, signal.GetValue( 0d ) );
Assert.AreEqual( 1f, signal.GetValue( 1d - MovieTime.Epsilon ) );
Assert.AreEqual( 2f, signal.GetValue( 1d ) );
}
[TestMethod]
public void Blend()
{
var signal = 1f.AsSignal().Blend( 2f, 0.5f );
Console.WriteLine( signal.ToString() );
Assert.AreEqual( 1.5f, signal.GetValue( default ) );
}
[TestMethod]
public void SlidingStretch()
{
var slideTransform = new SlidingStretchTransform( MovieTimeScale.FromCents( -1200 ), new MovieTimeRange( 1d, 2d ) );
Assert.AreEqual( MovieTransform.Identity, slideTransform.GetTransformAt( 0d ) );
Assert.AreEqual( MovieTransform.Identity, slideTransform.GetTransformAt( 1d ) );
Assert.AreEqual( new MovieTransform( 1d / 3d, MovieTimeScale.FromCents( -1200 ) ), slideTransform.GetTransformAt( 2d ) );
Assert.AreEqual( new MovieTransform( 1d / 3d, MovieTimeScale.FromCents( -1200 ) ), slideTransform.GetTransformAt( 3d ) );
}
[TestMethod]
[DataRow( 0d, 0d )]
[DataRow( 1d, 0d )]
[DataRow( 0d, 1d )]
[DataRow( 1d, 1d )]
public void SlidingStretchFromEnvelope( double fadeIn, double fadeOut )
{
var sourceDuration = MovieTime.FromSeconds( 1d );
var envelope = new TimeSelection( (2d, 4d),
new TimeSelection.Fade( fadeIn, InterpolationMode.Linear ),
new TimeSelection.Fade( fadeOut, InterpolationMode.Linear ) );
var (slideIn, slideOut) = SlidingStretchTransform.FromEnvelope( sourceDuration, envelope );
var finalTransform = slideOut.GetTransformAt( 1000d ) * slideIn.GetTransformAt( 1000d );
Assert.AreEqual( MovieTimeScale.Identity, finalTransform.Scale );
Assert.AreEqual( envelope.TotalTimeRange.Duration - sourceDuration, finalTransform.Translation );
}
}