diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/ChunkTask.java b/common/src/main/java/net/vulkanmod/render/chunk/build/ChunkTask.java new file mode 100644 index 000000000..bc963c2b2 --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/ChunkTask.java @@ -0,0 +1,325 @@ +package net.vulkanmod.render.chunk.build; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.VertexFormat; +import it.unimi.dsi.fastutil.objects.ReferenceArraySet; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.ItemBlockRenderTypes; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.block.BlockRenderDispatcher; +import net.minecraft.client.renderer.block.ModelBlockRenderer; +import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; +import net.minecraft.client.renderer.chunk.RenderChunkRegion; +import net.minecraft.client.renderer.chunk.VisGraph; +import net.minecraft.client.renderer.chunk.VisibilitySet; +import net.minecraft.core.BlockPos; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.Vec3; +import net.vulkanmod.Initializer; +import net.vulkanmod.interfaces.VisibilitySetExtended; +import net.vulkanmod.render.chunk.RenderSection; +import net.vulkanmod.render.chunk.WorldRenderer; +import net.vulkanmod.render.vertex.TerrainBufferBuilder; +import net.vulkanmod.render.vertex.TerrainRenderType; +import net.vulkanmod.vulkan.shader.ShaderManager; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ChunkTask { + private static TaskDispatcher taskDispatcher; + + protected AtomicBoolean cancelled = new AtomicBoolean(false); + protected final RenderSection renderSection; + public boolean highPriority = false; + + ChunkTask(RenderSection renderSection) { + this.renderSection = renderSection; + } + + public String name() { + return "generic_chk_task"; + } + + public CompletableFuture doTask(ThreadBuilderPack builderPack) { + return null; + } + + public void cancel() { + this.cancelled.set(true); + } + + public static void setTaskDispatcher(TaskDispatcher dispatcher) { + taskDispatcher = dispatcher; + } + + public static class BuildTask extends ChunkTask { + @Nullable + protected RenderChunkRegion region; + + //debug + private float buildTime; + private boolean submitted = false; + + public BuildTask(RenderSection renderSection, RenderChunkRegion renderChunkRegion, boolean highPriority) { + super(renderSection); + this.region = renderChunkRegion; + this.highPriority = highPriority; + } + + public String name() { + return "rend_chk_rebuild"; + } + + public CompletableFuture doTask(ThreadBuilderPack chunkBufferBuilderPack) { + //debug + this.submitted = true; + long startTime = System.nanoTime(); + + if (this.cancelled.get()) { + return CompletableFuture.completedFuture(Result.CANCELLED); + } else if (!this.renderSection.hasXYNeighbours()) { + this.region = null; + this.renderSection.setDirty(false); + this.cancelled.set(true); + return CompletableFuture.completedFuture(Result.CANCELLED); + } else if (this.cancelled.get()) { + return CompletableFuture.completedFuture(Result.CANCELLED); + } else { + Vec3 vec3 = WorldRenderer.getCameraPos(); + float f = (float)vec3.x; + float g = (float)vec3.y; + float h = (float)vec3.z; + CompileResults compileResults = this.compile(f, g, h, chunkBufferBuilderPack); + + this.renderSection.updateGlobalBlockEntities(compileResults.globalBlockEntities); + + if (this.cancelled.get()) { + compileResults.renderedLayers.values().forEach(UploadBuffer::release); + return CompletableFuture.completedFuture(Result.CANCELLED); + } else { + CompiledSection compiledChunk = new CompiledSection(); + compiledChunk.visibilitySet = compileResults.visibilitySet; + compiledChunk.renderableBlockEntities.addAll(compileResults.blockEntities); + compiledChunk.transparencyState = compileResults.transparencyState; + + if(!compileResults.renderedLayers.isEmpty()) + compiledChunk.isCompletelyEmpty = false; + + taskDispatcher.scheduleSectionUpdate(renderSection, compileResults.renderedLayers); + compiledChunk.renderTypes.addAll(compileResults.renderedLayers.keySet()); + + this.renderSection.setCompiledSection(compiledChunk); + this.renderSection.setVisibility(((VisibilitySetExtended)compiledChunk.visibilitySet).getVisibility()); + this.renderSection.setCompletelyEmpty(compiledChunk.isCompletelyEmpty); + + this.buildTime = (System.nanoTime() - startTime) * 0.000001f; + return CompletableFuture.completedFuture(Result.SUCCESSFUL); + + } + } + } + + private CompileResults compile(float camX, float camY, float camZ, ThreadBuilderPack chunkBufferBuilderPack) { + CompileResults compileResults = new CompileResults(); + + BlockPos blockPos = new BlockPos(renderSection.xOffset(), renderSection.yOffset(), renderSection.zOffset()).immutable(); + + BlockPos blockPos2 = blockPos.offset(15, 15, 15); + VisGraph visGraph = new VisGraph(); + RenderChunkRegion renderChunkRegion = this.region; + this.region = null; + PoseStack poseStack = new PoseStack(); + if (renderChunkRegion != null) { + ModelBlockRenderer.enableCaching(); + Set set = new ReferenceArraySet<>(RenderType.chunkBufferLayers().size()); + RandomSource randomSource = RandomSource.create(); + BlockRenderDispatcher blockRenderDispatcher = Minecraft.getInstance().getBlockRenderer(); + + for(BlockPos blockPos3 : BlockPos.betweenClosed(blockPos, blockPos2)) { + BlockState blockState = renderChunkRegion.getBlockState(blockPos3); + if (blockState.isSolidRender(renderChunkRegion, blockPos3)) { + visGraph.setOpaque(blockPos3); + } + + if (blockState.hasBlockEntity()) { + BlockEntity blockEntity = renderChunkRegion.getBlockEntity(blockPos3); + if (blockEntity != null) { + this.handleBlockEntity(compileResults, blockEntity); + } + } + + BlockState blockState2 = renderChunkRegion.getBlockState(blockPos3); + FluidState fluidState = blockState2.getFluidState(); + RenderType renderType; + TerrainBufferBuilder bufferBuilder; + if (!fluidState.isEmpty()) { + renderType = ItemBlockRenderTypes.getRenderLayer(fluidState); + + //Force compact RenderType + renderType = compactRenderTypes(renderType); + + bufferBuilder = chunkBufferBuilderPack.builder(renderType); + if (set.add(renderType)) { + bufferBuilder.begin(VertexFormat.Mode.QUADS, ShaderManager.TERRAIN_VERTEX_FORMAT); + } + + blockRenderDispatcher.renderLiquid(blockPos3, renderChunkRegion, bufferBuilder, blockState2, fluidState); + } + + if (blockState.getRenderShape() != RenderShape.INVISIBLE) { + renderType = ItemBlockRenderTypes.getChunkRenderType(blockState); + + //Force compact RenderType + renderType = compactRenderTypes(renderType); + + bufferBuilder = chunkBufferBuilderPack.builder(renderType); + if (set.add(renderType)) { + bufferBuilder.begin(VertexFormat.Mode.QUADS, ShaderManager.TERRAIN_VERTEX_FORMAT); + } + + poseStack.pushPose(); + poseStack.translate(blockPos3.getX() & 15, blockPos3.getY() & 15, blockPos3.getZ() & 15); + blockRenderDispatcher.renderBatched(blockState, blockPos3, renderChunkRegion, poseStack, bufferBuilder, true, randomSource); + poseStack.popPose(); + } + } + + if (set.contains(RenderType.translucent())) { + TerrainBufferBuilder bufferBuilder2 = chunkBufferBuilderPack.builder(RenderType.translucent()); + if (!bufferBuilder2.isCurrentBatchEmpty()) { + bufferBuilder2.setQuadSortOrigin(camX - (float)blockPos.getX(), camY - (float)blockPos.getY(), camZ - (float)blockPos.getZ()); + compileResults.transparencyState = bufferBuilder2.getSortState(); + } + } + + for(RenderType renderType2 : set) { + TerrainBufferBuilder.RenderedBuffer renderedBuffer = chunkBufferBuilderPack.builder(renderType2).endOrDiscardIfEmpty(); + if (renderedBuffer != null) { + UploadBuffer uploadBuffer = new UploadBuffer(renderedBuffer); + compileResults.renderedLayers.put(TerrainRenderType.get(renderType2), uploadBuffer); + } + + if(renderedBuffer != null) + renderedBuffer.release(); + } + + ModelBlockRenderer.clearCache(); + } + + compileResults.visibilitySet = visGraph.resolve(); + return compileResults; + } + + private RenderType compactRenderTypes(RenderType renderType) { + + if(Initializer.CONFIG.uniqueOpaqueLayer) { + if (renderType != RenderType.translucent()) { + if(renderType != RenderType.tripwire()) { + renderType = RenderType.cutoutMipped(); + } + else + renderType = RenderType.translucent(); + } + } + else { + if (renderType != RenderType.translucent() && renderType != RenderType.cutoutMipped()) { + if(renderType != RenderType.tripwire()) { + renderType = RenderType.cutout(); + } + else + renderType = RenderType.translucent(); + } + } + + return renderType; + } + + private void handleBlockEntity(CompileResults compileResults, E blockEntity) { + BlockEntityRenderer blockEntityRenderer = Minecraft.getInstance().getBlockEntityRenderDispatcher().getRenderer(blockEntity); + if (blockEntityRenderer != null) { + compileResults.blockEntities.add(blockEntity); + if (blockEntityRenderer.shouldRenderOffScreen(blockEntity)) { + compileResults.globalBlockEntities.add(blockEntity); + } + } + + } + + private static final class CompileResults { + public final List globalBlockEntities = new ArrayList<>(); + public final List blockEntities = new ArrayList<>(); + public final EnumMap renderedLayers = new EnumMap<>(TerrainRenderType.class); + public VisibilitySet visibilitySet = new VisibilitySet(); + @org.jetbrains.annotations.Nullable + public TerrainBufferBuilder.SortState transparencyState; + + CompileResults() { + } + } + } + + public static class SortTransparencyTask extends ChunkTask { + CompiledSection compiledSection; + + public SortTransparencyTask(RenderSection renderSection) { + super(renderSection); + this.compiledSection = renderSection.getCompiledSection(); + } + + public String name() { + return "rend_chk_sort"; + } + + public CompletableFuture doTask(ThreadBuilderPack builderPack) { + if (this.cancelled.get()) { + return CompletableFuture.completedFuture(Result.CANCELLED); + } else if (!renderSection.hasXYNeighbours()) { + this.cancelled.set(true); + return CompletableFuture.completedFuture(Result.CANCELLED); + } else { + Vec3 vec3 = WorldRenderer.getCameraPos(); + float f = (float)vec3.x; + float f1 = (float)vec3.y; + float f2 = (float)vec3.z; + TerrainBufferBuilder.SortState transparencyState = this.compiledSection.transparencyState; + if (transparencyState != null && this.compiledSection.renderTypes.contains(TerrainRenderType.TRANSLUCENT)) { + TerrainBufferBuilder bufferbuilder = builderPack.builder(RenderType.translucent()); + bufferbuilder.begin(VertexFormat.Mode.QUADS, ShaderManager.TERRAIN_VERTEX_FORMAT); + bufferbuilder.restoreSortState(transparencyState); +// bufferbuilder.setQuadSortOrigin(f - (float) this.renderSection.origin.getX(), f1 - (float) renderSection.origin.getY(), f2 - (float) renderSection.origin.getZ()); + bufferbuilder.setQuadSortOrigin(f - (float) this.renderSection.xOffset(), f1 - (float) renderSection.yOffset(), f2 - (float) renderSection.zOffset()); + this.compiledSection.transparencyState = bufferbuilder.getSortState(); + TerrainBufferBuilder.RenderedBuffer renderedBuffer = bufferbuilder.end(); + if (this.cancelled.get()) { + return CompletableFuture.completedFuture(Result.CANCELLED); + } else { + + UploadBuffer uploadBuffer = new UploadBuffer(renderedBuffer); + taskDispatcher.scheduleUploadChunkLayer(renderSection, TerrainRenderType.get(RenderType.translucent()), uploadBuffer); + renderedBuffer.release(); + return CompletableFuture.completedFuture(Result.SUCCESSFUL); + + } + } else { + return CompletableFuture.completedFuture(Result.CANCELLED); + } + } + } + } + + public enum Result { + CANCELLED, + SUCCESSFUL; + + } +} diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/CompiledSection.java b/common/src/main/java/net/vulkanmod/render/chunk/build/CompiledSection.java new file mode 100644 index 000000000..a8784ba86 --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/CompiledSection.java @@ -0,0 +1,45 @@ +package net.vulkanmod.render.chunk.build; + +import com.google.common.collect.Lists; +import it.unimi.dsi.fastutil.objects.ObjectArraySet; +import net.minecraft.client.renderer.chunk.VisibilitySet; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.vulkanmod.render.vertex.TerrainBufferBuilder; +import net.vulkanmod.render.vertex.TerrainRenderType; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Set; + +public class CompiledSection { + public static final CompiledSection UNCOMPILED = new CompiledSection() { + public boolean canSeeThrough(Direction dir1, Direction dir2) { + return false; + } + }; + public final Set renderTypes = new ObjectArraySet<>(); + boolean isCompletelyEmpty = true; + final List renderableBlockEntities = Lists.newArrayList(); + VisibilitySet visibilitySet = new VisibilitySet(); + @Nullable + TerrainBufferBuilder.SortState transparencyState; + + public boolean hasNoRenderableLayers() { + return this.isCompletelyEmpty; + } + + public boolean isEmpty(TerrainRenderType p_112759_) { + return !this.renderTypes.contains(p_112759_); + } + + public List getRenderableBlockEntities() { + return this.renderableBlockEntities; + } + + public boolean canSeeThrough(Direction dir1, Direction dir2) { + return this.visibilitySet.visibilityBetween(dir1, dir2); + } +} + + diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/LiquidRenderer.java b/common/src/main/java/net/vulkanmod/render/chunk/build/LiquidRenderer.java new file mode 100644 index 000000000..255a2214f --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/LiquidRenderer.java @@ -0,0 +1,418 @@ +package net.vulkanmod.render.chunk.build; + +import com.mojang.blaze3d.vertex.VertexConsumer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BiomeColors; +import net.minecraft.client.renderer.LevelRenderer; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.resources.model.ModelBakery; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.tags.FluidTags; +import net.minecraft.util.Mth; +import net.minecraft.world.level.BlockAndTintGetter; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.HalfTransparentBlock; +import net.minecraft.world.level.block.LeavesBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.joml.Vector3f; + +import java.util.Iterator; + +@Environment(EnvType.CLIENT) +public class LiquidRenderer { + private static final float MAX_FLUID_HEIGHT = 0.8888889F; + private final TextureAtlasSprite[] lavaIcons = new TextureAtlasSprite[2]; + private final TextureAtlasSprite[] waterIcons = new TextureAtlasSprite[2]; + private TextureAtlasSprite waterOverlay; + + public void setupSprites() { + this.lavaIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.LAVA.defaultBlockState()).getParticleIcon(); + this.lavaIcons[1] = ModelBakery.LAVA_FLOW.sprite(); + this.waterIcons[0] = Minecraft.getInstance().getModelManager().getBlockModelShaper().getBlockModel(Blocks.WATER.defaultBlockState()).getParticleIcon(); + this.waterIcons[1] = ModelBakery.WATER_FLOW.sprite(); + this.waterOverlay = ModelBakery.WATER_OVERLAY.sprite(); + } + + private static boolean isNeighborSameFluid(FluidState fluidState, FluidState fluidState2) { + return fluidState2.getType().isSame(fluidState.getType()); + } + + private static boolean isFaceOccludedByState(BlockGetter blockGetter, Direction direction, float f, BlockPos blockPos, BlockState blockState) { + if (blockState.canOcclude()) { + VoxelShape voxelShape = Shapes.box(0.0, 0.0, 0.0, 1.0, f, 1.0); + VoxelShape voxelShape2 = blockState.getOcclusionShape(blockGetter, blockPos); + return Shapes.blockOccudes(voxelShape, voxelShape2, direction); + } else { + return false; + } + } + + private static boolean isFaceOccludedByNeighbor(BlockGetter blockGetter, BlockPos blockPos, Direction direction, float f, BlockState blockState) { + return isFaceOccludedByState(blockGetter, direction, f, blockPos.relative(direction), blockState); + } + + private static boolean isFaceOccludedBySelf(BlockGetter blockGetter, BlockPos blockPos, BlockState blockState, Direction direction) { + return isFaceOccludedByState(blockGetter, direction.getOpposite(), 1.0F, blockPos, blockState); + } + + public static boolean shouldRenderFace(BlockAndTintGetter blockAndTintGetter, BlockPos blockPos, FluidState fluidState, BlockState blockState, Direction direction, FluidState fluidState2) { + return !isFaceOccludedBySelf(blockAndTintGetter, blockPos, blockState, direction) && !isNeighborSameFluid(fluidState, fluidState2); + } + + public void tessellate(BlockAndTintGetter blockAndTintGetter, BlockPos blockPos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState) { + boolean bl = fluidState.is(FluidTags.LAVA); + TextureAtlasSprite[] textureAtlasSprites = bl ? this.lavaIcons : this.waterIcons; + int i = bl ? 16777215 : BiomeColors.getAverageWaterColor(blockAndTintGetter, blockPos); + float f = (float)(i >> 16 & 255) / 255.0F; + float g = (float)(i >> 8 & 255) / 255.0F; + float h = (float)(i & 255) / 255.0F; + + BlockState blockState2 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.DOWN)); + FluidState fluidState2 = blockState2.getFluidState(); + BlockState blockState3 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.UP)); + FluidState fluidState3 = blockState3.getFluidState(); + BlockState blockState4 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.NORTH)); + FluidState fluidState4 = blockState4.getFluidState(); + BlockState blockState5 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.SOUTH)); + FluidState fluidState5 = blockState5.getFluidState(); + BlockState blockState6 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.WEST)); + FluidState fluidState6 = blockState6.getFluidState(); + BlockState blockState7 = blockAndTintGetter.getBlockState(blockPos.relative(Direction.EAST)); + FluidState fluidState7 = blockState7.getFluidState(); + + boolean bl2 = !isNeighborSameFluid(fluidState, fluidState3); + boolean renderDownFace = shouldRenderFace(blockAndTintGetter, blockPos, fluidState, blockState, Direction.DOWN, fluidState2) && !isFaceOccludedByNeighbor(blockAndTintGetter, blockPos, Direction.DOWN, 0.8888889F, blockState2); + boolean bl4 = shouldRenderFace(blockAndTintGetter, blockPos, fluidState, blockState, Direction.NORTH, fluidState4); + boolean bl5 = shouldRenderFace(blockAndTintGetter, blockPos, fluidState, blockState, Direction.SOUTH, fluidState5); + boolean bl6 = shouldRenderFace(blockAndTintGetter, blockPos, fluidState, blockState, Direction.WEST, fluidState6); + boolean bl7 = shouldRenderFace(blockAndTintGetter, blockPos, fluidState, blockState, Direction.EAST, fluidState7); + + if (bl2 || renderDownFace || bl7 || bl6 || bl4 || bl5) + { + float j = blockAndTintGetter.getShade(Direction.DOWN, true); + float k = blockAndTintGetter.getShade(Direction.UP, true); + float l = blockAndTintGetter.getShade(Direction.NORTH, true); + float m = blockAndTintGetter.getShade(Direction.WEST, true); + Fluid fluid = fluidState.getType(); + float n = this.getHeight(blockAndTintGetter, fluid, blockPos, blockState, fluidState); + float o; + float p; + float q; + float r; + if (n >= 1.0F) { + o = 1.0F; + p = 1.0F; + q = 1.0F; + r = 1.0F; + } else { + float s = this.getHeight(blockAndTintGetter, fluid, blockPos.north(), blockState4, fluidState4); + float t = this.getHeight(blockAndTintGetter, fluid, blockPos.south(), blockState5, fluidState5); + float u = this.getHeight(blockAndTintGetter, fluid, blockPos.east(), blockState7, fluidState7); + float v = this.getHeight(blockAndTintGetter, fluid, blockPos.west(), blockState6, fluidState6); + o = this.calculateAverageHeight(blockAndTintGetter, fluid, n, s, u, blockPos.relative(Direction.NORTH).relative(Direction.EAST)); + p = this.calculateAverageHeight(blockAndTintGetter, fluid, n, s, v, blockPos.relative(Direction.NORTH).relative(Direction.WEST)); + q = this.calculateAverageHeight(blockAndTintGetter, fluid, n, t, u, blockPos.relative(Direction.SOUTH).relative(Direction.EAST)); + r = this.calculateAverageHeight(blockAndTintGetter, fluid, n, t, v, blockPos.relative(Direction.SOUTH).relative(Direction.WEST)); + } + + double x0 = (blockPos.getX() & 15); + double y0 = (blockPos.getY() & 15); + double z0 = (blockPos.getZ() & 15); + float x = 0.001F; + float y = renderDownFace ? 0.001F : 0.0F; + float z; + float ab; + float ad; + float af; + float aa; + float ac; + float ae; + float ag; + + if (bl2 && !isFaceOccludedByNeighbor(blockAndTintGetter, blockPos, Direction.UP, Math.min(Math.min(p, r), Math.min(q, o)), blockState3)) { + p -= 0.001F; + r -= 0.001F; + q -= 0.001F; + o -= 0.001F; + Vec3 vec3 = fluidState.getFlow(blockAndTintGetter, blockPos); + TextureAtlasSprite textureAtlasSprite; + float ah; + float ai; + float r1; + if (vec3.x == 0.0 && vec3.z == 0.0) { + textureAtlasSprite = textureAtlasSprites[0]; + z = textureAtlasSprite.getU(0.0); + aa = textureAtlasSprite.getV(0.0); + ab = z; + ac = textureAtlasSprite.getV(16.0); + ad = textureAtlasSprite.getU(16.0); + ae = ac; + af = ad; + ag = aa; + } else { + textureAtlasSprite = textureAtlasSprites[1]; + ah = (float)Mth.atan2(vec3.z, vec3.x) - 1.5707964F; + ai = Mth.sin(ah) * 0.25F; + float aj = Mth.cos(ah) * 0.25F; + r1 = 8.0F; + z = textureAtlasSprite.getU((8.0F + (-aj - ai) * 16.0F)); + aa = textureAtlasSprite.getV((8.0F + (-aj + ai) * 16.0F)); + ab = textureAtlasSprite.getU((8.0F + (-aj + ai) * 16.0F)); + ac = textureAtlasSprite.getV((8.0F + (aj + ai) * 16.0F)); + ad = textureAtlasSprite.getU((8.0F + (aj + ai) * 16.0F)); + ae = textureAtlasSprite.getV((8.0F + (aj - ai) * 16.0F)); + af = textureAtlasSprite.getU((8.0F + (aj - ai) * 16.0F)); + ag = textureAtlasSprite.getV((8.0F + (-aj - ai) * 16.0F)); + } + + float al = (z + ab + ad + af) / 4.0F; + ah = (aa + ac + ae + ag) / 4.0F; + ai = textureAtlasSprites[0].uvShrinkRatio(); + z = Mth.lerp(ai, z, al); + ab = Mth.lerp(ai, ab, al); + ad = Mth.lerp(ai, ad, al); + af = Mth.lerp(ai, af, al); + aa = Mth.lerp(ai, aa, ah); + ac = Mth.lerp(ai, ac, ah); + ae = Mth.lerp(ai, ae, ah); + ag = Mth.lerp(ai, ag, ah); + int am = this.getLightColor(blockAndTintGetter, blockPos); + r1 = k * f; + float g1 = k * g; + float b1 = k * h; + +// this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)p, z0 + 0.0, r1, g1, b1, z, aa, am); +// this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)r, z0 + 1.0, r1, g1, b1, ab, ac, am); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)q, z0 + 1.0, r1, g1, b1, ad, ae, am); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)o, z0 + 0.0, r1, g1, b1, af, ag, am); +// if (fluidState.shouldRenderBackwardUpFace(blockAndTintGetter, blockPos.above())) { +// this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)p, z0 + 0.0, r1, g1, b1, z, aa, am); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)o, z0 + 0.0, r1, g1, b1, af, ag, am); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)q, z0 + 1.0, r1, g1, b1, ad, ae, am); +// this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)r, z0 + 1.0, r1, g1, b1, ab, ac, am); +// } + + Vector3f normal = new Vector3f(0.0f, r - p, 1.0f).cross(1.0f, q - p, 1.0f); + normal.normalize(); + + this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)p, z0 + 0.0, r1, g1, b1, z, aa, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)r, z0 + 1.0, r1, g1, b1, ab, ac, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)q, z0 + 1.0, r1, g1, b1, ad, ae, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)o, z0 + 0.0, r1, g1, b1, af, ag, am, normal.x(), normal.y(), normal.z()); + if (fluidState.shouldRenderBackwardUpFace(blockAndTintGetter, blockPos.above())) { + this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)p, z0 + 0.0, r1, g1, b1, z, aa, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)o, z0 + 0.0, r1, g1, b1, af, ag, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)q, z0 + 1.0, r1, g1, b1, ad, ae, am, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 0.0, y0 + (double)r, z0 + 1.0, r1, g1, b1, ab, ac, am, normal.x(), normal.y(), normal.z()); + } + } + + if (renderDownFace) { + z = textureAtlasSprites[0].getU0(); + ab = textureAtlasSprites[0].getU1(); + ad = textureAtlasSprites[0].getV0(); + af = textureAtlasSprites[0].getV1(); + int ap = this.getLightColor(blockAndTintGetter, blockPos.below()); + ac = j * f; + ae = j * g; + ag = j * h; + +// this.vertex(vertexConsumer, x0, y0 + (double)y, z0 + 1.0, ac, ae, ag, z, af, ap); +// this.vertex(vertexConsumer, x0, y0 + (double)y, z0, ac, ae, ag, z, ad, ap); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)y, z0, ac, ae, ag, ab, ad, ap); +// this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)y, z0 + 1.0, ac, ae, ag, ab, af, ap); + + Vector3f normal = new Vector3f(0.0f, 0.0f, -1.0f).cross(1.0f, 0.0f, -1.0f); + normal.normalize(); + + this.vertex(vertexConsumer, x0, y0 + (double)y, z0 + 1.0, ac, ae, ag, z, af, ap, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0, y0 + (double)y, z0, ac, ae, ag, z, ad, ap, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)y, z0, ac, ae, ag, ab, ad, ap, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, x0 + 1.0, y0 + (double)y, z0 + 1.0, ac, ae, ag, ab, af, ap,normal.x(), normal.y(), normal.z()); + } + + int aq = this.getLightColor(blockAndTintGetter, blockPos); + Iterator var76 = Direction.Plane.HORIZONTAL.iterator(); + + while(true) { + Direction direction; + double ar; + double at; + double as; + double au; + boolean bl8; + do { + do { + if (!var76.hasNext()) { + return; + } + + direction = var76.next(); + switch (direction) { + case NORTH -> { + af = p; + aa = o; + ar = x0; + as = x0 + 1.0; + at = z0 + 0.0010000000474974513; + au = z0 + 0.0010000000474974513; + bl8 = bl4; + } + case SOUTH -> { + af = q; + aa = r; + ar = x0 + 1.0; + as = x0; + at = z0 + 1.0 - 0.0010000000474974513; + au = z0 + 1.0 - 0.0010000000474974513; + bl8 = bl5; + } + case WEST -> { + af = r; + aa = p; + ar = x0 + 0.0010000000474974513; + as = x0 + 0.0010000000474974513; + at = z0 + 1.0; + au = z0; + bl8 = bl6; + } + default -> { + af = o; + aa = q; + ar = x0 + 1.0 - 0.0010000000474974513; + as = x0 + 1.0 - 0.0010000000474974513; + at = z0; + au = z0 + 1.0; + bl8 = bl7; + } + } + } while(!bl8); + } while(isFaceOccludedByNeighbor(blockAndTintGetter, blockPos, direction, Math.max(af, aa), blockAndTintGetter.getBlockState(blockPos.relative(direction)))); + + BlockPos blockPos2 = blockPos.relative(direction); + TextureAtlasSprite textureAtlasSprite2 = textureAtlasSprites[1]; + if (!bl) { + Block block = blockAndTintGetter.getBlockState(blockPos2).getBlock(); + if (block instanceof HalfTransparentBlock || block instanceof LeavesBlock) { + textureAtlasSprite2 = this.waterOverlay; + } + } + + float av = textureAtlasSprite2.getU(0.0); + float aw = textureAtlasSprite2.getU(8.0); + float ax = textureAtlasSprite2.getV((double)((1.0F - af) * 16.0F * 0.5F)); + float ay = textureAtlasSprite2.getV((double)((1.0F - aa) * 16.0F * 0.5F)); + float az = textureAtlasSprite2.getV(8.0); + float ba = direction.getAxis() == Direction.Axis.Z ? l : m; + float bb = k * ba * f; + float bc = k * ba * g; + float bd = k * ba * h; + +// this.vertex(vertexConsumer, ar, y0 + (double)af, at, bb, bc, bd, av, ax, aq); +// this.vertex(vertexConsumer, as, y0 + (double)aa, au, bb, bc, bd, aw, ay, aq); +// this.vertex(vertexConsumer, as, y0 + (double)y, au, bb, bc, bd, aw, az, aq); +// this.vertex(vertexConsumer, ar, y0 + (double)y, at, bb, bc, bd, av, az, aq); +// if (textureAtlasSprite2 != this.waterOverlay) { +// this.vertex(vertexConsumer, ar, y0 + (double)y, at, bb, bc, bd, av, az, aq); +// this.vertex(vertexConsumer, as, y0 + (double)y, au, bb, bc, bd, aw, az, aq); +// this.vertex(vertexConsumer, as, y0 + (double)aa, au, bb, bc, bd, aw, ay, aq); +// this.vertex(vertexConsumer, ar, y0 + (double)af, at, bb, bc, bd, av, ax, aq); +// } + + Vector3f normal = new Vector3f((float) (as - ar), (aa - af), (float) (au - at)).cross((float) (as - ar), (y - af), (float) (au - at)); + normal.normalize(); + + this.vertex(vertexConsumer, ar, y0 + (double)af, at, bb, bc, bd, av, ax, aq, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, as, y0 + (double)aa, au, bb, bc, bd, aw, ay, aq, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, as, y0 + (double)y, au, bb, bc, bd, aw, az, aq, normal.x(), normal.y(), normal.z()); + this.vertex(vertexConsumer, ar, y0 + (double)y, at, bb, bc, bd, av, az, aq, normal.x(), normal.y(), normal.z()); + if (textureAtlasSprite2 != this.waterOverlay) { + this.vertex(vertexConsumer, ar, y0 + (double)y, at, bb, bc, bd, av, az, aq); + this.vertex(vertexConsumer, as, y0 + (double)y, au, bb, bc, bd, aw, az, aq); + this.vertex(vertexConsumer, as, y0 + (double)aa, au, bb, bc, bd, aw, ay, aq); + this.vertex(vertexConsumer, ar, y0 + (double)af, at, bb, bc, bd, av, ax, aq); + } + } + } + } + + private float calculateAverageHeight(BlockAndTintGetter blockAndTintGetter, Fluid fluid, float f, float g, float h, BlockPos blockPos) { + if (!(h >= 1.0F) && !(g >= 1.0F)) { + float[] fs = new float[2]; + if (h > 0.0F || g > 0.0F) { + float i = this.getHeight(blockAndTintGetter, fluid, blockPos); + if (i >= 1.0F) { + return 1.0F; + } + + this.addWeightedHeight(fs, i); + } + + this.addWeightedHeight(fs, f); + this.addWeightedHeight(fs, h); + this.addWeightedHeight(fs, g); + return fs[0] / fs[1]; + } else { + return 1.0F; + } + } + + private void addWeightedHeight(float[] fs, float f) { + if (f >= 0.8F) { + fs[0] += f * 10.0F; + fs[1] += 10.0F; + } else if (f >= 0.0F) { + fs[0] += f; + fs[1]++; + } + + } + + private float getHeight(BlockAndTintGetter blockAndTintGetter, Fluid fluid, BlockPos blockPos) { + BlockState blockState = blockAndTintGetter.getBlockState(blockPos); + return this.getHeight(blockAndTintGetter, fluid, blockPos, blockState, blockState.getFluidState()); + } + + private float getHeight(BlockAndTintGetter blockAndTintGetter, Fluid fluid, BlockPos blockPos, BlockState blockState, FluidState fluidState) { + if (fluid.isSame(fluidState.getType())) { + BlockState blockState2 = blockAndTintGetter.getBlockState(blockPos.above()); + return fluid.isSame(blockState2.getFluidState().getType()) ? 1.0F : fluidState.getOwnHeight(); + } else { + return !blockState.isSolid() ? 0.0F : -1.0F; + } + } + + private void vertex(VertexConsumer vertexConsumer, double x, double y, double z, float r, float g, float b, float u, float v, int l) { +// vertexConsumer.vertex(x, y, z).color(r, g, b, 1.0F).uv(u, v).uv2(l).normal(0.0F, 1.0F, 0.0F).endVertex(); + vertexConsumer.vertex((float) x, (float) y, (float) z, r, g, b, 1.0f, u, v, 0, l, 0.0F, 1.0F, 0.0F); + } + + private void vertex(VertexConsumer vertexConsumer, double x, double y, double z, float r, float g, float b, float u, float v, int l, float normalX, float normalY, float normalZ) { +// vertexConsumer.vertex(x, y, z).color(r, g, b, 1.0F).uv(u, v).uv2(l).normal(0.0F, 1.0F, 0.0F).endVertex(); + vertexConsumer.vertex((float) x, (float) y, (float) z, r, g, b, 1.0f, u, v, 0, l, normalX, normalY, normalZ); + } + + private void putQuad() { + + } + + private int getLightColor(BlockAndTintGetter blockAndTintGetter, BlockPos blockPos) { + int i = LevelRenderer.getLightColor(blockAndTintGetter, blockPos); + int j = LevelRenderer.getLightColor(blockAndTintGetter, blockPos.above()); + int k = i & 255; + int l = j & 255; + int m = i >> 16 & 255; + int n = j >> 16 & 255; + return (Math.max(k, l)) | (Math.max(m, n)) << 16; + } +} diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/TaskDispatcher.java b/common/src/main/java/net/vulkanmod/render/chunk/build/TaskDispatcher.java new file mode 100644 index 000000000..28a608406 --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/TaskDispatcher.java @@ -0,0 +1,200 @@ +package net.vulkanmod.render.chunk.build; + +import com.google.common.collect.Queues; +import com.mojang.logging.LogUtils; +import net.vulkanmod.render.chunk.AreaUploadManager; +import net.vulkanmod.render.chunk.ChunkArea; +import net.vulkanmod.render.chunk.DrawBuffers; +import net.vulkanmod.render.chunk.RenderSection; +import net.vulkanmod.render.vertex.TerrainRenderType; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import java.util.EnumMap; +import java.util.Queue; + +public class TaskDispatcher { + private static final Logger LOGGER = LogUtils.getLogger(); + + private int highPriorityQuota = 2; + + private final Queue toUpload = Queues.newLinkedBlockingDeque(); + public final ThreadBuilderPack fixedBuffers; + + //TODO volatile? + private boolean stopThreads; + private Thread[] threads; + private int idleThreads; + private final Queue highPriorityTasks = Queues.newConcurrentLinkedQueue(); + private final Queue lowPriorityTasks = Queues.newConcurrentLinkedQueue(); + + public TaskDispatcher() { + this.fixedBuffers = new ThreadBuilderPack(); + + this.stopThreads = true; + } + + public void createThreads() { + if(!this.stopThreads) + return; + + this.stopThreads = false; + + int j = Math.max((Runtime.getRuntime().availableProcessors() - 1) / 2, 1); + + this.threads = new Thread[j]; + + for (int i = 0; i < j; i++) { + ThreadBuilderPack builderPack = new ThreadBuilderPack(); + Thread thread = new Thread( + () -> runTaskThread(builderPack)); + + this.threads[i] = thread; + thread.start(); + } + } + + private void runTaskThread(ThreadBuilderPack builderPack) { + while(!this.stopThreads) { + ChunkTask task = this.pollTask(); + + if(task == null) + synchronized (this) { + try { + this.idleThreads++; + this.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + this.idleThreads--; + } + + if(task == null) + continue; + + task.doTask(builderPack); + } + } + + public void schedule(ChunkTask chunkTask) { + if(chunkTask == null) + return; + + if (chunkTask.highPriority) { + this.highPriorityTasks.offer(chunkTask); + } else { + this.lowPriorityTasks.offer(chunkTask); + } + + synchronized (this) { + notify(); + } + } + + @Nullable + private ChunkTask pollTask() { + ChunkTask task = this.highPriorityTasks.poll(); + + if(task == null) + task = this.lowPriorityTasks.poll(); + + return task; + } + + public void stopThreads() { + if(this.stopThreads) + return; + + this.stopThreads = true; + + synchronized (this) { + notifyAll(); + } + + for (Thread thread : this.threads) { + try { + thread.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + public boolean uploadAllPendingUploads() { + + Runnable runnable; + boolean flag = false; + while((runnable = this.toUpload.poll()) != null) { + flag = true; + runnable.run(); + } + + AreaUploadManager.INSTANCE.submitUploads(); + + return flag; + } + + public void scheduleSectionUpdate(RenderSection section, EnumMap uploadBuffers) { + this.toUpload.add( + () -> this.doSectionUpdate(section, uploadBuffers) + ); + } + + private void doSectionUpdate(RenderSection section, EnumMap uploadBuffers) { + ChunkArea renderArea = section.getChunkArea(); + DrawBuffers drawBuffers = renderArea.getDrawBuffers(); + + for(TerrainRenderType renderType : TerrainRenderType.VALUES) { + UploadBuffer uploadBuffer = uploadBuffers.get(renderType); + + if(uploadBuffer != null) { + drawBuffers.upload(uploadBuffer, section.getDrawParameters(renderType)); + } else { + section.getDrawParameters(renderType).reset(renderArea); + } + } + } + + public void scheduleUploadChunkLayer(RenderSection section, TerrainRenderType renderType, UploadBuffer uploadBuffer) { + this.toUpload.add( + () -> this.doUploadChunkLayer(section, renderType, uploadBuffer) + ); + } + + private void doUploadChunkLayer(RenderSection section, TerrainRenderType renderType, UploadBuffer uploadBuffer) { + ChunkArea renderArea = section.getChunkArea(); + DrawBuffers drawBuffers = renderArea.getDrawBuffers(); + + drawBuffers.upload(uploadBuffer, section.getDrawParameters(renderType)); + } + + public int getIdleThreadsCount() { + return this.idleThreads; + } + + public void clearBatchQueue() { + while(!this.highPriorityTasks.isEmpty()) { + ChunkTask chunkTask = this.highPriorityTasks.poll(); + if (chunkTask != null) { + chunkTask.cancel(); + } + } + + while(!this.lowPriorityTasks.isEmpty()) { + ChunkTask chunkTask = this.lowPriorityTasks.poll(); + if (chunkTask != null) { + chunkTask.cancel(); + } + } + +// this.toBatchCount = 0; + } + + public String getStats() { +// this.toBatchCount = this.highPriorityTasks.size() + this.lowPriorityTasks.size(); +// return String.format("tB: %03d, toUp: %02d, FB: %02d", this.toBatchCount, this.toUpload.size(), this.freeBufferCount); + return String.format("iT: %d", this.idleThreads); + } + +} diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/ThreadBuilderPack.java b/common/src/main/java/net/vulkanmod/render/chunk/build/ThreadBuilderPack.java new file mode 100644 index 000000000..7d6a742e2 --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/ThreadBuilderPack.java @@ -0,0 +1,26 @@ +package net.vulkanmod.render.chunk.build; + +import net.minecraft.client.renderer.RenderType; +import net.vulkanmod.render.vertex.TerrainBufferBuilder; + +import java.util.Map; +import java.util.stream.Collectors; + +public class ThreadBuilderPack { + private final Map builders = RenderType.chunkBufferLayers().stream().collect(Collectors.toMap( + (renderType) -> renderType, + (renderType) -> new TerrainBufferBuilder(renderType.bufferSize()))); + + public TerrainBufferBuilder builder(RenderType renderType) { + return this.builders.get(renderType); + } + + public void clearAll() { + this.builders.values().forEach(TerrainBufferBuilder::clear); + } + + public void discardAll() { + this.builders.values().forEach(TerrainBufferBuilder::discard); + } + +} diff --git a/common/src/main/java/net/vulkanmod/render/chunk/build/UploadBuffer.java b/common/src/main/java/net/vulkanmod/render/chunk/build/UploadBuffer.java new file mode 100644 index 000000000..245d91b88 --- /dev/null +++ b/common/src/main/java/net/vulkanmod/render/chunk/build/UploadBuffer.java @@ -0,0 +1,50 @@ +package net.vulkanmod.render.chunk.build; + +import net.vulkanmod.render.chunk.util.Util; +import net.vulkanmod.render.vertex.TerrainBufferBuilder; +import org.lwjgl.system.MemoryUtil; + +import java.nio.ByteBuffer; + +public class UploadBuffer { + + public final int indexCount; + public final boolean autoIndices; + public final boolean indexOnly; + private final ByteBuffer vertexBuffer; + private final ByteBuffer indexBuffer; + + //debug + private boolean released = false; + + public UploadBuffer(TerrainBufferBuilder.RenderedBuffer renderedBuffer) { + TerrainBufferBuilder.DrawState drawState = renderedBuffer.drawState(); + this.indexCount = drawState.indexCount(); + this.autoIndices = drawState.sequentialIndex(); + this.indexOnly = drawState.indexOnly(); + + if(!this.indexOnly) + this.vertexBuffer = Util.createCopy(renderedBuffer.vertexBuffer()); + else + this.vertexBuffer = null; + + if(!drawState.sequentialIndex()) + this.indexBuffer = Util.createCopy(renderedBuffer.indexBuffer()); + else + this.indexBuffer = null; + } + + public int indexCount() { return indexCount; } + + public ByteBuffer getVertexBuffer() { return vertexBuffer; } + + public ByteBuffer getIndexBuffer() { return indexBuffer; } + + public void release() { + if(vertexBuffer != null) + MemoryUtil.memFree(vertexBuffer); + if(indexBuffer != null) + MemoryUtil.memFree(indexBuffer); + this.released = true; + } +}