Skip to content

Commit

Permalink
Filtered wind effects and rendering, and uniform snow build-up
Browse files Browse the repository at this point in the history
  • Loading branch information
lukebemish committed Dec 26, 2023
1 parent e17dee7 commit 3c3156f
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void tickWeather(Camera camera, int ticks) {
if (particlePos.getY() > level.getMinBuildHeight() && particlePos.getY() <= cameraPos.getY() + 10 && particlePos.getY() >= cameraPos.getY() - 10) {
var chunk = level.getChunkAt(particlePos);
var data = Services.PLATFORM.getChunkData(chunk);
var status = data.getWeatherStatus(particlePos);
var status = data.getWeatherStatusWindAware(particlePos);
if (status != null) {
if (random.nextFloat() <= status.intensity) {
if (status.category == WeatherCategory.RAIN || status.category == WeatherCategory.SLEET || status.category == WeatherCategory.HAIL) {
Expand Down Expand Up @@ -93,7 +93,7 @@ public void tickWeather(Camera camera, int ticks) {
BlockPos soundPos = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, cameraPos.offset(k, 0, l));
var chunk = level.getChunkAt(soundPos);
var data = Services.PLATFORM.getChunkData(chunk);
var status = data.getWeatherStatus(soundPos);
var status = data.getWeatherStatusWindAware(soundPos);
if (status != null && random.nextInt(3) < this.rainSoundTime++) {
this.rainSoundTime = 0;
if (status.category == WeatherCategory.RAIN || status.category == WeatherCategory.SLEET || status.category == WeatherCategory.HAIL) {
Expand Down Expand Up @@ -129,25 +129,29 @@ public void renderWeather(LightTexture lightTexture, float partialTick, double c
float time = (float) ticks + partialTick;
RenderSystem.setShader(GameRenderer::getParticleShader);
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
BlockPos.MutableBlockPos checkingPos = new BlockPos.MutableBlockPos();
for(int z = floorZ - layers; z <= floorZ + layers; ++z) {
for (int x = floorX - layers; x <= floorX + layers; ++x) {
int sizeIdx = (z - floorZ + 16) * 32 + x - floorX + 16;
float sizeX = this.rainSizeX[sizeIdx];
float sizeZ = this.rainSizeZ[sizeIdx];
mutableBlockPos.set(x, camY, z);
checkingPos.setX(x);
checkingPos.setZ(z);
//noinspection DataFlowIssue
var chunk = level.getChunkAt(mutableBlockPos);
var data = Services.PLATFORM.getChunkData(chunk);
var status = data.getWeatherStatus(mutableBlockPos);
if (status != null) {
float precipLevel = status.intensity;
int lowerY = level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z);
checkingPos.setY(lowerY);
int minY = Math.max(floorY - belowOffset, lowerY);
int maxY = Math.max(floorY + layers, lowerY);

int upperY = Math.max(floorY, lowerY);

if (minY != maxY) {
if (data.canSeeWind(checkingPos) && minY != maxY) {
// we actually have somewhere to render
// 3121: prime
// 45238971: 3, 15079657
Expand All @@ -167,7 +171,6 @@ public void renderWeather(LightTexture lightTexture, float partialTick, double c
bufferbuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE);
}

//float slowVerticalOffset = -((ticks & (512 - 1)) + partialTick) / 512.0F;
float speed = Mth.clamp(status.speed, 0, 1.25f);
float relSpeed = (speed / 1.25f);
relSpeed = 1 - (1 - relSpeed) * (1 - relSpeed) * (1 - relSpeed) * (1 - relSpeed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import dev.lukebemish.tempest.impl.Constants;
import dev.lukebemish.tempest.impl.data.WeatherCategory;
import dev.lukebemish.tempest.impl.data.WeatherMapData;
import dev.lukebemish.tempest.impl.util.QuasiRandomChunkVisitor;
import it.unimi.dsi.fastutil.ints.*;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
Expand All @@ -13,11 +14,9 @@
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.SnowLayerBlock;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
Expand Down Expand Up @@ -49,6 +48,8 @@ public class WeatherChunkData {
private static final int[] ZS = new int[] {0, 16, 0, 16};

private boolean initialized;
private int visitIndex = -1;
private final List<BlockPos> windCheckPositions = new ArrayList<>();

private final @Nullable Runnable setDirtyCallback;

Expand Down Expand Up @@ -254,6 +255,7 @@ public void tick(ServerLevel level, WeatherMapData.Built weatherMap) {
windZ[i] = weatherMap.windZ().query(x+XS[i], z+ZS[i], gameTime);
thunder[i] = Mth.clamp(weatherMap.thunder().query(x+XS[i], z+ZS[i], gameTime), -1, 1);
}
recalculateWindCheckPositions(level);
this.markDirty();
}

Expand Down Expand Up @@ -289,18 +291,42 @@ public void tick(ServerLevel level, WeatherMapData.Built weatherMap) {
}
}

if (meltAndFreeze(level, x, z)) {
boolean tryAgain = meltAndFreeze(level, x, z);
if (meltAndFreeze(level)) {
boolean tryAgain = meltAndFreeze(level);
if (tryAgain && level.random.nextBoolean()) {
meltAndFreeze(level, x, z);
meltAndFreeze(level);
}
}

this.update();
}

private boolean meltAndFreeze(ServerLevel level, int x, int z) {
BlockPos waterySurface = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, level.getBlockRandomPos(x, 0, z, 15)).below();
private void recalculateWindCheckPositions(Level level) {
var centerPos = chunk.getPos().getBlockAt(8, 0, 8);
float windX = windX(level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, centerPos));
float windZ = windZ(level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, centerPos));
float speed = Mth.sqrt(windX * windX + windZ * windZ);
float angle = Mth.clamp(speed, 0, 1.25F)/ 1.25F;
float singleX = -(float) Math.cos(angle) * windX / speed;
float singleY = (float) Math.sin(angle);
float singleZ = -(float) Math.cos(angle) * windZ / speed;
windCheckPositions.clear();
float xOff = 0;
float yOff = 0;
float zOff = 0;
for (int i = 0; i < 12; i++) {
xOff += singleX;
yOff += singleY;
zOff += singleZ;
windCheckPositions.add(new BlockPos(Math.round(xOff), Math.round(yOff), Math.round(zOff)));
}
}

private boolean meltAndFreeze(ServerLevel level) {
BlockPos waterySurface = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, QuasiRandomChunkVisitor.INSTANCE.inChunk(chunk, visitIndex, i -> this.visitIndex = i)).below();
if (!canSeeWindIgnoreLeaves(waterySurface)) {
return false;
}
float temp = temperature(waterySurface);
float precip = precipitation(waterySurface);
float thunder = thunder(waterySurface);
Expand All @@ -310,8 +336,10 @@ private boolean meltAndFreeze(ServerLevel level, int x, int z) {
if (isHailing(temp, precip, thunder)) {
if (level.random.nextFloat() < 0.4 * precip) {
BlockPos hailSurface = level.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, waterySurface).below();
if (tryHailBreak(level, hailSurface.above())) {
tryHailBreak(level, hailSurface);
if (canSeeWind(hailSurface)) {
if (tryHailBreak(level, hailSurface.above())) {
tryHailBreak(level, hailSurface);
}
}
}
repeat = level.random.nextFloat() < precip;
Expand Down Expand Up @@ -376,7 +404,7 @@ private boolean tryFreezeBlock(ServerLevel level, BlockPos toFreeze) {
if (state.getBlock() == Blocks.SNOW) {
int levels = state.getValue(SnowLayerBlock.LAYERS);
BlockState newState;
if (levels < 7) {
if (levels < 6) {
newState = state.setValue(SnowLayerBlock.LAYERS, levels + 1);
} else {
if (level.random.nextFloat() < 0.75f) {
Expand All @@ -391,6 +419,15 @@ private boolean tryFreezeBlock(ServerLevel level, BlockPos toFreeze) {
if (hasSpaceForSnow(level, toSnow)) {
level.setBlockAndUpdate(toSnow, Blocks.SNOW.defaultBlockState());
}
} else if (state.getBlock() == Blocks.POWDER_SNOW) {
BlockPos aboveSnow = toSnow.above();
BlockState upState = level.getBlockState(aboveSnow);
level.setBlockAndUpdate(toSnow, Blocks.SNOW_BLOCK.defaultBlockState());
if (upState.canBeReplaced() && Blocks.SNOW.defaultBlockState().canSurvive(level, aboveSnow)) {
if (hasSpaceForSnow(level, aboveSnow)) {
level.setBlockAndUpdate(aboveSnow, Blocks.SNOW.defaultBlockState());
}
}
}
return level.random.nextFloat() < precip;
}
Expand All @@ -400,6 +437,36 @@ private boolean tryFreezeBlock(ServerLevel level, BlockPos toFreeze) {
return level.random.nextFloat() < (level.random.nextBoolean() ? -temp : precip);
}

public boolean canSeeWindIgnoreLeaves(BlockPos pos) {
var mutablePos = new BlockPos.MutableBlockPos();
for (BlockPos check : windCheckPositions) {
mutablePos.setWithOffset(pos, check);
if (chunk.getLevel().isLoaded(mutablePos)) {
BlockState blockState = chunk.getLevel().getBlockState(mutablePos);
//noinspection deprecation
if ((blockState.blocksMotion() || !blockState.getFluidState().isEmpty()) && !(blockState.getBlock() instanceof LeavesBlock)) {
return false;
}
}
}
return true;
}

public boolean canSeeWind(BlockPos pos) {
var mutablePos = new BlockPos.MutableBlockPos();
for (BlockPos check : windCheckPositions) {
mutablePos.setWithOffset(pos, check);
if (chunk.getLevel().isLoaded(mutablePos)) {
BlockState blockState = chunk.getLevel().getBlockState(mutablePos);
//noinspection deprecation
if (blockState.blocksMotion() || !blockState.getFluidState().isEmpty()) {
return false;
}
}
}
return true;
}

private static boolean hasSpaceForSnow(ServerLevel level, BlockPos pos) {
for (int i = 0; i < 4; i++) {
pos = pos.below();
Expand Down Expand Up @@ -556,12 +623,18 @@ private boolean isFreezableWater(ServerLevel level, BlockPos toFreeze) {
}

void update(UpdateWeatherChunk updateWeatherChunk, Consumer<BlockPos> posUpdater) {
boolean recalcChecks = this.windX != updateWeatherChunk.windX || this.windZ != updateWeatherChunk.windZ;

this.temperature = updateWeatherChunk.temperature;
this.precipitation = updateWeatherChunk.precipitation;
this.windX = updateWeatherChunk.windX;
this.windZ = updateWeatherChunk.windZ;
this.thunder = updateWeatherChunk.thunder;

if (recalcChecks) {
recalculateWindCheckPositions(chunk.getLevel());
}

BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
for (int i = 0; i < updateWeatherChunk.posData.length; i++) {
int key = updateWeatherChunk.posData[i];
Expand All @@ -572,6 +645,14 @@ void update(UpdateWeatherChunk updateWeatherChunk, Consumer<BlockPos> posUpdater
}
}

public @Nullable WeatherCategory.WeatherStatus getWeatherStatusWindAware(BlockPos pos) {
var status = getWeatherStatus(pos);
if (canSeeWind(pos)) {
return status;
}
return null;
}

public @Nullable WeatherCategory.WeatherStatus getWeatherStatus(BlockPos pos) {
float precip = precipitation(pos);
if (precipitation(pos) > 0f) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class LevelMixin {
//noinspection DataFlowIssue
var level = (Level) (Object) this;
var data = Services.PLATFORM.getChunkData(level.getChunkAt(pos));
var status = data.getWeatherStatus(pos);
var status = data.getWeatherStatusWindAware(pos);
if (status != null && (status.category == WeatherCategory.RAIN || status.category == WeatherCategory.SLEET)) {
cir.setReturnValue(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public abstract class LivingEntityMixin extends Entity {
var pos = BlockPos.containing(this.getX(), this.getEyeY(), this.getZ());
if (level().canSeeSky(pos)){
var weatherData = Services.PLATFORM.getChunkData(this.level().getChunkAt(pos));
var status = weatherData.getWeatherStatus(pos);
var status = weatherData.getWeatherStatusWindAware(pos);
if (!this.level().isClientSide()) {
if ((this.tickCount & 8) == 0 && status != null && status.category == WeatherCategory.HAIL) {
var source = new DamageSource(this.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(Constants.HAIL_DAMAGE_TYPE));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package dev.lukebemish.tempest.impl.util;

import com.mojang.datafixers.util.Pair;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.LevelChunk;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.function.IntConsumer;

public class QuasiRandomChunkVisitor {
public static final QuasiRandomChunkVisitor INSTANCE = new QuasiRandomChunkVisitor(9875331409874325L, 8);
private final int[] xs;
private final int[] zs;
public final int size;

public QuasiRandomChunkVisitor(long seed, int iterations) {
List<Pair<Integer, Integer>> positions = new ArrayList<>();
for (int i = 0; i < iterations; i++) {
List<Pair<Integer, Integer>> temp = new ArrayList<>();
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
temp.add(Pair.of(x, z));
}
}
Collections.shuffle(temp, new Random(seed + i));
positions.addAll(temp);
}
xs = new int[positions.size()];
zs = new int[positions.size()];
for (int i = 0; i < positions.size(); i++) {
xs[i] = positions.get(i).getFirst();
zs[i] = positions.get(i).getSecond();
}
size = positions.size();
}

public BlockPos inChunk(LevelChunk chunk, int index, IntConsumer consumer) {
if (index < 0 || index >= size) {
index = chunk.getLevel().getRandom().nextInt(size);
}
int x = xs[index];
int z = zs[index];
consumer.accept((index + 1) % size);
return chunk.getPos().getBlockAt(x, 0, z);
}
}

0 comments on commit 3c3156f

Please sign in to comment.