diff --git a/src/main/java/ru/betterend/particle/ParticleSnowflake.java b/src/main/java/ru/betterend/particle/ParticleSnowflake.java new file mode 100644 index 00000000..088a2b79 --- /dev/null +++ b/src/main/java/ru/betterend/particle/ParticleSnowflake.java @@ -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 { + + 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/betterend/particle/ParticleSulphur.java b/src/main/java/ru/betterend/particle/ParticleSulphur.java index a188cb08..522690ed 100644 --- a/src/main/java/ru/betterend/particle/ParticleSulphur.java +++ b/src/main/java/ru/betterend/particle/ParticleSulphur.java @@ -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; diff --git a/src/main/java/ru/betterend/registry/EndBiomes.java b/src/main/java/ru/betterend/registry/EndBiomes.java index f1db8621..35bdc6da 100644 --- a/src/main/java/ru/betterend/registry/EndBiomes.java +++ b/src/main/java/ru/betterend/registry/EndBiomes.java @@ -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 biomeRegistry) { diff --git a/src/main/java/ru/betterend/registry/EndFeatures.java b/src/main/java/ru/betterend/registry/EndFeatures.java index d679299f..cb3be9c5 100644 --- a/src/main/java/ru/betterend/registry/EndFeatures.java +++ b/src/main/java/ru/betterend/registry/EndFeatures.java @@ -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); diff --git a/src/main/java/ru/betterend/registry/EndParticles.java b/src/main/java/ru/betterend/registry/EndParticles.java index ee73fc2f..6e43c3af 100644 --- a/src/main/java/ru/betterend/registry/EndParticles.java +++ b/src/main/java/ru/betterend/registry/EndParticles.java @@ -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 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) { diff --git a/src/main/java/ru/betterend/util/MHelper.java b/src/main/java/ru/betterend/util/MHelper.java index 5c6fc094..de241a06 100644 --- a/src/main/java/ru/betterend/util/MHelper.java +++ b/src/main/java/ru/betterend/util/MHelper.java @@ -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)); + } } diff --git a/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java b/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java new file mode 100644 index 00000000..79b99265 --- /dev/null +++ b/src/main/java/ru/betterend/world/biome/BiomeIceStarfield.java @@ -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)); + } +} diff --git a/src/main/java/ru/betterend/world/features/terrain/IceStarFeature.java b/src/main/java/ru/betterend/world/features/terrain/IceStarFeature.java new file mode 100644 index 00000000..93a7d010 --- /dev/null +++ b/src/main/java/ru/betterend/world/features/terrain/IceStarFeature.java @@ -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 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 getFibonacciPoints(int count) { + float max = count - 1; + List result = new ArrayList(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; + } +} diff --git a/src/main/resources/assets/betterend/particles/snowflake.json b/src/main/resources/assets/betterend/particles/snowflake.json new file mode 100644 index 00000000..e5945670 --- /dev/null +++ b/src/main/resources/assets/betterend/particles/snowflake.json @@ -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" + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_0.png b/src/main/resources/assets/betterend/textures/particle/snowflake_0.png new file mode 100644 index 00000000..e05607bf Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_0.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_1.png b/src/main/resources/assets/betterend/textures/particle/snowflake_1.png new file mode 100644 index 00000000..fe83141b Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_1.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_2.png b/src/main/resources/assets/betterend/textures/particle/snowflake_2.png new file mode 100644 index 00000000..f5c696e8 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_2.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_3.png b/src/main/resources/assets/betterend/textures/particle/snowflake_3.png new file mode 100644 index 00000000..2bded6bb Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_3.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_4.png b/src/main/resources/assets/betterend/textures/particle/snowflake_4.png new file mode 100644 index 00000000..7365be92 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_4.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_5.png b/src/main/resources/assets/betterend/textures/particle/snowflake_5.png new file mode 100644 index 00000000..ec3aa488 Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_5.png differ diff --git a/src/main/resources/assets/betterend/textures/particle/snowflake_6.png b/src/main/resources/assets/betterend/textures/particle/snowflake_6.png new file mode 100644 index 00000000..9165a38a Binary files /dev/null and b/src/main/resources/assets/betterend/textures/particle/snowflake_6.png differ