Compare commits

...

77 Commits

Author SHA1 Message Date
Zoe Gidiere 7ca61f82b2 Some API clean up 2024-10-12 00:22:18 -06:00
Zoe Gidiere 6851999926 Some refactoring 2024-10-11 19:45:12 -06:00
Zoe Gidiere 305255511d More cache improvements 2024-10-11 18:27:37 -06:00
Zoe Gidiere 819be16d83 Merge branch 'dev/cli' into dev/caching 2024-10-11 16:40:26 -06:00
Zoe Gidiere 8a028b193a Add no save option 2024-10-11 16:35:26 -06:00
Zoe Gidiere d31679e6be remove stuff from example 2024-10-11 16:06:40 -06:00
Zoe Gidiere 9d328b12b3 More cache improvements 2024-10-11 16:06:23 -06:00
Zoe Gidiere a743e9c015 Merge branch 'dev/cli' into dev/caching 2024-10-11 15:16:20 -06:00
Zoe Gidiere 43d52e4bc1 CLI Improvements 2024-10-11 15:16:04 -06:00
Zoe Gidiere accc07fa07 Bump Version 2024-10-11 15:15:38 -06:00
Zoe Gidiere c1d3155ddd Cache Improvements 2024-10-11 15:15:10 -06:00
Zoe Gidiere 9a01a6c6a0 more WIP on cache 2024-10-11 00:20:46 -06:00
Zoe Gidiere 1ae0d1f867 WIP Caching Hints and Caching Sampler 2024-10-09 23:36:12 -06:00
Zoe Gidiere 370b2e0122 Fix import 2024-10-09 13:01:54 -06:00
Zoe Gidiere 331075e54d Refractor name 2024-10-09 12:59:19 -06:00
Zoë Gidiere 973590f5fd Add parameter to turn off salting cellular lookup
another updated astrash commit
2024-10-09 12:42:24 -06:00
Astrash 1a5ab6b505 Add meta annotations 2024-10-09 12:42:13 -06:00
Astrash 14732328cd Add default 'from' values for linear map template 2024-10-09 12:41:55 -06:00
Zoë Gidiere 6c7974c302 Implement linear map normalizer
Updated astrash commit
2024-10-09 12:41:40 -06:00
Zoe Gidiere 1204b7a8c1 ArithmeticSampler Derivatives 2024-10-09 12:37:50 -06:00
Astrash fbe1c76e26 Forgot to include 4th derivative array index for 3D FBM 2024-10-09 21:50:08 +11:00
Astrash add7803e65 Simplify isDifferentiable static method impl 2024-10-09 20:56:37 +11:00
Astrash 4393a16b2f Use proper static isDifferentiable method 2024-10-09 20:56:11 +11:00
Astrash b4fa635455 Ensure throw with incorrect use of SimplexStyle derivatives
This should ensure that in the case an implementation fails to correctly
check isDifferentiable then performs a subsequent derivative based sample,
an error is thrown, rather than potentially missing a logic bug due to
noise of 0 and derivative of 0 being returned which could otherwise be
hard to narrow down.
2024-10-09 20:45:25 +11:00
Astrash 9f425c6159 Add support for derivatives in FBM 2024-10-09 20:39:45 +11:00
Astrash 158ffba2a5 Make FractalNoiseFunction impls support derivatives 2024-10-09 20:39:13 +11:00
Zoë Gidiere b7326c0ff6 Merge pull request #469 from PolyhedralDev/dev/fix-bukkit-spawner
fix spawners on bukkit
2024-09-24 23:27:44 -06:00
Zoe Gidiere f70a83aec3 fix spawners on bukkit 2024-09-24 23:26:49 -06:00
astrsh 589cf83c38 Flip comparison
As per request from @duplexsystem
2024-09-25 14:08:19 +10:00
Zoë Gidiere c374c2d5ef Merge pull request #467 from PolyhedralDev/ver/6.5.0
Ver/6.5.0
2024-09-24 09:41:58 -06:00
Zoe Gidiere 13782bb51e Remforamt 2024-09-24 09:11:43 -06:00
Zoe Gidiere 1aa3ef813c add default sampler to pseudoerosion 2024-09-24 09:05:33 -06:00
Zoe Gidiere 6cc53beecb Fix Sin/Cos 2024-09-23 23:14:35 -06:00
Zoe Gidiere 2c45ba91aa Fix up commit 2024-09-23 22:40:37 -06:00
Zoe Gidiere 6244ffca63 Revert "Sin/Cos Opts"
This reverts commit 1d05f95f74.
2024-09-23 22:38:45 -06:00
Zoe Gidiere 59d943b3ea Revert "Fix build"
This reverts commit 10d723372d.
2024-09-23 22:38:17 -06:00
Zoe Gidiere f605ee1937 Remove Final from psuedoerosion template 2024-09-23 22:35:01 -06:00
Zoe Gidiere f87b7c07d9 Reformat 2024-09-23 22:27:53 -06:00
Zoe Gidiere f1124aae6b Update platform mod file versions and authors 2024-09-23 21:18:47 -06:00
Zoe Gidiere 9c8298fe53 convert pseudoerosion to double 2024-09-23 21:09:19 -06:00
Zoe Gidiere d69b0dba90 reformat 2 2024-09-23 21:06:07 -06:00
Zoe Gidiere 42166d5239 bump version number 2024-09-23 20:59:02 -06:00
Zoe Gidiere d6772f51ea Reformat 2024-09-23 20:56:13 -06:00
Zoe Gidiere f469193909 Make seed affect pseudoerosion hasing 2024-09-23 20:51:00 -06:00
Zoe Gidiere 10d723372d Fix build 2024-09-23 20:45:47 -06:00
Zoe Gidiere 1d05f95f74 Sin/Cos Opts 2024-09-23 20:38:22 -06:00
Zoe Gidiere bd73427072 OpenSimplex 2 Derivative Impl 2024-09-23 20:29:41 -06:00
Zoe Gidiere 017b4608bc Fix build 2024-09-23 19:49:16 -06:00
Zoe Gidiere 5f340f8ffd slight open simplex 2s op\pt 2024-09-23 19:48:22 -06:00
Zoe Gidiere 5b35eb041b Remove temp DerivativeFractl nose 2024-09-23 19:43:56 -06:00
Zoe Gidiere 699d5d1d62 Derivable OpenSimplex2S Impl
baased on https://gist.github.com/KdotJPG/882f36b2ae5ae56a8fc91627ec1c87f4
2024-09-23 19:42:50 -06:00
Zoe Gidiere 07e3d0058d Refractor PseudoErosion 2024-09-23 16:46:48 -06:00
Zoe Gidiere b1ad820e1c Move pseudoerosion to NoiseFunction 2024-09-23 16:37:42 -06:00
Zoe Gidiere 3644ee7fca Implement DerivateNoiseFunction 2024-09-23 16:28:34 -06:00
Zoe Gidiere b14b355c6f Merge branch 'dev/fractal-gavoro-pseudoerosion' into ver/6.5.0 2024-09-21 17:22:47 -06:00
Zoe Gidiere 06457bf3ce some fixes 2024-09-18 15:43:31 -06:00
Zoe Gidiere b63f5f4a8f Reformat 2024-09-18 12:43:41 -06:00
Zoë Gidiere 8cf766e77b Merge pull request #455 from pumken/seasoned-samplers-2
Add salted version of sampler functions in EXPRESSION samplers
2024-09-17 17:59:26 -06:00
Zoe Gidiere a23d624ab9 Merge branch 'dev/1.21.1' into ver/6.5.0 2024-09-17 17:41:25 -06:00
Zoe Gidiere 15a298304e working 1.21.1 2024-09-17 17:40:56 -06:00
Zoe Gidiere 664d1a3191 working 1.20.6 2024-09-17 16:34:30 -06:00
Zoë Gidiere 73047284c8 Merge pull request #461 from OakLoaf/dev/1.21/bukkit
Updated Bukkit to 1.21/1.21.1
2024-09-17 09:43:05 -06:00
OakLoaf 3bef7d8572 Resolved errors when tab completing 2024-08-26 12:34:08 +01:00
OakLoaf 08df5fc278 Added 1.21.1 support 2024-08-26 11:24:04 +01:00
OakLoaf 922cd35e84 Updated Cloud dependency to v2 2024-08-16 11:07:03 +01:00
Oak 44d23573b3 Updated dependencies to Paper 1.21 snapshot 2024-07-20 20:17:25 +01:00
Oak e7cc275340 Corrected issues with worldGenContext not correctly being set (Ref: https://stackoverflow.com/questions/56039341/get-declared-fields-of-java-lang-reflect-fields-in-jdk12/56043252#56043252) 2024-06-23 14:16:50 +01:00
Oak fddc9a52b9 Temporarily disabled commands whilst awaiting Cloud Framework update 2024-06-19 13:34:35 +01:00
Oak 0758e13bc7 Made patch version optional as not all Minecraft versions have a patch number 2024-06-19 13:33:55 +01:00
Gavin Tran c20b1eaf10 Apply code style fixes 2024-04-21 15:39:48 -04:00
Gavin Tran 72f761678a Add SaltedNoiseFunctions in sampler conversion 2024-04-21 14:54:45 -04:00
Gavin Tran 62317a2f3f Add SaltedNoiseFunctions 2024-04-21 14:48:46 -04:00
Astrash 823afd592b Reduce number of erosion impulses
A reduction of impulse evaluations from 5^2 to 3^2, original algorithm used exponential falloff function which required evaluating impulses from further away to avoid artifacts. I've changed it to a parabolic falloff function which is exactly 0 at the largest possible distance from a cell origin, meaning any cells further than that will not have an effect, preventing said artifacting from occurring. The parabolic function could be replaced with an exponential function (or any other similar easing function) as long as it has an x-intercept at maxCellDistSq. In addition, an option to control whether averaging impulses has been added as it has more of a visual impact with the aforementioned changes.
2024-04-20 15:55:59 +10:00
Astrash ff03b38b81 Derivative API documentation 2024-04-19 21:25:16 +10:00
Astrash 35246d65b9 Require implementing derivable check 2024-04-19 21:07:53 +10:00
Astrash 41652d29df Use proper check for derivative sampler validation 2024-04-19 21:05:52 +10:00
Astrash 142d364682 WIP fractal-gavoro-pseudoerosion 2024-04-19 20:55:14 +10:00
131 changed files with 2315 additions and 574 deletions
+5 -4
View File
@@ -1,8 +1,8 @@
preRelease(true)
versionProjects(":common:api", version("6.5.0"))
versionProjects(":common:implementation", version("6.5.0"))
versionProjects(":platforms", version("6.5.0"))
versionProjects(":common:api", version("6.6.0"))
versionProjects(":common:implementation", version("6.6.0"))
versionProjects(":platforms", version("6.6.0"))
allprojects {
@@ -15,6 +15,7 @@ allprojects {
tasks.withType<JavaCompile>().configureEach {
options.isFork = true
options.isIncremental = true
options.release.set(21)
}
tasks.withType<Test>().configureEach {
@@ -44,7 +45,7 @@ afterEvaluate {
}
project(":platforms:bukkit:common").configureDistribution()
forSubProjects(":common:addons") {
apply(plugin = "com.github.johnrengelman.shadow")
apply(plugin = "com.gradleup.shadow")
tasks.named("build") {
finalizedBy(tasks.named("shadowJar"))
+3 -3
View File
@@ -16,11 +16,11 @@ repositories {
dependencies {
//TODO Allow pulling from Versions.kt
implementation("com.github.johnrengelman", "shadow", "8.1.1")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "1.7.1")
implementation("com.gradleup.shadow", "shadow-gradle-plugin", "8.3.1")
implementation("io.papermc.paperweight.userdev", "io.papermc.paperweight.userdev.gradle.plugin", "1.7.2")
implementation("org.ow2.asm", "asm", "9.7")
implementation("org.ow2.asm", "asm-tree", "9.7")
implementation("com.dfsek.tectonic", "common", "4.2.1")
implementation("org.yaml", "snakeyaml", "2.2")
implementation("org.yaml", "snakeyaml", "2.3")
}
@@ -48,6 +48,9 @@ fun Project.configureDependencies() {
maven("https://jitpack.io") {
name = "JitPack"
}
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "Sonatype Snapshots"
}
}
dependencies {
@@ -21,13 +21,14 @@ import kotlin.io.path.exists
fun Project.configureDistribution() {
apply(plugin = "com.github.johnrengelman.shadow")
apply(plugin = "com.gradleup.shadow")
val downloadDefaultPacks = tasks.create("downloadDefaultPacks") {
group = "terra"
doFirst {
file("${buildDir}/resources/main/packs/").deleteRecursively()
val defaultPackUrl = URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
val defaultPackUrl =
URL("https://github.com/PolyhedralDev/TerraOverworldConfig/releases/download/" + Versions.Terra.overworldConfig + "/default.zip")
downloadPack(defaultPackUrl, project)
}
}
+21 -18
View File
@@ -8,26 +8,28 @@ object Versions {
const val paralithic = "0.7.1"
const val strata = "1.3.2"
const val cloud = "1.8.4"
const val cloud = "2.0.0"
const val cloudPaper = "2.0.0-beta.10"
const val cloudFabric = "2.0.0-beta.9"
const val caffeine = "3.1.8"
const val slf4j = "2.0.13"
const val slf4j = "2.0.16"
object Internal {
const val shadow = "8.1.1"
const val shadow = "8.3.1"
const val apacheText = "1.12.0"
const val apacheIO = "2.16.1"
const val guava = "33.1.0-jre"
const val guava = "33.3.0-jre"
const val asm = "9.7"
const val snakeYml = "2.2"
const val snakeYml = "2.3"
const val jetBrainsAnnotations = "24.1.0"
const val junit = "5.10.2"
const val junit = "5.11.0"
}
}
object Fabric {
const val fabricAPI = "0.97.8+${Mod.minecraft}"
const val fabricAPI = "0.104.0+${Mod.minecraft}"
}
//
// object Quilt {
@@ -36,14 +38,14 @@ object Versions {
// }
object Mod {
const val mixin = "0.12.5+mixin.0.8.5"
const val mixin = "0.15.3+mixin.0.8.7"
const val minecraft = "1.20.6"
const val yarn = "$minecraft+build.1"
const val fabricLoader = "0.15.10"
const val minecraft = "1.21.1"
const val yarn = "$minecraft+build.3"
const val fabricLoader = "0.16.5"
const val architecuryLoom = "1.6.395"
const val architecturyPlugin = "3.4.155"
const val architecuryLoom = "1.7.413"
const val architecturyPlugin = "3.4.159"
}
//
// object Forge {
@@ -52,14 +54,14 @@ object Versions {
// }
object Bukkit {
const val minecraft = "1.21"
const val paperBuild = "$minecraft-R0.1-20240615.005026-1"
const val minecraft = "1.21.1"
const val paperBuild = "$minecraft-R0.1-20240917.151311-80"
const val paper = paperBuild
const val paperLib = "1.0.8"
const val reflectionRemapper = "0.1.1"
const val paperDevBundle = paperBuild
const val runPaper = "2.3.0"
const val paperWeight = "1.7.1"
const val runPaper = "2.3.1"
const val paperWeight = "1.7.2"
}
//
@@ -71,6 +73,7 @@ object Versions {
//
object CLI {
const val nbt = "6.1"
const val logback = "1.4.14"
const val logback = "1.5.8"
const val picocli = "4.7.6"
}
}
@@ -1,9 +1,9 @@
package com.dfsek.terra.addons.biome.extrusion.api;
import java.util.Collection;
import com.dfsek.terra.api.world.biome.Biome;
import java.util.Collection;
public interface Extrusion {
Biome extrude(Biome original, int x, int y, int z, long seed);
@@ -1,5 +1,7 @@
package com.dfsek.terra.addons.biome.pipeline.v2;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
@@ -11,7 +13,6 @@ import java.util.stream.StreamSupport;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -23,7 +24,7 @@ import com.dfsek.terra.api.world.biome.generation.BiomeProvider;
public class PipelineBiomeProvider implements BiomeProvider {
private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache;
private final LoadingCache<SeededVector2Key, BiomeChunk> biomeChunkCache;
private final int chunkSize;
private final int resolution;
private final NoiseSampler mutator;
@@ -90,7 +91,7 @@ public class PipelineBiomeProvider implements BiomeProvider {
int xInChunk = x - chunkWorldX;
int zInChunk = z - chunkWorldZ;
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome();
return biomeChunkCache.get(new SeededVector2Key(chunkWorldX, chunkWorldZ, seed)).get(xInChunk, zInChunk).getBiome();
}
@Override
@@ -1,10 +1,12 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import java.util.List;
public interface Pipeline {
BiomeChunk generateChunk(SeededVector worldCoordinates);
BiomeChunk generateChunk(SeededVector2Key worldCoordinates);
int getChunkSize();
@@ -1,19 +0,0 @@
package com.dfsek.terra.addons.biome.pipeline.v2.api;
public record SeededVector(long seed, int x, int z) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
@@ -29,7 +29,7 @@ public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> {
@Default
@Description("""
The resolution at which to sample biomes.
Larger values are quadratically faster, but produce lower quality results.
For example, a value of 3 would sample every 3 blocks.""")
protected @Meta int resolution = 1;
@@ -4,20 +4,20 @@ import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
public class BiomeChunkImpl implements BiomeChunk {
private final SeededVector worldOrigin;
private final SeededVector2Key worldOrigin;
private final int chunkOriginArrayIndex;
private final int worldCoordinateScale;
private final int size;
private PipelineBiome[] biomes;
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) {
public BiomeChunkImpl(SeededVector2Key worldOrigin, PipelineImpl pipeline) {
this.worldOrigin = worldOrigin;
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex();
@@ -44,7 +44,7 @@ public class BiomeChunkImpl implements BiomeChunk {
for(int gridZ = 0; gridZ < gridSize; gridZ++) {
int xIndex = gridOrigin + gridX * gridInterval;
int zIndex = gridOrigin + gridZ * gridInterval;
biomes[(xIndex * size) + zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex),
biomes[(xIndex * size) + zIndex] = pipeline.getSource().get(worldOrigin.seed, xIndexToWorldCoordinate(xIndex),
zIndexToWorldCoordinate(zIndex));
}
}
@@ -139,14 +139,14 @@ public class BiomeChunkImpl implements BiomeChunk {
}
private int xIndexToWorldCoordinate(int xIndex) {
return (worldOrigin.x() + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
return (worldOrigin.x + xIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private int zIndexToWorldCoordinate(int zIndex) {
return (worldOrigin.z() + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
return (worldOrigin.z + zIndex - chunkOriginArrayIndex) * worldCoordinateScale;
}
private SeededVector getOrigin() {
private SeededVector2Key getOrigin() {
return worldOrigin;
}
@@ -216,7 +216,7 @@ public class BiomeChunkImpl implements BiomeChunk {
}
public long worldSeed() {
return chunk.getOrigin().seed();
return chunk.getOrigin().seed;
}
}
}
@@ -1,5 +1,7 @@
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -8,7 +10,6 @@ import java.util.List;
import com.dfsek.terra.addons.biome.pipeline.v2.api.BiomeChunk;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Pipeline;
import com.dfsek.terra.addons.biome.pipeline.v2.api.SeededVector;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source;
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage;
@@ -55,7 +56,7 @@ public class PipelineImpl implements Pipeline {
}
@Override
public BiomeChunk generateChunk(SeededVector worldCoordinates) {
public BiomeChunk generateChunk(SeededVector2Key worldCoordinates) {
return new BiomeChunkImpl(worldCoordinates, this);
}
@@ -28,7 +28,7 @@ public class BiomePipelineTemplate extends BiomeProviderTemplate {
@Description("""
The initial size of biome chunks. This value must be at least 2.
<b>This is not the final size of biome chunks. Final chunks will be much larger</b>.
It is recommended to keep biome chunks' final size in the range of [50, 300]
to prevent performance issues. To calculate the size of biome chunks, simply
take initial-size and for each expand stage, multiply the running value by 2
@@ -22,7 +22,7 @@ public abstract class BiomeProviderTemplate implements ObjectTemplate<BiomeProvi
@Default
@Description("""
The resolution at which to sample biomes.
Larger values are quadratically faster, but produce lower quality results.
For example, a value of 3 would sample every 3 blocks.""")
protected @Meta int resolution = 1;
@@ -8,17 +8,15 @@
package com.dfsek.terra.addons.chunkgenerator.generation;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.addons.chunkgenerator.config.noise.BiomeNoiseProperties;
import com.dfsek.terra.addons.chunkgenerator.generation.math.SlantCalculationMethod;
import com.dfsek.terra.addons.chunkgenerator.generation.math.interpolation.LazilyEvaluatedInterpolator;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.Sampler3D;
import com.dfsek.terra.addons.chunkgenerator.generation.math.samplers.SamplerProvider;
import com.dfsek.terra.addons.chunkgenerator.palette.BiomePaletteInfo;
import com.dfsek.terra.addons.chunkgenerator.palette.slant.SlantHolder;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.config.ConfigPack;
@@ -1,7 +1,7 @@
package com.dfsek.terra.addons.commands.addons;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.CommandManager;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.description.Description;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.api.Platform;
@@ -30,7 +30,7 @@ public class AddonsCommandAddon implements AddonInitializer {
CommandManager<CommandSender> manager = event.getCommandManager();
manager.command(
manager.commandBuilder("addons", ArgumentDescription.of("List installed Terra addons"))
manager.commandBuilder("addons", Description.of("List installed Terra addons"))
.permission("terra.addons")
.handler(context -> {
StringBuilder addons = new StringBuilder("Installed addons:\n");
@@ -41,7 +41,7 @@ public class AddonsCommandAddon implements AddonInitializer {
.append('@')
.append(addon.getVersion().getFormatted())
.append('\n'));
context.getSender().sendMessage(addons.toString());
context.sender().sendMessage(addons.toString());
})
)
.command(
@@ -61,7 +61,7 @@ public class AddonsCommandAddon implements AddonInitializer {
.append('@')
.append(versions.getFormatted())
.append('\n'));
context.getSender().sendMessage(addonInfo.toString());
context.sender().sendMessage(addonInfo.toString());
})
);
});
@@ -1,7 +1,7 @@
package com.dfsek.terra.addons.commands.packs;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.CommandManager;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.description.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -35,7 +35,7 @@ public class PacksCommandAddon implements AddonInitializer {
CommandManager<CommandSender> manager = event.getCommandManager();
manager.command(
manager.commandBuilder("packs", ArgumentDescription.of("List installed config packs"))
manager.commandBuilder("packs", Description.of("List installed config packs"))
.permission("terra.packs")
.handler(context -> {
StringBuilder packs = new StringBuilder("Installed packs:\n");
@@ -43,12 +43,12 @@ public class PacksCommandAddon implements AddonInitializer {
.append(pack.getID())
.append('@')
.append(pack.getVersion().getFormatted()));
context.getSender().sendMessage(packs.toString());
context.sender().sendMessage(packs.toString());
})
)
.command(
manager.commandBuilder("packs")
.literal("info", ArgumentDescription.of("Get information about a pack"))
.literal("info", Description.of("Get information about a pack"))
.permission("terra.packs.info")
.argument(RegistryArgument.of("pack", platform.getConfigRegistry()))
.handler(context -> {
@@ -65,21 +65,21 @@ public class PacksCommandAddon implements AddonInitializer {
.append('@')
.append(versions.getFormatted())
.append('\n'));
context.getSender().sendMessage(packInfo.toString());
context.sender().sendMessage(packInfo.toString());
}))
.command(
manager.commandBuilder("packs")
.literal("reload", ArgumentDescription.of("Reload config packs"))
.literal("reload", Description.of("Reload config packs"))
.permission("terra.packs.reload")
.handler(context -> {
context.getSender().sendMessage("Reloading Terra...");
context.sender().sendMessage("Reloading Terra...");
logger.info("Reloading Terra...");
if(platform.reload()) {
logger.info("Terra reloaded successfully.");
context.getSender().sendMessage("Terra reloaded successfully.");
context.sender().sendMessage("Terra reloaded successfully.");
} else {
logger.error("Terra failed to reload.");
context.getSender().sendMessage(
context.sender().sendMessage(
"Terra failed to reload. See logs for more information.");
}
}));
@@ -1,7 +1,7 @@
package com.dfsek.terra.addons.commands.profiler;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.CommandManager;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.description.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -33,24 +33,24 @@ public class ProfilerCommandAddon implements AddonInitializer {
CommandManager<CommandSender> manager = event.getCommandManager();
manager
.command(
manager.commandBuilder("profiler", ArgumentDescription.of("Access the profiler"))
.literal("start", ArgumentDescription.of("Start profiling"), "st")
manager.commandBuilder("profiler", Description.of("Access the profiler"))
.literal("start", Description.of("Start profiling"), "st")
.permission("terra.profiler.start")
.handler(context -> {
platform.getProfiler().start();
context.getSender().sendMessage("Profiling started.");
context.sender().sendMessage("Profiling started.");
}))
.command(
manager.commandBuilder("profiler", ArgumentDescription.of("Access the profiler"))
.literal("stop", ArgumentDescription.of("Stop profiling"), "s")
manager.commandBuilder("profiler", Description.of("Access the profiler"))
.literal("stop", Description.of("Stop profiling"), "s")
.permission("terra.profiler.stop")
.handler(context -> {
platform.getProfiler().stop();
context.getSender().sendMessage("Profiling stopped.");
context.sender().sendMessage("Profiling stopped.");
}))
.command(
manager.commandBuilder("profiler", ArgumentDescription.of("Access the profiler"))
.literal("query", ArgumentDescription.of("Query profiler results"), "q")
manager.commandBuilder("profiler", Description.of("Access the profiler"))
.literal("query", Description.of("Query profiler results"), "q")
.permission("terra.profiler.query")
.handler(context -> {
StringBuilder data = new StringBuilder("Terra Profiler data: \n");
@@ -59,15 +59,15 @@ public class ProfilerCommandAddon implements AddonInitializer {
.append(timings.toString())
.append('\n'));
logger.info(data.toString());
context.getSender().sendMessage("Profiling data dumped to console.");
context.sender().sendMessage("Profiling data dumped to console.");
}))
.command(
manager.commandBuilder("profiler", ArgumentDescription.of("Access the profiler"))
.literal("reset", ArgumentDescription.of("Reset the profiler"), "r")
manager.commandBuilder("profiler", Description.of("Access the profiler"))
.literal("reset", Description.of("Reset the profiler"), "r")
.permission("terra.profiler.reset")
.handler(context -> {
platform.getProfiler().reset();
context.getSender().sendMessage("Profiler reset.");
context.sender().sendMessage("Profiler reset.");
}));
});
}
@@ -1,10 +1,11 @@
package com.dfsek.terra.addons.commands.structure;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.CommandManager;
import cloud.commandframework.arguments.standard.EnumArgument;
import cloud.commandframework.arguments.standard.LongArgument;
import cloud.commandframework.context.CommandContext;
import org.incendo.cloud.CommandManager;
import org.incendo.cloud.component.DefaultValue;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.description.Description;
import org.incendo.cloud.parser.standard.EnumParser;
import org.incendo.cloud.parser.standard.LongParser;
import java.util.Random;
@@ -31,7 +32,7 @@ public class StructureCommandAddon implements AddonInitializer {
private BaseAddon addon;
private static Registry<Structure> getStructureRegistry(CommandContext<CommandSender> sender) {
return sender.getSender().getEntity().orElseThrow().world().getPack().getRegistry(Structure.class);
return sender.sender().getEntity().orElseThrow().world().getPack().getRegistry(Structure.class);
}
@Override
@@ -43,16 +44,16 @@ public class StructureCommandAddon implements AddonInitializer {
CommandManager<CommandSender> manager = event.getCommandManager();
manager.command(
manager.commandBuilder("structures", ArgumentDescription.of("Manage or generate structures"))
manager.commandBuilder("structures", Description.of("Manage or generate structures"))
.literal("generate")
.argument(RegistryArgument.builder("structure",
.optional(RegistryArgument.builder("structure",
StructureCommandAddon::getStructureRegistry,
TypeKey.of(Structure.class)))
.argument(LongArgument.optional("seed", 0))
.argument(EnumArgument.optional(Rotation.class, "rotation", Rotation.NONE))
.optional("seed", LongParser.longParser(), DefaultValue.constant(0L))
.optional("rotation", EnumParser.enumParser(Rotation.class), DefaultValue.constant(Rotation.NONE))
.handler(context -> {
Structure structure = context.get("structure");
Entity sender = context.getSender().getEntity().orElseThrow();
Entity sender = context.sender().getEntity().orElseThrow();
structure.generate(
sender.position().toInt(),
sender.world(),
@@ -7,11 +7,11 @@
package com.dfsek.terra.addons.biome.holder;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
import java.util.Map;
import java.util.TreeMap;
import com.dfsek.terra.api.world.chunk.generation.util.Palette;
public class PaletteHolderBuilder {
private final TreeMap<Integer, Palette> paletteMap = new TreeMap<>();
@@ -7,11 +7,11 @@
package com.dfsek.terra.addons.feature.distributor.distributors;
import java.util.Set;
import com.dfsek.terra.addons.feature.distributor.util.Point;
import com.dfsek.terra.api.structure.feature.Distributor;
import java.util.Set;
public class PointSetDistributor implements Distributor {
private final Set<Point> points;
@@ -1,4 +1,4 @@
version = version("1.1.0")
version = version("1.2.0")
dependencies {
compileOnlyApi(project(":common:addons:manifest-addon-loader"))
@@ -17,6 +17,8 @@ import com.dfsek.terra.addons.manifest.api.AddonInitializer;
import com.dfsek.terra.addons.noise.config.CubicSplinePointTemplate;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.BinaryArithmeticTemplate;
import com.dfsek.terra.addons.noise.config.templates.CacheSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.DerivativeNoiseSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.DomainWarpTemplate;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.ImageSamplerTemplate;
@@ -28,6 +30,7 @@ import com.dfsek.terra.addons.noise.config.templates.noise.ConstantNoiseTemplate
import com.dfsek.terra.addons.noise.config.templates.noise.DistanceSamplerTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.ExpressionFunctionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.GaborNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.PseudoErosionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.SimpleNoiseTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.BrownianMotionTemplate;
import com.dfsek.terra.addons.noise.config.templates.noise.fractal.PingPongTemplate;
@@ -35,6 +38,7 @@ import com.dfsek.terra.addons.noise.config.templates.noise.fractal.RidgedFractal
import com.dfsek.terra.addons.noise.config.templates.normalizer.ClampNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.CubicSplineNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.ExpressionNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearMapNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.LinearNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.NormalNormalizerTemplate;
import com.dfsek.terra.addons.noise.config.templates.normalizer.PosterizationNormalizerTemplate;
@@ -63,6 +67,7 @@ import com.dfsek.terra.api.addon.BaseAddon;
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPreLoadEvent;
import com.dfsek.terra.api.event.functional.FunctionalEventHandler;
import com.dfsek.terra.api.inject.annotations.Inject;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.registry.CheckedRegistry;
import com.dfsek.terra.api.util.reflection.TypeKey;
@@ -94,9 +99,11 @@ public class NoiseAddon implements AddonInitializer {
(type, o, loader, depthTracker) -> DistanceSampler.DistanceFunction.valueOf((String) o))
.applyLoader(DimensionApplicableNoiseSampler.class, DimensionApplicableNoiseSampler::new)
.applyLoader(FunctionTemplate.class, FunctionTemplate::new)
.applyLoader(CubicSpline.Point.class, CubicSplinePointTemplate::new);
.applyLoader(CubicSpline.Point.class, CubicSplinePointTemplate::new)
.applyLoader(DerivativeNoiseSampler.class, DerivativeNoiseSamplerTemplate::new);
noiseRegistry.register(addon.key("LINEAR"), LinearNormalizerTemplate::new);
noiseRegistry.register(addon.key("LINEAR_MAP"), LinearMapNormalizerTemplate::new);
noiseRegistry.register(addon.key("NORMAL"), NormalNormalizerTemplate::new);
noiseRegistry.register(addon.key("CLAMP"), ClampNormalizerTemplate::new);
noiseRegistry.register(addon.key("PROBABILITY"), ProbabilityNormalizerTemplate::new);
@@ -117,7 +124,7 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("PERLIN"), () -> new SimpleNoiseTemplate(PerlinSampler::new));
noiseRegistry.register(addon.key("SIMPLEX"), () -> new SimpleNoiseTemplate(SimplexSampler::new));
noiseRegistry.register(addon.key("GABOR"), GaborNoiseTemplate::new);
noiseRegistry.register(addon.key("PSEUDOEROSION"), PseudoErosionTemplate::new);
noiseRegistry.register(addon.key("VALUE"), () -> new SimpleNoiseTemplate(ValueSampler::new));
noiseRegistry.register(addon.key("VALUE_CUBIC"), () -> new SimpleNoiseTemplate(ValueCubicSampler::new));
@@ -144,6 +151,8 @@ public class NoiseAddon implements AddonInitializer {
noiseRegistry.register(addon.key("MAX"), () -> new BinaryArithmeticTemplate<>(MaxSampler::new));
noiseRegistry.register(addon.key("MIN"), () -> new BinaryArithmeticTemplate<>(MinSampler::new));
noiseRegistry.register(addon.key("CACHE"), CacheSamplerTemplate::new);
Map<String, DimensionApplicableNoiseSampler> packSamplers = new LinkedHashMap<>();
Map<String, FunctionTemplate> packFunctions = new LinkedHashMap<>();
@@ -11,14 +11,14 @@ import com.dfsek.tectonic.api.config.template.ConfigTemplate;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import java.util.LinkedHashMap;
import java.util.Map;
import com.dfsek.terra.addons.noise.config.DimensionApplicableNoiseSampler;
import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.properties.Properties;
import java.util.LinkedHashMap;
import java.util.Map;
@SuppressWarnings("FieldMayBeFinal")
public class NoiseConfigPackTemplate implements ConfigTemplate, Properties {
@@ -0,0 +1,24 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.CacheSampler;
import com.dfsek.terra.addons.noise.samplers.LinearHeightmapSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
public class CacheSamplerTemplate extends SamplerTemplate<CacheSampler> {
@Value("sampler")
@Default
private NoiseSampler sampler;
public CacheSamplerTemplate() {
}
@Override
public NoiseSampler get() {
return new CacheSampler(sampler, getDimensions());
}
}
@@ -0,0 +1,26 @@
package com.dfsek.terra.addons.noise.config.templates;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.tectonic.api.exception.ValidationException;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
public class DerivativeNoiseSamplerTemplate extends SamplerTemplate<DerivativeNoiseSampler> {
@Value(".")
private NoiseSampler sampler;
@Override
public boolean validate() throws ValidationException {
if(!DerivativeNoiseSampler.isDifferentiable(sampler)) throw new ValidationException(
"Provided sampler does not support calculating a derivative");
return super.validate();
}
@Override
public DerivativeNoiseSampler get() {
return (DerivativeNoiseSampler) sampler;
}
}
@@ -34,7 +34,11 @@ public class CellularNoiseTemplate extends NoiseTemplate<CellularSampler> {
@Value("lookup")
@Default
private @Meta NoiseSampler lookup = new OpenSimplex2Sampler();
@Value("salt-lookup")
@Default
private @Meta boolean saltLookup = true;
@Override
public NoiseSampler get() {
CellularSampler sampler = new CellularSampler();
@@ -44,6 +48,7 @@ public class CellularNoiseTemplate extends NoiseTemplate<CellularSampler> {
sampler.setReturnType(cellularReturnType);
sampler.setDistanceFunction(cellularDistanceFunction);
sampler.setSalt(salt);
sampler.setSaltLookup(saltLookup);
return sampler;
}
}
@@ -0,0 +1,8 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.terra.addons.noise.samplers.noise.DerivativeNoiseFunction;
public abstract class DerivativeNoiseTemplate<T extends DerivativeNoiseFunction> extends NoiseTemplate<T> {
}
@@ -0,0 +1,79 @@
package com.dfsek.terra.addons.noise.config.templates.noise;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.samplers.noise.PseudoErosionSampler;
import com.dfsek.terra.addons.noise.samplers.noise.simplex.OpenSimplex2Sampler;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
public class PseudoErosionTemplate extends NoiseTemplate<PseudoErosionSampler> {
@Value("frequency")
@Default
protected @Meta double frequency = 1d;
@Value("octaves")
@Default
private int octaves = 4;
@Value("lacunarity")
@Default
private double lacunarity = 2.0;
@Value("gain")
@Default
private double gain = 0.5;
@Value("slope-strength")
@Default
private double slopeStrength = 1.0;
@Value("branch-strength")
@Default
private double branchStrength = 1.0;
@Value("strength")
@Default
private double strength = 0.04;
@Value("erosion-frequency")
@Default
private double erosionFrequency = 0.02;
@Value("sampler")
@Default
private DerivativeNoiseSampler heightSampler = new OpenSimplex2Sampler();
@Value("slope-mask.enable")
@Default
private boolean slopeMask = true;
@Value("slope-mask.none")
@Default
private double slopeMaskNone = -0.5;
@Value("slope-mask.full")
@Default
private double slopeMaskFull = 1;
@Value("jitter")
@Default
private double jitterModifier = 1;
@Value("average-impulses")
@Default
private boolean averageErosionImpulses = true;
@Override
public PseudoErosionSampler get() {
PseudoErosionSampler pseudoErosion = new PseudoErosionSampler(octaves, gain, lacunarity,
slopeStrength, branchStrength, strength,
erosionFrequency, heightSampler, slopeMask, slopeMaskFull, slopeMaskNone, jitterModifier, averageErosionImpulses);
pseudoErosion.setFrequency(frequency);
pseudoErosion.setSalt(salt);
return pseudoErosion;
}
}
@@ -0,0 +1,31 @@
package com.dfsek.terra.addons.noise.config.templates.normalizer;
import com.dfsek.tectonic.api.config.template.annotations.Default;
import com.dfsek.tectonic.api.config.template.annotations.Value;
import com.dfsek.terra.addons.noise.normalizer.LinearMapNormalizer;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearMapNormalizerTemplate extends NormalizerTemplate<LinearMapNormalizer> {
@Value("from.a")
@Default
private @Meta double aFrom = -1;
@Value("from.b")
@Default
private @Meta double bFrom = 1;
@Value("to.a")
private @Meta double aTo;
@Value("to.b")
private @Meta double bTo;
@Override
public NoiseSampler get() {
return new LinearMapNormalizer(function, aFrom, aTo, bFrom, bTo);
}
}
@@ -0,0 +1,28 @@
package com.dfsek.terra.addons.noise.normalizer;
import com.dfsek.terra.api.noise.NoiseSampler;
public class LinearMapNormalizer extends Normalizer {
private final double aFrom;
private final double aTo;
private final double bFrom;
private final double bTo;
public LinearMapNormalizer(NoiseSampler sampler, double aFrom, double aTo, double bFrom, double bTo) {
super(sampler);
this.aFrom = aFrom;
this.aTo = aTo;
this.bFrom = bFrom;
this.bTo = bTo;
}
@Override
public double normalize(double in) {
return (in - aFrom) * (aTo - bTo) / (aFrom - bFrom) + aTo;
}
}
@@ -11,6 +11,8 @@ import com.dfsek.terra.addons.noise.config.templates.FunctionTemplate;
import com.dfsek.terra.addons.noise.paralithic.defined.UserDefinedFunction;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.NoiseFunction3;
import com.dfsek.terra.addons.noise.paralithic.noise.SaltedNoiseFunction2;
import com.dfsek.terra.addons.noise.paralithic.noise.SaltedNoiseFunction3;
public class FunctionUtil {
@@ -23,10 +25,15 @@ public class FunctionUtil {
for(Map.Entry<String, FunctionTemplate> entry : functions.entrySet()) {
functionMap.put(entry.getKey(), UserDefinedFunction.newInstance(entry.getValue()));
}
samplers.forEach((id, sampler) -> functionMap.put(id,
sampler.getDimensions() == 2 ?
new NoiseFunction2(sampler.getSampler()) :
new NoiseFunction3(sampler.getSampler())));
samplers.forEach((id, sampler) -> {
if(sampler.getDimensions() == 2) {
functionMap.put(id, new NoiseFunction2(sampler.getSampler()));
functionMap.put(id + "Salted", new SaltedNoiseFunction2(sampler.getSampler()));
} else {
functionMap.put(id, new NoiseFunction3(sampler.getSampler()));
functionMap.put(id + "Salted", new SaltedNoiseFunction3(sampler.getSampler()));
}
});
return functionMap;
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.addons.noise.paralithic.noise;
import com.dfsek.paralithic.functions.dynamic.Context;
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
import com.dfsek.paralithic.node.Statefulness;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.noise.NoiseSampler;
public class SaltedNoiseFunction2 implements DynamicFunction {
private final NoiseSampler gen;
public SaltedNoiseFunction2(NoiseSampler gen) {
this.gen = gen;
}
@Override
public double eval(double... args) {
throw new UnsupportedOperationException("Cannot evaluate seeded function without seed context.");
}
@Override
public double eval(Context context, double... args) {
return gen.noise(((SeedContext) context).getSeed() + (long) args[2], args[0], args[1]);
}
@Override
public int getArgNumber() {
return 3;
}
@Override
public @NotNull Statefulness statefulness() {
return Statefulness.CONTEXTUAL;
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.addons.noise.paralithic.noise;
import com.dfsek.paralithic.functions.dynamic.Context;
import com.dfsek.paralithic.functions.dynamic.DynamicFunction;
import com.dfsek.paralithic.node.Statefulness;
import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.noise.NoiseSampler;
public class SaltedNoiseFunction3 implements DynamicFunction {
private final NoiseSampler gen;
public SaltedNoiseFunction3(NoiseSampler gen) {
this.gen = gen;
}
@Override
public double eval(double... args) {
throw new UnsupportedOperationException("Cannot evaluate seeded function without seed context.");
}
@Override
public double eval(Context context, double... args) {
return gen.noise(((SeedContext) context).getSeed() + (long) args[3], args[0], args[1], args[2]);
}
@Override
public int getArgNumber() {
return 4;
}
@Override
public @NotNull Statefulness statefulness() {
return Statefulness.CONTEXTUAL;
}
}
@@ -0,0 +1,74 @@
package com.dfsek.terra.addons.noise.samplers;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.cache.DoubleSeededVector2Key;
import com.dfsek.terra.api.util.cache.DoubleSeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
public class CacheSampler implements NoiseSampler {
private final NoiseSampler sampler;
private final ThreadLocal<Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>>> cache2D;
private final ThreadLocal<Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>>> cache3D;
public CacheSampler(NoiseSampler sampler, int dimensions) {
this.sampler = sampler;
if (dimensions == 2) {
LoadingCache<DoubleSeededVector2Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleNoise);
this.cache2D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector2Key(0, 0, 0), cache).mutable());
this.cache3D = null;
} else {
LoadingCache<DoubleSeededVector3Key, Double> cache = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleNoise);
this.cache3D = ThreadLocal.withInitial(() -> Pair.of(new DoubleSeededVector3Key(0, 0, 0, 0), cache).mutable());
this.cache2D = null;
}
}
private Double sampleNoise(DoubleSeededVector2Key vec) {
this.cache2D.get().setLeft(new DoubleSeededVector2Key(0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.z);
}
private Double sampleNoise(DoubleSeededVector3Key vec) {
this.cache3D.get().setLeft(new DoubleSeededVector3Key(0, 0, 0, 0));
return this.sampler.noise(vec.seed, vec.x, vec.y, vec.z);
}
@Override
public double noise(long seed, double x, double y) {
Mutable<DoubleSeededVector2Key, LoadingCache<DoubleSeededVector2Key, Double>> cachePair = cache2D.get();
DoubleSeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public double noise(long seed, double x, double y, double z) {
Mutable<DoubleSeededVector3Key, LoadingCache<DoubleSeededVector3Key, Double>> cachePair = cache3D.get();
DoubleSeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
}
@@ -12,4 +12,14 @@ public class AdditionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left + right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
for(int i = 0; i < dimensions; i++) {
out[i] = left[i] + right[i];
}
return out;
}
}
@@ -1,9 +1,10 @@
package com.dfsek.terra.addons.noise.samplers.arithmetic;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
public abstract class BinaryArithmeticSampler implements NoiseSampler {
public abstract class BinaryArithmeticSampler implements DerivativeNoiseSampler {
private final NoiseSampler left;
private final NoiseSampler right;
@@ -12,6 +13,11 @@ public abstract class BinaryArithmeticSampler implements NoiseSampler {
this.right = right;
}
@Override
public boolean isDifferentiable() {
return DerivativeNoiseSampler.isDifferentiable(left) && DerivativeNoiseSampler.isDifferentiable(right);
}
@Override
public double noise(long seed, double x, double y) {
return operate(left.noise(seed, x, y), right.noise(seed, x, y));
@@ -22,5 +28,17 @@ public abstract class BinaryArithmeticSampler implements NoiseSampler {
return operate(left.noise(seed, x, y, z), right.noise(seed, x, y, z));
}
@Override
public double[] noised(long seed, double x, double y) {
return operateDerivative(((DerivativeNoiseSampler)left).noised(seed, x, y), ((DerivativeNoiseSampler)right).noised(seed, x, y));
}
@Override
public double[] noised(long seed, double x, double y, double z) {
return operateDerivative(((DerivativeNoiseSampler)left).noised(seed, x, y, z), ((DerivativeNoiseSampler)right).noised(seed, x, y, z));
}
public abstract double operate(double left, double right);
public abstract double[] operateDerivative(double[] left, double[] right);
}
@@ -12,4 +12,15 @@ public class DivisionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left / right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
out[0] = left[0] / right[0];
for(int i = 1; i < dimensions; i++) {
out[i] = (left[i] * right[0] - left[0] * right[i]) / (right[0] * right[0]);
}
return out;
}
}
@@ -12,4 +12,11 @@ public class MaxSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return Math.max(left, right);
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
double leftValue = left[0];
double rightValue = right[0];
return leftValue > rightValue ? left : right;
}
}
@@ -12,4 +12,11 @@ public class MinSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return Math.min(left, right);
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
double leftValue = left[0];
double rightValue = right[0];
return leftValue < rightValue ? left : right;
}
}
@@ -12,4 +12,15 @@ public class MultiplicationSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left * right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
out[0] = left[0] * right[0];
for(int i = 1; i < dimensions; i++) {
out[i] = left[i] * right[0] + left[0] * right[i];
}
return out;
}
}
@@ -12,4 +12,14 @@ public class SubtractionSampler extends BinaryArithmeticSampler {
public double operate(double left, double right) {
return left - right;
}
@Override
public double[] operateDerivative(double[] left, double[] right) {
int dimensions = left.length;
double[] out = new double[dimensions];
for(int i = 0; i < dimensions; i++) {
out[i] = left[i] - right[i];
}
return out;
}
}
@@ -197,7 +197,9 @@ public class CellularSampler extends NoiseFunction {
private double jitterModifier = 1.0;
private NoiseSampler noiseLookup;
private boolean saltLookup;
public CellularSampler() {
noiseLookup = new OpenSimplex2Sampler();
}
@@ -217,7 +219,11 @@ public class CellularSampler extends NoiseFunction {
public void setReturnType(ReturnType returnType) {
this.returnType = returnType;
}
public void setSaltLookup(boolean saltLookup) {
this.saltLookup = saltLookup;
}
@Override
public double getNoiseRaw(long sl, double x, double y) {
int seed = (int) sl;
@@ -286,8 +292,8 @@ public class CellularSampler extends NoiseFunction {
case Distance2Sub -> distance1 - distance0 - 1;
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY);
case NoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), centerX, centerY);
case LocalNoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), x / frequency - centerX, y / frequency - centerY);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -377,8 +383,8 @@ public class CellularSampler extends NoiseFunction {
case Distance2Sub -> distance1 - distance0 - 1;
case Distance2Mul -> distance1 * distance0 * 0.5 - 1;
case Distance2Div -> distance0 / distance1 - 1;
case NoiseLookup -> noiseLookup.noise(sl, centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl, x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case NoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), centerX, centerY, centerZ);
case LocalNoiseLookup -> noiseLookup.noise(sl - (saltLookup ? 0 : salt), x / frequency - centerX, y / frequency - centerY, z / frequency - centerZ);
case Distance3 -> distance2 - 1;
case Distance3Add -> (distance2 + distance0) * 0.5 - 1;
case Distance3Sub -> distance2 - distance0 - 1;
@@ -0,0 +1,25 @@
package com.dfsek.terra.addons.noise.samplers.noise;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
public abstract class DerivativeNoiseFunction extends NoiseFunction implements DerivativeNoiseSampler {
@Override
public boolean isDifferentiable() {
return true;
}
@Override
public double[] noised(long seed, double x, double y) {
return getNoiseDerivativeRaw(seed + salt, x * frequency, y * frequency);
}
@Override
public double[] noised(long seed, double x, double y, double z) {
return getNoiseDerivativeRaw(seed + salt, x * frequency, y * frequency, z * frequency);
}
public abstract double[] getNoiseDerivativeRaw(long seed, double x, double y);
public abstract double[] getNoiseDerivativeRaw(long seed, double x, double y, double z);
}
@@ -0,0 +1,173 @@
package com.dfsek.terra.addons.noise.samplers.noise;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
public class PseudoErosionSampler extends NoiseFunction {
public static final double TAU = 2.0 * Math.PI;
private static final double HASH_X = 0.3183099f;
private static final double HASH_Y = 0.3678794f;
public final double gain;
public final double lacunarity;
public final double slopeStrength;
public final double branchStrength;
public final double erosionStrength;
private final int octaves;
private final double erosionFrequency;
private final DerivativeNoiseSampler sampler;
private final boolean slopeMask;
private final double slopeMaskFullSq;
private final double slopeMaskNoneSq;
private final double jitter;
private final double maxCellDistSq;
private final double maxCellDistSqRecip;
private final boolean averageErosionImpulses;
public PseudoErosionSampler(int octaves, double gain, double lacunarity, double slopeStrength, double branchStrength,
double erosionStrength, double erosionFrequency, DerivativeNoiseSampler sampler,
boolean slopeMask, double slopeMaskFull, double slopeMaskNone, double jitterModifier,
boolean averageErosionImpulses) {
this.octaves = octaves;
this.gain = gain;
this.lacunarity = lacunarity;
this.slopeStrength = slopeStrength;
this.branchStrength = branchStrength;
this.erosionStrength = erosionStrength;
this.erosionFrequency = erosionFrequency;
this.sampler = sampler;
this.slopeMask = slopeMask;
// Square these values and maintain sign since they're compared to a
// squared value, otherwise a sqrt would need to be used
this.slopeMaskFullSq = slopeMaskFull * slopeMaskFull * Math.signum(slopeMaskFull);
this.slopeMaskNoneSq = slopeMaskNone * slopeMaskNone * Math.signum((slopeMaskNone));
this.jitter = 0.43701595 * jitterModifier;
this.averageErosionImpulses = averageErosionImpulses;
this.maxCellDistSq = 1 + jitter * jitter;
this.maxCellDistSqRecip = 1 / maxCellDistSq;
}
public static double hashX(double seed, double n) {
// Swapped the components here
double nx = HASH_X * n * seed;
return -1.0f + 2.0f * fract(nx);
}
public static double hashY(double seed, double n) {
double ny = HASH_Y * n * seed;
return -1.0f + 2.0f * fract(ny);
}
public static double fract(double x) {
return (x - Math.floor(x));
}
public static double smoothstep(double edge0, double edge1, double x) {
// Scale, bias and saturate x to 0..1 range
x = clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
// Evaluate polynomial
return x * x * (3 - 2 * x);
}
public static double clamp(double x, double minVal, double maxVal) {
return Math.max(minVal, Math.min(maxVal, x));
}
public static double dot(double x1, double y1, double x2, double y2) {
return x1 * x2 + y1 * y2;
}
public double[] erosion(int seed, double x, double y, double dirX, double dirY) {
int gridX = (int) Math.round(x);
int gridY = (int) Math.round(y);
double noise = 0.0f;
double dirOutX = 0.0f;
double dirOutY = 0.0f;
double cumAmp = 0.0f;
for(int cellX = gridX - 1; cellX <= gridX + 1; cellX++) {
for(int cellY = gridY - 1; cellY <= gridY + 1; cellY++) {
double cellHash = hash(seed, cellX, cellY);
double cellOffsetX = hashX(seed, cellHash) * jitter;
double cellOffsetY = hashY(seed, cellHash) * jitter;
double cellOriginDeltaX = (x - cellX) + cellOffsetX;
double cellOriginDeltaY = (y - cellY) + cellOffsetY;
double cellOriginDistSq = cellOriginDeltaX * cellOriginDeltaX + cellOriginDeltaY * cellOriginDeltaY;
if(cellOriginDistSq > maxCellDistSq) continue; // Skip calculating cells too far away
double ampTmp = (cellOriginDistSq * maxCellDistSqRecip) - 1;
double amp = ampTmp * ampTmp; // Decrease cell amplitude further away
cumAmp += amp;
double directionalStrength = dot(cellOriginDeltaX, cellOriginDeltaY, dirX, dirY) * TAU;
noise += MathUtil.cos(directionalStrength) * amp;
double sinAngle = MathUtil.sin(directionalStrength) * amp;
dirOutX -= sinAngle * (cellOriginDeltaX + dirX);
dirOutY -= sinAngle * (cellOriginDeltaY + dirY);
}
}
if(averageErosionImpulses && cumAmp != 0) {
noise /= cumAmp;
dirOutX /= cumAmp;
dirOutY /= cumAmp;
}
return new double[]{ noise, dirOutX, dirOutY };
}
public double heightMap(long seed, double x, double y) {
double[] sample = sampler.noised(seed, x, y);
double height = sample[0];
double heightDirX = sample[1];
double heightDirY = sample[2];
// Take the curl of the normal to get the gradient facing down the slope
double baseDirX = heightDirY * slopeStrength;
double baseDirY = -heightDirX * slopeStrength;
double erosion = 0.0f;
double dirX = 0.0f;
double dirY = 0.0f;
double amp = 1.0f;
double cumAmp = 0.0f;
double freq = 1.0f;
// Stack erosion octaves
for(int i = 0; i < octaves; i++) {
double[] erosionResult = erosion((int) seed,
x * freq * erosionFrequency,
y * freq * erosionFrequency,
baseDirX + dirY * branchStrength,
baseDirY - dirX * branchStrength);
erosion += erosionResult[0] * amp;
dirX += erosionResult[1] * amp * freq;
dirY += erosionResult[2] * amp * freq;
cumAmp += amp;
amp *= gain;
freq *= lacunarity;
}
// Normalize erosion noise
erosion /= cumAmp;
// [-1, 1] -> [0, 1]
erosion = erosion * 0.5F + 0.5F;
// Without masking, erosion noise in areas with small gradients tend to produce mounds,
// this reduces erosion amplitude towards smaller gradients to avoid this
if(slopeMask) {
double dirMagSq = dot(baseDirX, baseDirY, baseDirX, baseDirY);
double flatness = smoothstep((double) slopeMaskNoneSq, slopeMaskFullSq, dirMagSq);
erosion *= flatness;
}
return height + erosion * erosionStrength;
}
@Override
public double getNoiseRaw(long seed, double x, double y) {
return heightMap(seed, x, y);
}
@Override
public double getNoiseRaw(long seed, double x, double y, double z) {
return getNoiseRaw(seed, x, z);
}
}
@@ -7,6 +7,7 @@
package com.dfsek.terra.addons.noise.samplers.noise.fractal;
import com.dfsek.terra.api.noise.DerivativeNoiseSampler;
import com.dfsek.terra.api.noise.NoiseSampler;
import com.dfsek.terra.api.util.MathUtil;
@@ -52,4 +53,60 @@ public class BrownianMotionSampler extends FractalNoiseFunction {
return sum;
}
@Override
public boolean isDifferentiable() {
return DerivativeNoiseSampler.isDifferentiable(input);
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
double[] sum = {0, 0, 0};
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
// This should only be called after `input` is verified as a `DerivativeNoiseSampler`
// so this should be a safe cast
double[] noise = ((DerivativeNoiseSampler) input).noised(seed++, x, y);
sum[0] += noise[0] * amp;
// Directional derivative of each octave can be subject to the same addition and product
// as per derivative sum and product rules in order to produce the correct final derivative
sum[1] += noise[1] * amp;
sum[2] += noise[2] * amp;
amp *= MathUtil.lerp(weightedStrength, 1.0, Math.min(noise[0] + 1, 2) * 0.5);
x *= lacunarity;
y *= lacunarity;
amp *= gain;
}
return sum;
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
double[] sum = {0, 0, 0, 0};
double amp = fractalBounding;
for(int i = 0; i < octaves; i++) {
double[] noise = ((DerivativeNoiseSampler) input).noised(seed++, x, y, z);
sum[0] += noise[0] * amp;
// See comment in 2D version
sum[1] += noise[1] * amp;
sum[2] += noise[2] * amp;
sum[3] += noise[3] * amp;
amp *= MathUtil.lerp(weightedStrength, 1.0, (noise[0] + 1) * 0.5);
x *= lacunarity;
y *= lacunarity;
z *= lacunarity;
amp *= gain;
}
return sum;
}
}
@@ -7,11 +7,11 @@
package com.dfsek.terra.addons.noise.samplers.noise.fractal;
import com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction;
import com.dfsek.terra.addons.noise.samplers.noise.DerivativeNoiseFunction;
import com.dfsek.terra.api.noise.NoiseSampler;
public abstract class FractalNoiseFunction extends NoiseFunction {
public abstract class FractalNoiseFunction extends DerivativeNoiseFunction {
protected final NoiseSampler input;
protected double fractalBounding = 1 / 1.75;
protected int octaves = 3;
@@ -52,4 +52,19 @@ public abstract class FractalNoiseFunction extends NoiseFunction {
public void setWeightedStrength(double weightedStrength) {
this.weightedStrength = weightedStrength;
}
@Override
public boolean isDifferentiable() {
return false;
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
}
@@ -276,4 +276,424 @@ public class OpenSimplex2SSampler extends SimplexStyleSampler {
return value * 9.046026385208288;
}
@Override
public boolean isDifferentiable() {
return true;
}
@Override
public double[] getNoiseDerivativeRaw(long sl, double x, double y) {
int seed = (int) sl;
// 2D OpenSimplex2S case is a modified 2D simplex noise.
final double SQRT3 = 1.7320508075688772935274463415059;
final double G2 = (3 - SQRT3) / 6;
final double F2 = 0.5f * (SQRT3 - 1);
double s = (x + y) * F2;
x += s;
y += s;
int i = (int) Math.floor(x);
int j = (int) Math.floor(y);
double xi = x - i;
double yi = y - j;
i *= PRIME_X;
j *= PRIME_Y;
int i1 = i + PRIME_X;
int j1 = j + PRIME_Y;
double t = (xi + yi) * G2;
double x0 = xi - t;
double y0 = yi - t;
double[] out = { 0.0f, 0.0f, 0.0f };
double a0 = (2.0 / 3.0) - x0 * x0 - y0 * y0;
double aa0 = a0 * a0, aaa0 = aa0 * a0, aaaa0 = aa0 * aa0;
int gi0 = gradCoordIndex(seed, i, j);
double gx0 = GRADIENTS_2_D[gi0], gy0 = GRADIENTS_2_D[gi0 | 1];
double rampValue0 = gx0 * x0 + gy0 * y0;
out[0] = aaaa0 * rampValue0;
out[1] = gx0 * aaaa0 - 8 * rampValue0 * aaa0 * x0;
out[2] = gy0 * aaaa0 - 8 * rampValue0 * aaa0 * y0;
double a1 = 2 * (1 - 2 * G2) * (1 / G2 - 2) * t + ((-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a0);
double x1 = x0 - (1 - 2 * G2);
double y1 = y0 - (1 - 2 * G2);
double aa1 = a1 * a1, aaa1 = aa1 * a1, aaaa1 = aa1 * aa1;
int gi1 = gradCoordIndex(seed, i1, j1);
double gx1 = GRADIENTS_2_D[gi1], gy1 = GRADIENTS_2_D[gi1 | 1];
double rampValue1 = gx1 * x1 + gy1 * y1;
out[0] += aaaa1 * rampValue1;
out[1] += gx1 * aaaa1 - 8 * rampValue1 * aaa1 * x1;
out[2] += gy1 * aaaa1 - 8 * rampValue1 * aaa1 * y1;
// Nested conditionals were faster than compact bit logic/arithmetic.
double xmyi = xi - yi;
if(t > G2) {
if(xi + xmyi > 1) {
double x2 = x0 + (3 * G2 - 2);
double y2 = y0 + (3 * G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i + (PRIME_X << 1), j + PRIME_Y);
double gx2 = GRADIENTS_2_D[gi2 | 0], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
} else {
double x2 = x0 + G2;
double y2 = y0 + (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i, j + PRIME_Y);
double gx2 = GRADIENTS_2_D[gi2], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
}
if(yi - xmyi > 1) {
double x3 = x0 + (3 * G2 - 1);
double y3 = y0 + (3 * G2 - 2);
double a3 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
if(a3 > 0) {
double aa3 = a3 * a3, aaa3 = aa3 * a3, aaaa3 = aa3 * aa3;
int gi3 = gradCoordIndex(seed, i + PRIME_X, j + (PRIME_Y << 1));
double gx3 = GRADIENTS_2_D[gi3], gy3 = GRADIENTS_2_D[gi3 | 1];
double rampValue3 = gx3 * x3 + gy3 * y3;
out[0] += aaaa3 * rampValue3;
out[1] += gx3 * aaaa3 - 8 * rampValue3 * aaa3 * x3;
out[2] += gy3 * aaaa3 - 8 * rampValue3 * aaa3 * y3;
}
} else {
double x3 = x0 + (G2 - 1);
double y3 = y0 + G2;
double a3 = (2.0 / 3.0) - x3 * x3 - y3 * y3;
if(a3 > 0) {
double aa3 = a3 * a3, aaa3 = aa3 * a3, aaaa3 = aa3 * aa3;
int gi3 = gradCoordIndex(seed, i + PRIME_X, j);
double gx3 = GRADIENTS_2_D[gi3], gy3 = GRADIENTS_2_D[gi3 | 1];
double rampValue3 = gx3 * x3 + gy3 * y3;
out[0] += aaaa3 * rampValue3;
out[1] += gx3 * aaaa3 - 8 * rampValue3 * aaa3 * x3;
out[2] += gy3 * aaaa3 - 8 * rampValue3 * aaa3 * y3;
}
}
} else {
if(xi + xmyi < 0) {
double x2 = x0 + (1 - G2);
double y2 = y0 - G2;
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i - PRIME_X, j);
double gx2 = GRADIENTS_2_D[gi2], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
} else {
double x2 = x0 + (G2 - 1);
double y2 = y0 + G2;
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i + PRIME_X, j);
double gx2 = GRADIENTS_2_D[gi2], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
}
if(yi < xmyi) {
double x2 = x0 - G2;
double y2 = y0 - (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i, j - PRIME_Y);
double gx2 = GRADIENTS_2_D[gi2], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
} else {
double x2 = x0 + G2;
double y2 = y0 + (G2 - 1);
double a2 = (2.0 / 3.0) - x2 * x2 - y2 * y2;
if(a2 > 0) {
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i, j + PRIME_Y);
double gx2 = GRADIENTS_2_D[gi2], gy2 = GRADIENTS_2_D[gi2 | 1];
double rampValue2 = gx2 * x2 + gy2 * y2;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y2;
}
}
}
out[0] *= 18.24196194486065;
out[1] *= 18.24196194486065;
out[2] *= 18.24196194486065;
return out;
}
@Override
public double[] getNoiseDerivativeRaw(long sl, double x, double y, double z) {
int seed = (int) sl;
// 3D OpenSimplex2S case uses two offset rotated cube grids.
final double R3 = (2.0 / 3.0);
double r = (x + y + z) * R3; // Rotation, not skew
x = r - x;
y = r - y;
z = r - z;
int i = (int) Math.floor(x);
int j = (int) Math.floor(y);
int k = (int) Math.floor(z);
double xi = x - i;
double yi = y - j;
double zi = z - k;
i *= PRIME_X;
j *= PRIME_Y;
k *= PRIME_Z;
int seed2 = seed + 1293373;
int xNMask = (int) (-0.5 - xi);
int yNMask = (int) (-0.5 - yi);
int zNMask = (int) (-0.5 - zi);
double[] out = { 0.0f, 0.0f, 0.0f, 0.0f };
double x0 = xi + xNMask;
double y0 = yi + yNMask;
double z0 = zi + zNMask;
double a0 = 0.75 - x0 * x0 - y0 * y0 - z0 * z0;
double aa0 = a0 * a0, aaa0 = aa0 * a0, aaaa0 = aa0 * aa0;
int gi0 = gradCoordIndex(seed, i + (xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (zNMask & PRIME_Z));
double gx0 = GRADIENTS_3D[gi0], gy0 = GRADIENTS_3D[gi0 | 1], gz0 = GRADIENTS_3D[gi0 | 2];
double rampValue0 = gx0 * x0 + gy0 * y0 + gz0 * z0;
out[0] = aaaa0 * rampValue0;
out[1] = gx0 * aaaa0 - 8 * rampValue0 * aaa0 * x0;
out[2] = gy0 * aaaa0 - 8 * rampValue0 * aaa0 * y0;
out[3] = gz0 * aaaa0 - 8 * rampValue0 * aaa0 * z0;
double x1 = xi - 0.5;
double y1 = yi - 0.5;
double z1 = zi - 0.5;
double a1 = 0.75 - x1 * x1 - y1 * y1 - z1 * z1;
double aa1 = a1 * a1, aaa1 = aa1 * a1, aaaa1 = aa1 * aa1;
int gi1 = gradCoordIndex(seed2, i + PRIME_X, j + PRIME_Y, k + PRIME_Z);
double gx1 = GRADIENTS_3D[gi1], gy1 = GRADIENTS_3D[gi1 | 1], gz1 = GRADIENTS_3D[gi1 | 2];
double rampValue1 = gx1 * x1 + gy1 * y1 + gz1 * z1;
out[0] += aaaa1 * rampValue1;
out[1] += gx1 * aaaa1 - 8 * rampValue1 * aaa1 * x1;
out[2] += gy1 * aaaa1 - 8 * rampValue1 * aaa1 * y1;
out[3] += gz1 * aaaa1 - 8 * rampValue1 * aaa1 * z1;
double xAFlipMask0 = ((xNMask | 1) << 1) * x1;
double yAFlipMask0 = ((yNMask | 1) << 1) * y1;
double zAFlipMask0 = ((zNMask | 1) << 1) * z1;
double xAFlipMask1 = (-2 - (xNMask << 2)) * x1 - 1.0;
double yAFlipMask1 = (-2 - (yNMask << 2)) * y1 - 1.0;
double zAFlipMask1 = (-2 - (zNMask << 2)) * z1 - 1.0;
boolean skip5 = false;
double a2 = xAFlipMask0 + a0;
if(a2 > 0) {
double x2 = x0 - (xNMask | 1);
double aa2 = a2 * a2, aaa2 = aa2 * a2, aaaa2 = aa2 * aa2;
int gi2 = gradCoordIndex(seed, i + (~xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (zNMask & PRIME_Z));
double gx2 = GRADIENTS_3D[gi2], gy2 = GRADIENTS_3D[gi2 | 1], gz2 = GRADIENTS_3D[gi2 | 2];
double rampValue2 = gx2 * x2 + gy2 * y0 + gz2 * z0;
out[0] += aaaa2 * rampValue2;
out[1] += gx2 * aaaa2 - 8 * rampValue2 * aaa2 * x2;
out[2] += gy2 * aaaa2 - 8 * rampValue2 * aaa2 * y0;
out[3] += gz2 * aaaa2 - 8 * rampValue2 * aaa2 * z0;
} else {
double a3 = yAFlipMask0 + zAFlipMask0 + a0;
if(a3 > 0) {
double y3 = y0 - (yNMask | 1);
double z3 = z0 - (zNMask | 1);
double aa3 = a3 * a3, aaa3 = aa3 * a3, aaaa3 = aa3 * aa3;
int gi3 = gradCoordIndex(seed, i + (xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (~zNMask & PRIME_Z));
double gx3 = GRADIENTS_3D[gi3], gy3 = GRADIENTS_3D[gi3 | 1], gz3 = GRADIENTS_3D[gi3 | 2];
double rampValue3 = gx3 * x0 + gy3 * y3 + gz3 * z3;
out[0] += aaaa3 * rampValue3;
out[1] += gx3 * aaaa3 - 8 * rampValue3 * aaa3 * x0;
out[2] += gy3 * aaaa3 - 8 * rampValue3 * aaa3 * y3;
out[3] += gz3 * aaaa3 - 8 * rampValue3 * aaa3 * z3;
}
double a4 = xAFlipMask1 + a1;
if(a4 > 0) {
double x4 = (xNMask | 1) + x1;
double aa4 = a4 * a4, aaa4 = aa4 * a4, aaaa4 = aa4 * aa4;
int gi4 = gradCoordIndex(seed2, i + (xNMask & (PRIME_X << 1)), j + PRIME_Y, k + PRIME_Z);
double gx4 = GRADIENTS_3D[gi4], gy4 = GRADIENTS_3D[gi4 | 1], gz4 = GRADIENTS_3D[gi4 | 2];
double rampValue4 = gx4 * x4 + gy4 * y1 + gz4 * z1;
out[0] += aaaa4 * rampValue4;
out[1] += gx4 * aaaa4 - 8 * rampValue4 * aaa4 * x4;
out[2] += gy4 * aaaa4 - 8 * rampValue4 * aaa4 * y1;
out[3] += gz4 * aaaa4 - 8 * rampValue4 * aaa4 * z1;
skip5 = true;
}
}
boolean skip9 = false;
double a6 = yAFlipMask0 + a0;
if(a6 > 0) {
double y6 = y0 - (yNMask | 1);
double aa6 = a6 * a6, aaa6 = aa6 * a6, aaaa6 = aa6 * aa6;
int gi6 = gradCoordIndex(seed, i + (xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (zNMask & PRIME_Z));
double gx6 = GRADIENTS_3D[gi6], gy6 = GRADIENTS_3D[gi6 | 1], gz6 = GRADIENTS_3D[gi6 | 2];
double rampValue6 = gx6 * x0 + gy6 * y6 + gz6 * z0;
out[0] += aaaa6 * rampValue6;
out[1] += gx6 * aaaa6 - 8 * rampValue6 * aaa6 * x0;
out[2] += gy6 * aaaa6 - 8 * rampValue6 * aaa6 * y6;
out[3] += gz6 * aaaa6 - 8 * rampValue6 * aaa6 * z0;
} else {
double a7 = xAFlipMask0 + zAFlipMask0 + a0;
if(a7 > 0) {
double x7 = x0 - (xNMask | 1);
double z7 = z0 - (zNMask | 1);
double aa7 = a7 * a7, aaa7 = aa7 * a7, aaaa7 = aa7 * aa7;
int gi7 = gradCoordIndex(seed, i + (~xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (~zNMask & PRIME_Z));
double gx7 = GRADIENTS_3D[gi7], gy7 = GRADIENTS_3D[gi7 | 1], gz7 = GRADIENTS_3D[gi7 | 2];
double rampValue7 = gx7 * x7 + gy7 * y0 + gz7 * z7;
out[0] += aaaa7 * rampValue7;
out[1] += gx7 * aaaa7 - 8 * rampValue7 * aaa7 * x7;
out[2] += gy7 * aaaa7 - 8 * rampValue7 * aaa7 * y0;
out[3] += gz7 * aaaa7 - 8 * rampValue7 * aaa7 * z7;
}
double a8 = yAFlipMask1 + a1;
if(a8 > 0) {
double y8 = (yNMask | 1) + y1;
double aa8 = a8 * a8, aaa8 = aa8 * a8, aaaa8 = aa8 * aa8;
int gi8 = gradCoordIndex(seed2, i + PRIME_X, j + (yNMask & (PRIME_Y << 1)), k + PRIME_Z);
double gx8 = GRADIENTS_3D[gi8], gy8 = GRADIENTS_3D[gi8 | 1], gz8 = GRADIENTS_3D[gi8 | 2];
double rampValue8 = gx8 * x1 + gy8 * y8 + gz8 * z1;
out[0] += aaaa8 * rampValue8;
out[1] += gx8 * aaaa8 - 8 * rampValue8 * aaa8 * x1;
out[2] += gy8 * aaaa8 - 8 * rampValue8 * aaa8 * y8;
out[3] += gz8 * aaaa8 - 8 * rampValue8 * aaa8 * z1;
skip9 = true;
}
}
boolean skipD = false;
double aA = zAFlipMask0 + a0;
if(aA > 0) {
double zA = z0 - (zNMask | 1);
double aaA = aA * aA, aaaA = aaA * aA, aaaaA = aaA * aaA;
int giA = gradCoordIndex(seed, i + (xNMask & PRIME_X), j + (yNMask & PRIME_Y), k + (~zNMask & PRIME_Z));
double gxA = GRADIENTS_3D[giA], gyA = GRADIENTS_3D[giA | 1], gzA = GRADIENTS_3D[giA | 2];
double rampValueA = gxA * x0 + gyA * y0 + gzA * zA;
out[0] += aaaaA * rampValueA;
out[1] += gxA * aaaaA - 8 * rampValueA * aaaA * x0;
out[2] += gyA * aaaaA - 8 * rampValueA * aaaA * y0;
out[3] += gzA * aaaaA - 8 * rampValueA * aaaA * zA;
} else {
double aB = xAFlipMask0 + yAFlipMask0 + a0;
if(aB > 0) {
double xB = x0 - (xNMask | 1);
double yB = y0 - (yNMask | 1);
double aaB = aB * aB, aaaB = aaB * aB, aaaaB = aaB * aaB;
int giB = gradCoordIndex(seed, i + (~xNMask & PRIME_X), j + (~yNMask & PRIME_Y), k + (zNMask & PRIME_Z));
double gxB = GRADIENTS_3D[giB], gyB = GRADIENTS_3D[giB | 1], gzB = GRADIENTS_3D[giB | 2];
double rampValueB = gxB * xB + gyB * yB + gzB * z0;
out[0] += aaaaB * rampValueB;
out[1] += gxB * aaaaB - 8 * rampValueB * aaaB * xB;
out[2] += gyB * aaaaB - 8 * rampValueB * aaaB * yB;
out[3] += gzB * aaaaB - 8 * rampValueB * aaaB * z0;
}
double aC = zAFlipMask1 + a1;
if(aC > 0) {
double zC = (zNMask | 1) + z1;
double aaC = aC * aC, aaaC = aaC * aC, aaaaC = aaC * aaC;
int giC = gradCoordIndex(seed2, i + PRIME_X, j + PRIME_Y, k + (zNMask & (PRIME_Z << 1)));
double gxC = GRADIENTS_3D[giC], gyC = GRADIENTS_3D[giC | 1], gzC = GRADIENTS_3D[giC | 2];
double rampValueC = gxC * x1 + gyC * y1 + gzC * zC;
out[0] += aaaaC * rampValueC;
out[1] += gxC * aaaaC - 8 * rampValueC * aaaC * x1;
out[2] += gyC * aaaaC - 8 * rampValueC * aaaC * y1;
out[3] += gzC * aaaaC - 8 * rampValueC * aaaC * zC;
skipD = true;
}
}
if(!skip5) {
double a5 = yAFlipMask1 + zAFlipMask1 + a1;
if(a5 > 0) {
double y5 = (yNMask | 1) + y1;
double z5 = (zNMask | 1) + z1;
double aa5 = a5 * a5, aaa5 = aa5 * a5, aaaa5 = aa5 * aa5;
int gi5 = gradCoordIndex(seed2, i + PRIME_X, j + (yNMask & (PRIME_Y << 1)), k + (zNMask & (PRIME_Z << 1)));
double gx5 = GRADIENTS_3D[gi5], gy5 = GRADIENTS_3D[gi5 | 1], gz5 = GRADIENTS_3D[gi5 | 2];
double rampValue5 = gx5 * x1 + gy5 * y5 + gz5 * z5;
out[0] += aaaa5 * rampValue5;
out[1] += gx5 * aaaa5 - 8 * rampValue5 * aaa5 * x1;
out[2] += gy5 * aaaa5 - 8 * rampValue5 * aaa5 * y5;
out[3] += gz5 * aaaa5 - 8 * rampValue5 * aaa5 * z5;
}
}
if(!skip9) {
double a9 = xAFlipMask1 + zAFlipMask1 + a1;
if(a9 > 0) {
double x9 = (xNMask | 1) + x1;
double z9 = (zNMask | 1) + z1;
double aa9 = a9 * a9, aaa9 = aa9 * a9, aaaa9 = aa9 * aa9;
int gi9 = gradCoordIndex(seed2, i + (xNMask & (PRIME_X << 1)), j + PRIME_Y, k + (zNMask & (PRIME_Z << 1)));
double gx9 = GRADIENTS_3D[gi9], gy9 = GRADIENTS_3D[gi9 | 1], gz9 = GRADIENTS_3D[gi9 | 2];
double rampValue9 = gx9 * x9 + gy9 * y1 + gz9 * z9;
out[0] += aaaa9 * rampValue9;
out[1] += gx9 * aaaa9 - 8 * rampValue9 * aaa9 * x9;
out[2] += gy9 * aaaa9 - 8 * rampValue9 * aaa9 * y1;
out[3] += gz9 * aaaa9 - 8 * rampValue9 * aaa9 * z9;
}
}
if(!skipD) {
double aD = xAFlipMask1 + yAFlipMask1 + a1;
if(aD > 0) {
double xD = (xNMask | 1) + x1;
double yD = (yNMask | 1) + y1;
double aaD = aD * aD, aaaD = aaD * aD, aaaaD = aaD * aaD;
int giD = gradCoordIndex(seed2, i + (xNMask & (PRIME_X << 1)), j + (yNMask & (PRIME_Y << 1)), k + PRIME_Z);
double gxD = GRADIENTS_3D[giD], gyD = GRADIENTS_3D[giD | 1], gzD = GRADIENTS_3D[giD | 2];
double rampValueD = gxD * xD + gyD * yD + gzD * z1;
out[0] += aaaaD * rampValueD;
out[1] += gxD * aaaaD - 8 * rampValueD * aaaD * xD;
out[2] += gyD * aaaaD - 8 * rampValueD * aaaD * yD;
out[3] += gzD * aaaaD - 8 * rampValueD * aaaD * z1;
}
}
out[0] *= 9.046026385208288;
out[1] *= 9.046026385208288;
out[2] *= 9.046026385208288;
out[3] *= 9.046026385208288;
return out;
}
}
@@ -37,41 +37,37 @@ public class OpenSimplex2Sampler extends SimplexStyleSampler {
i *= PRIME_X;
j *= PRIME_Y;
double n0, n1, n2;
double value = 0;
double a = 0.5 - x0 * x0 - y0 * y0;
if(a <= 0) n0 = 0;
else {
n0 = (a * a) * (a * a) * gradCoord(seed, i, j, x0, y0);
if(a > 0) {
value = (a * a) * (a * a) * gradCoord(seed, i, j, x0, y0);
}
double c = 2 * (1 - 2 * G2) * (1 / G2 - 2) * t + ((-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a);
if(c <= 0) n2 = 0;
else {
if(c > 0) {
double x2 = x0 + (2 * G2 - 1);
double y2 = y0 + (2 * G2 - 1);
n2 = (c * c) * (c * c) * gradCoord(seed, i + PRIME_X, j + PRIME_Y, x2, y2);
value += (c * c) * (c * c) * gradCoord(seed, i + PRIME_X, j + PRIME_Y, x2, y2);
}
if(y0 > x0) {
double x1 = x0 + G2;
double y1 = y0 + (G2 - 1);
double b = 0.5 - x1 * x1 - y1 * y1;
if(b <= 0) n1 = 0;
else {
n1 = (b * b) * (b * b) * gradCoord(seed, i, j + PRIME_Y, x1, y1);
if(b > 0) {
value += (b * b) * (b * b) * gradCoord(seed, i, j + PRIME_Y, x1, y1);
}
} else {
double x1 = x0 + (G2 - 1);
double y1 = y0 + G2;
double b = 0.5 - x1 * x1 - y1 * y1;
if(b <= 0) n1 = 0;
else {
n1 = (b * b) * (b * b) * gradCoord(seed, i + PRIME_X, j, x1, y1);
if(b > 0) {
value += (b * b) * (b * b) * gradCoord(seed, i + PRIME_X, j, x1, y1);
}
}
return (n0 + n1 + n2) * 99.83685446303647f;
return value * 99.83685446303647f;
}
@Override
@@ -157,4 +153,208 @@ public class OpenSimplex2Sampler extends SimplexStyleSampler {
return value * 32.69428253173828125;
}
@Override
public boolean isDifferentiable() {
return true;
}
@Override
public double[] getNoiseDerivativeRaw(long sl, double x, double y) {
int seed = (int) sl;
// 2D OpenSimplex2 case uses the same algorithm as ordinary Simplex.
final double G2 = (3 - SQRT3) / 6;
final double F2 = 0.5f * (SQRT3 - 1);
double s = (x + y) * F2;
x += s;
y += s;
int i = (int) Math.floor(x);
int j = (int) Math.floor(y);
double xi = x - i;
double yi = y - j;
double t = (xi + yi) * G2;
double x0 = xi - t;
double y0 = yi - t;
i *= PRIME_X;
j *= PRIME_Y;
double[] out = { 0.0f, 0.0f, 0.0f };
double a = 0.5 - x0 * x0 - y0 * y0;
if(a > 0) {
double aa = a * a, aaa = aa * a, aaaa = aa * aa;
int gi = gradCoordIndex(seed, i, j);
double gx = GRADIENTS_2_D[gi], gy = GRADIENTS_2_D[gi | 1];
double rampValue = gx * x0 + gy * y0;
out[0] += aaaa * rampValue;
out[1] += gx * aaaa - 8 * rampValue * aaa * x0;
out[2] += gy * aaaa - 8 * rampValue * aaa * y0;
}
double c = 2 * (1 - 2 * G2) * (1 / G2 - 2) * t + ((-2 * (1 - 2 * G2) * (1 - 2 * G2)) + a);
if(c > 0) {
double x2 = x0 + (2 * G2 - 1);
double y2 = y0 + (2 * G2 - 1);
double cc = c * c, ccc = cc * c, cccc = cc * cc;
int gi = gradCoordIndex(seed, i + PRIME_X, j + PRIME_Y);
double gx = GRADIENTS_2_D[gi], gy = GRADIENTS_2_D[gi | 1];
double rampValue = gx * x2 + gy * y2;
out[0] += cccc * rampValue;
out[1] += gx * cccc - 8 * rampValue * ccc * x2;
out[2] += gy * cccc - 8 * rampValue * ccc * y2;
}
if(y0 > x0) {
double x1 = x0 + G2;
double y1 = y0 + (G2 - 1);
double b = 0.5 - x1 * x1 - y1 * y1;
if(b > 0) {
double bb = b * b, bbb = bb * b, bbbb = bb * bb;
int gi = gradCoordIndex(seed, i, j + PRIME_Y);
double gx = GRADIENTS_2_D[gi], gy = GRADIENTS_2_D[gi | 1];
double rampValue = gx * x1 + gy * y1;
out[0] += bbbb * rampValue;
out[1] += gx * bbbb - 8 * rampValue * bbb * x1;
out[2] += gy * bbbb - 8 * rampValue * bbb * y1;
}
} else {
double x1 = x0 + (G2 - 1);
double y1 = y0 + G2;
double b = 0.5 - x1 * x1 - y1 * y1;
if(b > 0) {
double bb = b * b, bbb = bb * b, bbbb = bb * bb;
int gi = gradCoordIndex(seed, i + PRIME_X, j);
double gx = GRADIENTS_2_D[gi], gy = GRADIENTS_2_D[gi | 1];
double rampValue = gx * x1 + gy * y1;
out[0] += bbbb * rampValue;
out[1] += gx * bbbb - 8 * rampValue * bbb * x1;
out[2] += gy * bbbb - 8 * rampValue * bbb * y1;
}
}
out[0] *= 99.83685446303647f;
out[1] *= 99.83685446303647f;
out[2] *= 99.83685446303647f;
return out;
}
@Override
public double[] getNoiseDerivativeRaw(long sl, double x, double y, double z) {
int seed = (int) sl;
// 3D OpenSimplex2Sampler case uses two offset rotated cube grids.
final double R3 = (2.0 / 3.0);
double r = (x + y + z) * R3; // Rotation, not skew
x = r - x;
y = r - y;
z = r - z;
int i = (int) Math.round(x);
int j = (int) Math.round(y);
int k = (int) Math.round(z);
double x0 = x - i;
double y0 = y - j;
double z0 = z - k;
int xNSign = (int) (-1.0 - x0) | 1;
int yNSign = (int) (-1.0 - y0) | 1;
int zNSign = (int) (-1.0 - z0) | 1;
double ax0 = xNSign * -x0;
double ay0 = yNSign * -y0;
double az0 = zNSign * -z0;
i *= PRIME_X;
j *= PRIME_Y;
k *= PRIME_Z;
double[] out = { 0.0f, 0.0f, 0.0f, 0.0f };
double a = (0.6f - x0 * x0) - (y0 * y0 + z0 * z0);
for(int l = 0; ; l++) {
if(a > 0) {
double aa = a * a, aaa = aa * a, aaaa = aa * aa;
int gi = gradCoordIndex(seed, i, j, k);
double gx = GRADIENTS_3D[gi], gy = GRADIENTS_3D[gi | 1], gz = GRADIENTS_3D[gi | 2];
double rampValue = gx * x0 + gy * y0 + gz * z0;
out[0] += aaaa * rampValue;
out[1] += gx * aaaa - 8 * rampValue * aaa * x0;
out[1] += gy * aaaa - 8 * rampValue * aaa * y0;
out[2] += gz * aaaa - 8 * rampValue * aaa * z0;
}
if(ax0 >= ay0 && ax0 >= az0) {
double b = a + ax0 + ax0;
if(b > 1) {
b -= 1;
double bb = b * b, bbb = bb * b, bbbb = bb * bb;
int gi = gradCoordIndex(seed, i - xNSign * PRIME_X, j, k);
double gx = GRADIENTS_3D[gi], gy = GRADIENTS_3D[gi | 1], gz = GRADIENTS_3D[gi | 2];
double rampValue = gx * (x0 + xNSign) + gy * y0 + gz * z0;
out[0] += bbbb * rampValue;
out[1] += gx * bbbb - 8 * rampValue * bbb * (x0 + xNSign);
out[1] += gy * bbbb - 8 * rampValue * bbb * y0;
out[2] += gz * bbbb - 8 * rampValue * bbb * z0;
}
} else if(ay0 > ax0 && ay0 >= az0) {
double b = a + ay0 + ay0;
if(b > 1) {
b -= 1;
double bb = b * b, bbb = bb * b, bbbb = bb * bb;
int gi = gradCoordIndex(seed, i, j - yNSign * PRIME_Y, k);
double gx = GRADIENTS_3D[gi], gy = GRADIENTS_3D[gi | 1], gz = GRADIENTS_3D[gi | 2];
double rampValue = gx * x0 + gy * (y0 + yNSign) + gz * z0;
out[0] += bbbb * rampValue;
out[1] += gx * bbbb - 8 * rampValue * bbb * x0;
out[1] += gy * bbbb - 8 * rampValue * bbb * (y0 + yNSign);
out[2] += gz * bbbb - 8 * rampValue * bbb * z0;
}
} else {
double b = a + az0 + az0;
if(b > 1) {
b -= 1;
double bb = b * b, bbb = bb * b, bbbb = bb * bb;
int gi = gradCoordIndex(seed, i, j, k - zNSign * PRIME_Z);
double gx = GRADIENTS_3D[gi], gy = GRADIENTS_3D[gi | 1], gz = GRADIENTS_3D[gi | 2];
double rampValue = gx * x0 + gy * y0 + gz * (z0 + zNSign);
out[0] += bbbb * rampValue;
out[1] += gx * bbbb - 8 * rampValue * bbb * x0;
out[1] += gy * bbbb - 8 * rampValue * bbb * y0;
out[2] += gz * bbbb - 8 * rampValue * bbb * (z0 + zNSign);
}
}
if(l == 1) break;
ax0 = 0.5 - ax0;
ay0 = 0.5 - ay0;
az0 = 0.5 - az0;
x0 = xNSign * ax0;
y0 = yNSign * ay0;
z0 = zNSign * az0;
a += (0.75 - ax0) - (ay0 + az0);
i += (xNSign >> 1) & PRIME_X;
j += (yNSign >> 1) & PRIME_Y;
k += (zNSign >> 1) & PRIME_Z;
xNSign = -xNSign;
yNSign = -yNSign;
zNSign = -zNSign;
seed = ~seed;
}
out[0] *= 32.69428253173828125;
out[1] *= 32.69428253173828125;
out[2] *= 32.69428253173828125;
out[3] *= 32.69428253173828125;
return out;
}
}
@@ -7,13 +7,13 @@
package com.dfsek.terra.addons.noise.samplers.noise.simplex;
import com.dfsek.terra.addons.noise.samplers.noise.NoiseFunction;
import com.dfsek.terra.addons.noise.samplers.noise.DerivativeNoiseFunction;
/**
* Abstract NoiseSampler implementation for simplex-style noise functions.
*/
public abstract class SimplexStyleSampler extends NoiseFunction {
public abstract class SimplexStyleSampler extends DerivativeNoiseFunction {
protected static final double[] GRADIENTS_2_D = {
0.130526192220052d, 0.99144486137381d, 0.38268343236509d, 0.923879532511287d, 0.608761429008721d, 0.793353340291235d,
0.793353340291235d, 0.608761429008721d, 0.923879532511287d, 0.38268343236509d, 0.99144486137381d, 0.130526192220051d,
@@ -79,26 +79,53 @@ public abstract class SimplexStyleSampler extends NoiseFunction {
1, 1, 0, 0, 0, -1, 1, 0, -1, 1, 0, 0, 0, -1, -1, 0
};
protected static double gradCoord(int seed, int xPrimed, int yPrimed, double xd, double yd) {
protected static int gradCoordIndex(int seed, int xPrimed, int yPrimed) {
int hash = hash(seed, xPrimed, yPrimed);
hash ^= hash >> 15;
hash &= 127 << 1;
double xg = GRADIENTS_2_D[hash];
double yg = GRADIENTS_2_D[hash | 1];
return hash;
}
protected static double gradCoord(int seed, int xPrimed, int yPrimed, double xd, double yd) {
int index = gradCoordIndex(seed, xPrimed, yPrimed);
double xg = GRADIENTS_2_D[index];
double yg = GRADIENTS_2_D[index | 1];
return xd * xg + yd * yg;
}
protected static double gradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, double xd, double yd, double zd) {
protected static int gradCoordIndex(int seed, int xPrimed, int yPrimed, int zPrimed) {
int hash = hash(seed, xPrimed, yPrimed, zPrimed);
hash ^= hash >> 15;
hash &= 63 << 2;
double xg = GRADIENTS_3D[hash];
double yg = GRADIENTS_3D[hash | 1];
double zg = GRADIENTS_3D[hash | 2];
return hash;
}
protected static double gradCoord(int seed, int xPrimed, int yPrimed, int zPrimed, double xd, double yd, double zd) {
int index = gradCoordIndex(seed, xPrimed, yPrimed, zPrimed);
double xg = GRADIENTS_3D[index];
double yg = GRADIENTS_3D[index | 1];
double zg = GRADIENTS_3D[index | 2];
return xd * xg + yd * yg + zd * zg;
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
public double[] getNoiseDerivativeRaw(long seed, double x, double y, double z) {
throw new UnsupportedOperationException("Implementation failed to check or set isDifferentiable correctly");
}
@Override
public boolean isDifferentiable() {
return false;
}
}
@@ -11,13 +11,6 @@ import com.dfsek.tectonic.api.config.template.dynamic.DynamicTemplate;
import com.dfsek.tectonic.api.config.template.dynamic.DynamicValue;
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures;
import com.dfsek.terra.addons.generation.feature.config.FeatureStageTemplate;
import com.dfsek.terra.addons.manifest.api.AddonInitializer;
@@ -35,6 +28,9 @@ import com.dfsek.terra.api.util.reflection.TypeKey;
import com.dfsek.terra.api.world.biome.Biome;
import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import java.util.*;
import java.util.function.Supplier;
public class FeatureGenerationAddon implements AddonInitializer {
public static final TypeKey<Supplier<ObjectTemplate<GenerationStage>>> STAGE_TYPE_KEY = new TypeKey<>() {
@@ -7,9 +7,6 @@
package com.dfsek.terra.addons.generation.feature;
import java.util.Collections;
import java.util.Random;
import com.dfsek.terra.addons.generation.feature.config.BiomeFeatures;
import com.dfsek.terra.api.Platform;
import com.dfsek.terra.api.noise.NoiseSampler;
@@ -22,6 +19,9 @@ import com.dfsek.terra.api.world.chunk.generation.ProtoWorld;
import com.dfsek.terra.api.world.chunk.generation.stage.GenerationStage;
import com.dfsek.terra.api.world.chunk.generation.util.Column;
import java.util.Collections;
import java.util.Random;
public class FeatureGenerationStage implements GenerationStage, StringIdentifiable {
private final Platform platform;
@@ -62,7 +62,7 @@ public class FeatureGenerationStage implements GenerationStage, StringIdentifiab
world.getBiomeProvider()
.getColumn(
tx + (doBlending ? (int) (blendSampler.noise(seed, tx, tz) * blendAmplitude) : 0),
tz + (doBlending ? (int) (blendSampler.noise(seed+1, tx, tz) * blendAmplitude) : 0),
tz + (doBlending ? (int) (blendSampler.noise(seed + 1, tx, tz) * blendAmplitude) : 0),
world)
.forRanges(resolution, (min, max, biome) -> {
for(int subChunkX = 0; subChunkX < resolution; subChunkX++) {
@@ -17,6 +17,10 @@ import com.dfsek.terra.api.config.Loader;
import com.dfsek.terra.api.properties.Properties;
import com.dfsek.terra.api.util.generic.Lazy;
import com.github.benmanes.caffeine.cache.Scheduler;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/*
* Cache prevents configs from loading the same image multiple times into memory
@@ -26,8 +30,9 @@ record ImageCache(LoadingCache<String, Image> cache) implements Properties {
ImageLibraryPackConfigTemplate config = pack.getContext().get(ImageLibraryPackConfigTemplate.class);
ImageCache images;
if(!pack.getContext().has(ImageCache.class)) {
var cacheBuilder = Caffeine.newBuilder();
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS);
var cacheBuilder = Caffeine.newBuilder().executor(CACHE_EXECUTOR).scheduler(Scheduler.systemScheduler());
if(config.unloadOnTimeout()) cacheBuilder.expireAfterAccess(config.getCacheTimeout(), TimeUnit.SECONDS) .executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler());
images = new ImageCache(cacheBuilder.build(s -> loadImage(s, files)));
pack.getContext().put(images);
} else images = pack.getContext().get(ImageCache.class);
@@ -1,7 +1,5 @@
package com.dfsek.terra.addons.structure.mutator;
import java.util.Random;
import com.dfsek.terra.api.registry.key.Keyed;
import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.structure.Structure;
@@ -11,6 +9,8 @@ import com.dfsek.terra.api.world.WritableWorld;
import com.dfsek.terra.api.world.util.ReadInterceptor;
import com.dfsek.terra.api.world.util.WriteInterceptor;
import java.util.Random;
public class MutatedStructure implements Structure, Keyed<MutatedStructure> {
private final RegistryKey key;
@@ -7,13 +7,13 @@
package com.dfsek.terra.addons.terrascript.script.builders;
import java.util.List;
import com.dfsek.terra.addons.terrascript.parser.lang.Returnable;
import com.dfsek.terra.addons.terrascript.parser.lang.functions.FunctionBuilder;
import com.dfsek.terra.addons.terrascript.script.functions.CheckBlockFunction;
import com.dfsek.terra.addons.terrascript.tokenizer.Position;
import java.util.List;
public class CheckBlockFunctionBuilder implements FunctionBuilder<CheckBlockFunction> {
@SuppressWarnings("unchecked")
+1 -1
View File
@@ -2,7 +2,7 @@ dependencies {
api("ca.solo-studios", "strata", Versions.Libraries.strata)
compileOnlyApi("org.slf4j", "slf4j-api", Versions.Libraries.slf4j)
testImplementation("org.slf4j", "slf4j-api", Versions.Libraries.slf4j)
api("cloud.commandframework", "cloud-core", Versions.Libraries.cloud)
api("org.incendo", "cloud-core", Versions.Libraries.cloud)
api("com.dfsek.tectonic", "common", Versions.Libraries.tectonic)
@@ -81,4 +81,7 @@ public interface Platform extends LoaderRegistrar {
@NotNull
@Contract(pure = true)
Profiler getProfiler();
@Contract(pure = true)
int getGenerationThreads();
}
@@ -1,18 +1,19 @@
package com.dfsek.terra.api.command.arguments;
import cloud.commandframework.ArgumentDescription;
import cloud.commandframework.arguments.CommandArgument;
import cloud.commandframework.arguments.parser.ArgumentParseResult;
import cloud.commandframework.arguments.parser.ArgumentParser;
import cloud.commandframework.context.CommandContext;
import io.leangen.geantyref.TypeToken;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.incendo.cloud.component.CommandComponent;
import org.incendo.cloud.component.DefaultValue;
import org.incendo.cloud.context.CommandContext;
import org.incendo.cloud.context.CommandInput;
import org.incendo.cloud.parser.ArgumentParseResult;
import org.incendo.cloud.parser.ArgumentParser;
import org.incendo.cloud.parser.ParserDescriptor;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import java.util.List;
import java.util.Optional;
import java.util.Queue;
import java.util.function.BiFunction;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -22,39 +23,22 @@ import com.dfsek.terra.api.registry.key.RegistryKey;
import com.dfsek.terra.api.util.reflection.TypeKey;
public class RegistryArgument<T, R> extends CommandArgument<T, R> {
private RegistryArgument(
boolean required,
@NonNull String name,
Function<CommandContext<T>, Registry<R>> registryFunction,
TypeToken<R> typeToken,
@NonNull String defaultValue,
@Nullable BiFunction<CommandContext<T>, String, List<String>> suggestionsProvider,
@NonNull ArgumentDescription description
) {
super(required,
name,
new RegistryArgumentParser<>(registryFunction),
defaultValue,
typeToken,
suggestionsProvider,
description);
}
public class RegistryArgument {
public static <T, R> Builder<T, R> builder(String name, Registry<R> registry) {
return new Builder<>(name, registry);
}
public static <T, R> CommandArgument<T, R> of(String name, Registry<R> registry) {
public static <T, R> CommandComponent<T> of(String name, Registry<R> registry) {
return RegistryArgument.<T, R>builder(name, registry).build();
}
public static <T, R> CommandArgument<T, R> optional(String name, Registry<R> registry) {
return RegistryArgument.<T, R>builder(name, registry).asOptional().build();
public static <T, R> CommandComponent<T> optional(String name, Registry<R> registry) {
return RegistryArgument.<T, R>builder(name, registry).optional().build();
}
public static <T, R> CommandArgument<T, R> optional(String name, Registry<R> registry, String defaultKey) {
return RegistryArgument.<T, R>builder(name, registry).asOptionalWithDefault(defaultKey).build();
public static <T, R> CommandComponent<T> optional(String name, Registry<R> registry, DefaultValue<T, R> defaultKey) {
return RegistryArgument.<T, R>builder(name, registry).optional(defaultKey).build();
}
@SuppressWarnings("unchecked")
@@ -63,49 +47,36 @@ public class RegistryArgument<T, R> extends CommandArgument<T, R> {
return new Builder<>(name, registryFunction, (TypeToken<R>) TypeToken.get(registryType.getType()));
}
public static <T, R> CommandArgument<T, R> of(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType) {
public static <T, R> CommandComponent<T> of(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType) {
return RegistryArgument.<T, R>builder(name, registryFunction, registryType).build();
}
public static <T, R> CommandArgument<T, R> optional(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType) {
return RegistryArgument.builder(name, registryFunction, registryType).asOptional().build();
public static <T, R> CommandComponent<T> optional(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType) {
return RegistryArgument.builder(name, registryFunction, registryType).optional().build();
}
public static <T, R> CommandArgument<T, R> optional(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType, String defaultKey) {
return RegistryArgument.builder(name, registryFunction, registryType).asOptionalWithDefault(defaultKey).build();
public static <T, R> CommandComponent<T> optional(String name, Function<CommandContext<T>, Registry<R>> registryFunction,
TypeKey<R> registryType, DefaultValue<T, R> defaultKey) {
return RegistryArgument.builder(name, registryFunction, registryType).optional(defaultKey).build();
}
public static final class Builder<T, R> extends CommandArgument.Builder<T, R> {
private final Function<CommandContext<T>, Registry<R>> registryFunction;
private final TypeToken<R> typeToken;
public static final class Builder<T, R> extends CommandComponent.Builder<T, R> {
@SuppressWarnings("unchecked")
private Builder(@NonNull String name, Registry<R> registry) {
super((TypeToken<R>) TypeToken.get(registry.getType().getType()), name);
this.registryFunction = commandContext -> registry;
this.typeToken = (TypeToken<R>) TypeToken.get(registry.getType().getType());
super();
this.name(name);
this.parser(ParserDescriptor.of(
new RegistryArgumentParser<>(commandContext -> registry),
(TypeToken<R>) TypeToken.get(registry.getType().getType())));
}
private Builder(@NonNull String name, Function<CommandContext<T>, Registry<R>> registryFunction, TypeToken<R> typeToken) {
super(typeToken, name);
this.typeToken = typeToken;
this.registryFunction = registryFunction;
}
@Override
public @NonNull RegistryArgument<T, R> build() {
return new RegistryArgument<>(
isRequired(),
getName(),
registryFunction,
typeToken,
getDefaultValue(),
getSuggestionsProvider(),
getDefaultDescription()
);
super();
this.name(name);
this.parser(ParserDescriptor.of(new RegistryArgumentParser<>(registryFunction), typeToken));
}
}
@@ -119,12 +90,12 @@ public class RegistryArgument<T, R> extends CommandArgument<T, R> {
@Override
public @NonNull ArgumentParseResult<@NonNull R> parse(@NonNull CommandContext<@NonNull T> commandContext,
@NonNull Queue<@NonNull String> inputQueue) {
String input = inputQueue.remove();
String next = inputQueue.peek();
if(next != null && next.equals(":")) {
input += inputQueue.remove();
input += inputQueue.remove();
@NonNull CommandInput commandInput) {
String input = commandInput.readString();
String next = commandInput.peekString();
if(next.equals(":")) {
input += commandInput.readString();
input += commandInput.readString();
}
Registry<R> registry = registryFunction.apply(commandContext);
@@ -146,8 +117,17 @@ public class RegistryArgument<T, R> extends CommandArgument<T, R> {
}
@Override
public @NonNull List<@NonNull String> suggestions(@NonNull CommandContext<T> commandContext, @NonNull String input) {
return registryFunction.apply(commandContext).keys().stream().map(RegistryKey::toString).sorted().collect(Collectors.toList());
public @NonNull SuggestionProvider<T> suggestionProvider() {
return new SuggestionProvider<>() {
@Override
public @NonNull CompletableFuture<? extends @NonNull Iterable<? extends @NonNull Suggestion>> suggestionsFuture(
@NonNull CommandContext<T> context, @NonNull CommandInput input) {
// TODO: Verify whether this is correct
return CompletableFuture.completedFuture(registryFunction.apply(context).keys().stream().map(
registryKey -> Suggestion.suggestion(registryKey.toString())).sorted().collect(Collectors.toList()));
}
};
}
}
}
@@ -1,10 +1,10 @@
package com.dfsek.terra.api.event.events.platform;
import cloud.commandframework.CommandManager;
import com.dfsek.terra.api.command.CommandSender;
import com.dfsek.terra.api.event.events.Event;
import org.incendo.cloud.CommandManager;
public class CommandRegistrationEvent implements Event {
private final CommandManager<CommandSender> commandManager;
@@ -0,0 +1,42 @@
package com.dfsek.terra.api.noise;
/**
* A NoiseSampler which additionally may provide a 1st directional derivative
*/
public interface DerivativeNoiseSampler extends NoiseSampler {
static boolean isDifferentiable(NoiseSampler sampler) {
return sampler instanceof DerivativeNoiseSampler dSampler && dSampler.isDifferentiable();
}
/**
* Samplers may or may not be able to provide a derivative depending on what
* inputs they take, this method signals whether this is the case.
*
* @return If the noise sampler provides a derivative or not
*/
boolean isDifferentiable();
/**
* Derivative return version of standard 2D noise evaluation
*
* @param seed
* @param x
* @param y
*
* @return 3 element array, in index order: noise value, partial x derivative, partial y derivative
*/
double[] noised(long seed, double x, double y);
/**
* Derivative return version of standard 3D noise evaluation
*
* @param seed
* @param x
* @param y
* @param z
*
* @return 4 element array, in index order: noise value, partial x derivative, partial y derivative, partial z derivative
*/
double[] noised(long seed, double x, double y, double z);
}
@@ -0,0 +1,9 @@
package com.dfsek.terra.api.util.cache;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class CacheUtils {
public static final Executor CACHE_EXECUTOR = Executors.newSingleThreadExecutor();
}
@@ -0,0 +1,35 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector2Key {
public double x;
public double z;
public long seed;
public DoubleSeededVector2Key(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(double x, double z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,38 @@
package com.dfsek.terra.api.util.cache;
public class DoubleSeededVector3Key {
public double x;
public double y;
public double z;
public long seed;
public DoubleSeededVector3Key(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(double x, double y, double z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof DoubleSeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = (int) Double.doubleToLongBits(x);
code = 31 * code + (int) Double.doubleToLongBits(y);
code = 31 * code + (int) Double.doubleToLongBits(z);
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,37 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector2Key {
public int x;
public int z;
public long seed;
public SeededVector2Key(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
public void set(int x, int z, long seed) {
this.x = x;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2Key that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
@@ -0,0 +1,40 @@
package com.dfsek.terra.api.util.cache;
public class SeededVector3Key {
public int x;
public int y;
public int z;
public long seed;
public SeededVector3Key(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
public void set(int x, int y, int z, long seed) {
this.x = x;
this.y = y;
this.z = z;
this.seed = seed;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3Key that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + (Long.hashCode(seed));
}
}
@@ -8,6 +8,7 @@
package com.dfsek.terra.api.util.reflection;
import org.jetbrains.annotations.NotNull;
import sun.misc.Unsafe;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
@@ -26,6 +27,18 @@ import java.util.stream.Stream;
public final class ReflectionUtil {
private static final Unsafe UNSAFE;
static {
try {
final Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
UNSAFE = (Unsafe) unsafeField.get(null);
} catch(NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
public static Field[] getFields(@NotNull Class<?> type) {
Field[] result = type.getDeclaredFields();
Class<?> parentClass = type.getSuperclass();
@@ -35,6 +48,14 @@ public final class ReflectionUtil {
return result;
}
public static void setFinalField(Object obj, String fieldName, Object value) throws NoSuchFieldException {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
long fieldOffset = UNSAFE.objectFieldOffset(field);
UNSAFE.putObject(obj, fieldOffset, value);
}
public static Method[] getMethods(@NotNull Class<?> type) {
Method[] result = type.getDeclaredMethods();
Class<?> parentClass = type.getSuperclass();
@@ -7,6 +7,8 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.Platform;
import org.jetbrains.annotations.Contract;
import java.util.Optional;
@@ -91,7 +93,7 @@ public interface BiomeProvider {
return StreamSupport.stream(getBiomes().spliterator(), false);
}
default CachingBiomeProvider caching() {
default CachingBiomeProvider caching(Platform platform) {
if(this instanceof CachingBiomeProvider cachingBiomeProvider) {
return cachingBiomeProvider;
}
@@ -1,5 +1,11 @@
package com.dfsek.terra.api.world.biome.generation;
import com.dfsek.terra.api.util.cache.SeededVector2Key;
import com.dfsek.terra.api.util.cache.SeededVector3Key;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.api.util.generic.pair.Pair.Mutable;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.github.benmanes.caffeine.cache.Scheduler;
@@ -9,6 +15,8 @@ import java.util.Optional;
import com.dfsek.terra.api.Handle;
import com.dfsek.terra.api.world.biome.Biome;
import static com.dfsek.terra.api.util.cache.CacheUtils.CACHE_EXECUTOR;
/**
* A biome provider implementation that lazily evaluates biomes, and caches them.
@@ -18,24 +26,43 @@ import com.dfsek.terra.api.world.biome.Biome;
public class CachingBiomeProvider implements BiomeProvider, Handle {
protected final BiomeProvider delegate;
private final int res;
private final LoadingCache<SeededVector3, Biome> cache;
private final LoadingCache<SeededVector2, Optional<Biome>> baseCache;
private final ThreadLocal<Pair.Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>>> cache;
private final ThreadLocal<Pair.Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>>> baseCache;
protected CachingBiomeProvider(BiomeProvider delegate) {
this.delegate = delegate;
this.res = delegate.resolution();
this.cache = Caffeine
.newBuilder()
.scheduler(Scheduler.disabledScheduler())
.initialCapacity(98304)
.maximumSize(98304) // 1 full chunk (high res)
.build(vec -> delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed));
this.baseCache = Caffeine
LoadingCache<SeededVector2Key, Optional<Biome>> cache = Caffeine
.newBuilder()
.maximumSize(256) // 1 full chunk (high res)
.build(vec -> delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed));
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(256)
.maximumSize(256)
.build(this::sampleBiome);
this.baseCache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector2Key(0, 0, 0), cache).mutable());
LoadingCache<SeededVector3Key, Biome> cache3D = Caffeine
.newBuilder()
.executor(CACHE_EXECUTOR)
.scheduler(Scheduler.systemScheduler())
.initialCapacity(981504)
.maximumSize(981504)
.build(this::sampleBiome);
this.cache = ThreadLocal.withInitial(() -> Pair.of(new SeededVector3Key(0, 0, 0, 0), cache3D).mutable());
}
private Optional<Biome> sampleBiome(SeededVector2Key vec) {
this.baseCache.get().setLeft(new SeededVector2Key(0, 0, 0));
return this.delegate.getBaseBiome(vec.x * res, vec.z * res, vec.seed);
}
private Biome sampleBiome(SeededVector3Key vec) {
this.cache.get().setLeft(new SeededVector3Key(0, 0, 0, 0));
return this.delegate.getBiome(vec.x * res, vec.y * res, vec.z * res, vec.seed);
}
@Override
@@ -45,12 +72,18 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
@Override
public Biome getBiome(int x, int y, int z, long seed) {
return cache.get(new SeededVector3(x / res, y / res, z / res, seed));
Mutable<SeededVector3Key, LoadingCache<SeededVector3Key, Biome>> cachePair = cache.get();
SeededVector3Key mutableKey = cachePair.getLeft();
mutableKey.set(x, y, z, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
public Optional<Biome> getBaseBiome(int x, int z, long seed) {
return baseCache.get(new SeededVector2(x / res, z / res, seed));
Mutable<SeededVector2Key, LoadingCache<SeededVector2Key, Optional<Biome>>> cachePair = baseCache.get();
SeededVector2Key mutableKey = cachePair.getLeft();
mutableKey.set(x, z, seed);
return cachePair.getRight().get(mutableKey);
}
@Override
@@ -62,40 +95,4 @@ public class CachingBiomeProvider implements BiomeProvider, Handle {
public int resolution() {
return delegate.resolution();
}
private record SeededVector3(int x, int y, int z, long seed) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector3 that) {
return this.y == that.y && this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + y;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
private record SeededVector2(int x, int z, long seed) {
@Override
public boolean equals(Object obj) {
if(obj instanceof SeededVector2 that) {
return this.z == that.z && this.x == that.x && this.seed == that.seed;
}
return false;
}
@Override
public int hashCode() {
int code = x;
code = 31 * code + z;
return 31 * code + ((int) (seed ^ (seed >>> 32)));
}
}
}
@@ -310,6 +310,24 @@ public abstract class AbstractPlatform implements Platform {
}
}
public static int getGenerationThreadsWithReflection(String className, String fieldName, String project) {
try {
Class aClass = Class.forName(className);
int threads = aClass.getField(fieldName).getInt(null);
logger.info("{} found, setting {} generation threads.", project, threads);
return threads;
} catch(ClassNotFoundException e) {
logger.info("{} not found.", project);
} catch(NoSuchFieldException e) {
logger.warn("{} found, but {} field not found this probably means {0} has changed its code and " +
"Terra has not updated to reflect that.", project, fieldName);
} catch(IllegalAccessException e) {
logger.error("Failed to access {} field in {}, assuming 1 generation thread.", fieldName, project, e);
}
return 0;
}
@Override
public void register(TypeRegistry registry) {
loaders.register(registry);
@@ -339,4 +357,9 @@ public abstract class AbstractPlatform implements Platform {
public @NotNull Profiler getProfiler() {
return profiler;
}
@Override
public int getGenerationThreads() {
return 1;
}
}
@@ -232,7 +232,7 @@ public class ConfigPackImpl implements ConfigPack {
ConfigPackPostTemplate packPostTemplate = new ConfigPackPostTemplate();
selfLoader.load(packPostTemplate, packManifest);
seededBiomeProvider =
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching() : packPostTemplate.getProviderBuilder();
template.getBiomeCache() ? packPostTemplate.getProviderBuilder().caching(platform) : packPostTemplate.getProviderBuilder();
checkDeadEntries();
}
@@ -24,14 +24,15 @@ import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import com.dfsek.tectonic.api.loader.ConfigLoader;
import com.dfsek.tectonic.api.preprocessor.Result;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.reflection.TypeKey;
import org.jetbrains.annotations.NotNull;
import java.lang.reflect.AnnotatedType;
import java.util.Map;
import com.dfsek.terra.api.config.meta.Meta;
import com.dfsek.terra.api.util.reflection.TypeKey;
public class MetaNumberPreprocessor extends MetaPreprocessor<Meta> {
public static final TypeKey<String> META_STRING_KEY = new TypeKey<@Meta String>() {
Binary file not shown.
+2 -2
View File
@@ -1,7 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+1 -1
View File
@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
Vendored
+10 -10
View File
@@ -43,11 +43,11 @@ set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
@@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
-6
View File
@@ -2,12 +2,6 @@ plugins {
id("xyz.jpenilla.run-paper") version Versions.Bukkit.runPaper
}
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/") {
name = "Sonatype"
}
}
dependencies {
shaded(project(":platforms:bukkit:common"))
shaded(project(":platforms:bukkit:nms:v1_21", configuration = "reobf"))
+1 -1
View File
@@ -11,5 +11,5 @@ dependencies {
shadedApi("com.google.guava", "guava", Versions.Libraries.Internal.guava)
shadedApi("cloud.commandframework", "cloud-paper", Versions.Libraries.cloud)
shadedApi("org.incendo", "cloud-paper", Versions.Libraries.cloudPaper)
}
@@ -51,7 +51,13 @@ public class PlatformImpl extends AbstractPlatform {
private final TerraBukkitPlugin plugin;
private int generationThreads;
public PlatformImpl(TerraBukkitPlugin plugin) {
generationThreads = getGenerationThreadsWithReflection("ca.spottedleaf.moonrise.common.util.MoonriseCommon", "WORKER_THREADS", "Moonrise");
if (generationThreads == 0) {
generationThreads = 1;
}
this.plugin = plugin;
load();
}
@@ -108,6 +114,11 @@ public class PlatformImpl extends AbstractPlatform {
return itemHandle;
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
@Override
public void register(TypeRegistry registry) {
super.register(registry);
@@ -17,15 +17,16 @@
package com.dfsek.terra.bukkit;
import cloud.commandframework.brigadier.CloudBrigadierManager;
import cloud.commandframework.bukkit.CloudBukkitCapabilities;
import cloud.commandframework.execution.CommandExecutionCoordinator;
import cloud.commandframework.paper.PaperCommandManager;
import io.papermc.paper.threadedregions.scheduler.AsyncScheduler;
import io.papermc.paper.threadedregions.scheduler.GlobalRegionScheduler;
import org.bukkit.Bukkit;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.plugin.java.JavaPlugin;
import org.incendo.cloud.SenderMapper;
import org.incendo.cloud.brigadier.CloudBrigadierManager;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@@ -71,14 +72,14 @@ public class TerraBukkitPlugin extends JavaPlugin {
}
try {
PaperCommandManager<CommandSender> commandManager = getCommandSenderPaperCommandManager();
LegacyPaperCommandManager<CommandSender> commandManager = getCommandSenderPaperCommandManager();
platform.getEventManager().callEvent(new CommandRegistrationEvent(commandManager));
} catch(Exception e) { // This should never happen.
logger.error("""
TERRA HAS BEEN DISABLED
Errors occurred while registering commands.
Please report this to Terra.
""".strip(), e);
@@ -91,22 +92,26 @@ public class TerraBukkitPlugin extends JavaPlugin {
}
@NotNull
private PaperCommandManager<CommandSender> getCommandSenderPaperCommandManager() throws Exception {
PaperCommandManager<CommandSender> commandManager = new PaperCommandManager<>(this,
CommandExecutionCoordinator.simpleCoordinator(),
BukkitAdapter::adapt,
BukkitAdapter::adapt);
private LegacyPaperCommandManager<CommandSender> getCommandSenderPaperCommandManager() throws Exception {
// TODO: Update to PaperCommandManager
LegacyPaperCommandManager<CommandSender> commandManager = new LegacyPaperCommandManager<>(
this,
ExecutionCoordinator.simpleCoordinator(),
SenderMapper.create(
BukkitAdapter::adapt,
BukkitAdapter::adapt
));
if(commandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
commandManager.registerBrigadier();
final CloudBrigadierManager<?, ?> brigManager = commandManager.brigadierManager();
if(brigManager != null) {
brigManager.setNativeNumberSuggestions(false);
}
}
if(commandManager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
} else if(commandManager.hasCapability(CloudBukkitCapabilities.ASYNCHRONOUS_COMPLETION)) {
commandManager.registerAsynchronousCompletions();
}
return commandManager;
}
@@ -176,7 +181,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
logger.warn("""
You are using Mohist, so we will not give you any support for issues that may arise.
Since you enabled the "IKnowMohistCausesLotsOfIssuesButIWillUseItAnyways" flag, we won't disable Terra. But be warned.
> I felt a great disturbance in the JVM, as if millions of plugins suddenly cried out in stack traces and were suddenly silenced.
> I fear something terrible has happened.
> - Astrash
@@ -189,7 +194,7 @@ public class TerraBukkitPlugin extends JavaPlugin {
@Override
public @Nullable
ChunkGenerator getDefaultWorldGenerator(@NotNull String worldName, String id) {
if (id == null || id.trim().equals("")) { return null; }
if(id == null || id.trim().equals("")) { return null; }
return new BukkitChunkGeneratorWrapper(generatorMap.computeIfAbsent(worldName, name -> {
ConfigPack pack = platform.getConfigRegistry().getByID(id).orElseThrow(
() -> new IllegalArgumentException("No such config pack \"" + id + "\""));
@@ -23,13 +23,11 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
import com.dfsek.terra.api.block.state.BlockState;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.api.handle.WorldHandle;
import com.dfsek.terra.bukkit.util.BukkitUtils;
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
import com.dfsek.terra.bukkit.world.entity.BukkitEntityType;
public class BukkitWorldHandle implements WorldHandle {
@@ -61,21 +59,6 @@ public class BukkitWorldHandle implements WorldHandle {
@Override
public @NotNull EntityType getEntity(@NotNull String id) {
if (!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();;
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id);
String entityID = id.toUpperCase(Locale.ROOT).substring(10);
return new BukkitEntityType(switch(entityID) {
case "END_CRYSTAL" -> org.bukkit.entity.EntityType.END_CRYSTAL;
case "ENDER_CRYSTAL" -> throw new IllegalArgumentException(
"Invalid entity identifier " + id); // make sure this issue can't happen the other way around.
default -> org.bukkit.entity.EntityType.valueOf(entityID);
});
return BukkitUtils.getEntityType(id);
}
}
@@ -1,21 +1,25 @@
package com.dfsek.terra.bukkit.nms;
import com.dfsek.terra.bukkit.util.VersionUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.dfsek.terra.bukkit.PlatformImpl;
import com.dfsek.terra.bukkit.util.VersionUtil;
public interface Initializer {
String NMS = "v" + VersionUtil.getMinecraftVersionInfo().toString().replace(".", "_");
String NMS = VersionUtil.getMinecraftVersionInfo().toString().replace(".", "_");
String TERRA_PACKAGE = Initializer.class.getPackageName();
static boolean init(PlatformImpl platform) {
Logger logger = LoggerFactory.getLogger(Initializer.class);
try {
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + NMS + ".NMSInitializer");
String packageVersion = NMS;
if(NMS.equals("v1_21_1")) {
packageVersion = "v1_21";
}
Class<?> initializerClass = Class.forName(TERRA_PACKAGE + "." + packageVersion + ".NMSInitializer");
try {
Initializer initializer = (Initializer) initializerClass.getConstructor().newInstance();
initializer.initialize(platform);
@@ -2,11 +2,40 @@ package com.dfsek.terra.bukkit.util;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Locale;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.bukkit.world.entity.BukkitEntityType;
public class BukkitUtils {
private static final Logger logger = LoggerFactory.getLogger(BukkitUtils.class);
public static boolean isLiquid(BlockData blockState) {
Material material = blockState.getMaterial();
return material == Material.WATER || material == Material.LAVA;
}
public static EntityType getEntityType(String id) {
if(!id.contains(":")) { //TODO: remove in 7.0
String newid = "minecraft:" + id.toLowerCase();
;
logger.warn(
"Translating " + id + " to " + newid + ". In 1.20.3 entity parsing was reworked" +
". You are advised to perform this rename in your config backs as this translation will be removed in the next major " +
"version of Terra.");
}
if(!id.startsWith("minecraft:")) throw new IllegalArgumentException("Invalid entity identifier " + id);
String entityID = id.toUpperCase(Locale.ROOT).substring(10);
return new BukkitEntityType(switch(entityID) {
case "END_CRYSTAL" -> org.bukkit.entity.EntityType.END_CRYSTAL;
case "ENDER_CRYSTAL" -> throw new IllegalArgumentException(
"Invalid entity identifier " + id); // make sure this issue can't happen the other way around.
default -> org.bukkit.entity.EntityType.valueOf(entityID);
});
}
}
@@ -77,7 +77,7 @@ public final class VersionUtil {
public static final class MinecraftVersionInfo {
private static final Logger logger = LoggerFactory.getLogger(MinecraftVersionInfo.class);
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)");
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)(?:\\.(\\d+))?");
private final int major;
private final int minor;
private final int patch;
@@ -97,7 +97,7 @@ public final class VersionUtil {
if(versionMatcher.find()) {
major = Integer.parseInt(versionMatcher.group(1));
minor = Integer.parseInt(versionMatcher.group(2));
patch = Integer.parseInt(versionMatcher.group(3));
patch = versionMatcher.group(3) != null ? Integer.parseInt(versionMatcher.group(3)) : -1;
} else {
logger.warn("Error while parsing minecraft version info. Continuing launch, but setting all versions to -1.");
@@ -112,7 +112,11 @@ public final class VersionUtil {
if(major == -1 && minor == -1 && patch == -1)
return "Unknown";
return String.format("v%d.%d.%d", major, minor, patch);
if(patch >= 0) {
return String.format("v%d.%d.%d", major, minor, patch);
} else {
return String.format("v%d.%d", major, minor);
}
}
public int getMajor() {
@@ -20,7 +20,6 @@ package com.dfsek.terra.bukkit.world;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.TreeType;
import org.bukkit.entity.Player;
import org.bukkit.generator.WorldInfo;
import org.bukkit.util.Vector;
@@ -46,27 +45,12 @@ import com.dfsek.terra.bukkit.world.block.BukkitBlockTypeAndItem;
import com.dfsek.terra.bukkit.world.block.data.BukkitBlockState;
import com.dfsek.terra.bukkit.world.inventory.BukkitItemStack;
import com.dfsek.terra.bukkit.world.inventory.meta.BukkitEnchantment;
import com.dfsek.terra.transform.MapTransform;
import com.dfsek.terra.transform.TransformerImpl;
/**
* Utility class to adapt Bukkit enums to Terra enums.
*/
public final class BukkitAdapter {
public static TransformerImpl<TreeType, String> TREE_TRANSFORMER = new TransformerImpl.Builder<TreeType, String>()
.addTransform(new MapTransform<TreeType, String>()
.add(TreeType.COCOA_TREE, "JUNGLE_COCOA")
.add(TreeType.BIG_TREE, "LARGE_OAK")
.add(TreeType.TALL_REDWOOD, "LARGE_SPRUCE")
.add(TreeType.REDWOOD, "SPRUCE")
.add(TreeType.TREE, "OAK")
.add(TreeType.MEGA_REDWOOD, "MEGA_SPRUCE")
.add(TreeType.SWAMP, "SWAMP_OAK"))
.addTransform(TreeType::toString)
.build();
public static BlockState adapt(org.bukkit.block.data.BlockData data) {
return BukkitBlockState.newInstance(data);
}
@@ -1,9 +1,9 @@
package com.dfsek.terra.bukkit.world;
import org.bukkit.generator.WorldInfo;
import com.dfsek.terra.api.world.info.WorldProperties;
import org.bukkit.generator.WorldInfo;
public class BukkitWorldProperties implements WorldProperties {
private final WorldInfo delegate;
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
import com.dfsek.terra.api.block.entity.MobSpawner;
import com.dfsek.terra.api.block.entity.SerialState;
import com.dfsek.terra.api.entity.EntityType;
import com.dfsek.terra.bukkit.util.BukkitUtils;
import com.dfsek.terra.bukkit.world.entity.BukkitEntityType;
@@ -115,7 +116,7 @@ public class BukkitMobSpawner extends BukkitBlockEntity implements MobSpawner {
public void applyState(String state) {
SerialState.parse(state).forEach((k, v) -> {
switch(k) {
case "type" -> setSpawnedType(new BukkitEntityType(org.bukkit.entity.EntityType.valueOf(v.toUpperCase())));
case "type" -> setSpawnedType(BukkitUtils.getEntityType(v));
case "delay" -> setDelay(Integer.parseInt(v));
case "min_delay" -> setMinSpawnDelay(Integer.parseInt(v));
case "max_delay" -> setMaxSpawnDelay(Integer.parseInt(v));
@@ -2,8 +2,8 @@ name: "Terra"
main: "com.dfsek.terra.bukkit.TerraBukkitPlugin"
version: "@VERSION@"
load: "STARTUP"
author: dfsek
authors: [ "dfsek", "duplexsystem", "Astrash", "solonovamax", "Sancires", "Aureus", "RogueShade" ]
website: "@WIKI@"
api-version: "1.20"
api-version: "1.21.1"
description: "@DESCRIPTION@"
folia-supported: true
@@ -1,9 +1,5 @@
apply(plugin = "io.papermc.paperweight.userdev")
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
api(project(":platforms:bukkit:common"))
paperDevBundle(Versions.Bukkit.paperDevBundle)
@@ -41,7 +41,8 @@ public class AwfulBukkitHacks {
try {
BukkitPlatformBiome platformBiome = (BukkitPlatformBiome) biome.getPlatformBiome();
NamespacedKey vanillaBukkitKey = platformBiome.getHandle().getKey();
ResourceLocation vanillaMinecraftKey = ResourceLocation.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(), vanillaBukkitKey.getKey());
ResourceLocation vanillaMinecraftKey = ResourceLocation.fromNamespaceAndPath(vanillaBukkitKey.getNamespace(),
vanillaBukkitKey.getKey());
Biome platform = NMSBiomeInjector.createBiome(biome, Objects.requireNonNull(biomeRegistry.get(vanillaMinecraftKey)));
ResourceKey<Biome> delegateKey = ResourceKey.create(
@@ -37,10 +37,10 @@ public class NMSBiomeProvider extends BiomeSource {
@Override
protected @NotNull MapCodec<? extends BiomeSource> codec() {
return MapCodec.assumeMapUnsafe(BiomeSource.CODEC);
// return MapCodec.unit(null);
// BuiltInRegistries.BIOME_SOURCE.byNameCodec().dispatchMap(this::codec, Function.identity());
// BuiltInRegistries.BIOME_SOURCE.byNameCodec().dispatchStable(BiomeSource::codec, Function.identity());
// return BiomeSource.CODEC;
// return MapCodec.unit(null);
// BuiltInRegistries.BIOME_SOURCE.byNameCodec().dispatchMap(this::codec, Function.identity());
// BuiltInRegistries.BIOME_SOURCE.byNameCodec().dispatchStable(BiomeSource::codec, Function.identity());
// return BiomeSource.CODEC;
}
@Override
@@ -12,12 +12,12 @@ import org.bukkit.event.world.WorldInitEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.util.reflection.ReflectionUtil;
import com.dfsek.terra.bukkit.generator.BukkitChunkGeneratorWrapper;
@@ -41,21 +41,17 @@ public class NMSInjectListener implements Listener {
ChunkGenerator vanilla = serverWorld.getChunkSource().getGenerator();
NMSBiomeProvider provider = new NMSBiomeProvider(pack.getBiomeProvider(), craftWorld.getSeed());
ChunkMap chunkMap = serverWorld.getChunkSource().chunkMap;
WorldGenContext worldGenContext = chunkMap.worldGenContext;
try {
Field worldGenContextField = chunkMap.getClass().getDeclaredField("worldGenContext");
worldGenContextField.setAccessible(true);
WorldGenContext worldGenContext = (WorldGenContext) worldGenContextField.get(chunkMap);
worldGenContextField.set(chunkMap,
new WorldGenContext(
worldGenContext.level(),
new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed()),
worldGenContext.structureManager(),
worldGenContext.lightEngine(),
worldGenContext.mainThreadMailBox()
));
} catch(NoSuchFieldException | IllegalAccessException e) {
ReflectionUtil.setFinalField(chunkMap, "worldGenContext", new WorldGenContext(
worldGenContext.level(),
new NMSChunkGeneratorDelegate(vanilla, pack, provider, craftWorld.getSeed()),
worldGenContext.structureManager(),
worldGenContext.lightEngine(),
worldGenContext.mainThreadMailBox()
));
} catch(NoSuchFieldException e) {
throw new RuntimeException(e);
}
+9
View File
@@ -10,6 +10,9 @@ dependencies {
shadedApi("commons-io", "commons-io", Versions.Libraries.Internal.apacheIO)
shadedApi("com.github.Querz", "NBT", Versions.CLI.nbt)
shadedImplementation("info.picocli", "picocli", Versions.CLI.picocli)
annotationProcessor("info.picocli", "picocli-codegen", Versions.CLI.picocli)
shadedImplementation("com.google.guava", "guava", Versions.Libraries.Internal.guava)
shadedImplementation("ch.qos.logback", "logback-classic", Versions.CLI.logback)
@@ -26,6 +29,12 @@ tasks.withType<Jar> {
}
}
tasks.withType<JavaCompile> {
doFirst {
options.compilerArgs.add("-Aproject=${project.group}/${project.name}")
}
}
application {
mainClass.set(javaMainClass)
}
@@ -22,6 +22,8 @@ public class CLIPlatform extends AbstractPlatform {
private final CLIWorldHandle worldHandle = new CLIWorldHandle();
private final CLIItemHandle itemHandle = new CLIItemHandle();
private final int generationThreads = Runtime.getRuntime().availableProcessors() - 1;
public CLIPlatform() {
LOGGER.info("Root directory: {}", getDataFolder().getAbsoluteFile());
load();
@@ -58,4 +60,9 @@ public class CLIPlatform extends AbstractPlatform {
super.register(registry);
registry.registerLoader(PlatformBiome.class, (TypeLoader<PlatformBiome>) (annotatedType, o, configLoader, depthTracker) -> () -> o);
}
@Override
public int getGenerationThreads() {
return generationThreads;
}
}
@@ -5,41 +5,74 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.concurrent.Callable;
import com.dfsek.terra.api.config.ConfigPack;
import com.dfsek.terra.api.event.events.platform.PlatformInitializationEvent;
import com.dfsek.terra.api.util.vector.Vector2Int;
import com.dfsek.terra.cli.world.CLIWorld;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
public final class TerraCLI {
private static final Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
public static void main(String... args) {
//TODO auto pull in version
@Command(name = "TerraCLI", mixinStandardHelpOptions = true, version = "6.6.0",
description = "Generates a Terra World and saves it in minecraft region format.")
public final class TerraCLI implements Callable<Integer> {
@Option(names = { "-s", "--size"}, description = "Number of regions to generate.")
private int size = 2;
@Option(names = { "-p", "--pack"}, description = "Config pack to use.")
private String pack = "OVERWORLD";
@Option(names = { "--seed"}, description = "Seed for world generation.")
private long seed = 0;
@Option(names = { "--max-height"}, description = "Maximum height of the world.")
private int maxHeight = 320;
@Option(names = { "--min-height"}, description = "Minimum height of the world.")
private int minHeight = -64;
@Option(names = { "--no-save"}, description = "Don't save the world to disk.")
private boolean noSave = false;
@Override
public Integer call() {
Logger LOGGER = LoggerFactory.getLogger(TerraCLI.class);
LOGGER.info("Starting Terra CLI...");
CLIPlatform platform = new CLIPlatform();
platform.getEventManager().callEvent(new PlatformInitializationEvent());
ConfigPack generate = platform.getConfigRegistry().getByID("OVERWORLD").orElseThrow(); // TODO: make this a cli argument
ConfigPack generate = platform.getConfigRegistry().getByID(pack).orElseThrow();
CLIWorld world = new CLIWorld(2, 2, 384, -64, generate);
CLIWorld world = new CLIWorld(size, seed, maxHeight, minHeight, generate, noSave);
world.generate();
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
if(!noSave) {
world.serialize().parallel().forEach(mcaFile -> {
Vector2Int pos = mcaFile.getLeft();
String name = MCAUtil.createNameFromRegionLocation(pos.getX(), pos.getZ());
LOGGER.info("Writing region ({}, {}) to {}", pos.getX(), pos.getZ(), name);
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
try {
MCAUtil.write(mcaFile.getRight(), name);
} catch(IOException e) {
e.printStackTrace();
}
LOGGER.info("Wrote region to file.");
});
}
LOGGER.info("Done.");
System.exit(0);
return 0;
}
public static void main(String... args) {
int exitCode = new CommandLine(new TerraCLI()).execute(args);
System.exit(exitCode);
}
}
@@ -43,6 +43,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
private final ChunkGenerator chunkGenerator;
private final BiomeProvider biomeProvider;
private final ConfigPack pack;
private final boolean noSave;
private final AtomicInteger amount = new AtomicInteger(0);
private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() - 1);
@@ -51,7 +52,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
long seed,
int maxHeight,
int minHeight,
ConfigPack pack) {
ConfigPack pack, boolean noSave) {
this.size = size;
this.maxHeight = maxHeight;
this.minHeight = minHeight;
@@ -59,6 +60,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
this.chunkGenerator = pack.getGeneratorProvider().newInstance(pack);
this.biomeProvider = pack.getBiomeProvider();
this.pack = pack;
this.noSave = noSave;
size += 1;
@@ -73,6 +75,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
}
public void generate() {
ArrayList<Double> CPSHistory = new ArrayList<>();
int sizeChunks = size * 32;
List<Future<?>> futures = new ArrayList<>();
final AtomicLong start = new AtomicLong(System.nanoTime());
@@ -83,7 +86,13 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
futures.add(executor.submit(() -> {
try {
int num = amount.getAndIncrement();
CLIChunk chunk = getChunkAt(finalX, finalZ);
CLIChunk chunk;
if (!noSave) {
chunk = getChunkAt(finalX, finalZ);
} else {
chunk = new CLIChunk(Math.floorMod(finalX, 32), Math.floorMod(finalZ, 32), this);
}
BiomeProvider cachingBiomeProvider = pack.getBiomeProvider();
chunkGenerator.generateChunkData(chunk, this, cachingBiomeProvider, finalX, finalZ);
CLIProtoWorld protoWorld = new CLIProtoWorld(this, cachingBiomeProvider, finalX, finalZ);
@@ -91,6 +100,7 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
if(num % 240 == 239) {
long time = System.nanoTime();
double cps = num / ((double) (time - start.get()) / 1000000000);
CPSHistory.add(cps);
LOGGER.info("Generating chunk at ({}, {}), generated {} chunks at {}cps", finalX, finalZ, num, cps);
amount.set(0);
start.set(System.nanoTime());
@@ -109,6 +119,8 @@ public class CLIWorld implements ServerWorld, NBTSerializable<Stream<Pair<Vector
e.printStackTrace();
}
}
LOGGER.info("Average CPS: {}", CPSHistory.stream().mapToDouble(d -> d).average().orElse(0));
}
@Override
+2 -6
View File
@@ -26,8 +26,8 @@ dependencies {
modImplementation("net.fabricmc:fabric-loader:${Versions.Mod.fabricLoader}")
modImplementation("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud)
include("cloud.commandframework", "cloud-fabric", Versions.Libraries.cloud)
modImplementation("org.incendo", "cloud-fabric", Versions.Libraries.cloudFabric)
include("org.incendo", "cloud-fabric", Versions.Libraries.cloudFabric)
modRuntimeOnly("net.fabricmc.fabric-api", "fabric-api", Versions.Fabric.fabricAPI)
}
@@ -45,10 +45,6 @@ loom {
addonDir(project.file("./run/config/Terra/addons"), tasks.named("configureLaunch").get())
tasks {
compileJava {
options.release.set(17)
}
remapJar {
dependsOn("installAddons")
@@ -5,7 +5,13 @@
"name": "Terra",
"description": "@DESCRIPTION@",
"authors": [
"dfsek"
"dfsek",
"duplexsystem",
"Astrash",
"solonovamax",
"Sancires",
"Aureus",
"RogueShade"
],
"contact": {
"homepage": "@WIKI@",
@@ -26,9 +32,9 @@
"terra.common.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.1",
"java": ">=17",
"minecraft": ">=1.20.4",
"fabricloader": ">=0.16.5",
"java": ">=21",
"minecraft": ">=1.21.1",
"fabric": "*"
}
}
@@ -2,7 +2,7 @@
"required": true,
"minVersion": "0.8",
"package": "com.dfsek.terra.fabric.mixin",
"compatibilityLevel": "JAVA_17",
"compatibilityLevel": "JAVA_21",
"mixins": [
],
"client": [
@@ -2,7 +2,7 @@
"required": true,
"minVersion": "0.8",
"package": "com.dfsek.terra.forge.mixin",
"compatibilityLevel": "JAVA_17",
"compatibilityLevel": "JAVA_21",
"mixins": [
"lifecycle.NoiseConfigMixin"
],
@@ -3,6 +3,7 @@ package com.dfsek.terra.mod;
import com.dfsek.tectonic.api.TypeRegistry;
import com.dfsek.tectonic.api.depth.DepthTracker;
import com.dfsek.tectonic.api.exception.LoadException;
import net.minecraft.enchantment.Enchantment;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.registry.Registry;
@@ -67,32 +68,32 @@ public abstract class ModPlatform extends AbstractPlatform {
public void register(TypeRegistry registry) {
super.register(registry);
registry.registerLoader(PlatformBiome.class, (type, o, loader, depthTracker) -> parseBiome((String) o, depthTracker))
.registerLoader(Identifier.class, (type, o, loader, depthTracker) -> {
Identifier identifier = Identifier.tryParse((String) o);
if(identifier == null)
throw new LoadException("Invalid identifier: " + o, depthTracker);
return identifier;
})
.registerLoader(Precipitation.class, (type, o, loader, depthTracker) -> Precipitation.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(GrassColorModifier.class,
(type, o, loader, depthTracker) -> GrassColorModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(GrassColorModifier.class,
(type, o, loader, depthTracker) -> TemperatureModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(SpawnGroup.class,(type, o, loader, depthTracker) -> SpawnGroup.valueOf((String) o))
.registerLoader(BiomeParticleConfig.class, BiomeParticleConfigTemplate::new)
.registerLoader(SoundEvent.class, SoundEventTemplate::new)
.registerLoader(BiomeMoodSound.class, BiomeMoodSoundTemplate::new)
.registerLoader(BiomeAdditionsSound.class, BiomeAdditionsSoundTemplate::new)
.registerLoader(MusicSound.class, MusicSoundTemplate::new)
.registerLoader(EntityType.class, EntityTypeTemplate::new)
.registerLoader(SpawnCostConfig.class, SpawnCostConfig::new)
.registerLoader(SpawnEntry.class, SpawnEntryTemplate::new)
.registerLoader(SpawnTypeConfig.class, SpawnTypeConfig::new)
.registerLoader(SpawnSettings.class, SpawnSettingsTemplate::new)
.registerLoader(VillagerType.class, VillagerTypeTemplate::new);
.registerLoader(Identifier.class, (type, o, loader, depthTracker) -> {
Identifier identifier = Identifier.tryParse((String) o);
if(identifier == null)
throw new LoadException("Invalid identifier: " + o, depthTracker);
return identifier;
})
.registerLoader(Precipitation.class, (type, o, loader, depthTracker) -> Precipitation.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(GrassColorModifier.class,
(type, o, loader, depthTracker) -> GrassColorModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(GrassColorModifier.class,
(type, o, loader, depthTracker) -> TemperatureModifier.valueOf(((String) o).toUpperCase(
Locale.ROOT)))
.registerLoader(SpawnGroup.class, (type, o, loader, depthTracker) -> SpawnGroup.valueOf((String) o))
.registerLoader(BiomeParticleConfig.class, BiomeParticleConfigTemplate::new)
.registerLoader(SoundEvent.class, SoundEventTemplate::new)
.registerLoader(BiomeMoodSound.class, BiomeMoodSoundTemplate::new)
.registerLoader(BiomeAdditionsSound.class, BiomeAdditionsSoundTemplate::new)
.registerLoader(MusicSound.class, MusicSoundTemplate::new)
.registerLoader(EntityType.class, EntityTypeTemplate::new)
.registerLoader(SpawnCostConfig.class, SpawnCostConfig::new)
.registerLoader(SpawnEntry.class, SpawnEntryTemplate::new)
.registerLoader(SpawnTypeConfig.class, SpawnTypeConfig::new)
.registerLoader(SpawnSettings.class, SpawnSettingsTemplate::new)
.registerLoader(VillagerType.class, VillagerTypeTemplate::new);
}
private ProtoPlatformBiome parseBiome(String id, DepthTracker tracker) throws LoadException {
@@ -116,6 +117,8 @@ public abstract class ModPlatform extends AbstractPlatform {
public abstract Registry<MultiNoiseBiomeSourceParameterList> multiNoiseBiomeSourceParameterListRegistry();
public abstract Registry<Enchantment> enchantmentRegistry();
@Override
public @NotNull WorldHandle getWorldHandle() {
return worldHandle;
@@ -125,4 +128,6 @@ public abstract class ModPlatform extends AbstractPlatform {
public @NotNull ItemHandle getItemHandle() {
return itemHandle;
}
}

Some files were not shown because too many files have changed in this diff Show More