commit
358e09d05b
@ -0,0 +1,21 @@ |
||||
MIT License |
||||
|
||||
Copyright (c) 2020-2021 Polyhedral Development |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
of this software and associated documentation files (the "Software"), to deal |
||||
in the Software without restriction, including without limitation the rights |
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
copies of the Software, and to permit persons to whom the Software is |
||||
furnished to do so, subject to the following conditions: |
||||
|
||||
The above copyright notice and this permission notice shall be included in all |
||||
copies or substantial portions of the Software. |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
SOFTWARE. |
@ -0,0 +1,12 @@ |
||||
# biome-provider-pipeline-2 |
||||
|
||||
The second version of the Biome Pipeline, a procedural biome provider that uses a series |
||||
of "stages" to apply "mutations" to a 2D grid of biomes. |
||||
|
||||
Version 2 is a re-implementation of the original addon with the primary goal of providing |
||||
consistent scaling for noise relative to the world |
||||
(See https://github.com/PolyhedralDev/Terra/issues/264 for more details), and has been |
||||
included as a separate addon to maintain parity with packs utilizing the first version. |
||||
|
||||
This addon registers the `PIPELINE` biome provider type, and all associated |
||||
configurations. |
@ -0,0 +1,12 @@ |
||||
version = version("1.0.0") |
||||
|
||||
dependencies { |
||||
compileOnlyApi(project(":common:addons:manifest-addon-loader")) |
||||
|
||||
implementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama) |
||||
testImplementation("net.jafama", "jafama", Versions.Libraries.Internal.jafama) |
||||
} |
||||
|
||||
tasks.named<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>("shadowJar") { |
||||
relocate("net.jafama", "com.dfsek.terra.addons.biome.pipeline.lib.jafama") |
||||
} |
@ -0,0 +1,89 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; |
||||
|
||||
import java.util.function.Supplier; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.BiomePipelineTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.PipelineBiomeLoader; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.source.SamplerSourceTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.expander.ExpanderStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.BorderListStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.BorderStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.ReplaceListStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.ReplaceStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator.SmoothStageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
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.addons.manifest.api.AddonInitializer; |
||||
import com.dfsek.terra.api.Platform; |
||||
import com.dfsek.terra.api.addon.BaseAddon; |
||||
import com.dfsek.terra.api.event.events.config.pack.ConfigPackPostLoadEvent; |
||||
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.registry.CheckedRegistry; |
||||
import com.dfsek.terra.api.registry.Registry; |
||||
import com.dfsek.terra.api.util.reflection.TypeKey; |
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; |
||||
|
||||
|
||||
public class BiomePipelineAddon implements AddonInitializer { |
||||
|
||||
public static final TypeKey<Supplier<ObjectTemplate<Source>>> SOURCE_REGISTRY_KEY = new TypeKey<>() { |
||||
}; |
||||
|
||||
public static final TypeKey<Supplier<ObjectTemplate<Stage>>> STAGE_REGISTRY_KEY = new TypeKey<>() { |
||||
}; |
||||
public static final TypeKey<Supplier<ObjectTemplate<BiomeProvider>>> PROVIDER_REGISTRY_KEY = new TypeKey<>() { |
||||
}; |
||||
@Inject |
||||
private Platform platform; |
||||
|
||||
@Inject |
||||
private BaseAddon addon; |
||||
|
||||
@Override |
||||
public void initialize() { |
||||
platform.getEventManager() |
||||
.getHandler(FunctionalEventHandler.class) |
||||
.register(addon, ConfigPackPreLoadEvent.class) |
||||
.then(event -> { |
||||
CheckedRegistry<Supplier<ObjectTemplate<BiomeProvider>>> providerRegistry = event.getPack().getOrCreateRegistry( |
||||
PROVIDER_REGISTRY_KEY); |
||||
providerRegistry.register(addon.key("PIPELINE"), BiomePipelineTemplate::new); |
||||
}) |
||||
.then(event -> { |
||||
CheckedRegistry<Supplier<ObjectTemplate<Source>>> sourceRegistry = event.getPack().getOrCreateRegistry( |
||||
SOURCE_REGISTRY_KEY); |
||||
sourceRegistry.register(addon.key("SAMPLER"), SamplerSourceTemplate::new); |
||||
}) |
||||
.then(event -> { |
||||
CheckedRegistry<Supplier<ObjectTemplate<Stage>>> stageRegistry = event.getPack().getOrCreateRegistry( |
||||
STAGE_REGISTRY_KEY); |
||||
stageRegistry.register(addon.key("FRACTAL_EXPAND"), ExpanderStageTemplate::new); |
||||
stageRegistry.register(addon.key("SMOOTH"), SmoothStageTemplate::new); |
||||
stageRegistry.register(addon.key("REPLACE"), ReplaceStageTemplate::new); |
||||
stageRegistry.register(addon.key("REPLACE_LIST"), ReplaceListStageTemplate::new); |
||||
stageRegistry.register(addon.key("BORDER"), BorderStageTemplate::new); |
||||
stageRegistry.register(addon.key("BORDER_LIST"), BorderListStageTemplate::new); |
||||
}) |
||||
.failThrough(); |
||||
platform.getEventManager() |
||||
.getHandler(FunctionalEventHandler.class) |
||||
.register(addon, ConfigPackPostLoadEvent.class) |
||||
.then(event -> { |
||||
Registry<Biome> biomeRegistry = event.getPack().getRegistry(Biome.class); |
||||
event.getPack().applyLoader(PipelineBiome.class, new PipelineBiomeLoader(biomeRegistry)); |
||||
}); |
||||
} |
||||
} |
@ -0,0 +1,71 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2; |
||||
|
||||
import java.util.function.Consumer; |
||||
|
||||
import com.dfsek.terra.api.util.Column; |
||||
import com.dfsek.terra.api.util.function.IntIntObjConsumer; |
||||
import com.dfsek.terra.api.util.function.IntObjConsumer; |
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; |
||||
|
||||
|
||||
public class BiomePipelineColumn implements Column<Biome> { |
||||
private final int min; |
||||
private final int max; |
||||
|
||||
private final int x; |
||||
private final int z; |
||||
private final Biome biome; |
||||
|
||||
protected BiomePipelineColumn(BiomeProvider biomeProvider, int min, int max, int x, int z, long seed) { |
||||
this.min = min; |
||||
this.max = max; |
||||
this.x = x; |
||||
this.z = z; |
||||
this.biome = biomeProvider.getBiome(x, 0, z, seed); |
||||
} |
||||
|
||||
@Override |
||||
public int getMinY() { |
||||
return min; |
||||
} |
||||
|
||||
@Override |
||||
public int getMaxY() { |
||||
return max; |
||||
} |
||||
|
||||
@Override |
||||
public int getX() { |
||||
return x; |
||||
} |
||||
|
||||
@Override |
||||
public int getZ() { |
||||
return z; |
||||
} |
||||
|
||||
@Override |
||||
public Biome get(int y) { |
||||
return biome; |
||||
} |
||||
|
||||
@Override |
||||
public void forRanges(int resolution, IntIntObjConsumer<Biome> consumer) { |
||||
consumer.accept(min, max, biome); |
||||
} |
||||
|
||||
@Override |
||||
public void forEach(Consumer<Biome> consumer) { |
||||
for(int y = min; y < max; y++) { |
||||
consumer.accept(biome); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void forEach(IntObjConsumer<Biome> consumer) { |
||||
for(int y = min; y < max; y++) { |
||||
consumer.accept(y, biome); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,109 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2; |
||||
|
||||
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.github.benmanes.caffeine.cache.Caffeine; |
||||
import com.github.benmanes.caffeine.cache.LoadingCache; |
||||
import net.jafama.FastMath; |
||||
|
||||
import java.util.Comparator; |
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
import java.util.stream.StreamSupport; |
||||
|
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.registry.key.StringIdentifiable; |
||||
import com.dfsek.terra.api.util.Column; |
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; |
||||
|
||||
|
||||
public class PipelineBiomeProvider implements BiomeProvider { |
||||
|
||||
private final LoadingCache<SeededVector, BiomeChunk> biomeChunkCache; |
||||
private final int chunkSize; |
||||
private final int resolution; |
||||
private final NoiseSampler mutator; |
||||
private final double noiseAmp; |
||||
private final Set<Biome> biomes; |
||||
|
||||
public PipelineBiomeProvider(Pipeline pipeline, int resolution, NoiseSampler mutator, double noiseAmp) { |
||||
this.resolution = resolution; |
||||
this.mutator = mutator; |
||||
this.noiseAmp = noiseAmp; |
||||
this.chunkSize = pipeline.getChunkSize(); |
||||
this.biomeChunkCache = Caffeine.newBuilder() |
||||
.maximumSize(1024) |
||||
.build(pipeline::generateChunk); |
||||
|
||||
Set<PipelineBiome> biomeSet = new HashSet<>(); |
||||
pipeline.getSource().getBiomes().forEach(biomeSet::add); |
||||
Iterable<PipelineBiome> result = biomeSet; |
||||
for(Stage stage : pipeline.getStages()) { |
||||
result = stage.getBiomes(result); |
||||
} |
||||
this.biomes = new HashSet<>(); |
||||
Iterable<PipelineBiome> finalResult = result; |
||||
result.forEach(pipelineBiome -> { |
||||
if(pipelineBiome.isPlaceholder()) { |
||||
|
||||
StringBuilder biomeList = new StringBuilder("\n"); |
||||
StreamSupport.stream(finalResult.spliterator(), false) |
||||
.sorted(Comparator.comparing(StringIdentifiable::getID)) |
||||
.forEach(delegate -> biomeList |
||||
.append(" - ") |
||||
.append(delegate.getID()) |
||||
.append(':') |
||||
.append(delegate.getClass().getCanonicalName()) |
||||
.append('\n')); |
||||
throw new IllegalArgumentException("Biome Pipeline leaks placeholder biome \"" + pipelineBiome.getID() + |
||||
"\". Ensure there is a stage to guarantee replacement of the placeholder biome. Biomes: " + |
||||
biomeList); |
||||
} |
||||
this.biomes.add(pipelineBiome.getBiome()); |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
public Biome getBiome(int x, int y, int z, long seed) { |
||||
return getBiome(x, z, seed); |
||||
} |
||||
|
||||
public Biome getBiome(int x, int z, long seed) { |
||||
|
||||
x += mutator.noise(seed + 1, x, z) * noiseAmp; |
||||
z += mutator.noise(seed + 2, x, z) * noiseAmp; |
||||
|
||||
x /= resolution; |
||||
z /= resolution; |
||||
|
||||
int chunkX = FastMath.floorDiv(x, chunkSize); |
||||
int chunkZ = FastMath.floorDiv(z, chunkSize); |
||||
|
||||
int chunkWorldX = chunkX * chunkSize; |
||||
int chunkWorldZ = chunkZ * chunkSize; |
||||
|
||||
int xInChunk = x - chunkWorldX; |
||||
int zInChunk = z - chunkWorldZ; |
||||
|
||||
return biomeChunkCache.get(new SeededVector(seed, chunkWorldX, chunkWorldZ)).get(xInChunk, zInChunk).getBiome(); |
||||
} |
||||
|
||||
@Override |
||||
public Iterable<Biome> getBiomes() { |
||||
return biomes; |
||||
} |
||||
|
||||
@Override |
||||
public Column<Biome> getColumn(int x, int z, long seed, int min, int max) { |
||||
return new BiomePipelineColumn(this, min, max, x, z, seed); |
||||
} |
||||
|
||||
@Override |
||||
public int resolution() { |
||||
return resolution; |
||||
} |
||||
} |
@ -0,0 +1,10 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api; |
||||
|
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
|
||||
|
||||
public interface BiomeChunk { |
||||
|
||||
PipelineBiome get(int xInChunk, int zInChunk); |
||||
} |
@ -0,0 +1,29 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl.ViewPoint; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
|
||||
|
||||
/** |
||||
* Resizes the internal grid of a BiomeChunk when applied, serves the purpose of |
||||
* filling in null biomes as a result of this resizing. |
||||
*/ |
||||
public interface Expander extends Stage { |
||||
|
||||
PipelineBiome fillBiome(ViewPoint viewPoint); |
||||
|
||||
@Override |
||||
default int maxRelativeReadDistance() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
default PipelineBiome apply(ViewPoint viewPoint) { |
||||
PipelineBiome currentBiome = viewPoint.getBiome(); |
||||
if(currentBiome == null) { |
||||
return fillBiome(viewPoint); |
||||
} else { |
||||
return currentBiome; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,14 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api; |
||||
|
||||
import java.util.List; |
||||
|
||||
|
||||
public interface Pipeline { |
||||
BiomeChunk generateChunk(SeededVector worldCoordinates); |
||||
|
||||
int getChunkSize(); |
||||
|
||||
Source getSource(); |
||||
|
||||
List<Stage> getStages(); |
||||
} |
@ -0,0 +1,19 @@ |
||||
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))); |
||||
} |
||||
} |
@ -0,0 +1,11 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api; |
||||
|
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
|
||||
|
||||
public interface Source { |
||||
PipelineBiome get(long seed, int x, int z); |
||||
|
||||
Iterable<PipelineBiome> getBiomes(); |
||||
} |
@ -0,0 +1,15 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl.ViewPoint; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
|
||||
|
||||
public interface Stage { |
||||
PipelineBiome apply(ViewPoint viewPoint); |
||||
|
||||
int maxRelativeReadDistance(); |
||||
|
||||
default Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) { |
||||
return biomes; |
||||
} |
||||
} |
@ -0,0 +1,40 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome; |
||||
|
||||
import java.util.Set; |
||||
|
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
|
||||
|
||||
public final class DelegatedPipelineBiome implements PipelineBiome { |
||||
private final Biome biome; |
||||
|
||||
public DelegatedPipelineBiome(Biome biome) { |
||||
this.biome = biome; |
||||
} |
||||
|
||||
@Override |
||||
public Biome getBiome() { |
||||
return biome; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return biome.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if(!(obj instanceof DelegatedPipelineBiome that)) return false; |
||||
return that.biome.equals(this.biome); |
||||
} |
||||
|
||||
@Override |
||||
public Set<String> getTags() { |
||||
return biome.getTags(); |
||||
} |
||||
|
||||
@Override |
||||
public String getID() { |
||||
return biome.getID(); |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome; |
||||
|
||||
import java.util.Set; |
||||
|
||||
import com.dfsek.terra.api.registry.key.StringIdentifiable; |
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
|
||||
|
||||
public interface PipelineBiome extends StringIdentifiable { |
||||
Biome getBiome(); |
||||
|
||||
static PipelineBiome placeholder(String id) { |
||||
return new PlaceholderPipelineBiome(id); |
||||
} |
||||
|
||||
static PipelineBiome from(Biome biome) { |
||||
return new DelegatedPipelineBiome(biome); |
||||
} |
||||
|
||||
static PipelineBiome self() { |
||||
return SelfPipelineBiome.INSTANCE; |
||||
} |
||||
|
||||
Set<String> getTags(); |
||||
|
||||
default boolean isPlaceholder() { |
||||
return false; |
||||
} |
||||
|
||||
default boolean isSelf() { |
||||
return false; |
||||
} |
||||
|
||||
|
||||
} |
@ -0,0 +1,51 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
|
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
|
||||
|
||||
final class PlaceholderPipelineBiome implements PipelineBiome { |
||||
private final Set<String> tags; |
||||
private final String id; |
||||
|
||||
public PlaceholderPipelineBiome(String id) { |
||||
this.id = id; |
||||
tags = new HashSet<>(); |
||||
tags.add(id); |
||||
tags.add("ALL"); |
||||
} |
||||
|
||||
@Override |
||||
public Biome getBiome() { |
||||
throw new UnsupportedOperationException("Cannot get raw biome from placeholder pipeline biome"); |
||||
} |
||||
|
||||
@Override |
||||
public Set<String> getTags() { |
||||
return tags; |
||||
} |
||||
|
||||
@Override |
||||
public String getID() { |
||||
return id; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isPlaceholder() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return id.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if(!(obj instanceof PlaceholderPipelineBiome that)) return false; |
||||
|
||||
return this.id.equals(that.id); |
||||
} |
||||
} |
@ -0,0 +1,40 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.api.biome; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
|
||||
|
||||
final class SelfPipelineBiome implements PipelineBiome { |
||||
public static final SelfPipelineBiome INSTANCE = new SelfPipelineBiome(); |
||||
|
||||
private SelfPipelineBiome() { |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public Biome getBiome() { |
||||
throw new UnsupportedOperationException("Cannot get biome from self delegate"); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isSelf() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isPlaceholder() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public Set<String> getTags() { |
||||
return Collections.emptySet(); |
||||
} |
||||
|
||||
@Override |
||||
public String getID() { |
||||
return "SELF"; |
||||
} |
||||
} |
@ -0,0 +1,59 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Default; |
||||
import com.dfsek.tectonic.api.config.template.annotations.Description; |
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; |
||||
|
||||
import java.util.List; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.PipelineBiomeProvider; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.PipelineImpl; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.world.biome.generation.BiomeProvider; |
||||
|
||||
|
||||
@SuppressWarnings({ "FieldMayBeFinal", "unused" }) |
||||
public class BiomePipelineTemplate implements ObjectTemplate<BiomeProvider> { |
||||
@Value("resolution") |
||||
@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; |
||||
|
||||
@Value("pipeline.source") |
||||
@Description("The Biome Source to use for initial population of biomes.") |
||||
private @Meta Source source; |
||||
|
||||
@Value("pipeline.stages") |
||||
@Description("A list of pipeline stages to apply to the result of #source") |
||||
private @Meta List<@Meta Stage> stages; |
||||
|
||||
@Value("blend.sampler") |
||||
@Default |
||||
@Description("A sampler to use for blending the edges of biomes via domain warping.") |
||||
protected @Meta NoiseSampler blendSampler = NoiseSampler.zero(); |
||||
|
||||
@Value("blend.amplitude") |
||||
@Default |
||||
@Description("The amplitude at which to perform blending.") |
||||
protected @Meta double blendAmplitude = 0d; |
||||
|
||||
@Override |
||||
public BiomeProvider get() { |
||||
return new PipelineBiomeProvider(new PipelineImpl(source, stages, resolution, 500), resolution, blendSampler, blendAmplitude); |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config; |
||||
|
||||
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.loader.type.TypeLoader; |
||||
import org.jetbrains.annotations.NotNull; |
||||
|
||||
import java.lang.reflect.AnnotatedType; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
import com.dfsek.terra.api.registry.Registry; |
||||
import com.dfsek.terra.api.world.biome.Biome; |
||||
|
||||
|
||||
public class PipelineBiomeLoader implements TypeLoader<PipelineBiome> { |
||||
private final Registry<Biome> biomeRegistry; |
||||
|
||||
public PipelineBiomeLoader(Registry<Biome> biomeRegistry) { |
||||
this.biomeRegistry = biomeRegistry; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome load(@NotNull AnnotatedType t, @NotNull Object c, @NotNull ConfigLoader loader, DepthTracker depthTracker) |
||||
throws LoadException { |
||||
if(c.equals("SELF")) return PipelineBiome.self(); |
||||
return biomeRegistry |
||||
.getByID((String) c) |
||||
.map(PipelineBiome::from) |
||||
.orElseGet(() -> PipelineBiome.placeholder((String) c)); |
||||
} |
||||
} |
@ -0,0 +1,34 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.source; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Description; |
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.source.SamplerSource; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
public class SamplerSourceTemplate extends SourceTemplate { |
||||
@Value("sampler") |
||||
@Description("The sampler used to distribute biomes.") |
||||
private @Meta NoiseSampler noise; |
||||
|
||||
@Value("biomes") |
||||
@Description("The biomes to be distributed.") |
||||
private @Meta ProbabilityCollection<@Meta PipelineBiome> biomes; |
||||
|
||||
@Override |
||||
public Source get() { |
||||
return new SamplerSource(biomes, noise); |
||||
} |
||||
} |
@ -0,0 +1,17 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.source; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
|
||||
|
||||
public abstract class SourceTemplate implements ObjectTemplate<Source> { |
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Description; |
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
import com.dfsek.tectonic.api.config.template.object.ObjectTemplate; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
|
||||
|
||||
public abstract class StageTemplate implements ObjectTemplate<Stage> { |
||||
@Value("sampler") |
||||
@Description("Sampler to use for stage distribution.") |
||||
protected @Meta NoiseSampler noise; |
||||
} |
@ -0,0 +1,20 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.expander; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.stage.expander.FractalExpander; |
||||
|
||||
|
||||
public class ExpanderStageTemplate extends StageTemplate { |
||||
@Override |
||||
public Expander get() { |
||||
return new FractalExpander(noise); |
||||
} |
||||
} |
@ -0,0 +1,41 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
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.addons.biome.pipeline.v2.stage.mutators.BorderListStage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
public class BorderListStageTemplate extends StageTemplate { |
||||
@Value("from") |
||||
private @Meta String from; |
||||
|
||||
@Value("default-replace") |
||||
private @Meta String defaultReplace; |
||||
|
||||
@Value("default-to") |
||||
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo; |
||||
|
||||
@Value("replace") |
||||
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace; |
||||
|
||||
|
||||
@Override |
||||
public Stage get() { |
||||
return new BorderListStage(replace, from, defaultReplace, noise, defaultTo); |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
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.addons.biome.pipeline.v2.stage.mutators.BorderStage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
public class BorderStageTemplate extends StageTemplate { |
||||
@Value("from") |
||||
private @Meta String from; |
||||
|
||||
@Value("replace") |
||||
private @Meta String replace; |
||||
|
||||
@Value("to") |
||||
private @Meta ProbabilityCollection<@Meta PipelineBiome> to; |
||||
|
||||
@Override |
||||
public Stage get() { |
||||
return new BorderStage(from, replace, noise, to); |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
|
||||
import java.util.Map; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
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.addons.biome.pipeline.v2.stage.mutators.ReplaceListStage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
public class ReplaceListStageTemplate extends StageTemplate { |
||||
@Value("default-from") |
||||
private @Meta String defaultFrom; |
||||
|
||||
@Value("default-to") |
||||
private @Meta ProbabilityCollection<@Meta PipelineBiome> defaultTo; |
||||
|
||||
@Value("to") |
||||
private @Meta Map<@Meta PipelineBiome, @Meta ProbabilityCollection<@Meta PipelineBiome>> replace; |
||||
|
||||
@Override |
||||
public Stage get() { |
||||
return new ReplaceListStage(replace, defaultFrom, defaultTo, noise); |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator; |
||||
|
||||
import com.dfsek.tectonic.api.config.template.annotations.Value; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
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.addons.biome.pipeline.v2.stage.mutators.ReplaceStage; |
||||
import com.dfsek.terra.api.config.meta.Meta; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
@SuppressWarnings("unused") |
||||
public class ReplaceStageTemplate extends StageTemplate { |
||||
@Value("from") |
||||
private @Meta String from; |
||||
|
||||
@Value("to") |
||||
private @Meta ProbabilityCollection<@Meta PipelineBiome> to; |
||||
|
||||
@Override |
||||
public Stage get() { |
||||
return new ReplaceStage(from, to, noise); |
||||
} |
||||
} |
@ -0,0 +1,20 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.config.stage.mutator; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.config.stage.StageTemplate; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Stage; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators.SmoothStage; |
||||
|
||||
|
||||
public class SmoothStageTemplate extends StageTemplate { |
||||
@Override |
||||
public Stage get() { |
||||
return new SmoothStage(noise); |
||||
} |
||||
} |
@ -0,0 +1,217 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline; |
||||
|
||||
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 net.jafama.FastMath; |
||||
|
||||
import java.util.List; |
||||
|
||||
|
||||
public class BiomeChunkImpl implements BiomeChunk { |
||||
|
||||
private PipelineBiome[][] biomes; |
||||
private final SeededVector worldOrigin; |
||||
private final int chunkOriginArrayIndex; |
||||
private final int worldCoordinateScale; |
||||
|
||||
public BiomeChunkImpl(SeededVector worldOrigin, PipelineImpl pipeline) { |
||||
|
||||
this.worldOrigin = worldOrigin; |
||||
this.chunkOriginArrayIndex = pipeline.getChunkOriginArrayIndex(); |
||||
this.worldCoordinateScale = pipeline.getResolution(); |
||||
|
||||
int size = pipeline.getArraySize(); |
||||
|
||||
int expanderCount = pipeline.getExpanderCount(); |
||||
int expansionsApplied = 0; |
||||
|
||||
// Allocate working arrays
|
||||
this.biomes = new PipelineBiome[size][size]; |
||||
PipelineBiome[][] lookupArray = new PipelineBiome[size][size]; |
||||
// A second lookup array is required such that stage application doesn't affect lookups, otherwise application may cascade
|
||||
|
||||
// Construct working grid
|
||||
int gridOrigin = 0; |
||||
int gridInterval = calculateGridInterval(expanderCount, expansionsApplied); |
||||
int gridSize = (size / gridInterval); |
||||
gridSize += expanderCount > 0 ? 1 : 0; // Add an extra border if expansion occurs
|
||||
|
||||
// Fill working grid with initial cells
|
||||
for(int gridX = 0; gridX < gridSize; gridX++) { |
||||
for(int gridZ = 0; gridZ < gridSize; gridZ++) { |
||||
int xIndex = gridOrigin + gridX * gridInterval; |
||||
int zIndex = gridOrigin + gridZ * gridInterval; |
||||
biomes[xIndex][zIndex] = pipeline.getSource().get(worldOrigin.seed(), xIndexToWorldCoordinate(xIndex), zIndexToWorldCoordinate(zIndex)); |
||||
} |
||||
} |
||||
|
||||
for(Stage stage : pipeline.getStages()) { |
||||
if(stage instanceof Expander) { |
||||
// Shrink working grid size, the expander will fill in null cells (as a result of shrinking the grid) during mutation
|
||||
expansionsApplied++; |
||||
gridInterval = calculateGridInterval(expanderCount, expansionsApplied); |
||||
gridSize = expandSize(gridSize); |
||||
} |
||||
|
||||
int stageReadDistance = stage.maxRelativeReadDistance(); |
||||
if(stageReadDistance > 0) { |
||||
// Discard edges such that adjacent lookups are only ran on valid cells
|
||||
gridSize = contractBordersFromSize(gridSize, stageReadDistance); |
||||
gridOrigin += stageReadDistance * gridInterval; |
||||
} |
||||
|
||||
// Cycle arrays, the previously populated array is swapped to be used for lookups, and the result of the stage application
|
||||
// overwrites the previous lookup array. This saves having to allocate a new array copy each time
|
||||
PipelineBiome[][] tempArray = biomes; |
||||
biomes = lookupArray; |
||||
lookupArray = tempArray; |
||||
|
||||
// Apply stage to working grid
|
||||
for(int gridZ = 0; gridZ < gridSize; gridZ = gridZ + 1) { |
||||
for(int gridX = 0; gridX < gridSize; gridX = gridX + 1) { |
||||
int xIndex = gridOrigin + gridX * gridInterval; |
||||
int zIndex = gridOrigin + gridZ * gridInterval; |
||||
biomes[xIndex][zIndex] = stage.apply(new ViewPoint(this, gridInterval, gridX, gridZ, xIndex, zIndex, lookupArray)); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome get(int xInChunk, int zInChunk) { |
||||
int xIndex = xInChunk + chunkOriginArrayIndex; |
||||
int zIndex = zInChunk + chunkOriginArrayIndex; |
||||
return biomes[xIndex][zIndex]; |
||||
} |
||||
|
||||
private int xIndexToWorldCoordinate(int xIndex) { |
||||
return (worldOrigin.x() + xIndex - chunkOriginArrayIndex) * worldCoordinateScale; |
||||
} |
||||
|
||||
private int zIndexToWorldCoordinate(int zIndex) { |
||||
return (worldOrigin.z() + zIndex - chunkOriginArrayIndex) * worldCoordinateScale; |
||||
} |
||||
|
||||
protected static int initialSizeToArraySize(int expanderCount, int initialSize) { |
||||
int size = initialSize; |
||||
for(int i = 0; i < expanderCount; i++) { |
||||
size = expandSize(size); |
||||
} |
||||
return size; |
||||
} |
||||
|
||||
protected static int calculateChunkOriginArrayIndex(int totalExpanderCount, List<Stage> stages) { |
||||
int finalGridOrigin = calculateFinalGridOrigin(totalExpanderCount, stages); |
||||
int initialGridInterval = calculateGridInterval(totalExpanderCount, 0); |
||||
|
||||
// Round the final grid origin up to the nearest multiple of initialGridInterval, such that each
|
||||
// chunk samples points on the same overall grid.
|
||||
// Without this, shared chunk borders (required because of adjacent cell reads) will not be identical
|
||||
// because points would be sampled on grids at different offsets, resulting in artifacts at borders.
|
||||
return FastMath.ceilToInt((double) finalGridOrigin / initialGridInterval) * initialGridInterval; |
||||
} |
||||
|
||||
private static int calculateFinalGridOrigin(int totalExpanderCount, List<Stage> stages) { |
||||
int gridOrigin = 0; |
||||
int expansionsApplied = 0; |
||||
int gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied); |
||||
for (Stage stage : stages) { |
||||
if (stage instanceof Expander) { |
||||
expansionsApplied++; |
||||
gridInterval = calculateGridInterval(totalExpanderCount, expansionsApplied); |
||||
} |
||||
gridOrigin += stage.maxRelativeReadDistance() * gridInterval; |
||||
} |
||||
return gridOrigin; |
||||
} |
||||
|
||||
protected static int calculateChunkSize(int arraySize, int chunkOriginArrayIndex, int totalExpanderCount) { |
||||
return contractBordersFromSize(arraySize, chunkOriginArrayIndex) - (totalExpanderCount > 0 ? 1 : 0); |
||||
} |
||||
|
||||
private static int expandSize(int size) { |
||||
return size * 2 - 1; |
||||
} |
||||
|
||||
private static int contractBordersFromSize(int size, int border) { |
||||
return size - border * 2; |
||||
} |
||||
|
||||
private static int calculateGridInterval(int totalExpansions, int expansionsApplied) { |
||||
return 1 << (totalExpansions - expansionsApplied); |
||||
} |
||||
|
||||
private SeededVector getOrigin() { |
||||
return worldOrigin; |
||||
} |
||||
|
||||
/** |
||||
* Represents a point on the operating grid within the biomes array |
||||
*/ |
||||
public static class ViewPoint { |
||||
private final BiomeChunkImpl chunk; |
||||
private final PipelineBiome biome; |
||||
private final int gridInterval; |
||||
private final int gridX; |
||||
private final int gridZ; |
||||
private final int xIndex; |
||||
private final int zIndex; |
||||
private final PipelineBiome[][] lookupArray; |
||||
|
||||
private ViewPoint(BiomeChunkImpl chunk, int gridInterval, int gridX, int gridZ, int xIndex, int zIndex, PipelineBiome[][] lookupArray) { |
||||
this.chunk = chunk; |
||||
this.gridInterval = gridInterval; |
||||
this.gridX = gridX; |
||||
this.gridZ = gridZ; |
||||
this.xIndex = xIndex; |
||||
this.zIndex = zIndex; |
||||
this.lookupArray = lookupArray; |
||||
this.biome = lookupArray[xIndex][zIndex]; |
||||
} |
||||
|
||||
public PipelineBiome getRelativeBiome(int x, int z) { |
||||
int lookupXIndex = this.xIndex + x * gridInterval; |
||||
int lookupZIndex = this.zIndex + z * gridInterval; |
||||
return lookupArray[lookupXIndex][lookupZIndex]; |
||||
} |
||||
|
||||
public PipelineBiome getBiome() { |
||||
return biome; |
||||
} |
||||
|
||||
/** |
||||
* @return X position of the point relative to the operating grid |
||||
*/ |
||||
public int gridX() { |
||||
return gridX; |
||||
} |
||||
|
||||
/** |
||||
* @return Z position of the point relative to the operating grid |
||||
*/ |
||||
public int gridZ() { |
||||
return gridZ; |
||||
} |
||||
|
||||
/** |
||||
* @return X position of the point in the world |
||||
*/ |
||||
public int worldX() { |
||||
return chunk.xIndexToWorldCoordinate(xIndex); |
||||
} |
||||
|
||||
/** |
||||
* @return Z position of the point in the world |
||||
*/ |
||||
public int worldZ() { |
||||
return chunk.zIndexToWorldCoordinate(zIndex); |
||||
} |
||||
|
||||
public long worldSeed() { |
||||
return chunk.getOrigin().seed(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,92 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.pipeline; |
||||
|
||||
import org.slf4j.Logger; |
||||
import org.slf4j.LoggerFactory; |
||||
|
||||
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; |
||||
|
||||
|
||||
public class PipelineImpl implements Pipeline { |
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PipelineImpl.class); |
||||
|
||||
private final Source source; |
||||
private final List<Stage> stages; |
||||
private final int chunkSize; |
||||
private final int expanderCount; |
||||
private final int arraySize; |
||||
private final int chunkOriginArrayIndex; |
||||
private final int resolution; |
||||
|
||||
public PipelineImpl(Source source, List<Stage> stages, int resolution, int idealChunkArraySize) { |
||||
this.source = source; |
||||
this.stages = stages; |
||||
this.resolution = resolution; |
||||
this.expanderCount = (int) stages.stream().filter(s -> s instanceof Expander).count(); |
||||
|
||||
// Optimize for the ideal array size
|
||||
int arraySize; |
||||
int chunkOriginArrayIndex; |
||||
int chunkSize; |
||||
int initialSize = 1; |
||||
while (true) { |
||||
arraySize = BiomeChunkImpl.initialSizeToArraySize(expanderCount, initialSize); |
||||
chunkOriginArrayIndex = BiomeChunkImpl.calculateChunkOriginArrayIndex(expanderCount, stages); |
||||
chunkSize = BiomeChunkImpl.calculateChunkSize(arraySize, chunkOriginArrayIndex, expanderCount); |
||||
if (chunkSize > 1 && arraySize >= idealChunkArraySize) break; |
||||
initialSize++; |
||||
} |
||||
|
||||
this.arraySize = arraySize; |
||||
this.chunkOriginArrayIndex = chunkOriginArrayIndex; |
||||
this.chunkSize = chunkSize; |
||||
|
||||
logger.debug("Initialized a new biome pipeline:"); |
||||
logger.debug("Array size: {} (Target: {})", arraySize, idealChunkArraySize); |
||||
logger.debug("Internal array origin: {}", chunkOriginArrayIndex); |
||||
logger.debug("Chunk size: {}", chunkSize); |
||||
} |
||||
|
||||
@Override |
||||
public BiomeChunk generateChunk(SeededVector worldCoordinates) { |
||||
return new BiomeChunkImpl(worldCoordinates, this); |
||||
} |
||||
|
||||
@Override |
||||
public int getChunkSize() { |
||||
return chunkSize; |
||||
} |
||||
|
||||
@Override |
||||
public Source getSource() { |
||||
return source; |
||||
} |
||||
|
||||
@Override |
||||
public List<Stage> getStages() { |
||||
return stages; |
||||
} |
||||
|
||||
protected int getExpanderCount() { |
||||
return expanderCount; |
||||
} |
||||
|
||||
protected int getArraySize() { |
||||
return arraySize; |
||||
} |
||||
|
||||
protected int getChunkOriginArrayIndex() { |
||||
return chunkOriginArrayIndex; |
||||
} |
||||
|
||||
protected int getResolution() { |
||||
return resolution; |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.source; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
public class SamplerSource implements Source { |
||||
private final ProbabilityCollection<PipelineBiome> biomes; |
||||
private final NoiseSampler sampler; |
||||
|
||||
public SamplerSource(ProbabilityCollection<PipelineBiome> biomes, NoiseSampler sampler) { |
||||
this.biomes = biomes; |
||||
this.sampler = sampler; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome get(long seed, int x, int z) { |
||||
return biomes.get(sampler, x, z, seed); |
||||
} |
||||
|
||||
@Override |
||||
public Iterable<PipelineBiome> getBiomes() { |
||||
return biomes.getContents(); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.source; |
||||
|
||||
import java.util.Collections; |
||||
import java.util.Set; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Source; |
||||
|
||||
|
||||
public class SingleSource implements Source { |
||||
|
||||
private final PipelineBiome biome; |
||||
|
||||
public SingleSource(PipelineBiome biome) { |
||||
this.biome = biome; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome get(long seed, int x, int z) { |
||||
return biome; |
||||
} |
||||
|
||||
@Override |
||||
public Set<PipelineBiome> getBiomes() { |
||||
return Collections.singleton(biome); |
||||
} |
||||
} |
@ -0,0 +1,37 @@ |
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.expander; |
||||
|
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.Expander; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.api.biome.PipelineBiome; |
||||
import com.dfsek.terra.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
|
||||
|
||||
public class FractalExpander implements Expander { |
||||
|
||||
private final NoiseSampler sampler; |
||||
|
||||
public FractalExpander(NoiseSampler sampler) { |
||||
this.sampler = sampler; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome fillBiome(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
|
||||
int xMod2 = viewPoint.gridX() % 2; |
||||
int zMod2 = viewPoint.gridZ() % 2; |
||||
|
||||
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ()); |
||||
|
||||
if (xMod2 == 1 && zMod2 == 0) { // Pick one of 2 neighbors on X axis randomly
|
||||
return roll > 0 ? viewPoint.getRelativeBiome(-1, 0) : viewPoint.getRelativeBiome(1, 0); |
||||
|
||||
} else if (xMod2 == 0 && zMod2 == 1) { // Pick one of 2 neighbors on Z axis randomly
|
||||
return roll > 0 ? viewPoint.getRelativeBiome(0, -1) : viewPoint.getRelativeBiome(0, 1); |
||||
|
||||
} else { // Pick one of 4 corners randomly
|
||||
return roll > 0 ? |
||||
roll > 0.25 ? viewPoint.getRelativeBiome(-1, 1) : viewPoint.getRelativeBiome(1, 1) : |
||||
roll > -0.25 ? viewPoint.getRelativeBiome(-1, -1) : viewPoint.getRelativeBiome(1, -1); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.function.Predicate; |
||||
|
||||
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
import com.dfsek.terra.api.util.vector.Vector2Int; |
||||
|
||||
|
||||
public class BorderListStage implements Stage { |
||||
private final String border; |
||||
private final NoiseSampler noiseSampler; |
||||
private final ProbabilityCollection<PipelineBiome> replaceDefault; |
||||
private final String defaultReplace; |
||||
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace; |
||||
|
||||
private final Vector2Int[] borderPoints; |
||||
|
||||
public BorderListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String border, String defaultReplace, |
||||
NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replaceDefault) { |
||||
this.border = border; |
||||
this.noiseSampler = noiseSampler; |
||||
this.replaceDefault = replaceDefault; |
||||
this.defaultReplace = defaultReplace; |
||||
this.replace = replace; |
||||
|
||||
List<Vector2Int> points = new ArrayList<>(); |
||||
for(int x = -1; x <= 1; x++) { |
||||
for(int z = -1; z <= 1; z++) { |
||||
if(x == 0 && z == 0) continue; |
||||
points.add(Vector2Int.of(x, z)); |
||||
} |
||||
} |
||||
this.borderPoints = points.toArray(new Vector2Int[0]); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) { |
||||
Set<PipelineBiome> biomeSet = new HashSet<>(); |
||||
biomes.forEach(biomeSet::add); |
||||
biomeSet.addAll(replaceDefault.getContents().stream().filter(Predicate.not(PipelineBiome::isSelf)).toList()); |
||||
replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents())); |
||||
return biomeSet; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
PipelineBiome center = viewPoint.getBiome(); |
||||
if(center.getTags().contains(defaultReplace)) { |
||||
for(Vector2Int point : borderPoints) { |
||||
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ()); |
||||
if(current != null && current.getTags().contains(border)) { |
||||
if(replace.containsKey(center)) { |
||||
PipelineBiome replacement = replace.get(center).get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), |
||||
viewPoint.worldSeed()); |
||||
return replacement.isSelf() ? center : replacement; |
||||
} |
||||
PipelineBiome replacement = replaceDefault.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed()); |
||||
return replacement.isSelf() ? center : replacement; |
||||
} |
||||
} |
||||
} |
||||
return center; |
||||
} |
||||
|
||||
@Override |
||||
public int maxRelativeReadDistance() { |
||||
return 1; |
||||
} |
||||
} |
@ -0,0 +1,81 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashSet; |
||||
import java.util.List; |
||||
import java.util.Set; |
||||
import java.util.function.Predicate; |
||||
|
||||
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
import com.dfsek.terra.api.util.vector.Vector2Int; |
||||
|
||||
|
||||
public class BorderStage implements Stage { |
||||
private final String border; |
||||
private final NoiseSampler noiseSampler; |
||||
private final ProbabilityCollection<PipelineBiome> replace; |
||||
private final String replaceTag; |
||||
private final Vector2Int[] borderPoints; |
||||
|
||||
public BorderStage(String border, String replaceTag, NoiseSampler noiseSampler, ProbabilityCollection<PipelineBiome> replace) { |
||||
this.border = border; |
||||
this.noiseSampler = noiseSampler; |
||||
this.replace = replace; |
||||
this.replaceTag = replaceTag; |
||||
List<Vector2Int> points = new ArrayList<>(); |
||||
for(int x = -1; x <= 1; x++) { |
||||
for(int z = -1; z <= 1; z++) { |
||||
if(x == 0 && z == 0) continue; |
||||
points.add(Vector2Int.of(x, z)); |
||||
} |
||||
} |
||||
this.borderPoints = points.toArray(new Vector2Int[0]); |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
PipelineBiome center = viewPoint.getBiome(); |
||||
if(center.getTags().contains(replaceTag)) { |
||||
for(Vector2Int point : borderPoints) { |
||||
PipelineBiome current = viewPoint.getRelativeBiome(point.getX(), point.getZ()); |
||||
if(current != null && current.getTags().contains(border)) { |
||||
PipelineBiome replacement = replace.get(noiseSampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed()); |
||||
return replacement.isSelf() ? center : replacement; |
||||
} |
||||
} |
||||
} |
||||
return center; |
||||
} |
||||
|
||||
@Override |
||||
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) { |
||||
Set<PipelineBiome> biomeSet = new HashSet<>(); |
||||
biomes.forEach(biomeSet::add); |
||||
biomeSet.addAll( |
||||
replace |
||||
.getContents() |
||||
.stream() |
||||
.filter( |
||||
Predicate.not(PipelineBiome::isSelf) |
||||
) |
||||
.toList() |
||||
); |
||||
return biomeSet; |
||||
} |
||||
|
||||
@Override |
||||
public int maxRelativeReadDistance() { |
||||
return 1; |
||||
} |
||||
} |
@ -0,0 +1,78 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.stream.Stream; |
||||
|
||||
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
public class ReplaceListStage implements Stage { |
||||
private final Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace; |
||||
private final NoiseSampler sampler; |
||||
private final ProbabilityCollection<PipelineBiome> replaceDefault; |
||||
private final String defaultTag; |
||||
|
||||
public ReplaceListStage(Map<PipelineBiome, ProbabilityCollection<PipelineBiome>> replace, String defaultTag, |
||||
ProbabilityCollection<PipelineBiome> replaceDefault, NoiseSampler sampler) { |
||||
this.replace = replace; |
||||
this.sampler = sampler; |
||||
this.defaultTag = defaultTag; |
||||
this.replaceDefault = replaceDefault; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
PipelineBiome center = viewPoint.getBiome(); |
||||
if(replace.containsKey(center)) { |
||||
PipelineBiome biome = replace.get(center).get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed()); |
||||
return biome.isSelf() ? viewPoint.getBiome() : biome; |
||||
} |
||||
if(viewPoint.getBiome().getTags().contains(defaultTag)) { |
||||
PipelineBiome biome = replaceDefault.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed()); |
||||
return biome.isSelf() ? viewPoint.getBiome() : biome; |
||||
} |
||||
return center; |
||||
} |
||||
|
||||
@Override |
||||
public int maxRelativeReadDistance() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) { |
||||
Set<PipelineBiome> biomeSet = new HashSet<>(); |
||||
|
||||
Set<PipelineBiome> reject = new HashSet<>(); |
||||
|
||||
biomes.forEach(biome -> { |
||||
if(!biome.getTags().contains(defaultTag) && !replace.containsKey(biome)) { |
||||
biomeSet.add(biome); |
||||
} else { |
||||
reject.add(biome); |
||||
} |
||||
}); |
||||
biomeSet.addAll(replaceDefault.getContents().stream().flatMap(terraBiome -> { |
||||
if(terraBiome.isSelf()) return reject.stream(); |
||||
return Stream.of(terraBiome); |
||||
}).toList()); |
||||
replace.forEach((biome, collection) -> biomeSet.addAll(collection.getContents().stream().map(terraBiome -> { |
||||
if(terraBiome.isSelf()) return biome; |
||||
return terraBiome; |
||||
}).toList())); |
||||
return biomeSet; |
||||
} |
||||
} |
@ -0,0 +1,63 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators; |
||||
|
||||
import java.util.HashSet; |
||||
import java.util.Set; |
||||
import java.util.stream.Stream; |
||||
|
||||
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
import com.dfsek.terra.api.util.collection.ProbabilityCollection; |
||||
|
||||
|
||||
public class ReplaceStage implements Stage { |
||||
private final String replaceableTag; |
||||
private final ProbabilityCollection<PipelineBiome> replace; |
||||
private final NoiseSampler sampler; |
||||
|
||||
public ReplaceStage(String replaceable, ProbabilityCollection<PipelineBiome> replace, NoiseSampler sampler) { |
||||
this.replaceableTag = replaceable; |
||||
this.replace = replace; |
||||
this.sampler = sampler; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
if(viewPoint.getBiome().getTags().contains(replaceableTag)) { |
||||
PipelineBiome biome = replace.get(sampler, viewPoint.worldX(), viewPoint.worldZ(), viewPoint.worldSeed()); |
||||
return biome.isSelf() ? viewPoint.getBiome() : biome; |
||||
} |
||||
return viewPoint.getBiome(); |
||||
} |
||||
|
||||
@Override |
||||
public int maxRelativeReadDistance() { |
||||
return 0; |
||||
} |
||||
|
||||
@Override |
||||
public Iterable<PipelineBiome> getBiomes(Iterable<PipelineBiome> biomes) { |
||||
Set<PipelineBiome> biomeSet = new HashSet<>(); |
||||
Set<PipelineBiome> reject = new HashSet<>(); |
||||
biomes.forEach(biome -> { |
||||
if(!biome.getTags().contains(replaceableTag)) { |
||||
biomeSet.add(biome); |
||||
} else { |
||||
reject.add(biome); |
||||
} |
||||
}); |
||||
biomeSet.addAll(replace.getContents().stream().flatMap(terraBiome -> { |
||||
if(terraBiome.isSelf()) return reject.stream(); |
||||
return Stream.of(terraBiome); |
||||
}).toList()); |
||||
return biomeSet; |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
/* |
||||
* Copyright (c) 2020-2021 Polyhedral Development |
||||
* |
||||
* The Terra Core Addons are licensed under the terms of the MIT License. For more details, |
||||
* reference the LICENSE file in this module's root directory. |
||||
*/ |
||||
|
||||
package com.dfsek.terra.addons.biome.pipeline.v2.stage.mutators; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
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.addons.biome.pipeline.v2.pipeline.BiomeChunkImpl; |
||||
import com.dfsek.terra.api.noise.NoiseSampler; |
||||
|
||||
|
||||
public class SmoothStage implements Stage { |
||||
|
||||
private final NoiseSampler sampler; |
||||
|
||||
public SmoothStage(NoiseSampler sampler) { |
||||
this.sampler = sampler; |
||||
} |
||||
|
||||
@Override |
||||
public PipelineBiome apply(BiomeChunkImpl.ViewPoint viewPoint) { |
||||
PipelineBiome top = viewPoint.getRelativeBiome(1, 0); |
||||
PipelineBiome bottom = viewPoint.getRelativeBiome(-1, 0); |
||||
PipelineBiome left = viewPoint.getRelativeBiome(0, 1); |
||||
PipelineBiome right = viewPoint.getRelativeBiome(0, -1); |
||||
|
||||
double roll = sampler.noise(viewPoint.worldSeed(), viewPoint.worldX(), viewPoint.worldZ()); |
||||
|
||||
boolean vert = Objects.equals(top, bottom); |
||||
boolean horiz = Objects.equals(left, right); |
||||
|
||||
if(vert && horiz) { |
||||
return roll > 0 ? |
||||
roll > 0.25 ? left : right : |
||||
roll > -0.25 ? top : bottom; |
||||
} |
||||
if(vert) { |
||||
return roll > 0 ? top : bottom; |
||||
} |
||||
if(horiz) { |
||||
return roll > 0 ? left : right; |
||||
} |
||||
return viewPoint.getBiome(); |
||||
} |
||||
|
||||
@Override |
||||
public int maxRelativeReadDistance() { |
||||
return 1; |
||||
} |
||||
} |
@ -0,0 +1,12 @@ |
||||
schema-version: 1 |
||||
contributors: |
||||
- Terra contributors |
||||
id: biome-provider-pipeline-v2 |
||||
version: @VERSION@ |
||||
entrypoints: |
||||
- "com.dfsek.terra.addons.biome.pipeline.v2.BiomePipelineAddon" |
||||
website: |
||||
issues: https://github.com/PolyhedralDev/Terra/issues |
||||
source: https://github.com/PolyhedralDev/Terra |
||||
docs: https://terra.polydev.org |
||||
license: MIT License |
Loading…
Reference in new issue