Ice Starfield biome
96
src/main/java/ru/betterend/particle/ParticleSnowflake.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
package ru.betterend.particle;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.particle.Particle;
|
||||
import net.minecraft.client.particle.ParticleFactory;
|
||||
import net.minecraft.client.particle.ParticleTextureSheet;
|
||||
import net.minecraft.client.particle.SpriteBillboardParticle;
|
||||
import net.minecraft.client.particle.SpriteProvider;
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.particle.DefaultParticleType;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import ru.betterend.util.MHelper;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class ParticleSnowflake extends SpriteBillboardParticle {
|
||||
private int ticks;
|
||||
private double preVX;
|
||||
private double preVY;
|
||||
private double preVZ;
|
||||
private double nextVX;
|
||||
private double nextVY;
|
||||
private double nextVZ;
|
||||
|
||||
protected ParticleSnowflake(ClientWorld world, double x, double y, double z, double r, double g, double b, SpriteProvider sprites) {
|
||||
super(world, x, y, z, r, g, b);
|
||||
setSprite(sprites);
|
||||
|
||||
this.maxAge = MHelper.randRange(150, 300, random);
|
||||
this.scale = MHelper.randRange(0.05F, 0.3F, random);
|
||||
this.setColorAlpha(0F);
|
||||
|
||||
preVX = random.nextGaussian() * 0.015;
|
||||
preVY = random.nextGaussian() * 0.015;
|
||||
preVZ = random.nextGaussian() * 0.015;
|
||||
|
||||
nextVX = random.nextGaussian() * 0.015;
|
||||
nextVY = random.nextGaussian() * 0.015;
|
||||
nextVZ = random.nextGaussian() * 0.015;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
ticks ++;
|
||||
if (ticks > 200) {
|
||||
preVX = nextVX;
|
||||
preVY = nextVY;
|
||||
preVZ = nextVZ;
|
||||
nextVX = random.nextGaussian() * 0.015;
|
||||
nextVY = random.nextGaussian() * 0.015;
|
||||
nextVZ = random.nextGaussian() * 0.015;
|
||||
if (random.nextInt(4) == 0) {
|
||||
nextVY = Math.abs(nextVY);
|
||||
}
|
||||
ticks = 0;
|
||||
}
|
||||
double delta = (double) ticks / 200.0;
|
||||
|
||||
if (this.age <= 40) {
|
||||
this.setColorAlpha(this.age / 40F);
|
||||
}
|
||||
else if (this.age >= this.maxAge - 40) {
|
||||
this.setColorAlpha((this.maxAge - this.age) / 40F);
|
||||
}
|
||||
|
||||
if (this.age >= this.maxAge) {
|
||||
this.markDead();
|
||||
}
|
||||
|
||||
this.velocityX = MathHelper.lerp(delta, preVX, nextVX);
|
||||
this.velocityY = MathHelper.lerp(delta, preVY, nextVY);
|
||||
this.velocityZ = MathHelper.lerp(delta, preVZ, nextVZ);
|
||||
|
||||
super.tick();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleTextureSheet getType() {
|
||||
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT;
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public static class FactorySnowflake implements ParticleFactory<DefaultParticleType> {
|
||||
|
||||
private final SpriteProvider sprites;
|
||||
|
||||
public FactorySnowflake(SpriteProvider sprites) {
|
||||
this.sprites = sprites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Particle createParticle(DefaultParticleType type, ClientWorld world, double x, double y, double z, double vX, double vY, double vZ) {
|
||||
return new ParticleSnowflake(world, x, y, z, 1, 1, 1, sprites);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,6 +28,7 @@ public class ParticleSulphur extends SpriteBillboardParticle {
|
|||
|
||||
this.maxAge = MHelper.randRange(150, 300, random);
|
||||
this.scale = MHelper.randRange(0.05F, 0.15F, random);
|
||||
this.setColorAlpha(0);
|
||||
|
||||
preVX = random.nextGaussian() * 0.015;
|
||||
preVY = random.nextGaussian() * 0.015;
|
||||
|
|
|
@ -31,6 +31,7 @@ import ru.betterend.world.biome.BiomeChorusForest;
|
|||
import ru.betterend.world.biome.BiomeCrystalMountains;
|
||||
import ru.betterend.world.biome.BiomeDustWastelands;
|
||||
import ru.betterend.world.biome.BiomeFoggyMushroomland;
|
||||
import ru.betterend.world.biome.BiomeIceStarfield;
|
||||
import ru.betterend.world.biome.BiomeMegalake;
|
||||
import ru.betterend.world.biome.BiomeMegalakeGrove;
|
||||
import ru.betterend.world.biome.BiomePaintedMountains;
|
||||
|
@ -75,6 +76,9 @@ public class EndBiomes {
|
|||
public static final EndBiome BLOSSOMING_SPIRES = registerBiome(new BiomeBlossomingSpires(), BiomeType.LAND);
|
||||
public static final EndBiome SULPHUR_SPRINGS = registerBiome(new BiomeSulphurSprings(), BiomeType.LAND);
|
||||
|
||||
// Better End Void
|
||||
public static final EndBiome ICE_STARFIELD = registerBiome(new BiomeIceStarfield(), BiomeType.VOID);
|
||||
|
||||
public static void register() {}
|
||||
|
||||
public static void mutateRegistry(Registry<Biome> biomeRegistry) {
|
||||
|
|
|
@ -33,6 +33,7 @@ import ru.betterend.world.features.bushes.TenaneaBushFeature;
|
|||
import ru.betterend.world.features.terrain.EndLakeFeature;
|
||||
import ru.betterend.world.features.terrain.FloatingSpireFeature;
|
||||
import ru.betterend.world.features.terrain.GeyserFeature;
|
||||
import ru.betterend.world.features.terrain.IceStarFeature;
|
||||
import ru.betterend.world.features.terrain.RoundCaveFeature;
|
||||
import ru.betterend.world.features.terrain.SpireFeature;
|
||||
import ru.betterend.world.features.terrain.SulphuricCaveFeature;
|
||||
|
@ -116,6 +117,7 @@ public class EndFeatures {
|
|||
public static final EndFeature GEYSER = EndFeature.makeRawGenFeature("geyser", new GeyserFeature(), 8);
|
||||
public static final EndFeature SULPHURIC_LAKE = EndFeature.makeLakeFeature("sulphuric_lake", new SulphuricLakeFeature(), 8);
|
||||
public static final EndFeature SULPHURIC_CAVE = EndFeature.makeCountRawFeature("sulphuric_cave", new SulphuricCaveFeature(), 2);
|
||||
public static final EndFeature ICE_STAR = EndFeature.makeRawGenFeature("ice_star", new IceStarFeature(), 15);
|
||||
|
||||
// Ores //
|
||||
public static final EndFeature ENDER_ORE = EndFeature.makeOreFeature("ender_ore", EndBlocks.ENDER_ORE, 6, 3, 0, 4, 96);
|
||||
|
|
|
@ -11,6 +11,7 @@ import ru.betterend.particle.InfusionParticle;
|
|||
import ru.betterend.particle.InfusionParticleType;
|
||||
import ru.betterend.particle.ParticleGeyser;
|
||||
import ru.betterend.particle.ParticleGlowingSphere;
|
||||
import ru.betterend.particle.ParticleSnowflake;
|
||||
import ru.betterend.particle.ParticleSulphur;
|
||||
import ru.betterend.particle.PaticlePortalSphere;
|
||||
|
||||
|
@ -20,6 +21,7 @@ public class EndParticles {
|
|||
public static final ParticleType<InfusionParticleType> INFUSION = register("infusion", FabricParticleTypes.complex(InfusionParticleType.PARAMETERS_FACTORY));
|
||||
public static final DefaultParticleType SULPHUR_PARTICLE = register("sulphur_particle");
|
||||
public static final DefaultParticleType GEYSER_PARTICLE = registerFar("geyser_particle");
|
||||
public static final DefaultParticleType SNOWFLAKE = register("snowflake");
|
||||
|
||||
public static void register() {
|
||||
ParticleFactoryRegistry.getInstance().register(GLOWING_SPHERE, ParticleGlowingSphere.FactoryGlowingSphere::new);
|
||||
|
@ -27,6 +29,7 @@ public class EndParticles {
|
|||
ParticleFactoryRegistry.getInstance().register(INFUSION, InfusionParticle.InfusionFactory::new);
|
||||
ParticleFactoryRegistry.getInstance().register(SULPHUR_PARTICLE, ParticleSulphur.FactorySulphur::new);
|
||||
ParticleFactoryRegistry.getInstance().register(GEYSER_PARTICLE, ParticleGeyser.FactoryGeyser::new);
|
||||
ParticleFactoryRegistry.getInstance().register(SNOWFLAKE, ParticleSnowflake.FactorySnowflake::new);
|
||||
}
|
||||
|
||||
private static DefaultParticleType register(String name) {
|
||||
|
|
|
@ -2,11 +2,14 @@ package ru.betterend.util;
|
|||
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.client.util.math.Vector3f;
|
||||
|
||||
public class MHelper {
|
||||
public static final float PI2 = (float) (Math.PI * 2);
|
||||
private static final int ALPHA = 255 << 24;
|
||||
public static final Random RANDOM = new Random();
|
||||
private static final float RAD_TO_DEG = 57.295779513082320876798154814105F;
|
||||
public static final float PHI = (float) (Math.PI * (3 - Math.sqrt(5)));
|
||||
|
||||
public static int color(int r, int g, int b) {
|
||||
return ALPHA | (r << 16) | (g << 8) | b;
|
||||
|
@ -247,4 +250,32 @@ public class MHelper {
|
|||
public static final float radiandToDegrees(float value) {
|
||||
return value * RAD_TO_DEG;
|
||||
}
|
||||
|
||||
public static Vector3f cross(Vector3f vec1, Vector3f vec2)
|
||||
{
|
||||
float cx = vec1.getY() * vec2.getZ() - vec1.getZ() * vec2.getY();
|
||||
float cy = vec1.getZ() * vec2.getX() - vec1.getX() * vec2.getZ();
|
||||
float cz = vec1.getX() * vec2.getY() - vec1.getY() * vec2.getX();
|
||||
return new Vector3f(cx, cy, cz);
|
||||
}
|
||||
|
||||
public static Vector3f normalize(Vector3f vec) {
|
||||
float length = lengthSqr(vec.getX(), vec.getY(), vec.getZ());
|
||||
if (length > 0) {
|
||||
length = (float) Math.sqrt(length);
|
||||
float x = vec.getX() / length;
|
||||
float y = vec.getY() / length;
|
||||
float z = vec.getZ() / length;
|
||||
vec.set(x, y, z);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
public static float angle(Vector3f vec1, Vector3f vec2)
|
||||
{
|
||||
float dot = vec1.getX() * vec2.getX() + vec1.getY() * vec2.getY() + vec1.getZ() * vec2.getZ();
|
||||
float length1 = lengthSqr(vec1.getX(), vec1.getY(), vec1.getZ());
|
||||
float length2 = lengthSqr(vec2.getX(), vec2.getY(), vec2.getZ());
|
||||
return (float) Math.acos(dot / Math.sqrt(length1 * length2));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
package ru.betterend.world.biome;
|
||||
|
||||
import net.minecraft.entity.EntityType;
|
||||
import ru.betterend.registry.EndFeatures;
|
||||
import ru.betterend.registry.EndParticles;
|
||||
|
||||
public class BiomeIceStarfield extends EndBiome {
|
||||
public BiomeIceStarfield() {
|
||||
super(new BiomeDefinition("ice_starfield")
|
||||
.setFogColor(224, 245, 254)
|
||||
.setFogDensity(2.2F)
|
||||
.setFoliageColor(193, 244, 244)
|
||||
.setParticles(EndParticles.SNOWFLAKE, 0.001F)
|
||||
.addFeature(EndFeatures.ICE_STAR)
|
||||
.addMobSpawn(EntityType.ENDERMAN, 20, 1, 4));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ru.betterend.world.features.terrain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.util.math.Vector3f;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.StructureWorldAccess;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.feature.DefaultFeatureConfig;
|
||||
import ru.betterend.noise.OpenSimplexNoise;
|
||||
import ru.betterend.util.MHelper;
|
||||
import ru.betterend.util.sdf.SDF;
|
||||
import ru.betterend.util.sdf.operator.SDFRotation;
|
||||
import ru.betterend.util.sdf.operator.SDFTranslate;
|
||||
import ru.betterend.util.sdf.operator.SDFUnion;
|
||||
import ru.betterend.util.sdf.primitive.SDFCapedCone;
|
||||
import ru.betterend.world.features.DefaultFeature;
|
||||
|
||||
public class IceStarFeature extends DefaultFeature {
|
||||
@Override
|
||||
public boolean generate(StructureWorldAccess world, ChunkGenerator chunkGenerator, Random random, BlockPos pos, DefaultFeatureConfig config) {
|
||||
float size = MHelper.randRange(5F, 15F, random);
|
||||
int count = MHelper.randRange(7, 25, random);
|
||||
List<Vector3f> points = getFibonacciPoints(count);
|
||||
SDF sdf = null;
|
||||
SDF spike = new SDFCapedCone().setRadius1(3 + (size - 5) * 0.2F).setRadius2(0).setHeight(size).setBlock(Blocks.ICE);
|
||||
spike = new SDFTranslate().setTranslate(0, size - 0.5F, 0).setSource(spike);
|
||||
for (Vector3f point: points) {
|
||||
SDF rotated = spike;
|
||||
point = MHelper.normalize(point);
|
||||
float angle = MHelper.angle(Vector3f.POSITIVE_Y, point);
|
||||
if (angle > 0.01F && angle < 3.14F) {
|
||||
Vector3f axis = MHelper.normalize(MHelper.cross(Vector3f.POSITIVE_Y, point));
|
||||
rotated = new SDFRotation().setRotation(axis, angle).setSource(spike);
|
||||
}
|
||||
else if (angle > 1) {
|
||||
rotated = new SDFRotation().setRotation(Vector3f.POSITIVE_Y, (float) Math.PI).setSource(spike);
|
||||
}
|
||||
sdf = (sdf == null) ? rotated : new SDFUnion().setSourceA(sdf).setSourceB(rotated);
|
||||
}
|
||||
|
||||
int x1 = (pos.getX() >> 4) << 4;
|
||||
int z1 = (pos.getZ() >> 4) << 4;
|
||||
pos = new BlockPos(x1 + random.nextInt(16), MHelper.randRange(32, 128, random), z1 + random.nextInt(16));
|
||||
|
||||
OpenSimplexNoise noise1 = new OpenSimplexNoise(random.nextLong());
|
||||
OpenSimplexNoise noise2 = new OpenSimplexNoise(random.nextLong());
|
||||
|
||||
final boolean hasSnow = random.nextBoolean();
|
||||
BlockState layer = Blocks.SNOW.getDefaultState();
|
||||
BlockState snow = Blocks.SNOW_BLOCK.getDefaultState();
|
||||
BlockState blue = Blocks.BLUE_ICE.getDefaultState();
|
||||
BlockState packed = Blocks.PACKED_ICE.getDefaultState();
|
||||
sdf.setPostProcess((info) -> {
|
||||
BlockPos bpos = info.getPos();
|
||||
|
||||
if (hasSnow && !info.getState().isOf(Blocks.SNOW) && info.getStateUp().isAir()) {
|
||||
info.setBlockPos(bpos.up(), layer.with(Properties.LAYERS, MHelper.randRange(1, 3, random)));
|
||||
//info.setBlockPos(bpos.up(), layer);
|
||||
return snow;
|
||||
}
|
||||
|
||||
if (noise1.eval(bpos.getX(), bpos.getY(), bpos.getZ()) > 0.3) {
|
||||
return packed;
|
||||
}
|
||||
else if (noise2.eval(bpos.getX(), bpos.getY(), bpos.getZ()) > 0.3) {
|
||||
return blue;
|
||||
}
|
||||
else {
|
||||
return info.getState();
|
||||
}
|
||||
}).fillRecursive(world, pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Vector3f> getFibonacciPoints(int count) {
|
||||
float max = count - 1;
|
||||
List<Vector3f> result = new ArrayList<Vector3f>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
float y = 1F - (i / max) * 2F;
|
||||
float radius = (float) Math.sqrt(1F - y * y);
|
||||
float theta = MHelper.PHI * i;
|
||||
float x = (float) Math.cos(theta) * radius;
|
||||
float z = (float) Math.sin(theta) * radius;
|
||||
result.add(new Vector3f(x, y, z));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
11
src/main/resources/assets/betterend/particles/snowflake.json
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"textures": [
|
||||
"betterend:snowflake_0",
|
||||
"betterend:snowflake_1",
|
||||
"betterend:snowflake_2",
|
||||
"betterend:snowflake_3",
|
||||
"betterend:snowflake_4",
|
||||
"betterend:snowflake_5",
|
||||
"betterend:snowflake_6"
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |