diff --git a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java index f381204ad..7f2df4b81 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/model/ModelFactory.java @@ -10,6 +10,7 @@ import me.cortex.voxy.client.core.model.bakery.SoftwareModelTextureBakery; import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.common.Logger; +import me.cortex.voxy.common.util.LumiseneUtil; import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.Pair; import me.cortex.voxy.common.world.other.Mapper; @@ -188,7 +189,7 @@ public boolean addEntry(int blockId) { //Before we enqueue the baking of this blockstate, we must check if it has a fluid state associated with it // if it does, we must ensure that it is (effectivly) baked BEFORE we bake this blockstate - boolean isFluid = blockState.getBlock() instanceof LiquidBlock; + boolean isFluid = isFluidBlockState(blockState); if ((!isFluid) && (!blockState.getFluidState().isEmpty())) { //Insert into the fluid LUT var fluidState = blockState.getFluidState().createLegacyBlock(); @@ -359,7 +360,8 @@ private ModelBakeResultUpload processTextureBakeResult(int blockId, BlockState b //TODO: add thing for `blockState.hasEmissiveLighting()` and `blockState.getLuminance()` - boolean isFluid = blockState.getBlock() instanceof LiquidBlock; + boolean isFluid = isFluidBlockState(blockState); + boolean isLumisene = LumiseneUtil.isLumisene(blockState); int modelId = -1; @@ -377,7 +379,7 @@ private ModelBakeResultUpload processTextureBakeResult(int blockId, BlockState b } } - var colourProvider = getColourProvider(blockState.getBlock()); + var colourProvider = getColourProvider(blockState); boolean isBiomeColourDependent = false; if (colourProvider != null) { @@ -479,7 +481,7 @@ private ModelBakeResultUpload processTextureBakeResult(int blockId, BlockState b metadata |= layer == RenderType.translucent()?2:0; metadata |= needsDoubleSidedQuads?4:0; metadata |= ((!isFluid) && !blockState.getFluidState().isEmpty())?8:0;//Has a fluid state accosiacted with it and is not itself a fluid - metadata |= isFluid?16:0;//Is a fluid + metadata |= (isFluid && !isLumisene)?16:0;//Is a fluid metadata |= cullsSame?32:0; @@ -500,6 +502,13 @@ private ModelBakeResultUpload processTextureBakeResult(int blockId, BlockState b fullyOpaque = false; continue; } + if (face >= Direction.NORTH.get3DDataValue() && LumiseneUtil.isThinLumisene(blockState)) { + metadata |= 0xFF;//Mark the face as non-existent + MemoryUtil.memPutInt(faceUploadPtr, -1); + + fullyOpaque = false; + continue; + } var faceSize = TextureUtils.computeBounds(textureData[face], checkMode); int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode); @@ -707,7 +716,7 @@ private BiomeUploadResult addBiome0(int id, Biome biome) { int i = 0; long modelUpPtr = result.modelBiomeIndexPairs.address; for (var entry : this.modelsRequiringBiomeColours) { - var colourProvider = getColourProvider(entry.right().getBlock()); + var colourProvider = getColourProvider(entry.right()); if (colourProvider == null) { throw new IllegalStateException(); } @@ -726,10 +735,15 @@ private BiomeUploadResult addBiome0(int id, Biome biome) { return result; } - private static BlockColor getColourProvider(Block block) { + private static BlockColor getColourProvider(BlockState blockState) { + if (LumiseneUtil.isLumisene(blockState)) { + return null; + } + + Block block = blockState.getBlock(); BlockState defaultState = block.defaultBlockState(); var blockColors = Minecraft.getInstance().getBlockColors(); - if (block instanceof LiquidBlock) { + if (isFluidBlockState(blockState) || isFluidBlockState(defaultState)) { return (state, world, pos, tintIndex) -> blockColors.getColor(state, world, pos, tintIndex); } int color; @@ -744,6 +758,15 @@ private static BlockColor getColourProvider(Block block) { return null; } + public static boolean isFluidBlockState(BlockState state) { + if (state.getBlock() instanceof LiquidBlock) { + return true; + } + + FluidState fluidState = state.getFluidState(); + return !fluidState.isEmpty() && fluidState.createLegacyBlock().getBlock() == state.getBlock(); + } + //TODO: add a method to detect biome dependent colours (can do by detecting if getColor is ever called) // if it is, need to add it to a list and mark it as biome colour dependent or something then the shader // will either use the uint as an index or a direct colour multiplier diff --git a/src/main/java/me/cortex/voxy/client/core/model/bakery/SoftwareModelTextureBakery.java b/src/main/java/me/cortex/voxy/client/core/model/bakery/SoftwareModelTextureBakery.java index 0f5f7fc94..2a4c19bb4 100644 --- a/src/main/java/me/cortex/voxy/client/core/model/bakery/SoftwareModelTextureBakery.java +++ b/src/main/java/me/cortex/voxy/client/core/model/bakery/SoftwareModelTextureBakery.java @@ -6,7 +6,9 @@ import com.mojang.blaze3d.vertex.VertexConsumer; import me.cortex.voxy.client.core.model.ModelFactory; +import me.cortex.voxy.common.util.LumiseneUtil; import me.cortex.voxy.common.util.UnsafeUtil; +import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemBlockRenderTypes; import net.minecraft.client.renderer.RenderType; @@ -19,7 +21,6 @@ import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.LeavesBlock; -import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -143,7 +144,7 @@ public BlockEntity getBlockEntity(BlockPos pos) { @Override public BlockState getBlockState(BlockPos pos) { - if (shouldReturnAirForFluid(pos, face)) { + if (shouldReturnAirForFluid(pos, face) || shouldReturnAirAboveLumisene(state, pos)) { return Blocks.AIR.defaultBlockState(); } @@ -161,7 +162,7 @@ public BlockState getBlockState(BlockPos pos) { @Override public FluidState getFluidState(BlockPos pos) { - if (shouldReturnAirForFluid(pos, face)) { + if (shouldReturnAirForFluid(pos, face) || shouldReturnAirAboveLumisene(state, pos)) { return Blocks.AIR.defaultBlockState().getFluidState(); } @@ -193,7 +194,13 @@ public float getShade(Direction direction, boolean bl) { } else { this.opaqueVC.setDefaultMeta(this.opaqueVC.getDefaultMeta()&~1);//remove discard } - Minecraft.getInstance().getBlockRenderer().renderLiquid(BlockPos.ZERO, getter, vc, state, state.getFluidState()); + FluidState fluidState = state.getFluidState(); + var handler = FluidRenderHandlerRegistry.INSTANCE.get(fluidState.getType()); + if (handler != null) { + handler.renderFluid(BlockPos.ZERO, getter, vc, state, fluidState); + } else { + Minecraft.getInstance().getBlockRenderer().renderLiquid(BlockPos.ZERO, getter, vc, state, fluidState); + } this.translucentVC.setDefaultMeta(0);//Reset default meta this.opaqueVC.setDefaultMeta(0);//Reset default meta } @@ -204,6 +211,10 @@ private static boolean shouldReturnAirForFluid(BlockPos pos, int face) { return dot >= 1; } + private static boolean shouldReturnAirAboveLumisene(BlockState state, BlockPos pos) { + return pos.getY() > 0 && LumiseneUtil.isLumisene(state); + } + public void free() { this.opaqueVC.free(); this.translucentVC.free(); @@ -219,13 +230,10 @@ public void free() { public int renderToOutput(BlockState state, long outputBuffer) { MemoryUtil.memSet(outputBuffer, 0, 16 * 16 * 8 * 6); - boolean isBlock = true; - if (state.getBlock() instanceof LiquidBlock) { - isBlock = false; - } + boolean isBlock = !ModelFactory.isFluidBlockState(state); RenderType blockRenderLayer = null; - if (state.getBlock() instanceof LiquidBlock) { + if (!isBlock) { blockRenderLayer = ItemBlockRenderTypes.getRenderLayer(state.getFluidState()); } else { if (state.getBlock() instanceof LeavesBlock) { @@ -268,8 +276,6 @@ public int renderToOutput(BlockState state, long outputBuffer) { } } else {// Is fluid, slow path :( - if (!(state.getBlock() instanceof LiquidBlock)) - throw new IllegalStateException(); for (int i = 0; i < VIEWS.length; i++) { this.opaqueVC.reset(); this.translucentVC.reset(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java index 7bcc57815..79a4e7f73 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/building/RenderDataFactory.java @@ -5,6 +5,7 @@ import me.cortex.voxy.client.core.model.ModelQueries; import me.cortex.voxy.client.core.util.ScanMesher2D; import me.cortex.voxy.common.Logger; +import me.cortex.voxy.common.util.LumiseneUtil; import me.cortex.voxy.common.util.MemoryBuffer; import me.cortex.voxy.common.util.UnsafeUtil; import me.cortex.voxy.common.world.WorldEngine; @@ -241,6 +242,9 @@ private int prepareSectionData(final long[] rawSectionData) { //TODO: cache the results of this, then link it to `block` do same optimization as SaveLoadSystem3 long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId); + if (LumiseneUtil.isLumisene(this.world.getMapper(), block)) { + block = Mapper.withLight(block, 0xFF); + } sectionData[i * 2] = packPartialQuadData(modelId, block, modelMetadata); sectionData[i * 2 + 1] = modelMetadata; @@ -1718,4 +1722,4 @@ public BuiltSection generateMesh(WorldSection section) { public void free() { this.quadBuffer.free(); } -} \ No newline at end of file +} diff --git a/src/main/java/me/cortex/voxy/common/util/LumiseneUtil.java b/src/main/java/me/cortex/voxy/common/util/LumiseneUtil.java new file mode 100644 index 000000000..1b87ea44e --- /dev/null +++ b/src/main/java/me/cortex/voxy/common/util/LumiseneUtil.java @@ -0,0 +1,54 @@ +package me.cortex.voxy.common.util; + +import me.cortex.voxy.common.world.other.Mapper; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public final class LumiseneUtil { + private static final float MIN_VISIBLE_WALL_HEIGHT = 1.0f / 16.0f; + private static final Map STATE_CACHE = new ConcurrentHashMap<>(); + + private LumiseneUtil() { + } + + public static boolean isLumisene(Mapper mapper, long mappingId) { + return !Mapper.isAir(mappingId) && isLumisene(mapper.getBlockStateFromBlockId(Mapper.getBlockId(mappingId))); + } + + public static boolean isLumisene(BlockState state) { + return STATE_CACHE.computeIfAbsent(state, LumiseneUtil::computeIsLumisene); + } + + private static boolean computeIsLumisene(BlockState state) { + ResourceLocation blockId = BuiltInRegistries.BLOCK.getKey(state.getBlock()); + if (isLumiseneId(blockId)) { + return true; + } + + FluidState fluidState = state.getFluidState(); + return !fluidState.isEmpty() && isLumiseneId(BuiltInRegistries.FLUID.getKey(fluidState.getType())); + } + + public static boolean isThinLumisene(BlockState state) { + if (!isLumisene(state)) { + return false; + } + + FluidState fluidState = state.getFluidState(); + if (fluidState.isEmpty()) { + return false; + } + + float height = fluidState.getOwnHeight(); + return height > 0.0f && height < MIN_VISIBLE_WALL_HEIGHT; + } + + private static boolean isLumiseneId(ResourceLocation id) { + return id != null && "supplementaries".equals(id.getNamespace()) && id.getPath().contains("lumisene"); + } +} diff --git a/src/main/java/me/cortex/voxy/common/world/other/Mipper.java b/src/main/java/me/cortex/voxy/common/world/other/Mipper.java index f50f383df..7c4338f67 100644 --- a/src/main/java/me/cortex/voxy/common/world/other/Mipper.java +++ b/src/main/java/me/cortex/voxy/common/world/other/Mipper.java @@ -1,5 +1,7 @@ package me.cortex.voxy.common.world.other; +import me.cortex.voxy.common.util.LumiseneUtil; + import static me.cortex.voxy.common.world.other.Mapper.withLight; //Mipper for data @@ -57,8 +59,20 @@ public static long mip(long I000, long I100, long I001, long I101, max = Math.max((mapper.getBlockStateOpacity(I000)<<4), max); } + long lumisene = firstLumisene(mapper, I111); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I110); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I011); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I010); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I101); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I100); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I001); + if (Mapper.isAir(lumisene)) lumisene = firstLumisene(mapper, I000); + if (!Mapper.isAir(lumisene)) { + return withLight(lumisene, 0xFF); + } + if (max != -1) { - return switch (max&0b111) { + long result = switch (max&0b111) { case 0 -> I000; case 1 -> I001; case 2 -> I010; @@ -69,6 +83,7 @@ public static long mip(long I000, long I100, long I001, long I101, case 7 -> I111; default -> throw new IllegalStateException("Unexpected value: " + (max&0b111)); }; + return LumiseneUtil.isLumisene(mapper, result) ? withLight(result, 0xFF) : result; } else { int blockLight = (Mapper.getLightId(I000) & 0xF0) + (Mapper.getLightId(I001) & 0xF0) + (Mapper.getLightId(I010) & 0xF0) + (Mapper.getLightId(I011) & 0xF0) + (Mapper.getLightId(I100) & 0xF0) + (Mapper.getLightId(I101) & 0xF0) + (Mapper.getLightId(I110) & 0xF0) + (Mapper.getLightId(I111) & 0xF0); @@ -80,4 +95,8 @@ public static long mip(long I000, long I100, long I001, long I101, return withLight(I111, (blockLight << 4) | skyLight); } } + + private static long firstLumisene(Mapper mapper, long id) { + return LumiseneUtil.isLumisene(mapper, id) ? id : 0; + } } diff --git a/src/main/resources/assets/voxy/shaders/lod/quad_util.glsl b/src/main/resources/assets/voxy/shaders/lod/quad_util.glsl index 81fb8ca01..daefa0d97 100644 --- a/src/main/resources/assets/voxy/shaders/lod/quad_util.glsl +++ b/src/main/resources/assets/voxy/shaders/lod/quad_util.glsl @@ -95,7 +95,9 @@ uvec3 makeRemainingAttributes(const in BlockModel model, const in Quad quad, uin } uint addin = 0; - if (!isTranslucent) { + if (isTranslucent) { + tinting.w = 1.0; + } else { tinting.w = 0.0; //Encode the face, the lod level and uint encodedData = 0; @@ -186,4 +188,4 @@ float computeDirectionalFaceTint(bool isShaded, uint face) { return NO_SHADE_FACE_TINT; } } -#endif \ No newline at end of file +#endif