diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index 074e28b31..515429057 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -12,9 +12,10 @@ import com.simibubi.create.foundation.block.render.SpriteShifter; import com.simibubi.create.foundation.item.CustomItemModels; import com.simibubi.create.foundation.item.CustomRenderedItems; import com.simibubi.create.foundation.render.KineticRenderer; +import com.simibubi.create.foundation.render.OptifineHandler; import com.simibubi.create.foundation.render.SuperByteBufferCache; import com.simibubi.create.foundation.render.contraption.ContraptionRenderDispatcher; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; +import com.simibubi.create.foundation.render.gl.backend.Backend; import com.simibubi.create.foundation.utility.outliner.Outliner; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; @@ -31,8 +32,6 @@ import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.ModList; -import net.minecraftforge.fml.ModLoader; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; import java.util.ArrayList; @@ -62,10 +61,13 @@ public class CreateClient { modEventBus.addListener(CreateClient::onTextureStitch); modEventBus.addListener(AllParticleTypes::registerFactories); - ShaderHelper.initShaders(); + Backend.init(); + OptifineHandler.init(); } public static void clientInit(FMLClientSetupEvent event) { + kineticRenderer = new KineticRenderer(); + schematicSender = new ClientSchematicLoader(); schematicHandler = new SchematicHandler(); schematicAndQuillHandler = new SchematicAndQuillHandler(); @@ -74,8 +76,6 @@ public class CreateClient { bufferCache.registerCompartment(KineticTileEntityRenderer.KINETIC_TILE); bufferCache.registerCompartment(ContraptionRenderer.CONTRAPTION, 20); - kineticRenderer = new KineticRenderer(); - AllKeys.register(); AllContainerTypes.registerScreenFactories(); //AllTileEntities.registerRenderers(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java index 92ab725c9..2eaa1c517 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntity.java @@ -30,6 +30,8 @@ import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; import javax.annotation.Nullable; import java.util.List; @@ -475,13 +477,13 @@ public abstract class KineticTileEntity extends SmartTileEntity public void onLoad() { super.onLoad(); if (world != null && world.isRemote) - CreateClient.kineticRenderer.add(this); + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> CreateClient.kineticRenderer.add(this)); } @Override public void onChunkUnloaded() { if (world != null && world.isRemote) - CreateClient.kineticRenderer.remove(this); + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> CreateClient.kineticRenderer.remove(this)); } @Override diff --git a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntityRenderer.java index 99e2b08ec..fa4853051 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/base/KineticTileEntityRenderer.java @@ -6,6 +6,9 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.KineticDebugger; import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.config.ConfigBase; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.ColorHelper; @@ -38,6 +41,8 @@ public class KineticTileEntityRenderer extends SafeTileEntityRenderer protected void renderComponents(DeployerTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { IVertexBuilder vb = buffer.getBuffer(RenderType.getSolid()); - KineticTileEntityRenderer.renderRotatingKineticBlock(te, getRenderedBlockState(te), ms, vb, light); + if (!FastRenderDispatcher.available()) { + KineticTileEntityRenderer.renderRotatingKineticBlock(te, getRenderedBlockState(te), ms, vb, light); + } BlockState blockState = te.getBlockState(); BlockPos pos = te.getPos(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerTileEntity.java index 52c8ec809..0dfd92f12 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/deployer/DeployerTileEntity.java @@ -376,4 +376,9 @@ public class DeployerTileEntity extends KineticTileEntity { TooltipHelper.addHint(tooltip, "hint.full_deployer"); return true; } + + @Override + public boolean shouldRenderAsTE() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/fan/EncasedFanRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/fan/EncasedFanRenderer.java index cf65e08f5..3d11bccde 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/fan/EncasedFanRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/fan/EncasedFanRenderer.java @@ -7,6 +7,8 @@ import com.mojang.blaze3d.vertex.IVertexBuilder; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.render.SuperByteBuffer; @@ -26,6 +28,8 @@ public class EncasedFanRenderer extends KineticTileEntityRenderer { @Override protected void renderSafe(KineticTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { + if (FastRenderDispatcher.available()) return; + Direction direction = te.getBlockState() .get(FACING); IVertexBuilder vb = buffer.getBuffer(RenderType.getCutoutMipped()); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java index 04231f98d..ca1b39d8b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/mixer/MechanicalMixerRenderer.java @@ -5,6 +5,8 @@ import com.mojang.blaze3d.vertex.IVertexBuilder; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.render.SuperByteBuffer; @@ -31,8 +33,10 @@ public class MechanicalMixerRenderer extends KineticTileEntityRenderer { IVertexBuilder vb = buffer.getBuffer(RenderType.getSolid()); - SuperByteBuffer superBuffer = AllBlockPartials.SHAFTLESS_COGWHEEL.renderOn(blockState); - standardKineticRotationTransform(superBuffer, te, light).renderInto(ms, vb); + if (!FastRenderDispatcher.available()) { + SuperByteBuffer superBuffer = AllBlockPartials.SHAFTLESS_COGWHEEL.renderOn(blockState); + standardKineticRotationTransform(superBuffer, te, light).renderInto(ms, vb); + } int packedLightmapCoords = WorldRenderer.getLightmapCoordinates(te.getWorld(), blockState, pos); float renderedHeadOffset = mixer.getRenderedHeadOffset(partialTicks); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java index 0f2f52189..aebbe7ef3 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/saw/SawRenderer.java @@ -8,6 +8,8 @@ import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.AngleHelper; @@ -42,6 +44,9 @@ public class SawRenderer extends SafeTileEntityRenderer { renderBlade(te, ms, buffer, light); renderItems(te, partialTicks, ms, buffer, light, overlay); FilteringRenderer.renderOnTileEntity(te, partialTicks, ms, buffer, light, overlay); + + if (FastRenderDispatcher.available()) return; + renderShaft(te, ms, buffer, light, overlay); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java index 52e77bae2..7c711358f 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/processing/BasinOperatingTileEntity.java @@ -143,4 +143,8 @@ public abstract class BasinOperatingTileEntity extends KineticTileEntity { protected abstract Object getRecipeCacheKey(); + @Override + public boolean shouldRenderAsTE() { + return true; + } } diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerRenderer.java index 80c19026e..819503c0e 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/advanced/SpeedControllerRenderer.java @@ -1,11 +1,14 @@ package com.simibubi.create.content.contraptions.relays.advanced; import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; -import com.simibubi.create.foundation.render.instancing.InstancedModel; -import com.simibubi.create.foundation.render.instancing.RotatingData; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.tileEntity.renderer.SmartTileEntityRenderer; +import com.simibubi.create.foundation.render.SuperByteBuffer; + import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; public class SpeedControllerRenderer extends SmartTileEntityRenderer { @@ -18,7 +21,17 @@ public class SpeedControllerRenderer extends SmartTileEntityRenderer(tileEntityIn)); + + if (FastRenderDispatcher.available()) return; + + KineticTileEntityRenderer.renderRotatingBuffer(tileEntityIn, getRotatedModel(tileEntityIn), ms, + buffer.getBuffer(RenderType.getSolid()), light); + } + + private SuperByteBuffer getRotatedModel(SpeedControllerTileEntity te) { + return CreateClient.bufferCache.renderBlockIn(KineticTileEntityRenderer.KINETIC_TILE, + KineticTileEntityRenderer.shaft(KineticTileEntityRenderer.getRotationAxisOf(te))); } } + diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltRenderer.java index c33b9770e..3a815b1d9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/BeltRenderer.java @@ -9,11 +9,9 @@ import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.relays.belt.transport.TransportedItemStack; import com.simibubi.create.foundation.block.render.SpriteShiftEntry; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.render.ShadowRenderHelper; import com.simibubi.create.foundation.render.SuperByteBuffer; -import com.simibubi.create.foundation.render.instancing.BeltData; -import com.simibubi.create.foundation.render.instancing.InstancedModel; -import com.simibubi.create.foundation.render.instancing.RotatingData; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; @@ -34,10 +32,8 @@ import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; -import net.minecraft.world.LightType; import java.util.Random; -import java.util.function.Supplier; public class BeltRenderer extends SafeTileEntityRenderer { @@ -54,96 +50,89 @@ public class BeltRenderer extends SafeTileEntityRenderer { protected void renderSafe(BeltTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { - BlockState blockState = te.getBlockState(); - if (!AllBlocks.BELT.has(blockState)) - return; + if (!FastRenderDispatcher.available()) { - BeltSlope beltSlope = blockState.get(BeltBlock.SLOPE); - BeltPart part = blockState.get(BeltBlock.PART); - Direction facing = blockState.get(BeltBlock.HORIZONTAL_FACING); - AxisDirection axisDirection = facing.getAxisDirection(); + BlockState blockState = te.getBlockState(); + if (!AllBlocks.BELT.has(blockState)) return; - boolean downward = beltSlope == BeltSlope.DOWNWARD; - boolean upward = beltSlope == BeltSlope.UPWARD; - boolean diagonal = downward || upward; - boolean start = part == BeltPart.START; - boolean end = part == BeltPart.END; - boolean sideways = beltSlope == BeltSlope.SIDEWAYS; - boolean alongX = facing.getAxis() == Axis.X; + BeltSlope beltSlope = blockState.get(BeltBlock.SLOPE); + BeltPart part = blockState.get(BeltBlock.PART); + Direction facing = blockState.get(BeltBlock.HORIZONTAL_FACING); + AxisDirection axisDirection = facing.getAxisDirection(); - MatrixStacker msr = MatrixStacker.of(ms); - IVertexBuilder vb = buffer.getBuffer(RenderType.getSolid()); - float renderTick = AnimationTickHolder.getRenderTick(); + boolean downward = beltSlope == BeltSlope.DOWNWARD; + boolean upward = beltSlope == BeltSlope.UPWARD; + boolean diagonal = downward || upward; + boolean start = part == BeltPart.START; + boolean end = part == BeltPart.END; + boolean sideways = beltSlope == BeltSlope.SIDEWAYS; + boolean alongX = facing.getAxis() == Axis.X; - ms.push(); - msr.centre(); - msr.rotateY(AngleHelper.horizontalAngle(facing) + (upward ? 180 : 0) + (sideways ? 270 : 0)); - msr.rotateZ(sideways ? 90 : 0); - msr.rotateX(!diagonal && beltSlope != BeltSlope.HORIZONTAL ? 90 : 0); - msr.unCentre(); + MatrixStacker msr = MatrixStacker.of(ms); + IVertexBuilder vb = buffer.getBuffer(RenderType.getSolid()); + float renderTick = AnimationTickHolder.getRenderTick(); - if (downward || beltSlope == BeltSlope.VERTICAL && axisDirection == AxisDirection.POSITIVE) { - boolean b = start; - start = end; - end = b; - } - - for (boolean bottom : Iterate.trueAndFalse) { - - AllBlockPartials beltPartial = getBeltPartial(diagonal, start, end, bottom); - - SuperByteBuffer beltBuffer = beltPartial.renderOn(blockState) - .light(light); - - SpriteShiftEntry spriteShift = getSpriteShiftEntry(diagonal, bottom); - - // UV shift - float speed = te.getSpeed(); - if (speed != 0) { - float time = renderTick * axisDirection.getOffset(); - if (diagonal && (downward ^ alongX) || !sideways && !diagonal && alongX - || sideways && axisDirection == AxisDirection.NEGATIVE) - speed = -speed; - - float scrollMult = diagonal ? 3f / 8f : 0.5f; - - float spriteSize = spriteShift.getTarget().getMaxV() - spriteShift.getTarget().getMinV(); - - double scroll = speed * time / (36 * 16); - scroll = scroll - Math.floor(scroll); - scroll = scroll * spriteSize * scrollMult; - - beltBuffer.shiftUVScrolling(spriteShift, (float) scroll); - } - - beltBuffer.renderInto(ms, vb); - - // Diagonal belt do not have a separate bottom model - if (diagonal) - break; - } - ms.pop(); - - if (te.hasPulley()) { - // TODO 1.15 find a way to cache this model matrix computation - MatrixStack modelTransform = new MatrixStack(); - Direction dir = blockState.get(BeltBlock.HORIZONTAL_FACING) - .rotateY(); - if (sideways) - dir = Direction.UP; - msr = MatrixStacker.of(modelTransform); + ms.push(); msr.centre(); - if (dir.getAxis() == Axis.X) - msr.rotateY(90); - if (dir.getAxis() == Axis.Y) - msr.rotateX(90); - msr.rotateX(90); + msr.rotateY(AngleHelper.horizontalAngle(facing) + (upward ? 180 : 0) + (sideways ? 270 : 0)); + msr.rotateZ(sideways ? 90 : 0); + msr.rotateX(!diagonal && beltSlope != BeltSlope.HORIZONTAL ? 90 : 0); msr.unCentre(); - SuperByteBuffer superBuffer = CreateClient.bufferCache - .renderDirectionalPartial(AllBlockPartials.BELT_PULLEY, blockState, dir, modelTransform); - KineticTileEntityRenderer.standardKineticRotationTransform(superBuffer, te, light) - .renderInto(ms, vb); + if (downward || beltSlope == BeltSlope.VERTICAL && axisDirection == AxisDirection.POSITIVE) { + boolean b = start; + start = end; + end = b; + } + + for (boolean bottom : Iterate.trueAndFalse) { + + AllBlockPartials beltPartial = getBeltPartial(diagonal, start, end, bottom); + + SuperByteBuffer beltBuffer = beltPartial.renderOn(blockState).light(light); + + SpriteShiftEntry spriteShift = getSpriteShiftEntry(diagonal, bottom); + + // UV shift + float speed = te.getSpeed(); + if (speed != 0) { + float time = renderTick * axisDirection.getOffset(); + if (diagonal && (downward ^ alongX) || !sideways && !diagonal && alongX || sideways && axisDirection == AxisDirection.NEGATIVE) + speed = -speed; + + float scrollMult = diagonal ? 3f / 8f : 0.5f; + + float spriteSize = spriteShift.getTarget().getMaxV() - spriteShift.getTarget().getMinV(); + + double scroll = speed * time / (36 * 16); + scroll = scroll - Math.floor(scroll); + scroll = scroll * spriteSize * scrollMult; + + beltBuffer.shiftUVScrolling(spriteShift, (float) scroll); + } + + beltBuffer.renderInto(ms, vb); + + // Diagonal belt do not have a separate bottom model + if (diagonal) break; + } + ms.pop(); + + if (te.hasPulley()) { + // TODO 1.15 find a way to cache this model matrix computation + MatrixStack modelTransform = new MatrixStack(); + Direction dir = blockState.get(BeltBlock.HORIZONTAL_FACING).rotateY(); + if (sideways) dir = Direction.UP; + msr = MatrixStacker.of(modelTransform); + msr.centre(); + if (dir.getAxis() == Axis.X) msr.rotateY(90); + if (dir.getAxis() == Axis.Y) msr.rotateX(90); + msr.rotateX(90); + msr.unCentre(); + + SuperByteBuffer superBuffer = CreateClient.bufferCache.renderDirectionalPartial(AllBlockPartials.BELT_PULLEY, blockState, dir, modelTransform); + KineticTileEntityRenderer.standardKineticRotationTransform(superBuffer, te, light).renderInto(ms, vb); + } } renderItems(te, partialTicks, ms, buffer, light, overlay); diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftRenderer.java index c0f10c1e3..12fed3ad1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/encased/SplitShaftRenderer.java @@ -5,6 +5,7 @@ import com.simibubi.create.AllBlockPartials; import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.render.SuperByteBuffer; @@ -16,6 +17,7 @@ import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; import net.minecraft.util.math.BlockPos; +import sun.nio.cs.FastCharsetProvider; public class SplitShaftRenderer extends KineticTileEntityRenderer { @@ -26,6 +28,8 @@ public class SplitShaftRenderer extends KineticTileEntityRenderer { @Override protected void renderSafe(KineticTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { + if (FastRenderDispatcher.available()) return; + Block block = te.getBlockState().getBlock(); final Axis boxAxis = ((IRotate) block).getRotationAxis(te.getBlockState()); final BlockPos pos = te.getPos(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxRenderer.java index 5279893fd..2d5dd9a70 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/gearbox/GearboxRenderer.java @@ -4,6 +4,7 @@ import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.render.SuperByteBuffer; @@ -25,6 +26,8 @@ public class GearboxRenderer extends KineticTileEntityRenderer { @Override protected void renderSafe(KineticTileEntity te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) { + if (FastRenderDispatcher.available()) return; + final Axis boxAxis = te.getBlockState().get(BlockStateProperties.AXIS); final BlockPos pos = te.getPos(); float time = AnimationTickHolder.getRenderTick(); diff --git a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java index d28ec3bd8..58f90ff50 100644 --- a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java +++ b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java @@ -13,6 +13,7 @@ public class AllCommands { .then(ToggleDebugCommand.register()) .then(OverlayConfigCommand.register()) .then(FixLightingCommand.register()) + .then(ToggleExperimentalRenderingCommand.register()) //dev-util //Comment out for release diff --git a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java index b6790ab4b..2154690df 100644 --- a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java +++ b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java @@ -3,6 +3,9 @@ package com.simibubi.create.foundation.command; import java.util.function.Consumer; import java.util.function.Supplier; +import com.simibubi.create.foundation.render.FastRenderDispatcher; +import com.simibubi.create.foundation.render.gl.backend.Backend; +import javafx.scene.layout.Background; import org.apache.logging.log4j.LogManager; import com.simibubi.create.content.contraptions.goggles.GoggleConfigScreen; @@ -61,6 +64,7 @@ public class ConfigureConfigPacket extends SimplePacketBase { overlayScreen(() -> Actions::overlayScreen), fixLighting(() -> Actions::experimentalLighting), overlayReset(() -> Actions::overlayReset), + experimentalRendering(() -> Actions::experimentalRendering), ; @@ -79,6 +83,17 @@ public class ConfigureConfigPacket extends SimplePacketBase { private static void rainbowDebug(String value) { AllConfigs.CLIENT.rainbowDebug.set(Boolean.parseBoolean(value)); } + + @OnlyIn(Dist.CLIENT) + private static void experimentalRendering(String value) { + boolean last = AllConfigs.CLIENT.experimentalRendering.get(); + AllConfigs.CLIENT.experimentalRendering.set(Boolean.parseBoolean(value)); + Backend.refreshAvailability(); + + if (last != AllConfigs.CLIENT.experimentalRendering.get()) { + FastRenderDispatcher.refresh(); + } + } @OnlyIn(Dist.CLIENT) private static void overlayReset(String value) { diff --git a/src/main/java/com/simibubi/create/foundation/command/ToggleExperimentalRenderingCommand.java b/src/main/java/com/simibubi/create/foundation/command/ToggleExperimentalRenderingCommand.java new file mode 100644 index 000000000..82f5442df --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/command/ToggleExperimentalRenderingCommand.java @@ -0,0 +1,35 @@ +package com.simibubi.create.foundation.command; + +import com.mojang.brigadier.arguments.BoolArgumentType; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.simibubi.create.foundation.networking.AllPackets; +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.util.text.StringTextComponent; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.network.PacketDistributor; + +public class ToggleExperimentalRenderingCommand { + + static ArgumentBuilder register() { + return Commands.literal("experimentalRendering") + .requires(cs -> cs.hasPermissionLevel(0)) + .then(Commands.argument("value", BoolArgumentType.bool()) + .executes(ctx -> { + boolean value = BoolArgumentType.getBool(ctx, "value"); + //DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> AllConfigs.CLIENT.rainbowDebug.set(value)); + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> ConfigureConfigPacket.Actions.experimentalRendering.performAction(String.valueOf(value))); + + DistExecutor.runWhenOn(Dist.DEDICATED_SERVER, () -> () -> + AllPackets.channel.send( + PacketDistributor.PLAYER.with(() -> (ServerPlayerEntity) ctx.getSource().getEntity()), + new ConfigureConfigPacket(ConfigureConfigPacket.Actions.experimentalRendering.name(), String.valueOf(value)))); + + ctx.getSource().sendFeedback(new StringTextComponent((value ? "enabled" : "disabled") + " experimental rendering"), true); + + return 1; + })); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/config/CClient.java b/src/main/java/com/simibubi/create/foundation/config/CClient.java index b4aabef30..25fcc3fea 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CClient.java +++ b/src/main/java/com/simibubi/create/foundation/config/CClient.java @@ -1,5 +1,7 @@ package com.simibubi.create.foundation.config; +import com.simibubi.create.foundation.render.gl.backend.Backend; + public class CClient extends ConfigBase { public ConfigGroup client = group(0, "client", @@ -13,6 +15,9 @@ public class CClient extends ConfigBase { public ConfigBool rainbowDebug = b(true, "enableRainbowDebug", "Show colourful debug information while the F3-Menu is open."); + public ConfigRender experimentalRendering = + new ConfigRender("experimentalRendering", true, "Use modern OpenGL features to drastically increase performance."); + public ConfigInt overlayOffsetX = i(20, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetX", "Offset the overlay from goggle- and hover- information by this many pixels on the X axis; Use /create overlay"); public ConfigInt overlayOffsetY = i(0, Integer.MIN_VALUE, Integer.MAX_VALUE, "overlayOffsetY", "Offset the overlay from goggle- and hover- information by this many pixels on the Y axis; Use /create overlay"); @@ -21,4 +26,32 @@ public class CClient extends ConfigBase { return "client"; } + public class ConfigRender extends ConfigBool { + + public ConfigRender(String name, boolean def, String... comment) { + super(name, def, comment); + } + + /** + * Gets the configured value and checks if the rendering is actually supported. + * @return True if fast rendering is enabled and the current system/configuration is capable. + */ + @Override + public Boolean get() { + boolean enabled = super.get(); + + if (enabled) { + switch (Backend.getAvailability()) { + case FULL: + case PARTIAL: + return true; + default: + return false; + } + } else { + return false; + } + } + } + } diff --git a/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java b/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java index 8f4c954fc..2bdc39f96 100644 --- a/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java +++ b/src/main/java/com/simibubi/create/foundation/mixin/CancelTileEntityRenderMixin.java @@ -1,5 +1,6 @@ package com.simibubi.create.foundation.mixin; +import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.render.instancing.IInstanceRendered; import net.minecraft.client.renderer.chunk.ChunkRenderDispatcher; import net.minecraft.tileentity.TileEntity; @@ -25,8 +26,10 @@ public class CancelTileEntityRenderMixin { */ @Inject(at = @At("RETURN"), method = "getTileEntities", cancellable = true) private void noRenderInstancedTiles(CallbackInfoReturnable> cir) { -// List tiles = cir.getReturnValue(); -// -// tiles.removeIf(tile -> tile instanceof IInstanceRendered && !((IInstanceRendered) tile).shouldRenderAsTE()); + if (FastRenderDispatcher.available()) { + List tiles = cir.getReturnValue(); + + tiles.removeIf(tile -> tile instanceof IInstanceRendered && !((IInstanceRendered) tile).shouldRenderAsTE()); + } } } diff --git a/src/main/java/com/simibubi/create/foundation/render/AllProgramSpecs.java b/src/main/java/com/simibubi/create/foundation/render/AllProgramSpecs.java index 93ae82d72..c571f6ee8 100644 --- a/src/main/java/com/simibubi/create/foundation/render/AllProgramSpecs.java +++ b/src/main/java/com/simibubi/create/foundation/render/AllProgramSpecs.java @@ -3,12 +3,12 @@ package com.simibubi.create.foundation.render; import com.simibubi.create.Create; import com.simibubi.create.foundation.render.contraption.ContraptionProgram; import com.simibubi.create.foundation.render.gl.BasicProgram; +import com.simibubi.create.foundation.render.gl.backend.Backend; +import com.simibubi.create.foundation.render.gl.shader.GlProgram; import com.simibubi.create.foundation.render.gl.shader.ProgramSpec; import com.simibubi.create.foundation.render.gl.shader.ShaderConstants; import net.minecraft.util.ResourceLocation; -import static com.simibubi.create.foundation.render.gl.shader.ShaderHelper.register; - public class AllProgramSpecs { public static final ProgramSpec ROTATING = register(new ProgramSpec<>("rotating", Locations.ROTATING, Locations.INSTANCED, BasicProgram::new)); public static final ProgramSpec BELT = register(new ProgramSpec<>("belt", Locations.BELT, Locations.INSTANCED, BasicProgram::new)); @@ -17,6 +17,10 @@ public class AllProgramSpecs { public static final ProgramSpec CONTRAPTION_BELT = register(new ProgramSpec<>("contraption_belt", Locations.BELT, Locations.CONTRAPTION, ContraptionProgram::new, ShaderConstants.define("CONTRAPTION"))); public static final ProgramSpec CONTRAPTION_ACTOR = register(new ProgramSpec<>("contraption_actor", Locations.CONTRAPTION_ACTOR, Locations.CONTRAPTION, ContraptionProgram::new)); + private static

> S register(S spec) { + return Backend.register(spec); + } + public static class Locations { public static final ResourceLocation INSTANCED = loc("instanced.frag"); public static final ResourceLocation CONTRAPTION = loc("contraption.frag"); diff --git a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java index c704a15bd..ad75f735b 100644 --- a/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/FastRenderDispatcher.java @@ -3,9 +3,8 @@ package com.simibubi.create.foundation.render; import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; import com.simibubi.create.CreateClient; +import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.render.contraption.ContraptionRenderDispatcher; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; -import com.simibubi.create.foundation.render.instancing.TileEntityInstance; import com.simibubi.create.foundation.render.light.ILightListener; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.WorldAttached; @@ -25,6 +24,7 @@ import net.minecraft.world.ILightReader; import net.minecraft.world.LightType; import net.minecraft.world.chunk.Chunk; import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; import javax.annotation.Nullable; import java.util.ArrayList; @@ -56,18 +56,36 @@ public class FastRenderDispatcher { runQueue(queuedUpdates.get(world), CreateClient.kineticRenderer::update); } + public static boolean available() { + return AllConfigs.CLIENT.experimentalRendering.get(); + } + + public static void refresh() { + RenderWork.enqueue(() -> { + CreateClient.kineticRenderer.invalidate(); + Minecraft.getInstance().worldRenderer.loadRenderers(); + ClientWorld world = Minecraft.getInstance().world; + if (world != null) world.loadedTileEntityList.forEach(CreateClient.kineticRenderer::add); + }); + } + private static void runQueue(@Nullable ConcurrentHashMap.KeySetView changed, Consumer action) { if (changed == null) return; - // because of potential concurrency issues, we make a copy of what's in the set at the time we get here - ArrayList tiles = new ArrayList<>(changed); + if (available()) { + // because of potential concurrency issues, we make a copy of what's in the set at the time we get here + ArrayList tiles = new ArrayList<>(changed); - tiles.forEach(action); - - changed.removeAll(tiles); + tiles.forEach(action); + changed.removeAll(tiles); + } else { + changed.clear(); + } } public static void renderLayer(RenderType type, MatrixStack stack, double cameraX, double cameraY, double cameraZ) { + if (!available()) return; + Matrix4f viewProjection = Matrix4f.translate((float) -cameraX, (float) -cameraY, (float) -cameraZ); viewProjection.multiplyBackward(stack.peek().getModel()); viewProjection.multiplyBackward(getProjectionMatrix()); @@ -82,7 +100,7 @@ public class FastRenderDispatcher { //RenderSystem.disableDepthTest(); ContraptionRenderDispatcher.renderLayer(type, viewProjection); - ShaderHelper.releaseShader(); + GL20.glUseProgram(0); type.endDrawing(); } diff --git a/src/main/java/com/simibubi/create/foundation/render/OptifineHandler.java b/src/main/java/com/simibubi/create/foundation/render/OptifineHandler.java new file mode 100644 index 000000000..55665fcee --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/OptifineHandler.java @@ -0,0 +1,57 @@ +package com.simibubi.create.foundation.render; + +import com.simibubi.create.foundation.render.gl.backend.Backend; +import net.minecraft.client.Minecraft; + +import java.io.*; +import java.util.Optional; + +public class OptifineHandler { + private static Package optifine; + private static OptifineHandler handler; + + public final boolean usingShaders; + + public OptifineHandler(boolean usingShaders) { + this.usingShaders = usingShaders; + } + + public static Optional get() { + return Optional.ofNullable(handler); + } + + public static void init() { + optifine = Package.getPackage("net.optifine"); + + if (optifine == null) { + Backend.log.info("Optifine not detected."); + } else { + Backend.log.info("Optifine detected."); + + refresh(); + } + } + + public static void refresh() { + if (optifine == null) return; + + File dir = Minecraft.getInstance().gameDir; + + File shaderOptions = new File(dir, "optionsshaders.txt"); + + boolean shadersOff = true; + try { + BufferedReader reader = new BufferedReader(new FileReader(shaderOptions)); + + shadersOff = reader.lines().anyMatch(it -> it.replaceAll("\\s", "").equals("shaderPack=OFF")); + } catch (FileNotFoundException e) { + Backend.log.info("No shader config found."); + } + + handler = new OptifineHandler(!shadersOff); + } + + public boolean isUsingShaders() { + return usingShaders; + } +} diff --git a/src/main/java/com/simibubi/create/foundation/render/contraption/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/foundation/render/contraption/ContraptionRenderDispatcher.java index ceae5bb40..ab6075114 100644 --- a/src/main/java/com/simibubi/create/foundation/render/contraption/ContraptionRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/foundation/render/contraption/ContraptionRenderDispatcher.java @@ -6,8 +6,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Abs import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; import com.simibubi.create.foundation.render.AllProgramSpecs; -import com.simibubi.create.foundation.render.gl.shader.ShaderCallback; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; +import com.simibubi.create.foundation.render.gl.backend.Backend; import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.Matrix4f; @@ -95,7 +94,7 @@ public class ContraptionRenderDispatcher { GL11.glEnable(GL13.GL_TEXTURE_3D); GL13.glActiveTexture(GL40.GL_TEXTURE4); // the shaders expect light volumes to be in texture 4 - ContraptionProgram structureShader = ShaderHelper.getProgram(AllProgramSpecs.CONTRAPTION_STRUCTURE); + ContraptionProgram structureShader = Backend.getProgram(AllProgramSpecs.CONTRAPTION_STRUCTURE); structureShader.bind(viewProjection, 0); for (RenderedContraption renderer : renderers.values()) { renderer.doRenderLayer(layer, structureShader); diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/BasicProgram.java b/src/main/java/com/simibubi/create/foundation/render/gl/BasicProgram.java index 9677af03e..f00c9b443 100644 --- a/src/main/java/com/simibubi/create/foundation/render/gl/BasicProgram.java +++ b/src/main/java/com/simibubi/create/foundation/render/gl/BasicProgram.java @@ -1,7 +1,7 @@ package com.simibubi.create.foundation.render.gl; +import com.simibubi.create.foundation.render.gl.backend.Backend; import com.simibubi.create.foundation.render.gl.shader.GlProgram; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.renderer.Matrix4f; import net.minecraft.util.ResourceLocation; @@ -43,9 +43,9 @@ public class BasicProgram extends GlProgram { } protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { - ShaderHelper.MATRIX_BUFFER.position(0); - mat.write(ShaderHelper.MATRIX_BUFFER); - ShaderHelper.MATRIX_BUFFER.rewind(); - GL20.glUniformMatrix4fv(uniform, false, ShaderHelper.MATRIX_BUFFER); + Backend.MATRIX_BUFFER.position(0); + mat.write(Backend.MATRIX_BUFFER); + Backend.MATRIX_BUFFER.rewind(); + GL20.glUniformMatrix4fv(uniform, false, Backend.MATRIX_BUFFER); } } diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/backend/Backend.java b/src/main/java/com/simibubi/create/foundation/render/gl/backend/Backend.java index bbadc9fce..62c258479 100644 --- a/src/main/java/com/simibubi/create/foundation/render/gl/backend/Backend.java +++ b/src/main/java/com/simibubi/create/foundation/render/gl/backend/Backend.java @@ -1,20 +1,179 @@ package com.simibubi.create.foundation.render.gl.backend; +import com.simibubi.create.foundation.render.OptifineHandler; +import com.simibubi.create.foundation.render.gl.shader.*; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureUtil; +import net.minecraft.resources.IReloadableResourceManager; +import net.minecraft.resources.IResourceManager; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.resource.IResourceType; +import net.minecraftforge.resource.ISelectiveResourceReloadListener; +import net.minecraftforge.resource.VanillaResourceType; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.opengl.GL; +import org.lwjgl.opengl.GLCapabilities; +import org.lwjgl.system.MemoryUtil; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; import java.nio.ByteBuffer; +import java.nio.FloatBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; import java.util.function.Consumer; +import java.util.function.Predicate; public class Backend { - private static final MapBuffer MAP_BUFFER = MapBuffer.GL30_RANGE; + public static final Logger log = LogManager.getLogger(Backend.class); + public static final FloatBuffer FLOAT_BUFFER = MemoryUtil.memAllocFloat(1); // TODO: these leak 80 bytes of memory per program launch + public static final FloatBuffer VEC3_BUFFER = MemoryUtil.memAllocFloat(3); + public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16); - private Backend() { - throw new UnsupportedOperationException(); + private static final Backend instance = new Backend(); + public static Backend instance() { + return instance; } + private static final Map> registry = new HashMap<>(); + private static final Map, GlProgram> programs = new HashMap<>(); + + public static GLCapabilities capabilities; + private static RenderingAvailability availability; + private static MapBuffer mapBuffer; + + + public static void mapBuffer(int target, int offset, int length, Consumer upload) { - MAP_BUFFER.mapBuffer(target, offset, length, upload); + mapBuffer.mapBuffer(target, offset, length, upload); } public static void mapBuffer(int target, int size, Consumer upload) { - MAP_BUFFER.mapBuffer(target, 0, size, upload); + mapBuffer.mapBuffer(target, 0, size, upload); + } + + /** + * Register a shader program. TODO: replace with forge registry? + */ + public static

> S register(S spec) { + ResourceLocation name = spec.name; + if (registry.containsKey(name)) { + throw new IllegalStateException("Program spec '" + name + "' already registered."); + } + registry.put(name, spec); + return spec; + } + + @SuppressWarnings("unchecked") + public static

> P getProgram(S spec) { + return (P) programs.get(spec); + } + + /** + * Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order. + * + * @param clazz The class of the versioning enum. + * @param The type of the versioning enum. + * @return The first defined enum variant to return true. + */ + public static & GlVersioned> V getLatest(Class clazz) { + return getLatest(clazz, capabilities); + } + + /** + * Get the most compatible version of a specific OpenGL feature by iterating over enum constants in order. + * + * @param clazz The class of the versioning enum. + * @param caps The current system's supported features. + * @param The type of the versioning enum. + * @return The first defined enum variant to return true. + */ + public static & GlVersioned> V getLatest(Class clazz, GLCapabilities caps) { + V[] constants = clazz.getEnumConstants(); + V last = constants[constants.length - 1]; + if (!last.supported(caps)) { + throw new IllegalStateException(""); + } + + return Arrays.stream(constants).filter(it -> it.supported(caps)).findFirst().orElse(last); + } + + public static RenderingAvailability getAvailability() { + return availability; + } + + public static void init() { + // Can be null when running datagenerators due to the unfortunate time we call this + Minecraft mc = Minecraft.getInstance(); + if (mc != null && mc.getResourceManager() instanceof IReloadableResourceManager) { + ISelectiveResourceReloadListener listener = Backend::onResourceManagerReload; + ((IReloadableResourceManager) mc.getResourceManager()).addReloadListener(listener); + } + } + + public static void onResourceManagerReload(IResourceManager manager, Predicate predicate) { + if (predicate.test(VanillaResourceType.SHADERS)) { + capabilities = GL.createCapabilities(); + mapBuffer = getLatest(MapBuffer.class); + + OptifineHandler.refresh(); + refreshAvailability(); + + programs.values().forEach(GlProgram::delete); + programs.clear(); + for (ProgramSpec shader : registry.values()) { + loadProgram(manager, shader); + } + } + } + + private static

> void loadProgram(IResourceManager manager, S programSpec) { + GlShader vert = null; + GlShader frag = null; + try { + vert = loadShader(manager, programSpec.getVert(), ShaderType.VERTEX, programSpec.defines); + frag = loadShader(manager, programSpec.getFrag(), ShaderType.FRAGMENT, programSpec.defines); + + P program = GlProgram.builder(programSpec.name) + .attachShader(vert) + .attachShader(frag) + .build(programSpec.factory); + + programs.put(programSpec, program); + + log.info("Loaded program {}", programSpec.name); + } catch (IOException ex) { + log.error("Failed to load program {}", programSpec.name, ex); + } finally { + if (vert != null) vert.delete(); + if (frag != null) frag.delete(); + } + } + + private static GlShader loadShader(IResourceManager manager, ResourceLocation name, ShaderType type, GlShader.PreProcessor preProcessor) throws IOException { + try (InputStream is = new BufferedInputStream(manager.getResource(name).getInputStream())) { + String source = TextureUtil.func_225687_b_(is); + + if (source == null) { + throw new IOException("Could not load program " + name); + } else { + return new GlShader(type, name, source, preProcessor); + } + } + } + + public static void refreshAvailability() { + if (capabilities.OpenGL33) { + availability = RenderingAvailability.FULL; + + OptifineHandler.get() + .filter(OptifineHandler::isUsingShaders) + .ifPresent(it -> availability = RenderingAvailability.OPTIFINE_SHADERS); + } else { + availability = RenderingAvailability.INCAPABLE; + } } } diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/backend/GlVersioned.java b/src/main/java/com/simibubi/create/foundation/render/gl/backend/GlVersioned.java new file mode 100644 index 000000000..5f69d0ee0 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/gl/backend/GlVersioned.java @@ -0,0 +1,8 @@ +package com.simibubi.create.foundation.render.gl.backend; + +import org.lwjgl.opengl.GLCapabilities; + + +public interface GlVersioned { + boolean supported(GLCapabilities caps); +} diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java b/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java index 2d50966b0..17b83785e 100644 --- a/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java +++ b/src/main/java/com/simibubi/create/foundation/render/gl/backend/MapBuffer.java @@ -1,15 +1,19 @@ package com.simibubi.create.foundation.render.gl.backend; -import org.lwjgl.opengl.ARBMapBufferRange; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL30; +import org.lwjgl.opengl.*; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.function.Consumer; -public enum MapBuffer { +public enum MapBuffer implements GlVersioned { GL30_RANGE { + @Override + public boolean supported(GLCapabilities caps) { + return caps.OpenGL30; + } + @Override public void mapBuffer(int target, int offset, int length, Consumer upload) { ByteBuffer buffer = GL30.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT); @@ -21,6 +25,11 @@ public enum MapBuffer { } }, ARB_RANGE { + @Override + public boolean supported(GLCapabilities caps) { + return caps.GL_ARB_map_buffer_range; + } + @Override public void mapBuffer(int target, int offset, int length, Consumer upload) { ByteBuffer buffer = ARBMapBufferRange.glMapBufferRange(target, offset, length, GL30.GL_MAP_WRITE_BIT | GL30.GL_MAP_INVALIDATE_RANGE_BIT); @@ -32,6 +41,11 @@ public enum MapBuffer { } }, GL15_MAP { + @Override + public boolean supported(GLCapabilities caps) { + return caps.OpenGL15; + } + @Override public void mapBuffer(int target, int offset, int length, Consumer upload) { ByteBuffer buffer = GL15.glMapBuffer(target, GL15.GL_WRITE_ONLY); @@ -43,6 +57,11 @@ public enum MapBuffer { } }, UNSUPPORTED { + @Override + public boolean supported(GLCapabilities caps) { + return true; + } + @Override public void mapBuffer(int target, int offset, int length, Consumer upload) { throw new UnsupportedOperationException("glMapBuffer not supported"); diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/backend/RenderingAvailability.java b/src/main/java/com/simibubi/create/foundation/render/gl/backend/RenderingAvailability.java new file mode 100644 index 000000000..23841149b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/render/gl/backend/RenderingAvailability.java @@ -0,0 +1,27 @@ +package com.simibubi.create.foundation.render.gl.backend; + +public enum RenderingAvailability { + /** + * The current system does not support enough + * OpenGL features to enable fast rendering. + */ + INCAPABLE, + + /** + * The current system supports OpenGL 3.3. + */ + FULL, + + /** + * The current system supports OpenGL 2.0, + * or some ARBs that make it equivalent. + */ + PARTIAL, + + /** + * It doesn't matter what the current system + * supports because Optifine is installed and + * a shaderpack is enabled. + */ + OPTIFINE_SHADERS, +} diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlProgram.java b/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlProgram.java index f4c89d3b4..8a47796d8 100644 --- a/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlProgram.java +++ b/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlProgram.java @@ -1,6 +1,7 @@ package com.simibubi.create.foundation.render.gl.shader; import com.simibubi.create.foundation.render.gl.GlObject; +import com.simibubi.create.foundation.render.gl.backend.Backend; import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL20; @@ -29,13 +30,12 @@ public abstract class GlProgram extends GlObject { * Retrieves the index of the uniform with the given name. * @param uniform The name of the uniform to find the index of * @return The uniform's index - * @throws NullPointerException If no uniform exists with the given name */ public int getUniformLocation(String uniform) { int index = GL20.glGetUniformLocation(this.handle(), uniform); if (index < 0) { - ShaderHelper.log.error("No uniform exists in program '" + this.name + "' with name: " + uniform); + Backend.log.warn("No active uniform '{}' exists in program '{}'. Could be unused.", uniform, this.name); } return index; @@ -93,7 +93,7 @@ public abstract class GlProgram extends GlObject { String log = GL20.glGetProgramInfoLog(this.program); if (!log.isEmpty()) { - ShaderHelper.log.warn("Program link log for " + this.name + ": " + log); + Backend.log.warn("Program link log for " + this.name + ": " + log); } int result = GL20.glGetProgrami(this.program, GL20.GL_LINK_STATUS); diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlShader.java b/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlShader.java index 8352d159c..210878419 100644 --- a/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlShader.java +++ b/src/main/java/com/simibubi/create/foundation/render/gl/shader/GlShader.java @@ -1,8 +1,7 @@ package com.simibubi.create.foundation.render.gl.shader; import com.simibubi.create.foundation.render.gl.GlObject; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; -import com.simibubi.create.foundation.render.gl.shader.ShaderType; +import com.simibubi.create.foundation.render.gl.backend.Backend; import net.minecraft.util.ResourceLocation; import org.lwjgl.opengl.GL20; @@ -18,7 +17,7 @@ public class GlShader extends GlObject { if (preProcessor != null) { source = preProcessor.process(source); - ShaderHelper.log.info("Preprocessor run on " + name + ":\n" + source); + Backend.log.info("Preprocessor run on " + name);// + ":\n" + source); } GL20.glShaderSource(handle, source); @@ -27,7 +26,7 @@ public class GlShader extends GlObject { String log = GL20.glGetShaderInfoLog(handle); if (!log.isEmpty()) { - ShaderHelper.log.warn("Shader compilation log for " + name + ": " + log); + Backend.log.warn("Shader compilation log for " + name + ": " + log); } if (GL20.glGetShaderi(handle, GL20.GL_COMPILE_STATUS) != GL20.GL_TRUE) { diff --git a/src/main/java/com/simibubi/create/foundation/render/gl/shader/ShaderHelper.java b/src/main/java/com/simibubi/create/foundation/render/gl/shader/ShaderHelper.java deleted file mode 100644 index b3742e7ec..000000000 --- a/src/main/java/com/simibubi/create/foundation/render/gl/shader/ShaderHelper.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.simibubi.create.foundation.render.gl.shader; - -import com.mojang.blaze3d.platform.GlStateManager; -import com.simibubi.create.content.contraptions.KineticDebugger; -import com.simibubi.create.foundation.render.gl.BasicProgram; -import com.simibubi.create.foundation.utility.AnimationTickHolder; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.Matrix4f; -import net.minecraft.client.renderer.texture.TextureUtil; -import net.minecraft.client.shader.ShaderLinkHelper; -import net.minecraft.resources.IReloadableResourceManager; -import net.minecraft.resources.IResourceManager; -import net.minecraft.util.ResourceLocation; -import net.minecraftforge.resource.ISelectiveResourceReloadListener; -import net.minecraftforge.resource.VanillaResourceType; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.opengl.GL20; -import org.lwjgl.system.MemoryUtil; - -import javax.annotation.Nullable; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.FloatBuffer; -import java.util.HashMap; -import java.util.Map; - -public class ShaderHelper { - - public static final Logger log = LogManager.getLogger(ShaderHelper.class); - - public static final FloatBuffer FLOAT_BUFFER = MemoryUtil.memAllocFloat(1); // TODO: these leak 80 bytes of memory per program launch - public static final FloatBuffer VEC3_BUFFER = MemoryUtil.memAllocFloat(3); - public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16); - - private static final Map> REGISTRY = new HashMap<>(); - private static final Map, GlProgram> PROGRAMS = new HashMap<>(); - - public static

> S register(S spec) { - ResourceLocation name = spec.name; - if (REGISTRY.containsKey(name)) { - throw new IllegalStateException("Program spec '" + name + "' already registered."); - } - REGISTRY.put(name, spec); - return spec; - } - - public static void initShaders() { - // Can be null when running datagenerators due to the unfortunate time we call this - Minecraft mc = Minecraft.getInstance(); - if (mc != null && mc.getResourceManager() instanceof IReloadableResourceManager) { - ISelectiveResourceReloadListener listener = (manager, predicate) -> { - if (predicate.test(VanillaResourceType.SHADERS)) { - PROGRAMS.values().forEach(GlProgram::delete); - PROGRAMS.clear(); - for (ProgramSpec shader : REGISTRY.values()) { - loadProgram(manager, shader); - } - } - }; - ((IReloadableResourceManager) mc.getResourceManager()).addReloadListener(listener); - } - } - - @SuppressWarnings("unchecked") - public static

> P getProgram(S spec) { - return (P) PROGRAMS.get(spec); - } - - public static void releaseShader() { - GL20.glUseProgram(0); - } - - private static

> void loadProgram(IResourceManager manager, S programSpec) { - GlShader vert = null; - GlShader frag = null; - try { - vert = loadShader(manager, programSpec.getVert(), ShaderType.VERTEX, programSpec.defines); - frag = loadShader(manager, programSpec.getFrag(), ShaderType.FRAGMENT, programSpec.defines); - - P program = GlProgram.builder(programSpec.name) - .attachShader(vert) - .attachShader(frag) - .build(programSpec.factory); - - PROGRAMS.put(programSpec, program); - - log.info("Loaded program {}", programSpec.name); - } catch (IOException ex) { - log.error("Failed to load program {}", programSpec.name, ex); - } finally { - if (vert != null) vert.delete(); - if (frag != null) frag.delete(); - } - } - - private static GlShader loadShader(IResourceManager manager, ResourceLocation name, ShaderType type, GlShader.PreProcessor preProcessor) throws IOException { - try (InputStream is = new BufferedInputStream(manager.getResource(name).getInputStream())) { - String source = TextureUtil.func_225687_b_(is); - - if (source == null) { - throw new IOException("Could not load program " + name); - } else { - return new GlShader(type, name, source, preProcessor); - } - } - } -} diff --git a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderRegistry.java b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderRegistry.java index 3e8d402f8..a454828f6 100644 --- a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderRegistry.java +++ b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderRegistry.java @@ -18,7 +18,7 @@ public class InstancedTileRenderRegistry { @SuppressWarnings("unchecked") @Nullable - public TileEntityInstance create(InstancedTileRenderer manager, T tile) { + public TileEntityInstance create(InstancedTileRenderer manager, T tile) { TileEntityType type = tile.getType(); IRendererFactory factory = (IRendererFactory) this.renderers.get(type); diff --git a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderer.java b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderer.java index c7d0aad5a..6037e97fd 100644 --- a/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/render/instancing/InstancedTileRenderer.java @@ -3,11 +3,11 @@ package com.simibubi.create.foundation.render.instancing; import com.simibubi.create.foundation.render.FastRenderDispatcher; import com.simibubi.create.foundation.render.gl.BasicProgram; import com.simibubi.create.foundation.render.gl.shader.ShaderCallback; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.RenderType; import net.minecraft.tileentity.TileEntity; +import org.lwjgl.opengl.GL20; import javax.annotation.Nullable; import java.util.HashMap; @@ -42,15 +42,14 @@ public abstract class InstancedTileRenderer

{ if (instance != null) { return (TileEntityInstance) instance; } else if (create) { - return null; -// TileEntityInstance renderer = InstancedTileRenderRegistry.instance.create(this, tile); -// -// if (renderer != null) { -// FastRenderDispatcher.addedLastTick.get(tile.getWorld()).add(tile); -// instances.put(tile, renderer); -// } -// -// return renderer; + TileEntityInstance renderer = InstancedTileRenderRegistry.instance.create(this, tile); + + if (renderer != null) { + FastRenderDispatcher.addedLastTick.get(tile.getWorld()).add(tile); + instances.put(tile, renderer); + } + + return renderer; } else { return null; } @@ -116,6 +115,6 @@ public abstract class InstancedTileRenderer

{ material.render(layer, viewProjection, callback); } - ShaderHelper.releaseShader(); + GL20.glUseProgram(0); } } diff --git a/src/main/java/com/simibubi/create/foundation/render/instancing/RenderMaterial.java b/src/main/java/com/simibubi/create/foundation/render/instancing/RenderMaterial.java index 9a0520071..4feaa3e8f 100644 --- a/src/main/java/com/simibubi/create/foundation/render/instancing/RenderMaterial.java +++ b/src/main/java/com/simibubi/create/foundation/render/instancing/RenderMaterial.java @@ -8,9 +8,9 @@ import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.foundation.render.Compartment; import com.simibubi.create.foundation.render.SuperByteBufferCache; import com.simibubi.create.foundation.render.gl.BasicProgram; +import com.simibubi.create.foundation.render.gl.backend.Backend; import com.simibubi.create.foundation.render.gl.shader.ProgramSpec; import com.simibubi.create.foundation.render.gl.shader.ShaderCallback; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BlockRendererDispatcher; @@ -63,7 +63,7 @@ public class RenderMaterial

setup) { - P program = ShaderHelper.getProgram(programSpec); + P program = Backend.getProgram(programSpec); program.bind(viewProjection, 0); if (setup != null) setup.call(program); diff --git a/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java b/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java index 9ec917814..4109f49e6 100644 --- a/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java +++ b/src/main/java/com/simibubi/create/foundation/render/light/LightVolume.java @@ -2,7 +2,6 @@ package com.simibubi.create.foundation.render.light; import com.simibubi.create.foundation.render.RenderWork; import com.simibubi.create.foundation.render.gl.GlTexture; -import com.simibubi.create.foundation.render.gl.shader.ShaderHelper; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.SectionPos; import net.minecraft.world.ILightReader;