diff --git a/src/main/java/ru/bclib/noise/OpenSimplexNoise.java b/src/main/java/ru/bclib/noise/OpenSimplexNoise.java new file mode 100644 index 00000000..d002334f --- /dev/null +++ b/src/main/java/ru/bclib/noise/OpenSimplexNoise.java @@ -0,0 +1,2185 @@ +package ru.bclib.noise; + +/* + * OpenSimplex Noise in Java. + * by Kurt Spencer + * + * v1.1 (October 5, 2014) + * - Added 2D and 4D implementations. + * - Proper gradient sets for all dimensions, from a + * dimensionally-generalizable scheme with an actual + * rhyme and reason behind it. + * - Removed default permutation array in favor of + * default seed. + * - Changed seed-based constructor to be independent + * of any particular randomization library, so results + * will be the same when ported to other languages. + */ + +public class OpenSimplexNoise { + private static final double STRETCH_CONSTANT_2D = -0.211324865405187; // (1/Math.sqrt(2+1)-1)/2; + private static final double SQUISH_CONSTANT_2D = 0.366025403784439; // (Math.sqrt(2+1)-1)/2; + private static final double STRETCH_CONSTANT_3D = -1.0 / 6; // (1/Math.sqrt(3+1)-1)/3; + private static final double SQUISH_CONSTANT_3D = 1.0 / 3; // (Math.sqrt(3+1)-1)/3; + private static final double STRETCH_CONSTANT_4D = -0.138196601125011; // (1/Math.sqrt(4+1)-1)/4; + private static final double SQUISH_CONSTANT_4D = 0.309016994374947; // (Math.sqrt(4+1)-1)/4; + + private static final double NORM_CONSTANT_2D = 47; + private static final double NORM_CONSTANT_3D = 103; + private static final double NORM_CONSTANT_4D = 30; + + private static final long DEFAULT_SEED = 0; + + private short[] perm; + private short[] permGradIndex3D; + + public OpenSimplexNoise() { + this(DEFAULT_SEED); + } + + public OpenSimplexNoise(short[] perm) { + this.perm = perm; + permGradIndex3D = new short[256]; + + for (int i = 0; i < 256; i++) { + // Since 3D has 24 gradients, simple bitmask won't work, so + // precompute modulo array. + permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); + } + } + + // Initializes the class using a permutation array generated from a 64-bit + // seed. + // Generates a proper permutation (i.e. doesn't merely perform N successive + // pair swaps on a base array) + // Uses a simple 64-bit LCG. + public OpenSimplexNoise(long seed) { + perm = new short[256]; + permGradIndex3D = new short[256]; + short[] source = new short[256]; + for (short i = 0; i < 256; i++) + source[i] = i; + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + seed = seed * 6364136223846793005l + 1442695040888963407l; + for (int i = 255; i >= 0; i--) { + seed = seed * 6364136223846793005l + 1442695040888963407l; + int r = (int) ((seed + 31) % (i + 1)); + if (r < 0) + r += (i + 1); + perm[i] = source[r]; + permGradIndex3D[i] = (short) ((perm[i] % (gradients3D.length / 3)) * 3); + source[r] = source[i]; + } + } + + // 2D OpenSimplex Noise. + public double eval(double x, double y) { + + // Place input coordinates onto grid. + double stretchOffset = (x + y) * STRETCH_CONSTANT_2D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + + // Floor to get grid coordinates of rhombus (stretched square) + // super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + + // Skew out to get actual coordinates of rhombus origin. We'll need + // these later. + double squishOffset = (xsb + ysb) * SQUISH_CONSTANT_2D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + + // Compute grid coordinates relative to rhombus origin. + double xins = xs - xsb; + double yins = ys - ysb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext, dy_ext; + int xsv_ext, ysv_ext; + + double value = 0; + + // Contribution (1,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_2D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_2D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, dx1, dy1); + } + + // Contribution (0,1) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_2D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_2D; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, dx2, dy2); + } + + if (inSum <= 1) { // We're inside the triangle (2-Simplex) at (0,0) + double zins = 1 - inSum; + if (zins > xins || zins > yins) { // (0,0) is one of the closest two + // triangular vertices + if (xins > yins) { + xsv_ext = xsb + 1; + ysv_ext = ysb - 1; + dx_ext = dx0 - 1; + dy_ext = dy0 + 1; + } else { + xsv_ext = xsb - 1; + ysv_ext = ysb + 1; + dx_ext = dx0 + 1; + dy_ext = dy0 - 1; + } + } else { // (1,0) and (0,1) are the closest two vertices. + xsv_ext = xsb + 1; + ysv_ext = ysb + 1; + dx_ext = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + } else { // We're inside the triangle (2-Simplex) at (1,1) + double zins = 2 - inSum; + if (zins < xins || zins < yins) { // (0,0) is one of the closest two + // triangular vertices + if (xins > yins) { + xsv_ext = xsb + 2; + ysv_ext = ysb + 0; + dx_ext = dx0 - 2 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 + 0 - 2 * SQUISH_CONSTANT_2D; + } else { + xsv_ext = xsb + 0; + ysv_ext = ysb + 2; + dx_ext = dx0 + 0 - 2 * SQUISH_CONSTANT_2D; + dy_ext = dy0 - 2 - 2 * SQUISH_CONSTANT_2D; + } + } else { // (1,0) and (0,1) are the closest two vertices. + dx_ext = dx0; + dy_ext = dy0; + xsv_ext = xsb; + ysv_ext = ysb; + } + xsb += 1; + ysb += 1; + dx0 = dx0 - 1 - 2 * SQUISH_CONSTANT_2D; + dy0 = dy0 - 1 - 2 * SQUISH_CONSTANT_2D; + } + + // Contribution (0,0) or (1,1) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb, ysb, dx0, dy0); + } + + // Extra Vertex + double attn_ext = 2 - dx_ext * dx_ext - dy_ext * dy_ext; + if (attn_ext > 0) { + attn_ext *= attn_ext; + value += attn_ext * attn_ext * extrapolate(xsv_ext, ysv_ext, dx_ext, dy_ext); + } + + return value / NORM_CONSTANT_2D; + } + + // 3D OpenSimplex Noise. + public double eval(double x, double y, double z) { + + // Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z) * STRETCH_CONSTANT_3D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + + // Floor to get simplectic honeycomb coordinates of rhombohedron + // (stretched cube) super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + + // Skew out to get actual coordinates of rhombohedron origin. We'll need + // these later. + double squishOffset = (xsb + ysb + zsb) * SQUISH_CONSTANT_3D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + + // Compute simplectic honeycomb coordinates relative to rhombohedral + // origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins + zins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext0, dy_ext0, dz_ext0; + double dx_ext1, dy_ext1, dz_ext1; + int xsv_ext0, ysv_ext0, zsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1; + + double value = 0; + if (inSum <= 1) { // We're inside the tetrahedron (3-Simplex) at (0,0,0) + + // Determine which two of (0,0,1), (0,1,0), (1,0,0) are closest. + byte aPoint = 0x01; + double aScore = xins; + byte bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + + // Now we determine the two lattice points not part of the + // tetrahedron that may contribute. + // This depends on the closest two tetrahedral vertices, including + // (0,0,0) + double wins = 1 - inSum; + if (wins > aScore || wins > bScore) { // (0,0,0) is one of the + // closest two tetrahedral + // vertices. + byte c = (bScore > aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0; + if ((c & 0x01) == 0) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext0 -= 1; + dy_ext0 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0; + dz_ext1 = dz0 + 1; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1; + } + } else { // (0,0,0) is not one of the closest two tetrahedral + // vertices. + byte c = (byte) (aPoint | bPoint); // Our two extra vertices are + // determined by the closest + // two. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + } + } + + // Contribution (0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, dx0, dy0, dz0); + } + + // Contribution (1,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + // Contribution (0,1,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + // Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + } else if (inSum >= 2) { // We're inside the tetrahedron (3-Simplex) at + // (1,1,1) + + // Determine which two tetrahedral vertices are the closest, out of + // (1,1,0), (1,0,1), (0,1,1) but not (1,1,1). + byte aPoint = 0x06; + double aScore = xins; + byte bPoint = 0x05; + double bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x03; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x03; + } + + // Now we determine the two lattice points not part of the + // tetrahedron that may contribute. + // This depends on the closest two tetrahedral vertices, including + // (1,1,1) + double wins = 3 - inSum; + if (wins < aScore || wins < bScore) { // (1,1,1) is one of the + // closest two tetrahedral + // vertices. + byte c = (bScore < aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 3 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_3D; + } + } else { // (1,1,1) is not one of the closest two tetrahedral + // vertices. + byte c = (byte) (aPoint & bPoint); // Our two extra vertices are + // determined by the closest + // two. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_3D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysb + 1; + ysv_ext1 = ysb + 2; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy0 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsb + 1; + zsv_ext1 = zsb + 2; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz0 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + } + } + + // Contribution (1,1,0) + double dx3 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dy3 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dz3 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx3, dy3, dz3); + } + + // Contribution (1,0,1) + double dx2 = dx3; + double dy2 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dz2 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx2, dy2, dz2); + } + + // Contribution (0,1,1) + double dx1 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dy1 = dy3; + double dz1 = dz2; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx1, dy1, dz1); + } + + // Contribution (1,1,1) + dx0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, dx0, dy0, dz0); + } + } else { // We're inside the octahedron (Rectified 3-Simplex) in + // between. + double aScore; + byte aPoint; + boolean aIsFurtherSide; + double bScore; + byte bPoint; + boolean bIsFurtherSide; + + // Decide between point (0,0,1) and (1,1,0) as closest + double p1 = xins + yins; + if (p1 > 1) { + aScore = p1 - 1; + aPoint = 0x03; + aIsFurtherSide = true; + } else { + aScore = 1 - p1; + aPoint = 0x04; + aIsFurtherSide = false; + } + + // Decide between point (0,1,0) and (1,0,1) as closest + double p2 = xins + zins; + if (p2 > 1) { + bScore = p2 - 1; + bPoint = 0x05; + bIsFurtherSide = true; + } else { + bScore = 1 - p2; + bPoint = 0x02; + bIsFurtherSide = false; + } + + // The closest out of the two (1,0,0) and (0,1,1) will replace the + // furthest out of the two decided above, if closer. + double p3 = yins + zins; + if (p3 > 1) { + double score = p3 - 1; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x06; + aIsFurtherSide = true; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x06; + bIsFurtherSide = true; + } + } else { + double score = 1 - p3; + if (aScore <= bScore && aScore < score) { + aScore = score; + aPoint = 0x01; + aIsFurtherSide = false; + } else if (aScore > bScore && bScore < score) { + bScore = score; + bPoint = 0x01; + bIsFurtherSide = false; + } + } + + // Where each of the two closest points are determines how the extra + // two vertices are calculated. + if (aIsFurtherSide == bIsFurtherSide) { + if (aIsFurtherSide) { // Both closest points on (1,1,1) side + + // One of the two extra points is (1,1,1) + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + + // Other extra point is based on the shared axis. + byte c = (byte) (aPoint & bPoint); + if ((c & 0x01) != 0) { + dx_ext1 = dx0 - 2 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 2; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + } else if ((c & 0x02) != 0) { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb + 2; + zsv_ext1 = zsb; + } else { + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb + 2; + } + } else {// Both closest points on (0,0,0) side + + // One of the two extra points is (0,0,0) + dx_ext0 = dx0; + dy_ext0 = dy0; + dz_ext0 = dz0; + xsv_ext0 = xsb; + ysv_ext0 = ysb; + zsv_ext0 = zsb; + + // Other extra point is based on the omitted axis. + byte c = (byte) (aPoint | bPoint); + if ((c & 0x01) == 0) { + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb - 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb + 1; + } else if ((c & 0x02) == 0) { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb - 1; + zsv_ext1 = zsb + 1; + } else { + dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext1 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext1 = xsb + 1; + ysv_ext1 = ysb + 1; + zsv_ext1 = zsb - 1; + } + } + } else { // One point on (0,0,0) side, one point on (1,1,1) side + byte c1, c2; + if (aIsFurtherSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // One contribution is a permutation of (1,1,-1) + if ((c1 & 0x01) == 0) { + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb - 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb + 1; + } else if ((c1 & 0x02) == 0) { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 + 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 - 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb - 1; + zsv_ext0 = zsb + 1; + } else { + dx_ext0 = dx0 - 1 - SQUISH_CONSTANT_3D; + dy_ext0 = dy0 - 1 - SQUISH_CONSTANT_3D; + dz_ext0 = dz0 + 1 - SQUISH_CONSTANT_3D; + xsv_ext0 = xsb + 1; + ysv_ext0 = ysb + 1; + zsv_ext0 = zsb - 1; + } + + // One contribution is a permutation of (0,0,2) + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_3D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_3D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_3D; + xsv_ext1 = xsb; + ysv_ext1 = ysb; + zsv_ext1 = zsb; + if ((c2 & 0x01) != 0) { + dx_ext1 -= 2; + xsv_ext1 += 2; + } else if ((c2 & 0x02) != 0) { + dy_ext1 -= 2; + ysv_ext1 += 2; + } else { + dz_ext1 -= 2; + zsv_ext1 += 2; + } + } + + // Contribution (1,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_3D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_3D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_3D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, dx1, dy1, dz1); + } + + // Contribution (0,1,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_3D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_3D; + double dz2 = dz1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, dx2, dy2, dz2); + } + + // Contribution (0,0,1) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_3D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, dx3, dy3, dz3); + } + + // Contribution (1,1,0) + double dx4 = dx0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dy4 = dy0 - 1 - 2 * SQUISH_CONSTANT_3D; + double dz4 = dz0 - 0 - 2 * SQUISH_CONSTANT_3D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 0, dx4, dy4, dz4); + } + + // Contribution (1,0,1) + double dx5 = dx4; + double dy5 = dy0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dz5 = dz0 - 1 - 2 * SQUISH_CONSTANT_3D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 0, zsb + 1, dx5, dy5, dz5); + } + + // Contribution (0,1,1) + double dx6 = dx0 - 0 - 2 * SQUISH_CONSTANT_3D; + double dy6 = dy4; + double dz6 = dz5; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 0, ysb + 1, zsb + 1, dx6, dy6, dz6); + } + } + + // First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0; + if (attn_ext0 > 0) { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, dx_ext0, dy_ext0, dz_ext0); + } + + // Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1; + if (attn_ext1 > 0) { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, dx_ext1, dy_ext1, dz_ext1); + } + + return value / NORM_CONSTANT_3D; + } + + // 4D OpenSimplex Noise. + public double eval(double x, double y, double z, double w) { + + // Place input coordinates on simplectic honeycomb. + double stretchOffset = (x + y + z + w) * STRETCH_CONSTANT_4D; + double xs = x + stretchOffset; + double ys = y + stretchOffset; + double zs = z + stretchOffset; + double ws = w + stretchOffset; + + // Floor to get simplectic honeycomb coordinates of rhombo-hypercube + // super-cell origin. + int xsb = fastFloor(xs); + int ysb = fastFloor(ys); + int zsb = fastFloor(zs); + int wsb = fastFloor(ws); + + // Skew out to get actual coordinates of stretched rhombo-hypercube + // origin. We'll need these later. + double squishOffset = (xsb + ysb + zsb + wsb) * SQUISH_CONSTANT_4D; + double xb = xsb + squishOffset; + double yb = ysb + squishOffset; + double zb = zsb + squishOffset; + double wb = wsb + squishOffset; + + // Compute simplectic honeycomb coordinates relative to rhombo-hypercube + // origin. + double xins = xs - xsb; + double yins = ys - ysb; + double zins = zs - zsb; + double wins = ws - wsb; + + // Sum those together to get a value that determines which region we're + // in. + double inSum = xins + yins + zins + wins; + + // Positions relative to origin point. + double dx0 = x - xb; + double dy0 = y - yb; + double dz0 = z - zb; + double dw0 = w - wb; + + // We'll be defining these inside the next block and using them + // afterwards. + double dx_ext0, dy_ext0, dz_ext0, dw_ext0; + double dx_ext1, dy_ext1, dz_ext1, dw_ext1; + double dx_ext2, dy_ext2, dz_ext2, dw_ext2; + int xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0; + int xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1; + int xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2; + + double value = 0; + if (inSum <= 1) { // We're inside the pentachoron (4-Simplex) at + // (0,0,0,0) + + // Determine which two of (0,0,0,1), (0,0,1,0), (0,1,0,0), (1,0,0,0) + // are closest. + byte aPoint = 0x01; + double aScore = xins; + byte bPoint = 0x02; + double bScore = yins; + if (aScore >= bScore && zins > bScore) { + bScore = zins; + bPoint = 0x04; + } else if (aScore < bScore && zins > aScore) { + aScore = zins; + aPoint = 0x04; + } + if (aScore >= bScore && wins > bScore) { + bScore = wins; + bPoint = 0x08; + } else if (aScore < bScore && wins > aScore) { + aScore = wins; + aPoint = 0x08; + } + + // Now we determine the three lattice points not part of the + // pentachoron that may contribute. + // This depends on the closest two pentachoron vertices, including + // (0,0,0,0) + double uins = 1 - inSum; + if (uins > aScore || uins > bScore) { // (0,0,0,0) is one of the + // closest two pentachoron + // vertices. + byte c = (bScore > aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 + 1; + dx_ext1 = dx_ext2 = dx0; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 1; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0; + if ((c & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0; + if ((c & 0x03) != 0) { + if ((c & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw_ext1 = dw0; + dw_ext2 = dw0 + 1; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 1; + } + } else { // (0,0,0,0) is not one of the closest two pentachoron + // vertices. + byte c = (byte) (aPoint | bPoint); // Our three extra vertices + // are determined by the + // closest two. + + if ((c & 0x01) == 0) { + xsv_ext0 = xsv_ext2 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) { + ysv_ext1 -= 1; + dy_ext1 += 1; + } else { + ysv_ext2 -= 1; + dy_ext2 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) { + zsv_ext1 -= 1; + dz_ext1 += 1; + } else { + zsv_ext2 -= 1; + dz_ext2 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsv_ext1 = wsb; + wsv_ext2 = wsb - 1; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - SQUISH_CONSTANT_4D; + dw_ext2 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb + 1; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + } + + // Contribution (0,0,0,0) + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 0, dx0, dy0, dz0, dw0); + } + + // Contribution (1,0,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + // Contribution (0,1,0,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + // Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + // Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + } else if (inSum >= 3) { // We're inside the pentachoron (4-Simplex) at + // (1,1,1,1) + // Determine which two of (1,1,1,0), + // (1,1,0,1), (1,0,1,1), (0,1,1,1) + // are closest. + byte aPoint = 0x0E; + double aScore = xins; + byte bPoint = 0x0D; + double bScore = yins; + if (aScore <= bScore && zins < bScore) { + bScore = zins; + bPoint = 0x0B; + } else if (aScore > bScore && zins < aScore) { + aScore = zins; + aPoint = 0x0B; + } + if (aScore <= bScore && wins < bScore) { + bScore = wins; + bPoint = 0x07; + } else if (aScore > bScore && wins < aScore) { + aScore = wins; + aPoint = 0x07; + } + + // Now we determine the three lattice points not part of the + // pentachoron that may contribute. + // This depends on the closest two pentachoron vertices, including + // (0,0,0,0) + double uins = 4 - inSum; + if (uins < aScore || uins < bScore) { // (1,1,1,1) is one of the + // closest two pentachoron + // vertices. + byte c = (bScore < aScore ? bPoint : aPoint); // Our other + // closest + // vertex is the + // closest out + // of a and b. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsv_ext2 = xsb + 1; + dx_ext0 = dx0 - 2 - 4 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx_ext1 = dx_ext2 = dx0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext1 += 1; + dy_ext1 -= 1; + } else { + ysv_ext0 += 1; + dy_ext0 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy_ext1 = dy_ext2 = dy0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0x03) { + if ((c & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext2 += 1; + dz_ext2 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz_ext1 = dz_ext2 = dz0 - 4 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw_ext1 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 4 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw_ext1 = dw_ext2 = dw0 - 4 * SQUISH_CONSTANT_4D; + } + } else { // (1,1,1,1) is not one of the closest two pentachoron + // vertices. + byte c = (byte) (aPoint & bPoint); // Our three extra vertices + // are determined by the + // closest two. + + if ((c & 0x01) != 0) { + xsv_ext0 = xsv_ext2 = xsb + 1; + xsv_ext1 = xsb + 2; + dx_ext0 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext2 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsv_ext2 = xsb; + dx_ext0 = dx0 - 2 * SQUISH_CONSTANT_4D; + dx_ext1 = dx_ext2 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb + 1; + dy_ext0 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) != 0) { + ysv_ext2 += 1; + dy_ext2 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysv_ext2 = ysb; + dy_ext0 = dy0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy_ext2 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb + 1; + dz_ext0 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) != 0) { + zsv_ext2 += 1; + dz_ext2 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsv_ext2 = zsb; + dz_ext0 = dz0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz_ext2 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsv_ext1 = wsb + 1; + wsv_ext2 = wsb + 2; + dw_ext0 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsv_ext2 = wsb; + dw_ext0 = dw0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw_ext2 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + + // Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + // Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + // Contribution (0,1,1,1) + double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + // Contribution (1,1,1,1) + dx0 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy0 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz0 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw0 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + double attn0 = 2 - dx0 * dx0 - dy0 * dy0 - dz0 * dz0 - dw0 * dw0; + if (attn0 > 0) { + attn0 *= attn0; + value += attn0 * attn0 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 1, dx0, dy0, dz0, dw0); + } + } else if (inSum <= 2) { // We're inside the first dispentachoron + // (Rectified 4-Simplex) + double aScore; + byte aPoint; + boolean aIsBiggerSide = true; + double bScore; + byte bPoint; + boolean bIsBiggerSide = true; + + // Decide between (1,1,0,0) and (0,0,1,1) + if (xins + yins > zins + wins) { + aScore = xins + yins; + aPoint = 0x03; + } else { + aScore = zins + wins; + aPoint = 0x0C; + } + + // Decide between (1,0,1,0) and (0,1,0,1) + if (xins + zins > yins + wins) { + bScore = xins + zins; + bPoint = 0x05; + } else { + bScore = yins + wins; + bPoint = 0x0A; + } + + // Closer between (1,0,0,1) and (0,1,1,0) will replace the further + // of a and b, if closer. + if (xins + wins > yins + zins) { + double score = xins + wins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x09; + } + } else { + double score = yins + zins; + if (aScore >= bScore && score > bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore < bScore && score > aScore) { + aScore = score; + aPoint = 0x06; + } + } + + // Decide if (1,0,0,0) is closer. + double p1 = 2 - inSum + xins; + if (aScore >= bScore && p1 > bScore) { + bScore = p1; + bPoint = 0x01; + bIsBiggerSide = false; + } else if (aScore < bScore && p1 > aScore) { + aScore = p1; + aPoint = 0x01; + aIsBiggerSide = false; + } + + // Decide if (0,1,0,0) is closer. + double p2 = 2 - inSum + yins; + if (aScore >= bScore && p2 > bScore) { + bScore = p2; + bPoint = 0x02; + bIsBiggerSide = false; + } else if (aScore < bScore && p2 > aScore) { + aScore = p2; + aPoint = 0x02; + aIsBiggerSide = false; + } + + // Decide if (0,0,1,0) is closer. + double p3 = 2 - inSum + zins; + if (aScore >= bScore && p3 > bScore) { + bScore = p3; + bPoint = 0x04; + bIsBiggerSide = false; + } else if (aScore < bScore && p3 > aScore) { + aScore = p3; + aPoint = 0x04; + aIsBiggerSide = false; + } + + // Decide if (0,0,0,1) is closer. + double p4 = 2 - inSum + wins; + if (aScore >= bScore && p4 > bScore) { + bScore = p4; + bPoint = 0x08; + bIsBiggerSide = false; + } else if (aScore < bScore && p4 > aScore) { + aScore = p4; + aPoint = 0x08; + aIsBiggerSide = false; + } + + // Where each of the two closest points are determines how the extra + // three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { // Both closest points on the bigger side + byte c1 = (byte) (aPoint | bPoint); + byte c2 = (byte) (aPoint & bPoint); + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb; + xsv_ext1 = xsb - 1; + dx_ext0 = dx0 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysb; + ysv_ext1 = ysb - 1; + dy_ext0 = dy0 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsb; + zsv_ext1 = zsb - 1; + dz_ext0 = dz0 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - 2 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + } + + // One combination is a permutation of (0,0,0,2) based on c2 + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + + } else { // Both closest points on the smaller side + // One of the two extra points is (0,0,0,0) + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0; + dy_ext2 = dy0; + dz_ext2 = dz0; + dw_ext2 = dw0; + + // Other two points are based on the omitted axes. + byte c = (byte) (aPoint | bPoint); + + if ((c & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + } + } else { // One point on each "side" + byte c1, c2; + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // Two contributions are the bigger-sided point with each 0 + // replaced with -1. + if ((c1 & 0x01) == 0) { + xsv_ext0 = xsb - 1; + xsv_ext1 = xsb; + dx_ext0 = dx0 + 1 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb + 1; + dx_ext0 = dx_ext1 = dx0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) == 0) { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0x01) { + ysv_ext0 -= 1; + dy_ext0 += 1; + } else { + ysv_ext1 -= 1; + dy_ext1 += 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) == 0) { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0x03) { + zsv_ext0 -= 1; + dz_ext0 += 1; + } else { + zsv_ext1 -= 1; + dz_ext1 += 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) == 0) { + wsv_ext0 = wsb; + wsv_ext1 = wsb - 1; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dw_ext1 = dw0 + 1 - SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb + 1; + dw_ext0 = dw_ext1 = dw0 - 1 - SQUISH_CONSTANT_4D; + } + + // One contribution is a permutation of (0,0,0,2) based on the + // smaller-sided point + xsv_ext2 = xsb; + ysv_ext2 = ysb; + zsv_ext2 = zsb; + wsv_ext2 = wsb; + dx_ext2 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) != 0) { + xsv_ext2 += 2; + dx_ext2 -= 2; + } else if ((c2 & 0x02) != 0) { + ysv_ext2 += 2; + dy_ext2 -= 2; + } else if ((c2 & 0x04) != 0) { + zsv_ext2 += 2; + dz_ext2 -= 2; + } else { + wsv_ext2 += 2; + dw_ext2 -= 2; + } + } + + // Contribution (1,0,0,0) + double dx1 = dx0 - 1 - SQUISH_CONSTANT_4D; + double dy1 = dy0 - 0 - SQUISH_CONSTANT_4D; + double dz1 = dz0 - 0 - SQUISH_CONSTANT_4D; + double dw1 = dw0 - 0 - SQUISH_CONSTANT_4D; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 0, dx1, dy1, dz1, dw1); + } + + // Contribution (0,1,0,0) + double dx2 = dx0 - 0 - SQUISH_CONSTANT_4D; + double dy2 = dy0 - 1 - SQUISH_CONSTANT_4D; + double dz2 = dz1; + double dw2 = dw1; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 0, dx2, dy2, dz2, dw2); + } + + // Contribution (0,0,1,0) + double dx3 = dx2; + double dy3 = dy1; + double dz3 = dz0 - 1 - SQUISH_CONSTANT_4D; + double dw3 = dw1; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 0, dx3, dy3, dz3, dw3); + } + + // Contribution (0,0,0,1) + double dx4 = dx2; + double dy4 = dy1; + double dz4 = dz1; + double dw4 = dw0 - 1 - SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 0, ysb + 0, zsb + 0, wsb + 1, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + // Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + // Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + // Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + // Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + // Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } else { // We're inside the second dispentachoron (Rectified 4-Simplex) + double aScore; + byte aPoint; + boolean aIsBiggerSide = true; + double bScore; + byte bPoint; + boolean bIsBiggerSide = true; + + // Decide between (0,0,1,1) and (1,1,0,0) + if (xins + yins < zins + wins) { + aScore = xins + yins; + aPoint = 0x0C; + } else { + aScore = zins + wins; + aPoint = 0x03; + } + + // Decide between (0,1,0,1) and (1,0,1,0) + if (xins + zins < yins + wins) { + bScore = xins + zins; + bPoint = 0x0A; + } else { + bScore = yins + wins; + bPoint = 0x05; + } + + // Closer between (0,1,1,0) and (1,0,0,1) will replace the further + // of a and b, if closer. + if (xins + wins < yins + zins) { + double score = xins + wins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x06; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x06; + } + } else { + double score = yins + zins; + if (aScore <= bScore && score < bScore) { + bScore = score; + bPoint = 0x09; + } else if (aScore > bScore && score < aScore) { + aScore = score; + aPoint = 0x09; + } + } + + // Decide if (0,1,1,1) is closer. + double p1 = 3 - inSum + xins; + if (aScore <= bScore && p1 < bScore) { + bScore = p1; + bPoint = 0x0E; + bIsBiggerSide = false; + } else if (aScore > bScore && p1 < aScore) { + aScore = p1; + aPoint = 0x0E; + aIsBiggerSide = false; + } + + // Decide if (1,0,1,1) is closer. + double p2 = 3 - inSum + yins; + if (aScore <= bScore && p2 < bScore) { + bScore = p2; + bPoint = 0x0D; + bIsBiggerSide = false; + } else if (aScore > bScore && p2 < aScore) { + aScore = p2; + aPoint = 0x0D; + aIsBiggerSide = false; + } + + // Decide if (1,1,0,1) is closer. + double p3 = 3 - inSum + zins; + if (aScore <= bScore && p3 < bScore) { + bScore = p3; + bPoint = 0x0B; + bIsBiggerSide = false; + } else if (aScore > bScore && p3 < aScore) { + aScore = p3; + aPoint = 0x0B; + aIsBiggerSide = false; + } + + // Decide if (1,1,1,0) is closer. + double p4 = 3 - inSum + wins; + if (aScore <= bScore && p4 < bScore) { + bScore = p4; + bPoint = 0x07; + bIsBiggerSide = false; + } else if (aScore > bScore && p4 < aScore) { + aScore = p4; + aPoint = 0x07; + aIsBiggerSide = false; + } + + // Where each of the two closest points are determines how the extra + // three vertices are calculated. + if (aIsBiggerSide == bIsBiggerSide) { + if (aIsBiggerSide) { // Both closest points on the bigger side + byte c1 = (byte) (aPoint & bPoint); + byte c2 = (byte) (aPoint | bPoint); + + // Two contributions are permutations of (0,0,0,1) and + // (0,0,0,2) based on c1 + xsv_ext0 = xsv_ext1 = xsb; + ysv_ext0 = ysv_ext1 = ysb; + zsv_ext0 = zsv_ext1 = zsb; + wsv_ext0 = wsv_ext1 = wsb; + dx_ext0 = dx0 - SQUISH_CONSTANT_4D; + dy_ext0 = dy0 - SQUISH_CONSTANT_4D; + dz_ext0 = dz0 - SQUISH_CONSTANT_4D; + dw_ext0 = dw0 - SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 2 * SQUISH_CONSTANT_4D; + dy_ext1 = dy0 - 2 * SQUISH_CONSTANT_4D; + dz_ext1 = dz0 - 2 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) != 0) { + xsv_ext0 += 1; + dx_ext0 -= 1; + xsv_ext1 += 2; + dx_ext1 -= 2; + } else if ((c1 & 0x02) != 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + ysv_ext1 += 2; + dy_ext1 -= 2; + } else if ((c1 & 0x04) != 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + zsv_ext1 += 2; + dz_ext1 -= 2; + } else { + wsv_ext0 += 1; + dw_ext0 -= 1; + wsv_ext1 += 2; + dw_ext1 -= 2; + } + + // One contribution is a permutation of (1,1,1,-1) based on + // c2 + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } else { // Both closest points on the smaller side + // One of the two extra points is (1,1,1,1) + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 4 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 4 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 4 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 4 * SQUISH_CONSTANT_4D; + + // Other two points are based on the shared axes. + byte c = (byte) (aPoint & bPoint); + + if ((c & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x01) == 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c & 0x08) != 0) { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + } + } else { // One point on each "side" + byte c1, c2; + if (aIsBiggerSide) { + c1 = aPoint; + c2 = bPoint; + } else { + c1 = bPoint; + c2 = aPoint; + } + + // Two contributions are the bigger-sided point with each 1 + // replaced with 2. + if ((c1 & 0x01) != 0) { + xsv_ext0 = xsb + 2; + xsv_ext1 = xsb + 1; + dx_ext0 = dx0 - 2 - 3 * SQUISH_CONSTANT_4D; + dx_ext1 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + } else { + xsv_ext0 = xsv_ext1 = xsb; + dx_ext0 = dx_ext1 = dx0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x02) != 0) { + ysv_ext0 = ysv_ext1 = ysb + 1; + dy_ext0 = dy_ext1 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x01) == 0) { + ysv_ext0 += 1; + dy_ext0 -= 1; + } else { + ysv_ext1 += 1; + dy_ext1 -= 1; + } + } else { + ysv_ext0 = ysv_ext1 = ysb; + dy_ext0 = dy_ext1 = dy0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x04) != 0) { + zsv_ext0 = zsv_ext1 = zsb + 1; + dz_ext0 = dz_ext1 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + if ((c1 & 0x03) == 0) { + zsv_ext0 += 1; + dz_ext0 -= 1; + } else { + zsv_ext1 += 1; + dz_ext1 -= 1; + } + } else { + zsv_ext0 = zsv_ext1 = zsb; + dz_ext0 = dz_ext1 = dz0 - 3 * SQUISH_CONSTANT_4D; + } + + if ((c1 & 0x08) != 0) { + wsv_ext0 = wsb + 1; + wsv_ext1 = wsb + 2; + dw_ext0 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + dw_ext1 = dw0 - 2 - 3 * SQUISH_CONSTANT_4D; + } else { + wsv_ext0 = wsv_ext1 = wsb; + dw_ext0 = dw_ext1 = dw0 - 3 * SQUISH_CONSTANT_4D; + } + + // One contribution is a permutation of (1,1,1,-1) based on the + // smaller-sided point + xsv_ext2 = xsb + 1; + ysv_ext2 = ysb + 1; + zsv_ext2 = zsb + 1; + wsv_ext2 = wsb + 1; + dx_ext2 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + dy_ext2 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + dz_ext2 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + dw_ext2 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + if ((c2 & 0x01) == 0) { + xsv_ext2 -= 2; + dx_ext2 += 2; + } else if ((c2 & 0x02) == 0) { + ysv_ext2 -= 2; + dy_ext2 += 2; + } else if ((c2 & 0x04) == 0) { + zsv_ext2 -= 2; + dz_ext2 += 2; + } else { + wsv_ext2 -= 2; + dw_ext2 += 2; + } + } + + // Contribution (1,1,1,0) + double dx4 = dx0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dy4 = dy0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dz4 = dz0 - 1 - 3 * SQUISH_CONSTANT_4D; + double dw4 = dw0 - 3 * SQUISH_CONSTANT_4D; + double attn4 = 2 - dx4 * dx4 - dy4 * dy4 - dz4 * dz4 - dw4 * dw4; + if (attn4 > 0) { + attn4 *= attn4; + value += attn4 * attn4 * extrapolate(xsb + 1, ysb + 1, zsb + 1, wsb + 0, dx4, dy4, dz4, dw4); + } + + // Contribution (1,1,0,1) + double dx3 = dx4; + double dy3 = dy4; + double dz3 = dz0 - 3 * SQUISH_CONSTANT_4D; + double dw3 = dw0 - 1 - 3 * SQUISH_CONSTANT_4D; + double attn3 = 2 - dx3 * dx3 - dy3 * dy3 - dz3 * dz3 - dw3 * dw3; + if (attn3 > 0) { + attn3 *= attn3; + value += attn3 * attn3 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 1, dx3, dy3, dz3, dw3); + } + + // Contribution (1,0,1,1) + double dx2 = dx4; + double dy2 = dy0 - 3 * SQUISH_CONSTANT_4D; + double dz2 = dz4; + double dw2 = dw3; + double attn2 = 2 - dx2 * dx2 - dy2 * dy2 - dz2 * dz2 - dw2 * dw2; + if (attn2 > 0) { + attn2 *= attn2; + value += attn2 * attn2 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 1, dx2, dy2, dz2, dw2); + } + + // Contribution (0,1,1,1) + double dx1 = dx0 - 3 * SQUISH_CONSTANT_4D; + double dz1 = dz4; + double dy1 = dy4; + double dw1 = dw3; + double attn1 = 2 - dx1 * dx1 - dy1 * dy1 - dz1 * dz1 - dw1 * dw1; + if (attn1 > 0) { + attn1 *= attn1; + value += attn1 * attn1 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 1, dx1, dy1, dz1, dw1); + } + + // Contribution (1,1,0,0) + double dx5 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy5 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz5 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw5 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn5 = 2 - dx5 * dx5 - dy5 * dy5 - dz5 * dz5 - dw5 * dw5; + if (attn5 > 0) { + attn5 *= attn5; + value += attn5 * attn5 * extrapolate(xsb + 1, ysb + 1, zsb + 0, wsb + 0, dx5, dy5, dz5, dw5); + } + + // Contribution (1,0,1,0) + double dx6 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy6 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz6 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw6 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn6 = 2 - dx6 * dx6 - dy6 * dy6 - dz6 * dz6 - dw6 * dw6; + if (attn6 > 0) { + attn6 *= attn6; + value += attn6 * attn6 * extrapolate(xsb + 1, ysb + 0, zsb + 1, wsb + 0, dx6, dy6, dz6, dw6); + } + + // Contribution (1,0,0,1) + double dx7 = dx0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dy7 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz7 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw7 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn7 = 2 - dx7 * dx7 - dy7 * dy7 - dz7 * dz7 - dw7 * dw7; + if (attn7 > 0) { + attn7 *= attn7; + value += attn7 * attn7 * extrapolate(xsb + 1, ysb + 0, zsb + 0, wsb + 1, dx7, dy7, dz7, dw7); + } + + // Contribution (0,1,1,0) + double dx8 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy8 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz8 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw8 = dw0 - 0 - 2 * SQUISH_CONSTANT_4D; + double attn8 = 2 - dx8 * dx8 - dy8 * dy8 - dz8 * dz8 - dw8 * dw8; + if (attn8 > 0) { + attn8 *= attn8; + value += attn8 * attn8 * extrapolate(xsb + 0, ysb + 1, zsb + 1, wsb + 0, dx8, dy8, dz8, dw8); + } + + // Contribution (0,1,0,1) + double dx9 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy9 = dy0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dz9 = dz0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dw9 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn9 = 2 - dx9 * dx9 - dy9 * dy9 - dz9 * dz9 - dw9 * dw9; + if (attn9 > 0) { + attn9 *= attn9; + value += attn9 * attn9 * extrapolate(xsb + 0, ysb + 1, zsb + 0, wsb + 1, dx9, dy9, dz9, dw9); + } + + // Contribution (0,0,1,1) + double dx10 = dx0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dy10 = dy0 - 0 - 2 * SQUISH_CONSTANT_4D; + double dz10 = dz0 - 1 - 2 * SQUISH_CONSTANT_4D; + double dw10 = dw0 - 1 - 2 * SQUISH_CONSTANT_4D; + double attn10 = 2 - dx10 * dx10 - dy10 * dy10 - dz10 * dz10 - dw10 * dw10; + if (attn10 > 0) { + attn10 *= attn10; + value += attn10 * attn10 * extrapolate(xsb + 0, ysb + 0, zsb + 1, wsb + 1, dx10, dy10, dz10, dw10); + } + } + + // First extra vertex + double attn_ext0 = 2 - dx_ext0 * dx_ext0 - dy_ext0 * dy_ext0 - dz_ext0 * dz_ext0 - dw_ext0 * dw_ext0; + if (attn_ext0 > 0) { + attn_ext0 *= attn_ext0; + value += attn_ext0 * attn_ext0 + * extrapolate(xsv_ext0, ysv_ext0, zsv_ext0, wsv_ext0, dx_ext0, dy_ext0, dz_ext0, dw_ext0); + } + + // Second extra vertex + double attn_ext1 = 2 - dx_ext1 * dx_ext1 - dy_ext1 * dy_ext1 - dz_ext1 * dz_ext1 - dw_ext1 * dw_ext1; + if (attn_ext1 > 0) { + attn_ext1 *= attn_ext1; + value += attn_ext1 * attn_ext1 + * extrapolate(xsv_ext1, ysv_ext1, zsv_ext1, wsv_ext1, dx_ext1, dy_ext1, dz_ext1, dw_ext1); + } + + // Third extra vertex + double attn_ext2 = 2 - dx_ext2 * dx_ext2 - dy_ext2 * dy_ext2 - dz_ext2 * dz_ext2 - dw_ext2 * dw_ext2; + if (attn_ext2 > 0) { + attn_ext2 *= attn_ext2; + value += attn_ext2 * attn_ext2 + * extrapolate(xsv_ext2, ysv_ext2, zsv_ext2, wsv_ext2, dx_ext2, dy_ext2, dz_ext2, dw_ext2); + } + + return value / NORM_CONSTANT_4D; + } + + private double extrapolate(int xsb, int ysb, double dx, double dy) { + int index = perm[(perm[xsb & 0xFF] + ysb) & 0xFF] & 0x0E; + return gradients2D[index] * dx + gradients2D[index + 1] * dy; + } + + private double extrapolate(int xsb, int ysb, int zsb, double dx, double dy, double dz) { + int index = permGradIndex3D[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF]; + return gradients3D[index] * dx + gradients3D[index + 1] * dy + gradients3D[index + 2] * dz; + } + + private double extrapolate(int xsb, int ysb, int zsb, int wsb, double dx, double dy, double dz, double dw) { + int index = perm[(perm[(perm[(perm[xsb & 0xFF] + ysb) & 0xFF] + zsb) & 0xFF] + wsb) & 0xFF] & 0xFC; + return gradients4D[index] * dx + gradients4D[index + 1] * dy + gradients4D[index + 2] * dz + + gradients4D[index + 3] * dw; + } + + private static int fastFloor(double x) { + int xi = (int) x; + return x < xi ? xi - 1 : xi; + } + + // Gradients for 2D. They approximate the directions to the + // vertices of an octagon from the center. + private static byte[] gradients2D = new byte[] { 5, 2, 2, 5, -5, 2, -2, 5, 5, -2, 2, -5, -5, -2, -2, -5, }; + + // Gradients for 3D. They approximate the directions to the + // vertices of a rhombicuboctahedron from the center, skewed so + // that the triangular and square facets can be inscribed inside + // circles of the same radius. + private static byte[] gradients3D = new byte[] { -11, 4, 4, -4, 11, 4, -4, 4, 11, 11, 4, 4, 4, 11, 4, 4, 4, 11, -11, + -4, 4, -4, -11, 4, -4, -4, 11, 11, -4, 4, 4, -11, 4, 4, -4, 11, -11, 4, -4, -4, 11, -4, -4, 4, -11, 11, 4, + -4, 4, 11, -4, 4, 4, -11, -11, -4, -4, -4, -11, -4, -4, -4, -11, 11, -4, -4, 4, -11, -4, 4, -4, -11, }; + + // Gradients for 4D. They approximate the directions to the + // vertices of a disprismatotesseractihexadecachoron from the center, + // skewed so that the tetrahedral and cubic facets can be inscribed inside + // spheres of the same radius. + private static byte[] gradients4D = new byte[] { 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, 1, 1, 1, 1, 3, -3, 1, 1, 1, -1, 3, + 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, 1, 1, -1, 1, 3, -3, -1, 1, 1, -1, -3, 1, + 1, -1, -1, 3, 1, -1, -1, 1, 3, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, 1, 1, 1, -1, 3, -3, 1, -1, 1, -1, 3, -1, + 1, -1, 1, -3, 1, -1, 1, -1, 3, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, 1, 1, -1, -1, 3, -3, -1, -1, 1, -1, + -3, -1, 1, -1, -1, -3, 1, -1, -1, -1, 3, 3, 1, 1, -1, 1, 3, 1, -1, 1, 1, 3, -1, 1, 1, 1, -3, -3, 1, 1, -1, + -1, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, 3, -1, 1, -1, 1, -3, 1, -1, 1, -1, 3, -1, 1, -1, 1, -3, -3, -1, 1, + -1, -1, -3, 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, 3, 1, -1, -1, 1, 3, -1, -1, 1, 1, -3, -1, 1, 1, -1, -3, -3, + 1, -1, -1, -1, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, 3, -1, -1, -1, 1, -3, -1, -1, 1, -1, -3, -1, 1, -1, + -1, -3, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, -1, -1, -1, -1, -3, }; +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/noise/VoronoiNoise.java b/src/main/java/ru/bclib/noise/VoronoiNoise.java new file mode 100644 index 00000000..07bea100 --- /dev/null +++ b/src/main/java/ru/bclib/noise/VoronoiNoise.java @@ -0,0 +1,140 @@ +package ru.bclib.noise; + +import java.util.Random; + +import net.minecraft.core.BlockPos; +import ru.bclib.util.MHelper; + +public class VoronoiNoise { + private static final Random RANDOM = new Random(); + final int seed; + + public VoronoiNoise() { + this(0); + } + + public VoronoiNoise(int seed) { + this.seed = seed; + } + + private int getSeed(int x, int y, int z) { + int h = seed + x * 374761393 + y * 668265263 + z; + h = (h ^ (h >> 13)) * 1274126177; + return h ^ (h >> 16); + } + + public double sample(double x, double y, double z) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + } + } + } + } + + return Math.sqrt(d); + } + + public Random getRandom(double x, double y, double z) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + + int posX = 0; + int posY = 0; + int posZ = 0; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + posX = pox; + posY = poy; + posZ = poz; + } + } + } + } + + posX += ix; + posY += iy; + posZ += iz; + + int seed = MHelper.getSeed(posY, posX, posZ); + RANDOM.setSeed(seed); + + return RANDOM; + } + + public BlockPos[] getPos(double x, double y, double z, double scale) { + int ix = MHelper.floor(x); + int iy = MHelper.floor(y); + int iz = MHelper.floor(z); + + float px = (float) (x - ix); + float py = (float) (y - iy); + float pz = (float) (z - iz); + + float d = 10; + float selX = 0; + float selY = 0; + float selZ = 0; + float selXPre = 0; + float selYPre = 0; + float selZPre = 0; + + for (int pox = -1; pox < 2; pox++) { + for (int poy = -1; poy < 2; poy++) { + for (int poz = -1; poz < 2; poz++) { + RANDOM.setSeed(getSeed(pox + ix, poy + iy, poz + iz)); + float pointX = pox + RANDOM.nextFloat(); + float pointY = poy + RANDOM.nextFloat(); + float pointZ = poz + RANDOM.nextFloat(); + float d2 = MHelper.lengthSqr(pointX - px, pointY - py, pointZ - pz); + if (d2 < d) { + d = d2; + selXPre = selX; + selYPre = selY; + selZPre = selZ; + selX = pointX; + selY = pointY; + selZ = pointZ; + } + } + } + } + + BlockPos p1 = new BlockPos((ix + (double) selX) * scale, (iy + (double) selY) * scale, (iz + (double) selZ) * scale); + BlockPos p2 = new BlockPos((ix + (double) selXPre) * scale, (iy + (double) selYPre) * scale, (iz + (double) selZPre) * scale); + return new BlockPos[] {p1, p2}; + } +} diff --git a/src/main/java/ru/bclib/sdf/PosInfo.java b/src/main/java/ru/bclib/sdf/PosInfo.java new file mode 100644 index 00000000..b8a64c4d --- /dev/null +++ b/src/main/java/ru/bclib/sdf/PosInfo.java @@ -0,0 +1,104 @@ +package ru.bclib.sdf; + +import java.util.Map; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; + +public class PosInfo implements Comparable { + private static final BlockState AIR = Blocks.AIR.defaultBlockState(); + private final Map blocks; + private final Map add; + private final BlockPos pos; + private BlockState state; + + public static PosInfo create(Map blocks, Map add, BlockPos pos) { + return new PosInfo(blocks, add, pos); + } + + private PosInfo(Map blocks, Map add, BlockPos pos) { + this.blocks = blocks; + this.add = add; + this.pos = pos; + blocks.put(pos, this); + } + + public BlockState getState() { + return state; + } + + public BlockState getState(BlockPos pos) { + PosInfo info = blocks.get(pos); + if (info == null) { + info = add.get(pos); + return info == null ? AIR : info.getState(); + } + return info.getState(); + } + + public void setState(BlockState state) { + this.state = state; + } + + public void setState(BlockPos pos, BlockState state) { + PosInfo info = blocks.get(pos); + if (info != null) { + info.setState(state); + } + } + + public BlockState getState(Direction dir) { + PosInfo info = blocks.get(pos.relative(dir)); + if (info == null) { + info = add.get(pos.relative(dir)); + return info == null ? AIR : info.getState(); + } + return info.getState(); + } + + public BlockState getState(Direction dir, int distance) { + PosInfo info = blocks.get(pos.relative(dir, distance)); + if (info == null) { + return AIR; + } + return info.getState(); + } + + public BlockState getStateUp() { + return getState(Direction.UP); + } + + public BlockState getStateDown() { + return getState(Direction.DOWN); + } + + @Override + public int hashCode() { + return pos.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof PosInfo)) { + return false; + } + return pos.equals(((PosInfo) obj).pos); + } + + @Override + public int compareTo(PosInfo info) { + return this.pos.getY() - info.pos.getY(); + } + + public BlockPos getPos() { + return pos; + } + + public void setBlockPos(BlockPos pos, BlockState state) { + PosInfo info = new PosInfo(blocks, add, pos); + info.state = state; + add.put(pos, info); + } +} diff --git a/src/main/java/ru/bclib/sdf/SDF.java b/src/main/java/ru/bclib/sdf/SDF.java new file mode 100644 index 00000000..ca89f7e3 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/SDF.java @@ -0,0 +1,310 @@ +package ru.bclib.sdf; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.ServerLevelAccessor; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.AABB; +import ru.bclib.structures.StructureWorld; +import ru.bclib.util.BlocksHelper; + +public abstract class SDF { + private List> postProcesses = Lists.newArrayList(); + private Function canReplace = (state) -> { + return state.getMaterial().isReplaceable(); + }; + + public abstract float getDistance(float x, float y, float z); + + public abstract BlockState getBlockState(BlockPos pos); + + public SDF addPostProcess(Function postProcess) { + this.postProcesses.add(postProcess); + return this; + } + + public SDF setReplaceFunction(Function canReplace) { + this.canReplace = canReplace; + return this; + } + + public void fillRecursive(ServerLevelAccessor world, BlockPos start) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center: ends) { + for (Direction dir: Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + + if (!blocks.contains(bPos) && canReplace.apply(world.getBlockState(wpos))) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + BlockState state = getBlockState(wpos); + PosInfo.create(mapWorld, addInfo, wpos).setState(state); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillArea(ServerLevelAccessor world, BlockPos center, AABB box) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + + MutableBlockPos mut = new MutableBlockPos(); + for (int y = (int) box.minY; y <= box.maxY; y++) { + mut.setY(y); + for (int x = (int) box.minX; x <= box.maxX; x++) { + mut.setX(x); + for (int z = (int) box.minZ; z <= box.maxZ; z++) { + mut.setZ(z); + if (canReplace.apply(world.getBlockState(mut))) { + BlockPos fpos = mut.subtract(center); + if (this.getDistance(fpos.getX(), fpos.getY(), fpos.getZ()) < 0) { + PosInfo.create(mapWorld, addInfo, mut.immutable()).setState(getBlockState(mut)); + } + } + } + } + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillRecursiveIgnore(ServerLevelAccessor world, BlockPos start, Function ignore) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center: ends) { + for (Direction dir: Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + BlockState state = world.getBlockState(wpos); + boolean ign = ignore.apply(state); + if (!blocks.contains(bPos) && (ign || canReplace.apply(state))) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + PosInfo.create(mapWorld, addInfo, wpos).setState(ign ? state : getBlockState(bPos)); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + if (infos.size() > 0) { + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + if (canReplace.apply(world.getBlockState(info.getPos()))) { + BlocksHelper.setWithoutUpdate(world, info.getPos(), info.getState()); + } + }); + } + } + + public void fillRecursive(StructureWorld world, BlockPos start) { + Map mapWorld = Maps.newHashMap(); + Map addInfo = Maps.newHashMap(); + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center: ends) { + for (Direction dir: Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + + if (!blocks.contains(bPos)) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + BlockState state = getBlockState(wpos); + PosInfo.create(mapWorld, addInfo, wpos).setState(state); + add.add(bPos.immutable()); + } + } + } + } + + blocks.addAll(ends); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + List infos = new ArrayList(mapWorld.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + world.setBlock(info.getPos(), info.getState()); + }); + + infos.clear(); + infos.addAll(addInfo.values()); + Collections.sort(infos); + postProcesses.forEach((postProcess) -> { + infos.forEach((info) -> { + info.setState(postProcess.apply(info)); + }); + }); + infos.forEach((info) -> { + world.setBlock(info.getPos(), info.getState()); + }); + } + + public Set getPositions(ServerLevelAccessor world, BlockPos start) { + Set blocks = Sets.newHashSet(); + Set ends = Sets.newHashSet(); + Set add = Sets.newHashSet(); + ends.add(new BlockPos(0, 0, 0)); + boolean run = true; + + MutableBlockPos bPos = new MutableBlockPos(); + + while (run) { + for (BlockPos center: ends) { + for (Direction dir: Direction.values()) { + bPos.set(center).move(dir); + BlockPos wpos = bPos.offset(start); + BlockState state = world.getBlockState(wpos); + if (!blocks.contains(wpos) && canReplace.apply(state)) { + if (this.getDistance(bPos.getX(), bPos.getY(), bPos.getZ()) < 0) { + add.add(bPos.immutable()); + } + } + } + } + + ends.forEach((end) -> blocks.add(end.offset(start))); + ends.clear(); + ends.addAll(add); + add.clear(); + + run &= !ends.isEmpty(); + } + + return blocks; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFBinary.java b/src/main/java/ru/bclib/sdf/operator/SDFBinary.java new file mode 100644 index 00000000..0e223e54 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFBinary.java @@ -0,0 +1,34 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import ru.bclib.sdf.SDF; + +public abstract class SDFBinary extends SDF { + protected SDF sourceA; + protected SDF sourceB; + protected boolean firstValue; + + public SDFBinary setSourceA(SDF sourceA) { + this.sourceA = sourceA; + return this; + } + + public SDFBinary setSourceB(SDF sourceB) { + this.sourceB = sourceB; + return this; + } + + protected void selectValue(float a, float b) { + firstValue = a < b; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + if (firstValue) { + return sourceA.getBlockState(pos); + } else { + return sourceB.getBlockState(pos); + } + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java b/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java new file mode 100644 index 00000000..1b096f5e --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFCoordModify.java @@ -0,0 +1,22 @@ +package ru.bclib.sdf.operator; + +import java.util.function.Consumer; + +import com.mojang.math.Vector3f; + +public class SDFCoordModify extends SDFUnary { + private static final Vector3f POS = new Vector3f(); + private Consumer function; + + public SDFCoordModify setFunction(Consumer function) { + this.function = function; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + POS.set(x, y, z); + function.accept(POS); + return this.source.getDistance(POS.x(), POS.y(), POS.z()); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java b/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java new file mode 100644 index 00000000..2960f254 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFCopyRotate.java @@ -0,0 +1,19 @@ +package ru.bclib.sdf.operator; + +import ru.bclib.util.MHelper; + +public class SDFCopyRotate extends SDFUnary { + int count = 1; + + public SDFCopyRotate setCount(int count) { + this.count = count; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = (float) Math.atan2(x, z); + float pz = MHelper.length(x, z); + return this.source.getDistance(px, y, pz); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java b/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java new file mode 100644 index 00000000..5ca88dee --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFDisplacement.java @@ -0,0 +1,21 @@ +package ru.bclib.sdf.operator; + +import java.util.function.Function; + +import com.mojang.math.Vector3f; + +public class SDFDisplacement extends SDFUnary { + private static final Vector3f POS = new Vector3f(); + private Function displace; + + public SDFDisplacement setFunction(Function displace) { + this.displace = displace; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + POS.set(x, y, z); + return this.source.getDistance(x, y, z) + displace.apply(POS); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java b/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java new file mode 100644 index 00000000..d4d2174d --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFFlatWave.java @@ -0,0 +1,28 @@ +package ru.bclib.sdf.operator; + +public class SDFFlatWave extends SDFDisplacement { + private int rayCount = 1; + private float intensity; + private float angle; + + public SDFFlatWave() { + setFunction((pos) -> { + return (float) Math.cos(Math.atan2(pos.x(), pos.z()) * rayCount + angle) * intensity; + }); + } + + public SDFFlatWave setRaysCount(int count) { + this.rayCount = count; + return this; + } + + public SDFFlatWave setAngle(float angle) { + this.angle = angle; + return this; + } + + public SDFFlatWave setIntensity(float intensity) { + this.intensity = intensity; + return this; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java b/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java new file mode 100644 index 00000000..5629607a --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFHeightmap.java @@ -0,0 +1,64 @@ +package ru.bclib.sdf.operator; + +import com.mojang.blaze3d.platform.NativeImage; + +import net.minecraft.util.Mth; + +public class SDFHeightmap extends SDFDisplacement { + private float intensity = 1F; + private NativeImage map; + private float offsetX; + private float offsetZ; + private float scale; + private float cos = 1; + private float sin = 0; + + public SDFHeightmap() { + setFunction((pos) -> { + if (map == null) { + return 0F; + } + float px = Mth.clamp(pos.x() * scale + offsetX, 0, map.getWidth() - 2); + float pz = Mth.clamp(pos.z() * scale + offsetZ, 0, map.getHeight() - 2); + float dx = (px * cos - pz * sin); + float dz = (pz * cos + px * sin); + int x1 = Mth.floor(dx); + int z1 = Mth.floor(dz); + int x2 = x1 + 1; + int z2 = z1 + 1; + dx = dx - x1; + dz = dz - z1; + float a = (map.getPixelRGBA(x1, z1) & 255) / 255F; + float b = (map.getPixelRGBA(x2, z1) & 255) / 255F; + float c = (map.getPixelRGBA(x1, z2) & 255) / 255F; + float d = (map.getPixelRGBA(x2, z2) & 255) / 255F; + a = Mth.lerp(dx, a, b); + b = Mth.lerp(dx, c, d); + return -Mth.lerp(dz, a, b) * intensity; + }); + } + + public SDFHeightmap setMap(NativeImage map) { + this.map = map; + offsetX = map.getWidth() * 0.5F; + offsetZ = map.getHeight() * 0.5F; + scale = map.getWidth(); + return this; + } + + public SDFHeightmap setAngle(float angle) { + sin = Mth.sin(angle); + cos = Mth.cos(angle); + return this; + } + + public SDFHeightmap setScale(float scale) { + this.scale = map.getWidth() * scale; + return this; + } + + public SDFHeightmap setIntensity(float intensity) { + this.intensity = intensity; + return this; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java b/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java new file mode 100644 index 00000000..d5ef436c --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFIntersection.java @@ -0,0 +1,13 @@ +package ru.bclib.sdf.operator; + +import ru.bclib.util.MHelper; + +public class SDFIntersection extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.max(a, b); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFInvert.java b/src/main/java/ru/bclib/sdf/operator/SDFInvert.java new file mode 100644 index 00000000..b6cb1be0 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFInvert.java @@ -0,0 +1,8 @@ +package ru.bclib.sdf.operator; + +public class SDFInvert extends SDFUnary { + @Override + public float getDistance(float x, float y, float z) { + return -this.source.getDistance(x, y, z); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java b/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java new file mode 100644 index 00000000..921768db --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFRadialNoiseMap.java @@ -0,0 +1,60 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.util.Mth; +import ru.bclib.noise.OpenSimplexNoise; +import ru.bclib.util.MHelper; + +public class SDFRadialNoiseMap extends SDFDisplacement { + private static final float SIN = Mth.sin(0.5F); + private static final float COS = Mth.cos(0.5F); + + private OpenSimplexNoise noise; + private float intensity = 1F; + private float radius = 1F; + private short offsetX; + private short offsetZ; + + public SDFRadialNoiseMap() { + setFunction((pos) -> { + if (intensity == 0) { + return 0F; + } + float px = pos.x() / radius; + float pz = pos.z() / radius; + float distance = MHelper.lengthSqr(px, pz); + if (distance > 1) { + return 0F; + } + distance = 1 - Mth.sqrt(distance); + float nx = px * COS - pz * SIN; + float nz = pz * COS + px * SIN; + distance *= getNoise(nx * 0.75 + offsetX, nz * 0.75 + offsetZ); + return distance * intensity; + }); + } + + private float getNoise(double x, double z) { + return (float) noise.eval(x, z) + (float) noise.eval(x * 3 + 1000, z * 3) * 0.5F + (float) noise.eval(x * 9 + 1000, z * 9) * 0.2F; + } + + public SDFRadialNoiseMap setSeed(long seed) { + noise = new OpenSimplexNoise(seed); + return this; + } + + public SDFRadialNoiseMap setIntensity(float intensity) { + this.intensity = intensity; + return this; + } + + public SDFRadialNoiseMap setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFRadialNoiseMap setOffset(int x, int z) { + offsetX = (short) (x & 32767); + offsetZ = (short) (z & 32767); + return this; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRotation.java b/src/main/java/ru/bclib/sdf/operator/SDFRotation.java new file mode 100644 index 00000000..67e65ebf --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFRotation.java @@ -0,0 +1,21 @@ +package ru.bclib.sdf.operator; + +import com.mojang.math.Quaternion; +import com.mojang.math.Vector3f; + +public class SDFRotation extends SDFUnary { + private static final Vector3f POS = new Vector3f(); + private Quaternion rotation; + + public SDFRotation setRotation(Vector3f axis, float rotationAngle) { + rotation = new Quaternion(axis, rotationAngle, false); + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + POS.set(x, y, z); + POS.transform(rotation); + return source.getDistance(POS.x(), POS.y(), POS.z()); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFRound.java b/src/main/java/ru/bclib/sdf/operator/SDFRound.java new file mode 100644 index 00000000..fd5b0e51 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFRound.java @@ -0,0 +1,15 @@ +package ru.bclib.sdf.operator; + +public class SDFRound extends SDFUnary { + private float radius; + + public SDFRound setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return this.source.getDistance(x, y, z) - radius; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFScale.java b/src/main/java/ru/bclib/sdf/operator/SDFScale.java new file mode 100644 index 00000000..18ee0eaf --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFScale.java @@ -0,0 +1,15 @@ +package ru.bclib.sdf.operator; + +public class SDFScale extends SDFUnary { + private float scale; + + public SDFScale setScale(float scale) { + this.scale = scale; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x / scale, y / scale, z / scale) * scale; + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java b/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java new file mode 100644 index 00000000..3693d751 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFScale3D.java @@ -0,0 +1,19 @@ +package ru.bclib.sdf.operator; + +public class SDFScale3D extends SDFUnary { + private float x; + private float y; + private float z; + + public SDFScale3D setScale(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x / this.x, y / this.y, z / this.z); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java new file mode 100644 index 00000000..393d66d2 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFSmoothIntersection.java @@ -0,0 +1,21 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothIntersection extends SDFBinary { + private float radius; + + public SDFSmoothIntersection setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F - 0.5F * (b - a) / radius, 0F, 1F); + return Mth.lerp(h, b, a) + radius * h * (1F - h); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java new file mode 100644 index 00000000..020b8590 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFSmoothSubtraction.java @@ -0,0 +1,21 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothSubtraction extends SDFBinary { + private float radius; + + public SDFSmoothSubtraction setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F - 0.5F * (b + a) / radius, 0F, 1F); + return Mth.lerp(h, b, -a) + radius * h * (1F - h); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java b/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java new file mode 100644 index 00000000..493c1c9b --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFSmoothUnion.java @@ -0,0 +1,21 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.util.Mth; + +public class SDFSmoothUnion extends SDFBinary { + private float radius; + + public SDFSmoothUnion setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + float h = Mth.clamp(0.5F + 0.5F * (b - a) / radius, 0F, 1F); + return Mth.lerp(h, b, a) - radius * h * (1F - h); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java b/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java new file mode 100644 index 00000000..d0356868 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFSubtraction.java @@ -0,0 +1,13 @@ +package ru.bclib.sdf.operator; + +import ru.bclib.util.MHelper; + +public class SDFSubtraction extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.max(a, -b); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java b/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java new file mode 100644 index 00000000..99168225 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFTranslate.java @@ -0,0 +1,19 @@ +package ru.bclib.sdf.operator; + +public class SDFTranslate extends SDFUnary { + float x; + float y; + float z; + + public SDFTranslate setTranslate(float x, float y, float z) { + this.x = x; + this.y = y; + this.z = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return source.getDistance(x - this.x, y - this.y, z - this.z); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFUnary.java b/src/main/java/ru/bclib/sdf/operator/SDFUnary.java new file mode 100644 index 00000000..83882752 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFUnary.java @@ -0,0 +1,19 @@ +package ru.bclib.sdf.operator; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import ru.bclib.sdf.SDF; + +public abstract class SDFUnary extends SDF { + protected SDF source; + + public SDFUnary setSource(SDF source) { + this.source = source; + return this; + } + + @Override + public BlockState getBlockState(BlockPos pos) { + return source.getBlockState(pos); + } +} diff --git a/src/main/java/ru/bclib/sdf/operator/SDFUnion.java b/src/main/java/ru/bclib/sdf/operator/SDFUnion.java new file mode 100644 index 00000000..b9db5ab0 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/operator/SDFUnion.java @@ -0,0 +1,13 @@ +package ru.bclib.sdf.operator; + +import ru.bclib.util.MHelper; + +public class SDFUnion extends SDFBinary { + @Override + public float getDistance(float x, float y, float z) { + float a = this.sourceA.getDistance(x, y, z); + float b = this.sourceB.getDistance(x, y, z); + this.selectValue(a, b); + return MHelper.min(a, b); + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java b/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java new file mode 100644 index 00000000..891253da --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFCappedCone.java @@ -0,0 +1,39 @@ +package ru.bclib.sdf.primitive; + +import net.minecraft.util.Mth; +import ru.bclib.util.MHelper; + +public class SDFCappedCone extends SDFPrimitive { + private float radius1; + private float radius2; + private float height; + + public SDFCappedCone setRadius1(float radius) { + this.radius1 = radius; + return this; + } + + public SDFCappedCone setRadius2(float radius) { + this.radius2 = radius; + return this; + } + + public SDFCappedCone setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float qx = MHelper.length(x, z); + float k2x = radius2 - radius1; + float k2y = 2 * height; + float cax = qx - MHelper.min(qx, (y < 0F) ? radius1 : radius2); + float cay = Math.abs(y) - height; + float mlt = Mth.clamp(MHelper.dot(radius2 - qx, height - y, k2x, k2y) / MHelper.dot(k2x, k2y, k2x, k2y), 0F, 1F); + float cbx = qx - radius2 + k2x * mlt; + float cby = y - height + k2y * mlt; + float s = (cbx < 0F && cay < 0F) ? -1F : 1F; + return s * (float) Math.sqrt(MHelper.min(MHelper.dot(cax, cay, cax, cay), MHelper.dot(cbx, cby, cbx, cby))); + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java b/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java new file mode 100644 index 00000000..a29f724e --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFCapsule.java @@ -0,0 +1,24 @@ +package ru.bclib.sdf.primitive; + +import net.minecraft.util.Mth; +import ru.bclib.util.MHelper; + +public class SDFCapsule extends SDFPrimitive { + private float radius; + private float height; + + public SDFCapsule setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFCapsule setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return MHelper.length(x, y - Mth.clamp(y, 0, height), z) - radius; + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java b/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java new file mode 100644 index 00000000..1f06cbf5 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFFlatland.java @@ -0,0 +1,8 @@ +package ru.bclib.sdf.primitive; + +public class SDFFlatland extends SDFPrimitive { + @Override + public float getDistance(float x, float y, float z) { + return y; + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java b/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java new file mode 100644 index 00000000..23a5b55c --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFHexPrism.java @@ -0,0 +1,26 @@ +package ru.bclib.sdf.primitive; + +import ru.bclib.util.MHelper; + +public class SDFHexPrism extends SDFPrimitive { + private float radius; + private float height; + + public SDFHexPrism setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFHexPrism setHeight(float height) { + this.height = height; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = Math.abs(x); + float py = Math.abs(y); + float pz = Math.abs(z); + return MHelper.max(py - height, MHelper.max((px * 0.866025F + pz * 0.5F), pz) - radius); + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFLine.java b/src/main/java/ru/bclib/sdf/primitive/SDFLine.java new file mode 100644 index 00000000..9fd316d7 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFLine.java @@ -0,0 +1,49 @@ +package ru.bclib.sdf.primitive; + +import net.minecraft.util.Mth; +import ru.bclib.util.MHelper; + +public class SDFLine extends SDFPrimitive { + private float radius; + private float x1; + private float y1; + private float z1; + private float x2; + private float y2; + private float z2; + + public SDFLine setRadius(float radius) { + this.radius = radius; + return this; + } + + public SDFLine setStart(float x, float y, float z) { + this.x1 = x; + this.y1 = y; + this.z1 = z; + return this; + } + + public SDFLine setEnd(float x, float y, float z) { + this.x2 = x; + this.y2 = y; + this.z2 = z; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float pax = x - x1; + float pay = y - y1; + float paz = z - z1; + + float bax = x2 - x1; + float bay = y2 - y1; + float baz = z2 - z1; + + float dpb = MHelper.dot(pax, pay, paz, bax, bay, baz); + float dbb = MHelper.dot(bax, bay, baz, bax, bay, baz); + float h = Mth.clamp(dpb / dbb, 0F, 1F); + return MHelper.length(pax - bax * h, pay - bay * h, paz - baz * h) - radius; + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFPie.java b/src/main/java/ru/bclib/sdf/primitive/SDFPie.java new file mode 100644 index 00000000..0ad180f4 --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFPie.java @@ -0,0 +1,31 @@ +package ru.bclib.sdf.primitive; + +import net.minecraft.util.Mth; +import ru.bclib.util.MHelper; + +public class SDFPie extends SDFPrimitive { + private float sin; + private float cos; + private float radius; + + public SDFPie setAngle(float angle) { + this.sin = (float) Math.sin(angle); + this.cos = (float) Math.cos(angle); + return this; + } + + public SDFPie setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + float px = Math.abs(x); + float l = MHelper.length(px, y, z) - radius; + float m = MHelper.dot(px, z, sin, cos); + m = Mth.clamp(m, 0, radius); + m = MHelper.length(px - sin * m, z - cos * m); + return MHelper.max(l, m * (float) Math.signum(cos * px - sin * z)); + } +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java b/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java new file mode 100644 index 00000000..e40b61ab --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFPrimitive.java @@ -0,0 +1,39 @@ +package ru.bclib.sdf.primitive; + +import java.util.function.Function; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import ru.bclib.sdf.SDF; + +public abstract class SDFPrimitive extends SDF { + protected Function placerFunction; + + public SDFPrimitive setBlock(Function placerFunction) { + this.placerFunction = placerFunction; + return this; + } + + public SDFPrimitive setBlock(BlockState state) { + this.placerFunction = (pos) -> { + return state; + }; + return this; + } + + public SDFPrimitive setBlock(Block block) { + this.placerFunction = (pos) -> { + return block.defaultBlockState(); + }; + return this; + } + + public BlockState getBlockState(BlockPos pos) { + return placerFunction.apply(pos); + } + + /*public abstract CompoundTag toNBT(CompoundTag root) { + + }*/ +} diff --git a/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java b/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java new file mode 100644 index 00000000..c8ff092b --- /dev/null +++ b/src/main/java/ru/bclib/sdf/primitive/SDFSphere.java @@ -0,0 +1,17 @@ +package ru.bclib.sdf.primitive; + +import ru.bclib.util.MHelper; + +public class SDFSphere extends SDFPrimitive { + private float radius; + + public SDFSphere setRadius(float radius) { + this.radius = radius; + return this; + } + + @Override + public float getDistance(float x, float y, float z) { + return MHelper.length(x, y, z) - radius; + } +} diff --git a/src/main/java/ru/bclib/structures/StructureWorld.java b/src/main/java/ru/bclib/structures/StructureWorld.java new file mode 100644 index 00000000..78b1c05f --- /dev/null +++ b/src/main/java/ru/bclib/structures/StructureWorld.java @@ -0,0 +1,170 @@ +package ru.bclib.structures; + +import java.util.Map; + +import com.google.common.collect.Maps; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.levelgen.structure.BoundingBox; + +public class StructureWorld { + private Map parts = Maps.newHashMap(); + private ChunkPos lastPos; + private Part lastPart; + private int minX = Integer.MAX_VALUE; + private int minY = Integer.MAX_VALUE; + private int minZ = Integer.MAX_VALUE; + private int maxX = Integer.MIN_VALUE; + private int maxY = Integer.MIN_VALUE; + private int maxZ = Integer.MIN_VALUE; + + public StructureWorld() {} + + public StructureWorld(CompoundTag tag) { + minX = tag.getInt("minX"); + maxX = tag.getInt("maxX"); + minY = tag.getInt("minY"); + maxY = tag.getInt("maxY"); + minZ = tag.getInt("minZ"); + maxZ = tag.getInt("maxZ"); + + ListTag map = tag.getList("parts", 10); + map.forEach((element) -> { + CompoundTag compound = (CompoundTag) element; + Part part = new Part(compound); + int x = compound.getInt("x"); + int z = compound.getInt("z"); + parts.put(new ChunkPos(x, z), part); + }); + } + + public void setBlock(BlockPos pos, BlockState state) { + ChunkPos cPos = new ChunkPos(pos); + + if (cPos.equals(lastPos)) { + lastPart.addBlock(pos, state); + return; + } + + Part part = parts.get(cPos); + if (part == null) { + part = new Part(); + parts.put(cPos, part); + + if (cPos.x < minX) minX = cPos.x; + if (cPos.x > maxX) maxX = cPos.x; + if (cPos.z < minZ) minZ = cPos.z; + if (cPos.z > maxZ) maxZ = cPos.z; + } + if (pos.getY() < minY) minY = pos.getY(); + if (pos.getY() > maxY) maxY = pos.getY(); + part.addBlock(pos, state); + + lastPos = cPos; + lastPart = part; + } + + public boolean placeChunk(WorldGenLevel world, ChunkPos chunkPos) { + Part part = parts.get(chunkPos); + if (part != null) { + ChunkAccess chunk = world.getChunk(chunkPos.x, chunkPos.z); + part.placeChunk(chunk); + return true; + } + return false; + } + + public CompoundTag toBNT() { + CompoundTag tag = new CompoundTag(); + tag.putInt("minX", minX); + tag.putInt("maxX", maxX); + tag.putInt("minY", minY); + tag.putInt("maxY", maxY); + tag.putInt("minZ", minZ); + tag.putInt("maxZ", maxZ); + ListTag map = new ListTag(); + tag.put("parts", map); + parts.forEach((pos, part) -> { + map.add(part.toNBT(pos.x, pos.z)); + }); + return tag; + } + + public BoundingBox getBounds() { + if (minX == Integer.MAX_VALUE || maxX == Integer.MIN_VALUE || minZ == Integer.MAX_VALUE || maxZ == Integer.MIN_VALUE) { + return BoundingBox.getUnknownBox(); + } + return new BoundingBox(minX << 4, minY, minZ << 4, (maxX << 4) | 15, maxY, (maxZ << 4) | 15); + } + + private static final class Part { + Map blocks = Maps.newHashMap(); + + public Part() {} + + public Part(CompoundTag tag) { + ListTag map = tag.getList("blocks", 10); + ListTag map2 = tag.getList("states", 10); + BlockState[] states = new BlockState[map2.size()]; + for (int i = 0; i < states.length; i++) { + states[i] = NbtUtils.readBlockState((CompoundTag) map2.get(i)); + } + + map.forEach((element) -> { + CompoundTag block = (CompoundTag) element; + BlockPos pos = NbtUtils.readBlockPos(block.getCompound("pos")); + int stateID = block.getInt("state"); + BlockState state = stateID < states.length ? states[stateID] : Block.stateById(stateID); + blocks.put(pos, state); + }); + } + + void addBlock(BlockPos pos, BlockState state) { + BlockPos inner = new BlockPos(pos.getX() & 15, pos.getY(), pos.getZ() & 15); + blocks.put(inner, state); + } + + void placeChunk(ChunkAccess chunk) { + blocks.forEach((pos, state) -> { + chunk.setBlockState(pos, state, false); + }); + } + + CompoundTag toNBT(int x, int z) { + CompoundTag tag = new CompoundTag(); + tag.putInt("x", x); + tag.putInt("z", z); + ListTag map = new ListTag(); + tag.put("blocks", map); + ListTag stateMap = new ListTag(); + tag.put("states", stateMap); + + int[] id = new int[1]; + Map states = Maps.newHashMap(); + + blocks.forEach((pos, state) -> { + int stateID = states.getOrDefault(states, -1); + if (stateID < 0) { + stateID = id[0] ++; + states.put(state, stateID); + stateMap.add(NbtUtils.writeBlockState(state)); + } + + CompoundTag block = new CompoundTag(); + block.put("pos", NbtUtils.writeBlockPos(pos)); + block.putInt("state", stateID); + map.add(block); + }); + + return tag; + } + } +} diff --git a/src/main/java/ru/bclib/util/BlocksHelper.java b/src/main/java/ru/bclib/util/BlocksHelper.java new file mode 100644 index 00000000..b4940728 --- /dev/null +++ b/src/main/java/ru/bclib/util/BlocksHelper.java @@ -0,0 +1,171 @@ +package ru.bclib.util; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import com.google.common.collect.Maps; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.Property; + +public class BlocksHelper { + public static final BooleanProperty ROOTS = BooleanProperty.create("roots"); + private static final Map COLOR_BY_BLOCK = Maps.newHashMap(); + + public static final int FLAG_UPDATE_BLOCK = 1; + public static final int FLAG_SEND_CLIENT_CHANGES = 2; + public static final int FLAG_NO_RERENDER = 4; + public static final int FORSE_RERENDER = 8; + public static final int FLAG_IGNORE_OBSERVERS = 16; + + public static final int SET_SILENT = FLAG_UPDATE_BLOCK | FLAG_IGNORE_OBSERVERS | FLAG_SEND_CLIENT_CHANGES; + public static final int SET_OBSERV = FLAG_UPDATE_BLOCK | FLAG_SEND_CLIENT_CHANGES; + public static final Direction[] HORIZONTAL = makeHorizontal(); + public static final Direction[] DIRECTIONS = Direction.values(); + + private static final MutableBlockPos POS = new MutableBlockPos(); + protected static final BlockState AIR = Blocks.AIR.defaultBlockState(); + protected static final BlockState WATER = Blocks.WATER.defaultBlockState(); + + private static final Vec3i[] OFFSETS = new Vec3i[] { + new Vec3i(-1, -1, -1), new Vec3i(-1, -1, 0), new Vec3i(-1, -1, 1), + new Vec3i(-1, 0, -1), new Vec3i(-1, 0, 0), new Vec3i(-1, 0, 1), + new Vec3i(-1, 1, -1), new Vec3i(-1, 1, 0), new Vec3i(-1, 1, 1), + + new Vec3i(0, -1, -1), new Vec3i(0, -1, 0), new Vec3i(0, -1, 1), + new Vec3i(0, 0, -1), new Vec3i(0, 0, 0), new Vec3i(0, 0, 1), + new Vec3i(0, 1, -1), new Vec3i(0, 1, 0), new Vec3i(0, 1, 1), + + new Vec3i(1, -1, -1), new Vec3i(1, -1, 0), new Vec3i(1, -1, 1), + new Vec3i(1, 0, -1), new Vec3i(1, 0, 0), new Vec3i(1, 0, 1), + new Vec3i(1, 1, -1), new Vec3i(1, 1, 0), new Vec3i(1, 1, 1) + }; + + public static void addBlockColor(Block block, int color) { + COLOR_BY_BLOCK.put(block, color); + } + + public static int getBlockColor(Block block) { + return COLOR_BY_BLOCK.getOrDefault(block, 0xFF000000); + } + + public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, BlockState state) { + world.setBlock(pos, state, SET_SILENT); + } + + public static void setWithoutUpdate(LevelAccessor world, BlockPos pos, Block block) { + world.setBlock(pos, block.defaultBlockState(), SET_SILENT); + } + + public static void setWithUpdate(LevelAccessor world, BlockPos pos, BlockState state) { + world.setBlock(pos, state, SET_OBSERV); + } + + public static void setWithUpdate(LevelAccessor world, BlockPos pos, Block block) { + world.setBlock(pos, block.defaultBlockState(), SET_OBSERV); + } + + public static int upRay(LevelAccessor world, BlockPos pos, int maxDist) { + int length = 0; + for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.above(j))); j++) { + length++; + } + return length; + } + + public static int downRay(LevelAccessor world, BlockPos pos, int maxDist) { + int length = 0; + for (int j = 1; j < maxDist && (world.isEmptyBlock(pos.below(j))); j++) { + length++; + } + return length; + } + + public static int downRayRep(LevelAccessor world, BlockPos pos, int maxDist) { + POS.set(pos); + for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { + POS.setY(POS.getY() - 1); + } + return pos.getY() - POS.getY(); + } + + public static int raycastSqr(LevelAccessor world, BlockPos pos, int dx, int dy, int dz, int maxDist) { + POS.set(pos); + for (int j = 1; j < maxDist && (world.getBlockState(POS)).getMaterial().isReplaceable(); j++) { + POS.move(dx, dy, dz); + } + return (int) pos.distSqr(POS); + } + + public static BlockState rotateHorizontal(BlockState state, Rotation rotation, Property facing) { + return state.setValue(facing, rotation.rotate(state.getValue(facing))); + } + + public static BlockState mirrorHorizontal(BlockState state, Mirror mirror, Property facing) { + return state.rotate(mirror.getRotation(state.getValue(facing))); + } + + public static int getLengthDown(LevelAccessor world, BlockPos pos, Block block) { + int count = 1; + while (world.getBlockState(pos.below(count)).getBlock() == block) { + count++; + } + return count; + } + + public static void cover(LevelAccessor world, BlockPos center, Block ground, BlockState cover, int radius, Random random) { + HashSet points = new HashSet(); + HashSet points2 = new HashSet(); + if (world.getBlockState(center).getBlock() == ground) { + points.add(center); + points2.add(center); + for (int i = 0; i < radius; i++) { + Iterator iterator = points.iterator(); + while (iterator.hasNext()) { + BlockPos pos = iterator.next(); + for (Vec3i offset : OFFSETS) { + if (random.nextBoolean()) { + BlockPos pos2 = pos.offset(offset); + if (random.nextBoolean() && world.getBlockState(pos2).getBlock() == ground + && !points.contains(pos2)) + points2.add(pos2); + } + } + } + points.addAll(points2); + points2.clear(); + } + Iterator iterator = points.iterator(); + while (iterator.hasNext()) { + BlockPos pos = iterator.next(); + BlocksHelper.setWithoutUpdate(world, pos, cover); + } + } + } + + public static Direction[] makeHorizontal() { + return new Direction[] { Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST }; + } + + public static Direction randomHorizontal(Random random) { + return HORIZONTAL[random.nextInt(4)]; + } + + public static Direction randomDirection(Random random) { + return DIRECTIONS[random.nextInt(6)]; + } + + public static boolean isFluid(BlockState blockState) { + return !blockState.getFluidState().isEmpty(); + } +} diff --git a/src/main/java/ru/bclib/util/SplineHelper.java b/src/main/java/ru/bclib/util/SplineHelper.java new file mode 100644 index 00000000..0b1e8a75 --- /dev/null +++ b/src/main/java/ru/bclib/util/SplineHelper.java @@ -0,0 +1,344 @@ +package ru.bclib.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import java.util.function.Function; + +import com.google.common.collect.Lists; +import com.mojang.math.Vector3f; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.util.Mth; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.block.state.BlockState; +import ru.bclib.sdf.SDF; +import ru.bclib.sdf.operator.SDFUnion; +import ru.bclib.sdf.primitive.SDFLine; + +public class SplineHelper { + public static List makeSpline(float x1, float y1, float z1, float x2, float y2, float z2, int points) { + List spline = Lists.newArrayList(); + spline.add(new Vector3f(x1, y1, z1)); + int count = points - 1; + for (int i = 1; i < count; i++) { + float delta = (float) i / (float) count; + float x = Mth.lerp(delta, x1, x2); + float y = Mth.lerp(delta, y1, y2); + float z = Mth.lerp(delta, z1, z2); + spline.add(new Vector3f(x, y, z)); + } + spline.add(new Vector3f(x2, y2, z2)); + return spline; + } + + public static List smoothSpline(List spline, int segmentPoints) { + List result = Lists.newArrayList(); + Vector3f start = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f end = spline.get(i); + for (int j = 0; j < segmentPoints; j++) { + float delta = (float) j / segmentPoints; + delta = 0.5F - 0.5F * Mth.cos(delta * 3.14159F); + result.add(lerp(start, end, delta)); + } + start = end; + } + result.add(start); + return result; + } + + private static Vector3f lerp(Vector3f start, Vector3f end, float delta) { + float x = Mth.lerp(delta, start.x(), end.x()); + float y = Mth.lerp(delta, start.y(), end.y()); + float z = Mth.lerp(delta, start.z(), end.z()); + return new Vector3f(x, y, z); + } + + public static void offsetParts(List spline, Random random, float dx, float dy, float dz) { + int count = spline.size(); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float x = pos.x() + (float) random.nextGaussian() * dx; + float y = pos.y() + (float) random.nextGaussian() * dy; + float z = pos.z() + (float) random.nextGaussian() * dz; + pos.set(x, y, z); + } + } + + public static void powerOffset(List spline, float distance, float power) { + int count = spline.size(); + float max = count + 1; + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float x = (float) i / max; + float y = pos.y() + (float) Math.pow(x, power) * distance; + pos.set(pos.x(), y, pos.z()); + } + } + + public static SDF buildSDF(List spline, float radius1, float radius2, Function placerFunction) { + int count = spline.size(); + float max = count - 2; + SDF result = null; + Vector3f start = spline.get(0); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float delta = (float) (i - 1) / max; + SDF line = new SDFLine() + .setRadius(Mth.lerp(delta, radius1, radius2)) + .setStart(start.x(), start.y(), start.z()) + .setEnd(pos.x(), pos.y(), pos.z()) + .setBlock(placerFunction); + result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); + start = pos; + } + return result; + } + + public static SDF buildSDF(List spline, Function radiusFunction, Function placerFunction) { + int count = spline.size(); + float max = count - 2; + SDF result = null; + Vector3f start = spline.get(0); + for (int i = 1; i < count; i++) { + Vector3f pos = spline.get(i); + float delta = (float) (i - 1) / max; + SDF line = new SDFLine() + .setRadius(radiusFunction.apply(delta)) + .setStart(start.x(), start.y(), start.z()) + .setEnd(pos.x(), pos.y(), pos.z()) + .setBlock(placerFunction); + result = result == null ? line : new SDFUnion().setSourceA(result).setSourceB(line); + start = pos; + } + return result; + } + + public static boolean fillSpline(List spline, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { + Vector3f startPos = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f endPos = spline.get(i); + if (!(fillLine(startPos, endPos, world, state, pos, replace))) { + return false; + } + startPos = endPos; + } + + return true; + } + + public static void fillSplineForce(List spline, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { + Vector3f startPos = spline.get(0); + for (int i = 1; i < spline.size(); i++) { + Vector3f endPos = spline.get(i); + fillLineForce(startPos, endPos, world, state, pos, replace); + startPos = endPos; + } + } + + public static boolean fillLine(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { + float dx = end.x() - start.x(); + float dy = end.y() - start.y(); + float dz = end.z() - start.z(); + float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); + int count = MHelper.floor(max + 1); + dx /= max; + dy /= max; + dz /= max; + float x = start.x(); + float y = start.y(); + float z = start.z(); + boolean down = Math.abs(dy) > 0.2; + + BlockState bState; + MutableBlockPos bPos = new MutableBlockPos(); + for (int i = 0; i < count; i++) { + bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); + bState = world.getBlockState(bPos); + if (bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + else { + return false; + } + x += dx; + y += dy; + z += dz; + } + bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); + bState = world.getBlockState(bPos); + if (bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && bState.equals(state) || replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + return true; + } + else { + return false; + } + } + + public static void fillLineForce(Vector3f start, Vector3f end, WorldGenLevel world, BlockState state, BlockPos pos, Function replace) { + float dx = end.x() - start.x(); + float dy = end.y() - start.y(); + float dz = end.z() - start.z(); + float max = MHelper.max(Math.abs(dx), Math.abs(dy), Math.abs(dz)); + int count = MHelper.floor(max + 1); + dx /= max; + dy /= max; + dz /= max; + float x = start.x(); + float y = start.y(); + float z = start.z(); + boolean down = Math.abs(dy) > 0.2; + + BlockState bState; + MutableBlockPos bPos = new MutableBlockPos(); + for (int i = 0; i < count; i++) { + bPos.set(x + pos.getX(), y + pos.getY(), z + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + x += dx; + y += dy; + z += dz; + } + bPos.set(end.x() + pos.getX(), end.y() + pos.getY(), end.z() + pos.getZ()); + bState = world.getBlockState(bPos); + if (replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + bPos.setY(bPos.getY() - 1); + bState = world.getBlockState(bPos); + if (down && replace.apply(bState)) { + BlocksHelper.setWithoutUpdate(world, bPos, state); + } + } + } + + public static boolean canGenerate(List spline, float scale, BlockPos start, WorldGenLevel world, Function canReplace) { + int count = spline.size(); + Vector3f vec = spline.get(0); + MutableBlockPos mut = new MutableBlockPos(); + float x1 = start.getX() + vec.x() * scale; + float y1 = start.getY() + vec.y() * scale; + float z1 = start.getZ() + vec.z() * scale; + for (int i = 1; i < count; i++) { + vec = spline.get(i); + float x2 = start.getX() + vec.x() * scale; + float y2 = start.getY() + vec.y() * scale; + float z2 = start.getZ() + vec.z() * scale; + + for (float py = y1; py < y2; py += 3) { + if (py - start.getY() < 10) continue; + float lerp = (py - y1) / (y2 - y1); + float x = Mth.lerp(lerp, x1, x2); + float z = Mth.lerp(lerp, z1, z2); + mut.set(x, py, z); + if (!canReplace.apply(world.getBlockState(mut))) { + return false; + } + } + + x1 = x2; + y1 = y2; + z1 = z2; + } + return true; + } + + public static boolean canGenerate(List spline, BlockPos start, WorldGenLevel world, Function canReplace) { + int count = spline.size(); + Vector3f vec = spline.get(0); + MutableBlockPos mut = new MutableBlockPos(); + float x1 = start.getX() + vec.x(); + float y1 = start.getY() + vec.y(); + float z1 = start.getZ() + vec.z(); + for (int i = 1; i < count; i++) { + vec = spline.get(i); + float x2 = start.getX() + vec.x(); + float y2 = start.getY() + vec.y(); + float z2 = start.getZ() + vec.z(); + + for (float py = y1; py < y2; py += 3) { + if (py - start.getY() < 10) continue; + float lerp = (py - y1) / (y2 - y1); + float x = Mth.lerp(lerp, x1, x2); + float z = Mth.lerp(lerp, z1, z2); + mut.set(x, py, z); + if (!canReplace.apply(world.getBlockState(mut))) { + return false; + } + } + + x1 = x2; + y1 = y2; + z1 = z2; + } + return true; + } + + public static Vector3f getPos(List spline, float index) { + int i = (int) index; + int last = spline.size() - 1; + if (i >= last) { + return spline.get(last); + } + float delta = index - i; + Vector3f p1 = spline.get(i); + Vector3f p2 = spline.get(i + 1); + float x = Mth.lerp(delta, p1.x(), p2.x()); + float y = Mth.lerp(delta, p1.y(), p2.y()); + float z = Mth.lerp(delta, p1.z(), p2.z()); + return new Vector3f(x, y, z); + } + + public static void rotateSpline(List spline, float angle) { + for (Vector3f v: spline) { + float sin = (float) Math.sin(angle); + float cos = (float) Math.cos(angle); + float x = v.x() * cos + v.z() * sin; + float z = v.x() * sin + v.z() * cos; + v.set(x, v.y(), z); + } + } + + public static List copySpline(List spline) { + List result = new ArrayList(spline.size()); + for (Vector3f v: spline) { + result.add(new Vector3f(v.x(), v.y(), v.z())); + } + return result; + } + + public static void scale(List spline, float scale) { + scale(spline, scale, scale, scale); + } + + public static void scale(List spline, float x, float y, float z) { + for (Vector3f v: spline) { + v.set(v.x() * x, v.y() * y, v.z() * z); + } + } + + public static void offset(List spline, Vector3f offset) { + for (Vector3f v: spline) { + v.set(offset.x() + v.x(), offset.y() + v.y(), offset.z() + v.z()); + } + } +} diff --git a/src/main/java/ru/bclib/util/TagHelper.java b/src/main/java/ru/bclib/util/TagHelper.java new file mode 100644 index 00000000..f5dc604d --- /dev/null +++ b/src/main/java/ru/bclib/util/TagHelper.java @@ -0,0 +1,73 @@ +package ru.bclib.util; + +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.Tag; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.ItemLike; +import net.minecraft.world.level.block.Block; + +public class TagHelper { + private static final Map> TAGS_BLOCK = Maps.newConcurrentMap(); + private static final Map> TAGS_ITEM = Maps.newConcurrentMap(); + + public static void addTag(Tag.Named tag, Block... blocks) { + ResourceLocation tagID = tag.getName(); + Set set = TAGS_BLOCK.computeIfAbsent(tagID, k -> Sets.newHashSet()); + for (Block block: blocks) { + ResourceLocation id = Registry.BLOCK.getKey(block); + if (id != Registry.BLOCK.getDefaultKey()) { + set.add(id); + } + } + } + + public static void addTag(Tag.Named tag, ItemLike... items) { + ResourceLocation tagID = tag.getName(); + Set set = TAGS_ITEM.computeIfAbsent(tagID, k -> Sets.newHashSet()); + for (ItemLike item: items) { + ResourceLocation id = Registry.ITEM.getKey(item.asItem()); + if (id != Registry.ITEM.getDefaultKey()) { + set.add(id); + } + } + } + + @SafeVarargs + public static void addTags(ItemLike item, Tag.Named... tags) { + for (Tag.Named tag: tags) { + addTag(tag, item); + } + } + + @SafeVarargs + public static void addTags(Block block, Tag.Named... tags) { + for (Tag.Named tag: tags) { + addTag(tag, block); + } + } + + public static Tag.Builder apply(Tag.Builder builder, Set ids) { + ids.forEach(value -> builder.addElement(value, "Better End Code")); + return builder; + } + + public static Map apply(String entry, Map tagsMap) { + Map> endTags = null; + if (entry.equals("block")) { + endTags = TAGS_BLOCK; + } else if (entry.equals("item")) { + endTags = TAGS_ITEM; + } + if (endTags != null) { + endTags.forEach((id, ids) -> apply(tagsMap.computeIfAbsent(id, key -> Tag.Builder.tag()), ids)); + } + return tagsMap; + } +} diff --git a/src/main/java/ru/bclib/util/TranslationHelper.java b/src/main/java/ru/bclib/util/TranslationHelper.java new file mode 100644 index 00000000..68ad5412 --- /dev/null +++ b/src/main/java/ru/bclib/util/TranslationHelper.java @@ -0,0 +1,119 @@ +package ru.bclib.util; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Collections; +import java.util.List; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import net.minecraft.core.Registry; +import net.minecraft.data.BuiltinRegistries; +import net.minecraft.resources.ResourceLocation; + +public class TranslationHelper { + public static void printMissingNames(String modID) { + List missingNamesEn = Lists.newArrayList(); + List missingNamesRu = Lists.newArrayList(); + + Gson gson = new Gson(); + InputStream streamEn = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/en_us.json"); + InputStream streamRu = TranslationHelper.class.getResourceAsStream("/assets/" + modID + "/lang/ru_ru.json"); + JsonObject translationEn = gson.fromJson(new InputStreamReader(streamEn), JsonObject.class); + JsonObject translationRu = gson.fromJson(new InputStreamReader(streamRu), JsonObject.class); + + Registry.BLOCK.forEach(block -> { + if (Registry.BLOCK.getKey(block).getNamespace().equals(modID)) { + String name = block.getName().getString(); + if (!translationEn.has(name)) { + missingNamesEn.add(name); + } + if (!translationRu.has(name)) { + missingNamesRu.add(name); + } + } + }); + + Registry.ITEM.forEach(item -> { + if (Registry.ITEM.getKey(item).getNamespace().equals(modID)) { + String name = item.getDescription().getString(); + if (!translationEn.has(name)) { + missingNamesEn.add(name); + } + if (!translationRu.has(name)) { + missingNamesRu.add(name); + } + } + }); + + BuiltinRegistries.BIOME.forEach(biome -> { + ResourceLocation id = BuiltinRegistries.BIOME.getKey(biome); + if (id.getNamespace().equals(modID)) { + String name = "biome." + modID + "." + id.getPath(); + if (!translationEn.has(name)) { + missingNamesEn.add(name); + } + if (!translationRu.has(name)) { + missingNamesRu.add(name); + } + } + }); + + Registry.ENTITY_TYPE.forEach((entity) -> { + ResourceLocation id = Registry.ENTITY_TYPE.getKey(entity); + if (id.getNamespace().equals(modID)) { + String name = "entity." + modID + "." + id.getPath(); + if (!translationEn.has(name)) { + missingNamesEn.add(name); + } + if (!translationRu.has(name)) { + missingNamesRu.add(name); + } + } + }); + + if (!missingNamesEn.isEmpty() || !missingNamesRu.isEmpty()) { + + System.out.println("========================================"); + System.out.println(" MISSING NAMES LIST"); + + if (!missingNamesEn.isEmpty()) { + Collections.sort(missingNamesEn); + System.out.println("========================================"); + System.out.println(" ENGLISH"); + System.out.println("========================================"); + missingNamesEn.forEach((name) -> { + System.out.println(" \"" + name + "\": \"" + fastTranslateEn(name) + "\","); + }); + } + + if (!missingNamesRu.isEmpty()) { + Collections.sort(missingNamesRu); + System.out.println("========================================"); + System.out.println(" RUSSIAN"); + System.out.println("========================================"); + missingNamesRu.forEach((name) -> { + System.out.println(" \"" + name + "\": \"\","); + }); + } + + System.out.println("========================================"); + } + } + + public static String fastTranslateEn(String text) { + String[] words = text.substring(text.lastIndexOf('.') + 1).split("_"); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < words.length; i++) { + String word = words[i]; + builder.append(Character.toUpperCase(word.charAt(0))); + builder.append(word, 1, word.length()); + if (i < words.length - 1) { + builder.append(' '); + } + } + return builder.toString(); + } +}