From 4001d7550b920ca6d31e86d973655e023165037f Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sat, 23 Dec 2023 16:44:26 +0100 Subject: [PATCH 1/9] copy paste gtceu fake world rendering --- .../modularui/utils/fakeworld/BlockInfo.java | 63 +++ .../utils/fakeworld/DummyChunkProvider.java | 57 +++ .../utils/fakeworld/DummySaveHandler.java | 105 +++++ .../modularui/utils/fakeworld/DummyWorld.java | 93 ++++ .../fakeworld/FBOWorldSceneRenderer.java | 136 ++++++ .../utils/fakeworld/ISceneRenderHook.java | 8 + .../ImmediateWorldSceneRenderer.java | 42 ++ .../utils/fakeworld/TrackedDummyWorld.java | 93 ++++ .../utils/fakeworld/WorldSceneRenderer.java | 444 ++++++++++++++++++ 9 files changed, 1041 insertions(+) create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyChunkProvider.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummySaveHandler.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyWorld.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java new file mode 100644 index 000000000..ee6db424f --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java @@ -0,0 +1,63 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import com.google.common.base.Preconditions; + +/** + * BlockInfo represents immutable information for block in world + * This includes block state and tile entity, and needed for complete representation + * of some complex blocks like machines, when rendering or manipulating them without world instance + */ +public class BlockInfo { + + public static final BlockInfo EMPTY = new BlockInfo(Blocks.AIR); + + private final IBlockState blockState; + private final TileEntity tileEntity; + private final Object info; + + public BlockInfo(Block block) { + this(block.getDefaultState()); + } + + public BlockInfo(IBlockState blockState) { + this(blockState, null); + } + + public BlockInfo(IBlockState blockState, TileEntity tileEntity) { + this(blockState, tileEntity, null); + } + + public BlockInfo(IBlockState blockState, TileEntity tileEntity, Object info) { + this.blockState = blockState; + this.tileEntity = tileEntity; + this.info = info; + Preconditions.checkArgument(tileEntity == null || blockState.getBlock().hasTileEntity(blockState), + "Cannot create block info with tile entity for block not having it"); + } + + public IBlockState getBlockState() { + return blockState; + } + + public TileEntity getTileEntity() { + return tileEntity; + } + + public Object getInfo() { + return info; + } + + public void apply(World world, BlockPos pos) { + world.setBlockState(pos, blockState); + if (tileEntity != null) { + world.setTileEntity(pos, tileEntity); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyChunkProvider.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyChunkProvider.java new file mode 100644 index 000000000..79d301418 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyChunkProvider.java @@ -0,0 +1,57 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.World; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; + +import io.netty.util.collection.LongObjectHashMap; +import io.netty.util.collection.LongObjectMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DummyChunkProvider implements IChunkProvider { + + private final World world; + private final LongObjectMap loadedChunks = new LongObjectHashMap<>(); + + public DummyChunkProvider(World world) { + this.world = world; + } + + @Nullable + @Override + public Chunk getLoadedChunk(int x, int z) { + return loadedChunks.get(ChunkPos.asLong(x, z)); + } + + @NotNull + @Override + public Chunk provideChunk(int x, int z) { + long chunkKey = ChunkPos.asLong(x, z); + if (loadedChunks.containsKey(chunkKey)) + return loadedChunks.get(chunkKey); + Chunk chunk = new Chunk(world, x, z); + loadedChunks.put(chunkKey, chunk); + return chunk; + } + + @Override + public boolean tick() { + for (Chunk chunk : loadedChunks.values()) { + chunk.onTick(false); + } + return !loadedChunks.isEmpty(); + } + + @NotNull + @Override + public String makeString() { + return "Dummy"; + } + + @Override + public boolean isChunkGeneratedAt(int x, int z) { + return true; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummySaveHandler.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummySaveHandler.java new file mode 100644 index 000000000..66040aeed --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummySaveHandler.java @@ -0,0 +1,105 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.util.datafix.DataFixer; +import net.minecraft.world.World; +import net.minecraft.world.WorldProvider; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.IChunkLoader; +import net.minecraft.world.gen.structure.template.TemplateManager; +import net.minecraft.world.storage.IPlayerFileData; +import net.minecraft.world.storage.ISaveHandler; +import net.minecraft.world.storage.WorldInfo; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.File; + +@SuppressWarnings("all") +public class DummySaveHandler implements ISaveHandler, IPlayerFileData, IChunkLoader { + + @Nullable + @Override + public WorldInfo loadWorldInfo() { + return null; + } + + @Override + public void checkSessionLock() {} + + @NotNull + @Override + public IChunkLoader getChunkLoader(@NotNull WorldProvider provider) { + return this; + } + + @NotNull + @Override + public IPlayerFileData getPlayerNBTManager() { + return this; + } + + @NotNull + @Override + public TemplateManager getStructureTemplateManager() { + return new TemplateManager("", new DataFixer(0)); + } + + @Override + public void saveWorldInfoWithPlayer(@NotNull WorldInfo worldInformation, @NotNull NBTTagCompound tagCompound) {} + + @Override + public void saveWorldInfo(@NotNull WorldInfo worldInformation) {} + + @NotNull + @Override + public File getWorldDirectory() { + return null; + } + + @NotNull + @Override + public File getMapFileFromName(@NotNull String mapName) { + return null; + } + + @Nullable + @Override + public Chunk loadChunk(@NotNull World worldIn, int x, int z) { + return null; + } + + @Override + public void saveChunk(@NotNull World worldIn, @NotNull Chunk chunkIn) {} + + @Override + public void saveExtraChunkData(@NotNull World worldIn, @NotNull Chunk chunkIn) {} + + @Override + public void chunkTick() {} + + @Override + public void flush() {} + + @Override + public boolean isChunkGeneratedAt(int x, int z) { + return false; + } + + @Override + public void writePlayerData(@NotNull EntityPlayer player) {} + + @Nullable + @Override + public NBTTagCompound readPlayerData(@NotNull EntityPlayer player) { + return null; + } + + @NotNull + @Override + public String[] getAvailablePlayerDat() { + return new String[0]; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyWorld.java new file mode 100644 index 000000000..9ea204d90 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/DummyWorld.java @@ -0,0 +1,93 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.profiler.Profiler; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.*; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.IChunkProvider; +import net.minecraft.world.storage.WorldInfo; +import net.minecraftforge.fml.common.ObfuscationReflectionHelper; +import net.minecraftforge.fml.relauncher.FMLLaunchHandler; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class DummyWorld extends World { + + private static final WorldSettings DEFAULT_SETTINGS = new WorldSettings( + 1L, GameType.SURVIVAL, true, false, WorldType.DEFAULT); + + public static final DummyWorld INSTANCE = new DummyWorld(); + + public DummyWorld() { + super(new DummySaveHandler(), new WorldInfo(DEFAULT_SETTINGS, "DummyServer"), new WorldProviderSurface(), + new Profiler(), false); + // Guarantee the dimension ID was not reset by the provider + this.provider.setDimension(Integer.MAX_VALUE); + int providerDim = this.provider.getDimension(); + this.provider.setWorld(this); + this.provider.setDimension(providerDim); + this.chunkProvider = this.createChunkProvider(); + this.calculateInitialSkylight(); + this.calculateInitialWeather(); + this.getWorldBorder().setSize(30000000); + // De-allocate lightUpdateBlockList, checkLightFor uses this + ObfuscationReflectionHelper.setPrivateValue(World.class, this, null, + FMLLaunchHandler.isDeobfuscatedEnvironment() ? "lightUpdateBlockList" : "field_72994_J"); + } + + @Override + public void notifyNeighborsRespectDebug(@NotNull BlockPos pos, @NotNull Block blockType, boolean p_175722_3_) { + // NOOP - do not trigger forge events + } + + @Override + public void notifyNeighborsOfStateChange(@NotNull BlockPos pos, @NotNull Block blockType, boolean updateObservers) { + // NOOP - do not trigger forge events + } + + @Override + public void notifyNeighborsOfStateExcept(@NotNull BlockPos pos, @NotNull Block blockType, + @NotNull EnumFacing skipSide) { + // NOOP - do not trigger forge events + } + + @Override + public void markAndNotifyBlock(@NotNull BlockPos pos, @Nullable Chunk chunk, @NotNull IBlockState iblockstate, + @NotNull IBlockState newState, int flags) { + // NOOP - do not trigger forge events + } + + @Override + public void notifyBlockUpdate(@NotNull BlockPos pos, @NotNull IBlockState oldState, @NotNull IBlockState newState, + int flags) {} + + @Override + public void markBlockRangeForRenderUpdate(@NotNull BlockPos rangeMin, @NotNull BlockPos rangeMax) {} + + @Override + public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {} + + @Override + public void updateObservingBlocksAt(@NotNull BlockPos pos, @NotNull Block blockType) {} + + @NotNull + @Override + protected IChunkProvider createChunkProvider() { + return new DummyChunkProvider(this); + } + + @Override + protected boolean isChunkLoaded(int x, int z, boolean allowEmpty) { + return chunkProvider.isChunkGeneratedAt(x, z); + } + + @Override + // De-allocated lightUpdateBlockList, default return + public boolean checkLightFor(@NotNull EnumSkyBlock lightType, @NotNull BlockPos pos) { + return true; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java new file mode 100644 index 000000000..c6cd637ac --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java @@ -0,0 +1,136 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.ModularUI; + +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.shader.Framebuffer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.vector.Vector3f; + +/** + * It looks similar to {@link ImmediateWorldSceneRenderer}, but totally different. + * It uses FBO and is more universality and efficient(X). + * FBO can be rendered anywhere more flexibly, not just in the GUI. + * If you have scene rendering needs, you will love this FBO renderer. + * TODO OP_LIST might be used in the future to further improve performance. + */ +@SideOnly(Side.CLIENT) +public class FBOWorldSceneRenderer extends WorldSceneRenderer { + + private int resolutionWidth = 1080; + private int resolutionHeight = 1080; + private Framebuffer fbo; + + public FBOWorldSceneRenderer(World world, int resolutionWidth, int resolutionHeight) { + super(world); + setFBOSize(resolutionWidth, resolutionHeight); + } + + public FBOWorldSceneRenderer(World world, Framebuffer fbo) { + super(world); + this.fbo = fbo; + } + + public int getResolutionWidth() { + return resolutionWidth; + } + + public int getResolutionHeight() { + return resolutionHeight; + } + + /*** + * This will modify the size of the FBO. You'd better know what you're doing before you call it. + */ + public void setFBOSize(int resolutionWidth, int resolutionHeight) { + this.resolutionWidth = resolutionWidth; + this.resolutionHeight = resolutionHeight; + releaseFBO(); + try { + fbo = new Framebuffer(resolutionWidth, resolutionHeight, true); + } catch (Exception e) { + ModularUI.LOGGER.error(e); + } + } + + public RayTraceResult screenPos2BlockPosFace(int mouseX, int mouseY) { + int lastID = bindFBO(); + RayTraceResult looking = super.screenPos2BlockPosFace(mouseX, mouseY, 0, 0, this.resolutionWidth, + this.resolutionHeight); + unbindFBO(lastID); + return looking; + } + + public Vector3f blockPos2ScreenPos(BlockPos pos, boolean depth) { + int lastID = bindFBO(); + Vector3f winPos = super.blockPos2ScreenPos(pos, depth, 0, 0, this.resolutionWidth, this.resolutionHeight); + unbindFBO(lastID); + return winPos; + } + + public void render(float x, float y, float width, float height, float mouseX, float mouseY) { + // bind to FBO + int lastID = bindFBO(); + super.render(0, 0, this.resolutionWidth, this.resolutionHeight, (int) (this.resolutionWidth * mouseX / width), + (int) (this.resolutionHeight * (1 - mouseY / height))); + // unbind FBO + unbindFBO(lastID); + + // bind FBO as texture + GlStateManager.enableTexture2D(); + GlStateManager.disableLighting(); + lastID = GL11.glGetInteger(GL11.GL_TEXTURE_2D); + GlStateManager.bindTexture(fbo.framebufferTexture); + GlStateManager.color(1, 1, 1, 1); + + // render rect with FBO texture + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferbuilder = tessellator.getBuffer(); + bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX); + + bufferbuilder.pos(x + width, y + height, 0).tex(1, 0).endVertex(); + bufferbuilder.pos(x + width, y, 0).tex(1, 1).endVertex(); + bufferbuilder.pos(x, y, 0).tex(0, 1).endVertex(); + bufferbuilder.pos(x, y + height, 0).tex(0, 0).endVertex(); + tessellator.draw(); + + GlStateManager.bindTexture(lastID); + } + + public void render(float x, float y, float width, float height, int mouseX, int mouseY) { + render(x, y, width, height, (float) mouseX, (float) mouseY); + } + + private int bindFBO() { + int lastID = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); + fbo.setFramebufferColor(0.0F, 0.0F, 0.0F, 0.0F); + fbo.framebufferClear(); + fbo.bindFramebuffer(true); + GlStateManager.pushMatrix(); + return lastID; + } + + private void unbindFBO(int lastID) { + GlStateManager.popMatrix(); + fbo.unbindFramebufferTexture(); + OpenGlHelper.glBindFramebuffer(OpenGlHelper.GL_FRAMEBUFFER, lastID); + } + + public void releaseFBO() { + if (fbo != null) { + fbo.deleteFramebuffer(); + } + fbo = null; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java new file mode 100644 index 000000000..d0a5de3a6 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java @@ -0,0 +1,8 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.BlockRenderLayer; + +public interface ISceneRenderHook { + + void apply(boolean isTESR, int pass, BlockRenderLayer layer); +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java new file mode 100644 index 000000000..6e39a66c7 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java @@ -0,0 +1,42 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import org.lwjgl.opengl.GL11; + +import java.awt.*; + +@SideOnly(Side.CLIENT) +public class ImmediateWorldSceneRenderer extends WorldSceneRenderer { + + public ImmediateWorldSceneRenderer(World world) { + super(world); + } + + @Override + protected Rectangle getPositionedRect(int x, int y, int width, int height) { + Minecraft mc = Minecraft.getMinecraft(); + ScaledResolution resolution = new ScaledResolution(mc); + // compute window size from scaled width & height + int windowWidth = (int) (width / (resolution.getScaledWidth() * 1.0) * mc.displayWidth); + int windowHeight = (int) (height / (resolution.getScaledHeight() * 1.0) * mc.displayHeight); + // translate gui coordinates to window's ones (y is inverted) + int windowX = (int) (x / (resolution.getScaledWidth() * 1.0) * mc.displayWidth); + int windowY = mc.displayHeight - (int) (y / (resolution.getScaledHeight() * 1.0) * mc.displayHeight) - + windowHeight; + + return super.getPositionedRect(windowX, windowY, windowWidth, windowHeight); + } + + @Override + protected void clearView(int x, int y, int width, int height) { + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(x, y, width, height); + super.clearView(x, y, width, height); + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java new file mode 100644 index 000000000..77dc484d9 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java @@ -0,0 +1,93 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import org.jetbrains.annotations.NotNull; +import org.lwjgl.util.vector.Vector3f; + +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +@SideOnly(Side.CLIENT) +public class TrackedDummyWorld extends DummyWorld { + + public final Set renderedBlocks = new ObjectOpenHashSet<>(); + private Predicate renderFilter; + private final World proxyWorld; + + private final Vector3f minPos = new Vector3f(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + private final Vector3f maxPos = new Vector3f(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + + public void setRenderFilter(Predicate renderFilter) { + this.renderFilter = renderFilter; + } + + public TrackedDummyWorld() { + proxyWorld = null; + } + + public TrackedDummyWorld(World world) { + proxyWorld = world; + } + + public void addBlocks(Map renderedBlocks) { + renderedBlocks.forEach(this::addBlock); + } + + public void addBlock(BlockPos pos, BlockInfo blockInfo) { + if (blockInfo.getBlockState().getBlock() == Blocks.AIR) + return; + this.renderedBlocks.add(pos); + blockInfo.apply(this, pos); + } + + @Override + public TileEntity getTileEntity(@NotNull BlockPos pos) { + if (renderFilter != null && !renderFilter.test(pos)) + return null; + return proxyWorld != null ? proxyWorld.getTileEntity(pos) : super.getTileEntity(pos); + } + + @NotNull + @Override + public IBlockState getBlockState(@NotNull BlockPos pos) { + if (renderFilter != null && !renderFilter.test(pos)) + return Blocks.AIR.getDefaultState(); // return air if not rendering this block + return proxyWorld != null ? proxyWorld.getBlockState(pos) : super.getBlockState(pos); + } + + @Override + public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newState, int flags) { + minPos.setX(Math.min(minPos.getX(), pos.getX())); + minPos.setY(Math.min(minPos.getY(), pos.getY())); + minPos.setZ(Math.min(minPos.getZ(), pos.getZ())); + maxPos.setX(Math.max(maxPos.getX(), pos.getX())); + maxPos.setY(Math.max(maxPos.getY(), pos.getY())); + maxPos.setZ(Math.max(maxPos.getZ(), pos.getZ())); + return super.setBlockState(pos, newState, flags); + } + + public Vector3f getSize() { + Vector3f result = new Vector3f(); + result.setX(maxPos.getX() - minPos.getX() + 1); + result.setY(maxPos.getY() - minPos.getY() + 1); + result.setZ(maxPos.getZ() - minPos.getZ() + 1); + return result; + } + + public Vector3f getMinPos() { + return minPos; + } + + public Vector3f getMaxPos() { + return maxPos; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java new file mode 100644 index 000000000..f9dedbe8e --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java @@ -0,0 +1,444 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.utils.Color; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.vector.Vector3f; + +import java.awt.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Created with IntelliJ IDEA. + * + * @Author: KilaBash + * @Date: 2021/08/23 + * @Description: Abstract class, and extend a lot of features compared with the original one. + */ +@SideOnly(Side.CLIENT) +public abstract class WorldSceneRenderer { + + protected static final FloatBuffer MODELVIEW_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + protected static final FloatBuffer PROJECTION_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + protected static final IntBuffer VIEWPORT_BUFFER = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()) + .asIntBuffer(); + protected static final FloatBuffer PIXEL_DEPTH_BUFFER = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()) + .asFloatBuffer(); + protected static final FloatBuffer OBJECT_POS_BUFFER = ByteBuffer.allocateDirect(3 * 4) + .order(ByteOrder.nativeOrder()).asFloatBuffer(); + + public final World world; + public final Map, ISceneRenderHook> renderedBlocksMap; + private Consumer beforeRender; + private Consumer afterRender; + private Consumer onLookingAt; + private int clearColor; + private RayTraceResult lastTraceResult; + private Vector3f eyePos = new Vector3f(0, 0, 10f); + private Vector3f lookAt = new Vector3f(0, 0, 0); + private Vector3f worldUp = new Vector3f(0, 1, 0); + + public WorldSceneRenderer(World world) { + this.world = world; + renderedBlocksMap = new LinkedHashMap<>(); + } + + public WorldSceneRenderer setBeforeWorldRender(Consumer callback) { + this.beforeRender = callback; + return this; + } + + public WorldSceneRenderer setAfterWorldRender(Consumer callback) { + this.afterRender = callback; + return this; + } + + public WorldSceneRenderer addRenderedBlocks(Collection blocks, ISceneRenderHook renderHook) { + if (blocks != null) { + this.renderedBlocksMap.put(blocks, renderHook); + } + return this; + } + + public WorldSceneRenderer setOnLookingAt(Consumer onLookingAt) { + this.onLookingAt = onLookingAt; + return this; + } + + public void setClearColor(int clearColor) { + this.clearColor = clearColor; + } + + public RayTraceResult getLastTraceResult() { + return lastTraceResult; + } + + public void render(float x, float y, float width, float height, int mouseX, int mouseY) { + // setupCamera + Rectangle positionedRect = getPositionedRect((int) x, (int) y, (int) width, (int) height); + Rectangle mouse = getPositionedRect(mouseX, mouseY, 0, 0); + mouseX = mouse.x; + mouseY = mouse.y; + setupCamera(positionedRect); + // render TrackedDummyWorld + drawWorld(); + // check lookingAt + this.lastTraceResult = null; + if (onLookingAt != null && mouseX > positionedRect.x && + mouseX < positionedRect.x + positionedRect.width && mouseY > positionedRect.y && + mouseY < positionedRect.y + positionedRect.height) { + Vector3f hitPos = unProject(mouseX, mouseY); + RayTraceResult result = rayTrace(hitPos); + if (result != null) { + this.lastTraceResult = null; + this.lastTraceResult = result; + onLookingAt.accept(result); + } + } + // resetCamera + resetCamera(); + } + + public Vector3f getEyePos() { + return eyePos; + } + + public Vector3f getLookAt() { + return lookAt; + } + + public Vector3f getWorldUp() { + return worldUp; + } + + public void setCameraLookAt(Vector3f eyePos, Vector3f lookAt, Vector3f worldUp) { + this.eyePos = eyePos; + this.lookAt = lookAt; + this.worldUp = worldUp; + } + + public void setCameraLookAt(Vector3f lookAt, double radius, double rotationPitch, double rotationYaw) { + this.lookAt = lookAt; + Vector3f pos = new Vector3f((float) Math.cos(rotationPitch), (float) 0, (float) Math.sin(rotationPitch)); + pos.y += (float) (Math.tan(rotationYaw) * pos.length()); + pos.normalise().scale((float) radius); + this.eyePos = pos.translate(lookAt.x, lookAt.y, lookAt.z); + } + + protected Rectangle getPositionedRect(int x, int y, int width, int height) { + return new Rectangle(x, y, width, height); + } + + protected void setupCamera(Rectangle rect) { + int x = rect.x; + int y = rect.y; + int width = rect.width; + int height = rect.height; + + GlStateManager.pushAttrib(); + + Minecraft.getMinecraft().entityRenderer.disableLightmap(); + GlStateManager.disableLighting(); + GlStateManager.enableDepth(); + GlStateManager.enableBlend(); + + // setup viewport and clear GL buffers + GlStateManager.viewport(x, y, width, height); + + clearView(x, y, width, height); + + // setup projection matrix to perspective + GlStateManager.matrixMode(GL11.GL_PROJECTION); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + + float aspectRatio = width / (height * 1.0f); + GLU.gluPerspective(60.0f, aspectRatio, 0.1f, 10000.0f); + + // setup modelview matrix + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + GLU.gluLookAt(eyePos.x, eyePos.y, eyePos.z, lookAt.x, lookAt.y, lookAt.z, worldUp.x, worldUp.y, worldUp.z); + } + + protected void clearView(int x, int y, int width, int height) { + Color.setGlColor(clearColor); + GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + } + + protected static void resetCamera() { + // reset viewport + Minecraft minecraft = Minecraft.getMinecraft(); + GlStateManager.viewport(0, 0, minecraft.displayWidth, minecraft.displayHeight); + + // reset projection matrix + GlStateManager.matrixMode(GL11.GL_PROJECTION); + GlStateManager.popMatrix(); + + // reset modelview matrix + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + GlStateManager.popMatrix(); + + GlStateManager.disableBlend(); + GlStateManager.disableDepth(); + + // reset attributes + GlStateManager.popAttrib(); + } + + protected void drawWorld() { + if (beforeRender != null) { + beforeRender.accept(this); + } + + Minecraft mc = Minecraft.getMinecraft(); + GlStateManager.enableCull(); + GlStateManager.enableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + mc.entityRenderer.disableLightmap(); + mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + BlockRenderLayer oldRenderLayer = MinecraftForgeClient.getRenderLayer(); + GlStateManager.disableLighting(); + GlStateManager.enableTexture2D(); + GlStateManager.enableAlpha(); + + try { // render block in each layer + for (BlockRenderLayer layer : BlockRenderLayer.values()) { + ForgeHooksClient.setRenderLayer(layer); + int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; + + renderedBlocksMap.forEach((renderedBlocks, hook) -> { + if (hook != null) { + hook.apply(false, pass, layer); + } else { + setDefaultPassRenderState(pass); + } + + BufferBuilder buffer = Tessellator.getInstance().getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); + BlockRendererDispatcher blockrendererdispatcher = mc.getBlockRendererDispatcher(); + + for (BlockPos pos : renderedBlocks) { + IBlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + if (block == Blocks.AIR) continue; + state = state.getActualState(world, pos); + if (block.canRenderInLayer(state, layer)) { + blockrendererdispatcher.renderBlock(state, pos, world, buffer); + } + } + + Tessellator.getInstance().draw(); + Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); + }); + } + } finally { + ForgeHooksClient.setRenderLayer(oldRenderLayer); + } + + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableLighting(); + + // render TESR + for (int pass = 0; pass < 2; pass++) { + ForgeHooksClient.setRenderPass(pass); + int finalPass = pass; + renderedBlocksMap.forEach((renderedBlocks, hook) -> { + if (hook != null) { + hook.apply(true, finalPass, null); + } else { + setDefaultPassRenderState(finalPass); + } + for (BlockPos pos : renderedBlocks) { + TileEntity tile = world.getTileEntity(pos); + if (tile != null) { + if (tile.shouldRenderInPass(finalPass)) { + TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); + } + } + } + }); + } + ForgeHooksClient.setRenderPass(-1); + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + + if (afterRender != null) { + afterRender.accept(this); + } + } + + public static void setDefaultPassRenderState(int pass) { + GlStateManager.color(1, 1, 1, 1); + if (pass == 0) { // SOLID + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + } else { // TRANSLUCENT + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.depthMask(false); + } + } + + public RayTraceResult rayTrace(Vector3f hitPos) { + Vec3d startPos = new Vec3d(this.eyePos.x, this.eyePos.y, this.eyePos.z); + hitPos.scale(2); // Double view range to ensure pos can be seen. + Vec3d endPos = new Vec3d((hitPos.x - startPos.x), (hitPos.y - startPos.y), (hitPos.z - startPos.z)); + return this.world.rayTraceBlocks(startPos, endPos); + } + + public static Vector3f project(BlockPos pos) { + // read current rendering parameters + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); + GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); + GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); + + // rewind buffers after write by OpenGL glGet calls + MODELVIEW_MATRIX_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + VIEWPORT_BUFFER.rewind(); + + // call gluProject with retrieved parameters + GLU.gluProject(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, MODELVIEW_MATRIX_BUFFER, + PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, OBJECT_POS_BUFFER); + + // rewind buffers after read by gluProject + VIEWPORT_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + MODELVIEW_MATRIX_BUFFER.rewind(); + + // rewind buffer after write by gluProject + OBJECT_POS_BUFFER.rewind(); + + // obtain position in Screen + float winX = OBJECT_POS_BUFFER.get(); + float winY = OBJECT_POS_BUFFER.get(); + float winZ = OBJECT_POS_BUFFER.get(); + + // rewind buffer after read + OBJECT_POS_BUFFER.rewind(); + + return new Vector3f(winX, winY, winZ); + } + + public static Vector3f unProject(int mouseX, int mouseY) { + // read depth of pixel under mouse + GL11.glReadPixels(mouseX, mouseY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, PIXEL_DEPTH_BUFFER); + + // rewind buffer after write by glReadPixels + PIXEL_DEPTH_BUFFER.rewind(); + + // retrieve depth from buffer (0.0-1.0f) + float pixelDepth = PIXEL_DEPTH_BUFFER.get(); + + // rewind buffer after read + PIXEL_DEPTH_BUFFER.rewind(); + + // read current rendering parameters + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); + GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); + GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); + + // rewind buffers after write by OpenGL glGet calls + MODELVIEW_MATRIX_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + VIEWPORT_BUFFER.rewind(); + + // call gluUnProject with retrieved parameters + GLU.gluUnProject(mouseX, mouseY, pixelDepth, MODELVIEW_MATRIX_BUFFER, PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, + OBJECT_POS_BUFFER); + + // rewind buffers after read by gluUnProject + VIEWPORT_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + MODELVIEW_MATRIX_BUFFER.rewind(); + + // rewind buffer after write by gluUnProject + OBJECT_POS_BUFFER.rewind(); + + // obtain absolute position in world + float posX = OBJECT_POS_BUFFER.get(); + float posY = OBJECT_POS_BUFFER.get(); + float posZ = OBJECT_POS_BUFFER.get(); + + // rewind buffer after read + OBJECT_POS_BUFFER.rewind(); + + return new Vector3f(posX, posY, posZ); + } + + /*** + * For better performance, You'd better handle the event {@link #setOnLookingAt(Consumer)} or + * {@link #getLastTraceResult()} + * + * @param mouseX xPos in Texture + * @param mouseY yPos in Texture + * @return RayTraceResult Hit + */ + protected RayTraceResult screenPos2BlockPosFace(int mouseX, int mouseY, int x, int y, int width, int height) { + // render a frame + GlStateManager.enableDepth(); + setupCamera(getPositionedRect(x, y, width, height)); + + drawWorld(); + + Vector3f hitPos = unProject(mouseX, mouseY); + RayTraceResult result = rayTrace(hitPos); + + resetCamera(); + + return result; + } + + /*** + * For better performance, You'd better do project in {@link #setAfterWorldRender(Consumer)} + * + * @param pos BlockPos + * @param depth should pass Depth Test + * @return x, y, z + */ + protected Vector3f blockPos2ScreenPos(BlockPos pos, boolean depth, int x, int y, int width, int height) { + // render a frame + GlStateManager.enableDepth(); + setupCamera(getPositionedRect(x, y, width, height)); + + drawWorld(); + Vector3f winPos = project(pos); + + resetCamera(); + + return winPos; + } +} From 3447b7feadd88907e6df5ac13e5571a676d0ebf1 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sun, 24 Dec 2023 08:12:39 +0100 Subject: [PATCH 2/9] first working fake world renderer --- .../modularui/drawable/FakeWorld.java | 40 +++++++++++++++++++ .../modularui/test/EventHandler.java | 2 +- .../modularui/test/ResizerTest.java | 20 +++++----- .../utils/fakeworld/TrackedDummyWorld.java | 9 +++++ .../utils/fakeworld/WorldSceneRenderer.java | 34 +++++++--------- 5 files changed, 76 insertions(+), 29 deletions(-) create mode 100644 src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java diff --git a/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java b/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java new file mode 100644 index 000000000..9c89a4d0a --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java @@ -0,0 +1,40 @@ +package com.cleanroommc.modularui.drawable; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.fakeworld.FBOWorldSceneRenderer; +import com.cleanroommc.modularui.utils.fakeworld.TrackedDummyWorld; +import com.cleanroommc.modularui.utils.fakeworld.WorldSceneRenderer; + +import net.minecraft.client.shader.Framebuffer; + +public class FakeWorld implements IDrawable { + + private static Framebuffer FBO; + private final WorldSceneRenderer renderer; + + public FakeWorld(WorldSceneRenderer renderer) { + this.renderer = renderer; + } + + public FakeWorld(TrackedDummyWorld world) { + if (FBO == null) { + FBO = new Framebuffer(1080, 1080, true); + } + this.renderer = new FBOWorldSceneRenderer(world, FBO); + double pitch = Math.PI / 4; + double yaw = Math.PI / 4; + this.renderer.setCameraLookAt(world.getCenter(), 10, pitch, yaw); + this.renderer.addRenderedBlocks(world.renderedBlocks, null); + + /*this.renderer.setBeforeWorldRender(renderer -> { + renderer.setCameraLookAt(world.getCenter(), world.getMaxSize() / 2 + 2, pitch, yaw); + });*/ + } + + @Override + public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + this.renderer.render(x, y, width, height, context.getAbsMouseX(), context.getAbsMouseY()); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java index 6279ae347..ebd287748 100644 --- a/src/main/java/com/cleanroommc/modularui/test/EventHandler.java +++ b/src/main/java/com/cleanroommc/modularui/test/EventHandler.java @@ -16,7 +16,7 @@ public static void onItemUse(PlayerInteractEvent.RightClickItem event) { .inFrontOf(Minecraft.getMinecraft().player, 5, false) .screenScale(0.5f) .open(new TestGui());*/ - ClientGUI.open(new TestGui()); + ClientGUI.open(new ResizerTest()); } } } diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index f3eb14abb..f56cc0e12 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -1,14 +1,20 @@ package com.cleanroommc.modularui.test; +import com.cleanroommc.modularui.drawable.FakeWorld; import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.CustomModularScreen; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.utils.Alignment; +import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; +import com.cleanroommc.modularui.utils.fakeworld.TrackedDummyWorld; import com.cleanroommc.modularui.widgets.ButtonWidget; import com.cleanroommc.modularui.widgets.layout.Column; import com.cleanroommc.modularui.widgets.layout.Row; +import net.minecraft.init.Blocks; +import net.minecraft.util.math.BlockPos; + import org.jetbrains.annotations.NotNull; public class ResizerTest extends CustomModularScreen { @@ -23,16 +29,12 @@ public class ResizerTest extends CustomModularScreen { .background(new SpriteDrawable(sprite)) .size(20) .align(Alignment.Center));*/ + TrackedDummyWorld world = new TrackedDummyWorld(); + world.addBlock(new BlockPos(0, 0, 0), new BlockInfo(Blocks.DIAMOND_BLOCK.getDefaultState())); + world.addBlock(new BlockPos(0, 1, 0), new BlockInfo(Blocks.BEDROCK.getDefaultState())); + world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState())); return ModularPanel.defaultPanel("main") .size(150) - .child(new Column() - .alignX(0.5f) - .heightRel(1f) - .margin(0, 7) - .coverChildrenWidth() - .mainAxisAlignment(Alignment.MainAxis.SPACE_BETWEEN) - .child(new ButtonWidget<>().width(40)) - .child(new Row().height(30).widthRel(1f).background(GuiTextures.CHECKBOARD).debugName("row")) - .child(new ButtonWidget<>())); + .overlay(new FakeWorld(world).asIcon().size(140)); } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java index 77dc484d9..726a20a68 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java @@ -75,6 +75,15 @@ public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newStat return super.setBlockState(pos, newState, flags); } + public Vector3f getCenter() { + Vector3f center = (Vector3f) Vector3f.sub(maxPos, minPos, null).scale(0.5f); + return Vector3f.add(center, minPos, center); + } + + public float getMaxSize() { + return Math.max(maxPos.x - minPos.x, Math.max(maxPos.y - minPos.y, maxPos.z - minPos.z)); + } + public Vector3f getSize() { Vector3f result = new Vector3f(); result.setX(maxPos.getX() - minPos.getX() + 1); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java index f9dedbe8e..9cb754832 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java @@ -21,6 +21,7 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import org.apache.commons.lang3.tuple.Pair; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; import org.lwjgl.util.vector.Vector3f; @@ -30,17 +31,12 @@ import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; +import java.util.List; import java.util.function.Consumer; /** - * Created with IntelliJ IDEA. - * - * @Author: KilaBash - * @Date: 2021/08/23 - * @Description: Abstract class, and extend a lot of features compared with the original one. + * Abstract class, and extend a lot of features compared with the original one. */ @SideOnly(Side.CLIENT) public abstract class WorldSceneRenderer { @@ -57,7 +53,7 @@ public abstract class WorldSceneRenderer { .order(ByteOrder.nativeOrder()).asFloatBuffer(); public final World world; - public final Map, ISceneRenderHook> renderedBlocksMap; + public final List, ISceneRenderHook>> renderedBlocksMap; private Consumer beforeRender; private Consumer afterRender; private Consumer onLookingAt; @@ -69,7 +65,7 @@ public abstract class WorldSceneRenderer { public WorldSceneRenderer(World world) { this.world = world; - renderedBlocksMap = new LinkedHashMap<>(); + renderedBlocksMap = new ArrayList<>(); } public WorldSceneRenderer setBeforeWorldRender(Consumer callback) { @@ -84,7 +80,7 @@ public WorldSceneRenderer setAfterWorldRender(Consumer callb public WorldSceneRenderer addRenderedBlocks(Collection blocks, ISceneRenderHook renderHook) { if (blocks != null) { - this.renderedBlocksMap.put(blocks, renderHook); + this.renderedBlocksMap.add(Pair.of(blocks, renderHook)); } return this; } @@ -237,9 +233,9 @@ protected void drawWorld() { ForgeHooksClient.setRenderLayer(layer); int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; - renderedBlocksMap.forEach((renderedBlocks, hook) -> { - if (hook != null) { - hook.apply(false, pass, layer); + renderedBlocksMap.forEach(pair -> { + if (pair.getValue() != null) { + pair.getValue().apply(false, pass, layer); } else { setDefaultPassRenderState(pass); } @@ -248,7 +244,7 @@ protected void drawWorld() { buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); BlockRendererDispatcher blockrendererdispatcher = mc.getBlockRendererDispatcher(); - for (BlockPos pos : renderedBlocks) { + for (BlockPos pos : pair.getKey()) { IBlockState state = world.getBlockState(pos); Block block = state.getBlock(); if (block == Blocks.AIR) continue; @@ -273,13 +269,13 @@ protected void drawWorld() { for (int pass = 0; pass < 2; pass++) { ForgeHooksClient.setRenderPass(pass); int finalPass = pass; - renderedBlocksMap.forEach((renderedBlocks, hook) -> { - if (hook != null) { - hook.apply(true, finalPass, null); + renderedBlocksMap.forEach(pair -> { + if (pair.getValue() != null) { + pair.getValue().apply(true, finalPass, null); } else { setDefaultPassRenderState(finalPass); } - for (BlockPos pos : renderedBlocks) { + for (BlockPos pos : pair.getKey()) { TileEntity tile = world.getTileEntity(pos); if (tile != null) { if (tile.shouldRenderInPass(finalPass)) { From e4ff4547f8318ac48f6faec9c0b2f3a1997daa70 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Thu, 28 Dec 2023 19:00:49 +0100 Subject: [PATCH 3/9] lots of stuff --- .../modularui/drawable/FakeWorld.java | 3 - .../modularui/screen/GuiScreenWrapper.java | 1 + .../modularui/test/ResizerTest.java | 16 +- .../cleanroommc/modularui/utils/GuiUtils.java | 10 +- .../modularui/utils/fakeworld/BlockInfo.java | 123 ++++- .../utils/fakeworld/BlockPosUtil.java | 80 ++++ .../modularui/utils/fakeworld/BoxSchema.java | 47 ++ .../modularui/utils/fakeworld/Camera.java | 59 +++ .../fakeworld/FBOWorldSceneRenderer.java | 136 ------ .../utils/fakeworld/ISceneRenderHook.java | 8 - .../modularui/utils/fakeworld/ISchema.java | 18 + .../ImmediateWorldSceneRenderer.java | 42 -- .../utils/fakeworld/PosListSchema.java | 50 ++ .../modularui/utils/fakeworld/Projection.java | 87 ++++ .../utils/fakeworld/SchemaRenderer.java | 280 +++++++++++ .../utils/fakeworld/SchemaWorld.java | 88 ++++ .../utils/fakeworld/SimpleSchema.java | 117 +++++ .../modularui/utils/fakeworld/Structure.java | 36 ++ .../utils/fakeworld/TrackedDummyWorld.java | 102 ---- .../utils/fakeworld/WorldSceneRenderer.java | 440 ------------------ .../modularui/widget/sizer/Area.java | 5 + 21 files changed, 985 insertions(+), 763 deletions(-) create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/Projection.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java diff --git a/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java b/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java index 9c89a4d0a..62cf0248f 100644 --- a/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java +++ b/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java @@ -3,9 +3,6 @@ import com.cleanroommc.modularui.api.drawable.IDrawable; import com.cleanroommc.modularui.screen.viewport.GuiContext; import com.cleanroommc.modularui.theme.WidgetTheme; -import com.cleanroommc.modularui.utils.fakeworld.FBOWorldSceneRenderer; -import com.cleanroommc.modularui.utils.fakeworld.TrackedDummyWorld; -import com.cleanroommc.modularui.utils.fakeworld.WorldSceneRenderer; import net.minecraft.client.shader.Framebuffer; diff --git a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java index 5a3070532..23bde0e92 100644 --- a/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java +++ b/src/main/java/com/cleanroommc/modularui/screen/GuiScreenWrapper.java @@ -37,6 +37,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; import org.lwjgl.input.Keyboard; +import org.lwjgl.opengl.GL11; import java.io.IOException; import java.util.Set; diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index f56cc0e12..6da2630ef 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -1,17 +1,13 @@ package com.cleanroommc.modularui.test; -import com.cleanroommc.modularui.drawable.FakeWorld; -import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.CustomModularScreen; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; -import com.cleanroommc.modularui.utils.fakeworld.TrackedDummyWorld; -import com.cleanroommc.modularui.widgets.ButtonWidget; -import com.cleanroommc.modularui.widgets.layout.Column; -import com.cleanroommc.modularui.widgets.layout.Row; +import com.cleanroommc.modularui.utils.fakeworld.BoxSchema; +import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; +import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; import net.minecraft.util.math.BlockPos; @@ -33,8 +29,12 @@ public class ResizerTest extends CustomModularScreen { world.addBlock(new BlockPos(0, 0, 0), new BlockInfo(Blocks.DIAMOND_BLOCK.getDefaultState())); world.addBlock(new BlockPos(0, 1, 0), new BlockInfo(Blocks.BEDROCK.getDefaultState())); world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState())); + double pitch = Math.PI / 4; + double yaw = Math.PI / 4; return ModularPanel.defaultPanel("main") .size(150) - .overlay(new FakeWorld(world).asIcon().size(140)); + .overlay(new SchemaRenderer(BoxSchema.of(Minecraft.getMinecraft().world, new BlockPos(Minecraft.getMinecraft().player), 5)) + .cameraFunc((camera, schema) -> camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 10, pitch, yaw)) + .asIcon().size(140)); } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java b/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java index 611b38dac..f600bf91e 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java +++ b/src/main/java/com/cleanroommc/modularui/utils/GuiUtils.java @@ -25,23 +25,23 @@ public static Matrix4f getTransformationMatrix() { } public static Matrix4f getTransformationMatrix(Matrix4f matrix4f) { - floatBuffer.position(0); + floatBuffer.rewind(); getTransformationBuffer(floatBuffer); matrix4f.load(floatBuffer); return matrix4f; } public static void setTransformationMatrix(Matrix4f matrix) { - floatBuffer.position(0); + floatBuffer.rewind(); matrix.store(floatBuffer); - floatBuffer.position(0); + floatBuffer.rewind(); GL11.glLoadMatrix(floatBuffer); } public static void applyTransformationMatrix(Matrix4f matrix) { - floatBuffer.position(0); + floatBuffer.rewind(); matrix.store(floatBuffer); - floatBuffer.position(0); + floatBuffer.rewind(); GL11.glMultMatrix(floatBuffer); } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java index ee6db424f..44e4effa7 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java @@ -5,9 +5,12 @@ import net.minecraft.init.Blocks; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import com.google.common.base.Preconditions; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * BlockInfo represents immutable information for block in world @@ -17,29 +20,33 @@ public class BlockInfo { public static final BlockInfo EMPTY = new BlockInfo(Blocks.AIR); + public static final BlockInfo INVALID = new BlockInfo(Blocks.AIR); - private final IBlockState blockState; - private final TileEntity tileEntity; - private final Object info; + public static BlockInfo of(IBlockAccess world, BlockPos pos) { + IBlockState blockState = world.getBlockState(pos); + if (blockState.getBlock().isAir(blockState, world, pos)) { + return EMPTY; + } + TileEntity tile = null; + if (blockState.getBlock().hasTileEntity(blockState)) { + tile = world.getTileEntity(pos); + } + return new BlockInfo(blockState, tile); + } - public BlockInfo(Block block) { + private IBlockState blockState; + private TileEntity tileEntity; + + public BlockInfo(@NotNull Block block) { this(block.getDefaultState()); } - public BlockInfo(IBlockState blockState) { + public BlockInfo(@NotNull IBlockState blockState) { this(blockState, null); } - public BlockInfo(IBlockState blockState, TileEntity tileEntity) { - this(blockState, tileEntity, null); - } - - public BlockInfo(IBlockState blockState, TileEntity tileEntity, Object info) { - this.blockState = blockState; - this.tileEntity = tileEntity; - this.info = info; - Preconditions.checkArgument(tileEntity == null || blockState.getBlock().hasTileEntity(blockState), - "Cannot create block info with tile entity for block not having it"); + public BlockInfo(@NotNull IBlockState blockState, @Nullable TileEntity tileEntity) { + set(blockState, tileEntity); } public IBlockState getBlockState() { @@ -50,14 +57,92 @@ public TileEntity getTileEntity() { return tileEntity; } - public Object getInfo() { - return info; - } - public void apply(World world, BlockPos pos) { world.setBlockState(pos, blockState); if (tileEntity != null) { world.setTileEntity(pos, tileEntity); + } else { + tileEntity = world.getTileEntity(pos); + } + } + + BlockInfo set(IBlockState state, TileEntity tile) { + Preconditions.checkNotNull(state, "Block state must not be null!"); + Preconditions.checkArgument(tile == null || state.getBlock().hasTileEntity(state), + "Cannot create block info with tile entity for block not having it!"); + this.blockState = state; + this.tileEntity = tile; + return this; + } + + public boolean isMutable() { + return false; + } + + public Mut toMutable() { + return new Mut(this.blockState, this.tileEntity); + } + + public BlockInfo toImmutable() { + return this; + } + + public BlockInfo copy() { + return new BlockInfo(this.blockState, this.tileEntity); + } + + public static class Mut extends BlockInfo { + + public static final Mut SHARED = new Mut(); + + public Mut() { + this(Blocks.AIR); + } + + public Mut(@NotNull Block block) { + super(block); + } + + public Mut(@NotNull IBlockState blockState) { + super(blockState); + } + + public Mut(@NotNull IBlockState blockState, @Nullable TileEntity tileEntity) { + super(blockState, tileEntity); + } + + @Override + public Mut set(IBlockState state, TileEntity tile) { + return (Mut) super.set(state, tile); + } + + public Mut set(IBlockAccess world, BlockPos pos) { + IBlockState blockState = world.getBlockState(pos); + TileEntity tile = null; + if (blockState.getBlock().hasTileEntity(blockState)) { + tile = world.getTileEntity(pos); + } + return set(blockState, tile); + } + + @Override + public boolean isMutable() { + return true; + } + + @Override + public Mut toMutable() { + return this; + } + + @Override + public BlockInfo toImmutable() { + return new BlockInfo(getBlockState(), getTileEntity()); + } + + @Override + public Mut copy() { + return new Mut(getBlockState(), getTileEntity()); } } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java new file mode 100644 index 000000000..b7c72f52b --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java @@ -0,0 +1,80 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public class BlockPosUtil { + + public static final BlockPos MAX = new BlockPos(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + public static final BlockPos MIN = new BlockPos(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + + public static int getManhattanDistance(BlockPos p1, BlockPos p2) { + return getXDist(p1, p2) + getYDist(p1, p2) + getZDist(p1, p2); + } + + public static int getBlockCountInside(BlockPos p1, BlockPos p2) { + return getXDist(p1, p2) * getYDist(p1, p2) * getZDist(p1, p2); + } + + public static int getXDist(BlockPos p1, BlockPos p2) { + return Math.abs(p1.getX() - p2.getX()); + } + + public static int getYDist(BlockPos p1, BlockPos p2) { + return Math.abs(p1.getY() - p2.getY()); + } + + public static int getZDist(BlockPos p1, BlockPos p2) { + return Math.abs(p1.getZ() - p2.getZ()); + } + + public static BlockPos getMin(BlockPos p1, BlockPos p2) { + return new BlockPos(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()), Math.min(p1.getZ(), p2.getZ())); + } + + public static BlockPos getMax(BlockPos p1, BlockPos p2) { + return new BlockPos(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.max(p1.getZ(), p2.getZ())); + } + + public static void setMin(BlockPos.MutableBlockPos p1, BlockPos p2) { + p1.setPos(Math.min(p1.getX(), p2.getX()), Math.min(p1.getY(), p2.getY()), Math.min(p1.getZ(), p2.getZ())); + } + + public static void setMax(BlockPos.MutableBlockPos p1, BlockPos p2) { + p1.setPos(Math.max(p1.getX(), p2.getX()), Math.max(p1.getY(), p2.getY()), Math.max(p1.getZ(), p2.getZ())); + } + + public static BlockPos getCenter(BlockPos p1, BlockPos p2) { + BlockPos min = getMin(p1, p2); + return new BlockPos(getXDist(p1, p2) / 2 + min.getX(), getYDist(p1, p2) / 2 + min.getY(), getYDist(p1, p2) / 2 + min.getY()); + } + + public static Vec3d getCenterD(BlockPos p1, BlockPos p2) { + return getCenterD(getMin(p1, p2), getXDist(p1, p2), getYDist(p1, p2), getZDist(p1, p2)); + } + + public static Vec3d getCenterD(BlockPos origin, int xs, int ys, int zs) { + return new Vec3d(xs / 2.0 + origin.getX(), ys / 2.0 + origin.getY(), zs / 2.0 + origin.getY()); + } + + public static Iterable getAllInside(BlockPos p1, BlockPos p2, boolean includeBorder) { + int x0 = Math.min(p1.getX(), p2.getX()), y0 = Math.min(p1.getY(), p2.getY()), z0 = Math.min(p1.getZ(), p2.getZ()); + int x1 = Math.max(p1.getX(), p2.getX()), y1 = Math.max(p1.getY(), p2.getY()), z1 = Math.max(p1.getZ(), p2.getZ()); + if (includeBorder) { + x0--; + y0--; + z0--; + } else { + x1--; + y1--; + z1--; + } + return BlockPos.getAllInBoxMutable(x0, y0, z0, x1, y1, z1); + } + + public static boolean isOnBorder(BlockPos boxMin, BlockPos boxMax, BlockPos p) { + return p.getX() == boxMin.getX() || p.getX() == boxMax.getX() || + p.getY() == boxMin.getY() || p.getY() == boxMax.getY() || + p.getZ() == boxMin.getZ() || p.getZ() == boxMax.getZ(); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java new file mode 100644 index 000000000..3cbfa74d9 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java @@ -0,0 +1,47 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class BoxSchema extends PosListSchema { + + public static BoxSchema of(World world, BlockPos center, int r) { + return new BoxSchema(world, center.add(-r, -r, -r), center.add(r, r, r)); + } + + private final World world; + private final BlockPos min, max; + private final Vec3d center; + + public BoxSchema(World world, BlockPos min, BlockPos max) { + super(world, BlockPosUtil.getAllInside(min, max, false)); + this.world = world; + this.min = BlockPosUtil.getMin(min, max); + this.max = BlockPosUtil.getMax(min, max); + this.center = BlockPosUtil.getCenterD(min, max); + } + + @Override + public World getWorld() { + return world; + } + + @Override + public Vec3d getFocus() { + return center; + } + + @Override + public BlockPos getOrigin() { + return min; + } + + public BlockPos getMin() { + return min; + } + + public BlockPos getMax() { + return max; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java new file mode 100644 index 000000000..6a9865c2c --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java @@ -0,0 +1,59 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.Vec3i; + +import org.lwjgl.util.vector.Vector3f; + +public class Camera { + + public static final double PI2 = 2 * Math.PI; + + private final Vector3f pos; + private final Vector3f lookAt; + + public Camera(Vector3f pos, Vector3f lookAt) { + this.pos = pos; + this.lookAt = lookAt; + } + + public Camera setLookAt(Vector3f pos, Vector3f lookAt) { + this.pos.set(pos); + this.lookAt.set(lookAt); + return this; + } + + public Camera setLookAt(float x, float y, float z) { + this.lookAt.set(x, y, z); + return this; + } + + public Camera setPos(float x, float y, float z) { + this.lookAt.set(x, y, z); + return this; + } + + public Camera setLookAt(Vector3f lookAt, double radius, double rotationPitch, double rotationYaw) { + return setLookAt(lookAt.x, lookAt.y, lookAt.z, radius, rotationPitch, rotationYaw); + } + + public Camera setLookAt(Vec3i lookAt, double radius, double rotationPitch, double rotationYaw) { + return setLookAt(lookAt.getX(), lookAt.getY(), lookAt.getZ(), radius, rotationPitch, rotationYaw); + } + + public Camera setLookAt(float lookAtX, float lookAtY, float lookAtZ, double radius, double rotationPitch, double rotationYaw) { + setLookAt(lookAtX, lookAtY, lookAtZ); + Vector3f pos = new Vector3f((float) Math.cos(rotationPitch), (float) 0, (float) Math.sin(rotationPitch)); + pos.y += (float) (Math.tan(rotationYaw) * pos.length()); + pos.normalise().scale((float) radius); + this.pos.set(pos.translate(lookAtX, lookAtY, lookAtZ)); + return this; + } + + public Vector3f getPos() { + return pos; + } + + public Vector3f getLookAt() { + return lookAt; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java deleted file mode 100644 index c6cd637ac..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/FBOWorldSceneRenderer.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import com.cleanroommc.modularui.ModularUI; - -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.shader.Framebuffer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -import org.lwjgl.opengl.EXTFramebufferObject; -import org.lwjgl.opengl.GL11; -import org.lwjgl.util.vector.Vector3f; - -/** - * It looks similar to {@link ImmediateWorldSceneRenderer}, but totally different. - * It uses FBO and is more universality and efficient(X). - * FBO can be rendered anywhere more flexibly, not just in the GUI. - * If you have scene rendering needs, you will love this FBO renderer. - * TODO OP_LIST might be used in the future to further improve performance. - */ -@SideOnly(Side.CLIENT) -public class FBOWorldSceneRenderer extends WorldSceneRenderer { - - private int resolutionWidth = 1080; - private int resolutionHeight = 1080; - private Framebuffer fbo; - - public FBOWorldSceneRenderer(World world, int resolutionWidth, int resolutionHeight) { - super(world); - setFBOSize(resolutionWidth, resolutionHeight); - } - - public FBOWorldSceneRenderer(World world, Framebuffer fbo) { - super(world); - this.fbo = fbo; - } - - public int getResolutionWidth() { - return resolutionWidth; - } - - public int getResolutionHeight() { - return resolutionHeight; - } - - /*** - * This will modify the size of the FBO. You'd better know what you're doing before you call it. - */ - public void setFBOSize(int resolutionWidth, int resolutionHeight) { - this.resolutionWidth = resolutionWidth; - this.resolutionHeight = resolutionHeight; - releaseFBO(); - try { - fbo = new Framebuffer(resolutionWidth, resolutionHeight, true); - } catch (Exception e) { - ModularUI.LOGGER.error(e); - } - } - - public RayTraceResult screenPos2BlockPosFace(int mouseX, int mouseY) { - int lastID = bindFBO(); - RayTraceResult looking = super.screenPos2BlockPosFace(mouseX, mouseY, 0, 0, this.resolutionWidth, - this.resolutionHeight); - unbindFBO(lastID); - return looking; - } - - public Vector3f blockPos2ScreenPos(BlockPos pos, boolean depth) { - int lastID = bindFBO(); - Vector3f winPos = super.blockPos2ScreenPos(pos, depth, 0, 0, this.resolutionWidth, this.resolutionHeight); - unbindFBO(lastID); - return winPos; - } - - public void render(float x, float y, float width, float height, float mouseX, float mouseY) { - // bind to FBO - int lastID = bindFBO(); - super.render(0, 0, this.resolutionWidth, this.resolutionHeight, (int) (this.resolutionWidth * mouseX / width), - (int) (this.resolutionHeight * (1 - mouseY / height))); - // unbind FBO - unbindFBO(lastID); - - // bind FBO as texture - GlStateManager.enableTexture2D(); - GlStateManager.disableLighting(); - lastID = GL11.glGetInteger(GL11.GL_TEXTURE_2D); - GlStateManager.bindTexture(fbo.framebufferTexture); - GlStateManager.color(1, 1, 1, 1); - - // render rect with FBO texture - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder bufferbuilder = tessellator.getBuffer(); - bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX); - - bufferbuilder.pos(x + width, y + height, 0).tex(1, 0).endVertex(); - bufferbuilder.pos(x + width, y, 0).tex(1, 1).endVertex(); - bufferbuilder.pos(x, y, 0).tex(0, 1).endVertex(); - bufferbuilder.pos(x, y + height, 0).tex(0, 0).endVertex(); - tessellator.draw(); - - GlStateManager.bindTexture(lastID); - } - - public void render(float x, float y, float width, float height, int mouseX, int mouseY) { - render(x, y, width, height, (float) mouseX, (float) mouseY); - } - - private int bindFBO() { - int lastID = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); - fbo.setFramebufferColor(0.0F, 0.0F, 0.0F, 0.0F); - fbo.framebufferClear(); - fbo.bindFramebuffer(true); - GlStateManager.pushMatrix(); - return lastID; - } - - private void unbindFBO(int lastID) { - GlStateManager.popMatrix(); - fbo.unbindFramebufferTexture(); - OpenGlHelper.glBindFramebuffer(OpenGlHelper.GL_FRAMEBUFFER, lastID); - } - - public void releaseFBO() { - if (fbo != null) { - fbo.deleteFramebuffer(); - } - fbo = null; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java deleted file mode 100644 index d0a5de3a6..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISceneRenderHook.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import net.minecraft.util.BlockRenderLayer; - -public interface ISceneRenderHook { - - void apply(boolean isTESR, int pass, BlockRenderLayer layer); -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java new file mode 100644 index 000000000..074dcdd29 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java @@ -0,0 +1,18 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Map; + +public interface ISchema extends Iterable> { + + World getWorld(); + + Vec3d getFocus(); + + BlockPos getOrigin(); +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java deleted file mode 100644 index 6e39a66c7..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ImmediateWorldSceneRenderer.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -import org.lwjgl.opengl.GL11; - -import java.awt.*; - -@SideOnly(Side.CLIENT) -public class ImmediateWorldSceneRenderer extends WorldSceneRenderer { - - public ImmediateWorldSceneRenderer(World world) { - super(world); - } - - @Override - protected Rectangle getPositionedRect(int x, int y, int width, int height) { - Minecraft mc = Minecraft.getMinecraft(); - ScaledResolution resolution = new ScaledResolution(mc); - // compute window size from scaled width & height - int windowWidth = (int) (width / (resolution.getScaledWidth() * 1.0) * mc.displayWidth); - int windowHeight = (int) (height / (resolution.getScaledHeight() * 1.0) * mc.displayHeight); - // translate gui coordinates to window's ones (y is inverted) - int windowX = (int) (x / (resolution.getScaledWidth() * 1.0) * mc.displayWidth); - int windowY = mc.displayHeight - (int) (y / (resolution.getScaledHeight() * 1.0) * mc.displayHeight) - - windowHeight; - - return super.getPositionedRect(windowX, windowY, windowWidth, windowHeight); - } - - @Override - protected void clearView(int x, int y, int width, int height) { - GL11.glEnable(GL11.GL_SCISSOR_TEST); - GL11.glScissor(x, y, width, height); - super.clearView(x, y, width, height); - GL11.glDisable(GL11.GL_SCISSOR_TEST); - } -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java new file mode 100644 index 000000000..b8a634251 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java @@ -0,0 +1,50 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.Map; + +public abstract class PosListSchema implements ISchema { + + private final World world; + private final Iterable posList; + + public PosListSchema(World world, Iterable posList) { + this.world = world; + this.posList = posList; + } + + @Override + public World getWorld() { + return world; + } + + @NotNull + @Override + public Iterator> iterator() { + return new Iterator<>() { + + private final Iterator posIt = PosListSchema.this.posList.iterator(); + private final MutablePair pair = new MutablePair<>(); + + @Override + public boolean hasNext() { + return posIt.hasNext(); + } + + @Override + public Pair next() { + BlockPos pos = posIt.next(); + pair.setLeft(pos); + pair.setRight(BlockInfo.Mut.SHARED.set(PosListSchema.this.world, pos)); + return pair; + } + }; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Projection.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Projection.java new file mode 100644 index 000000000..8a877c633 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Projection.java @@ -0,0 +1,87 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; + +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.vector.Vector3f; + +import java.nio.FloatBuffer; +import java.nio.IntBuffer; + +public class Projection { + + public static final Projection INSTANCE = new Projection(); + + protected static final FloatBuffer MODELVIEW_MATRIX_BUFFER = BufferUtils.createFloatBuffer(16); + protected static final FloatBuffer PROJECTION_MATRIX_BUFFER = BufferUtils.createFloatBuffer(16); + protected static final IntBuffer VIEWPORT_BUFFER = BufferUtils.createIntBuffer(16); + protected static final FloatBuffer PIXEL_DEPTH_BUFFER = BufferUtils.createFloatBuffer(1); + protected static final FloatBuffer OBJECT_POS_BUFFER = BufferUtils.createFloatBuffer(3); + + private Projection() {} + + public Vector3f project(BlockPos pos) { + // read current rendering parameters + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); + GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); + GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); + + // rewind buffers after write by OpenGL glGet calls + MODELVIEW_MATRIX_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + VIEWPORT_BUFFER.rewind(); + OBJECT_POS_BUFFER.rewind(); + + // call gluProject with retrieved parameters + GLU.gluProject(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, MODELVIEW_MATRIX_BUFFER, + PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, OBJECT_POS_BUFFER); + + // rewind buffers after read + VIEWPORT_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + MODELVIEW_MATRIX_BUFFER.rewind(); + OBJECT_POS_BUFFER.rewind(); + + // obtain position in Screen + float winX = OBJECT_POS_BUFFER.get(); + float winY = OBJECT_POS_BUFFER.get(); + float winZ = OBJECT_POS_BUFFER.get(); + + return new Vector3f(winX, winY, winZ); + } + + public Vector3f unProject(int screenX, int screenY) { + // read current rendering parameters + GL11.glReadPixels(screenX, screenY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, PIXEL_DEPTH_BUFFER); + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); + GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); + GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); + + // rewind buffers after write by OpenGL glGet calls + PIXEL_DEPTH_BUFFER.rewind(); + MODELVIEW_MATRIX_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + VIEWPORT_BUFFER.rewind(); + OBJECT_POS_BUFFER.rewind(); + + // call gluUnProject with retrieved parameters + GLU.gluUnProject(screenX, screenY, PIXEL_DEPTH_BUFFER.get(), MODELVIEW_MATRIX_BUFFER, PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, + OBJECT_POS_BUFFER); + + // rewind buffers after read + PIXEL_DEPTH_BUFFER.rewind(); + VIEWPORT_BUFFER.rewind(); + PROJECTION_MATRIX_BUFFER.rewind(); + MODELVIEW_MATRIX_BUFFER.rewind(); + OBJECT_POS_BUFFER.rewind(); + + // obtain absolute position in world + float posX = OBJECT_POS_BUFFER.get(); + float posY = OBJECT_POS_BUFFER.get(); + float posZ = OBJECT_POS_BUFFER.get(); + + return new Vector3f(posX, posY, posZ); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java new file mode 100644 index 000000000..cd3dfe79a --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java @@ -0,0 +1,280 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.widget.sizer.Area; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.shader.Framebuffer; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.MinecraftForgeClient; + +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.vector.Vector3f; + +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +public class SchemaRenderer implements IDrawable { + + private static final Framebuffer FBO = new Framebuffer(1080, 1080, true); + + private final ISchema schema; + private final Framebuffer framebuffer; + private final Camera camera = new Camera(new Vector3f(), new Vector3f()); + private boolean cameraSetup = false; + private Consumer onRayTrace; + private Consumer afterRender; + private BiConsumer cameraFunc; + private int clearColor = 0; + + public SchemaRenderer(ISchema schema, Framebuffer framebuffer) { + this.schema = schema; + this.framebuffer = framebuffer; + } + + public SchemaRenderer(ISchema schema) { + this(schema, FBO); + } + + public SchemaRenderer cameraFunc(BiConsumer camera) { + this.cameraFunc = camera; + return this; + } + + public SchemaRenderer onRayTrace(Consumer consumer) { + this.onRayTrace = consumer; + return this; + } + + public SchemaRenderer afterRender(Consumer consumer) { + this.afterRender = consumer; + return this; + } + + @Override + public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { + render(x, y, width, height, context.getMouseX(), context.getMouseY()); + } + + public void render(int x, int y, int width, int height, int mouseX, int mouseY) { + if (this.cameraFunc != null) { + this.cameraFunc.accept(this.camera, this.schema); + } + int lastFbo = bindFBO(); + setupCamera(this.framebuffer.framebufferWidth, this.framebuffer.framebufferHeight); + renderWorld(); + if (this.onRayTrace != null && Area.isInside(x, y, width, height, mouseX, mouseY)) { + this.onRayTrace.accept(new IRayTracer() { + @Override + public RayTraceResult rayTrace(int screenX, int screenY) { + return SchemaRenderer.this.rayTrace(Projection.INSTANCE.unProject(screenX, screenY)); + } + + @Override + public RayTraceResult rayTraceMousePos() { + return rayTrace(mouseX, mouseY); + } + }); + } + resetCamera(); + unbindFBO(lastFbo); + + // bind FBO as texture + GlStateManager.enableTexture2D(); + GlStateManager.disableLighting(); + lastFbo = GL11.glGetInteger(GL11.GL_TEXTURE_2D); + GlStateManager.bindTexture(this.framebuffer.framebufferTexture); + GlStateManager.color(1, 1, 1, 1); + + // render rect with FBO texture + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder bufferbuilder = tessellator.getBuffer(); + bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX); + + bufferbuilder.pos(x + width, y + height, 0).tex(1, 0).endVertex(); + bufferbuilder.pos(x + width, y, 0).tex(1, 1).endVertex(); + bufferbuilder.pos(x, y, 0).tex(0, 1).endVertex(); + bufferbuilder.pos(x, y + height, 0).tex(0, 0).endVertex(); + tessellator.draw(); + + GlStateManager.bindTexture(lastFbo); + } + + private void renderWorld() { + Minecraft mc = Minecraft.getMinecraft(); + GlStateManager.enableCull(); + GlStateManager.enableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + mc.entityRenderer.disableLightmap(); + mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + BlockRenderLayer oldRenderLayer = MinecraftForgeClient.getRenderLayer(); + GlStateManager.disableLighting(); + GlStateManager.enableTexture2D(); + GlStateManager.enableAlpha(); + + try { // render block in each layer + for (BlockRenderLayer layer : BlockRenderLayer.values()) { + ForgeHooksClient.setRenderLayer(layer); + int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; + setDefaultPassRenderState(pass); + BufferBuilder buffer = Tessellator.getInstance().getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); + BlockRendererDispatcher blockrendererdispatcher = mc.getBlockRendererDispatcher(); + this.schema.forEach(pair -> { + BlockPos pos = pair.getKey(); + IBlockState state = pair.getValue().getBlockState(); + if (!state.getBlock().isAir(state, this.schema.getWorld(), pos) && state.getBlock().canRenderInLayer(state, layer)) { + blockrendererdispatcher.renderBlock(state, pos, this.schema.getWorld(), buffer); + } + }); + Tessellator.getInstance().draw(); + Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); + } + } finally { + ForgeHooksClient.setRenderLayer(oldRenderLayer); + } + + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableLighting(); + + // render TESR + for (int pass = 0; pass < 2; pass++) { + ForgeHooksClient.setRenderPass(pass); + int finalPass = pass; + GlStateManager.color(1, 1, 1, 1); + setDefaultPassRenderState(pass); + this.schema.forEach(pair -> { + BlockPos pos = pair.getKey(); + TileEntity tile = pair.getValue().getTileEntity(); + if (tile != null && tile.shouldRenderInPass(finalPass)) { + TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); + } + }); + } + ForgeHooksClient.setRenderPass(-1); + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + if (this.afterRender != null) { + this.afterRender.accept(Projection.INSTANCE); + } + } + + private static void setDefaultPassRenderState(int pass) { + GlStateManager.color(1, 1, 1, 1); + if (pass == 0) { // SOLID + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + } else { // TRANSLUCENT + GlStateManager.enableBlend(); + GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GlStateManager.depthMask(false); + } + } + + protected void setupCamera(int width, int height) { + //GlStateManager.pushAttrib(); + + Minecraft.getMinecraft().entityRenderer.disableLightmap(); + GlStateManager.disableLighting(); + GlStateManager.enableDepth(); + GlStateManager.enableBlend(); + + // setup viewport and clear GL buffers + GlStateManager.viewport(0, 0, width, height); + Color.setGlColor(clearColor); + GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + + // setup projection matrix to perspective + GlStateManager.matrixMode(GL11.GL_PROJECTION); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + + float aspectRatio = width / (height * 1.0f); + GLU.gluPerspective(60.0f, aspectRatio, 0.1f, 10000.0f); + + // setup modelview matrix + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + GlStateManager.pushMatrix(); + GlStateManager.loadIdentity(); + var c = this.camera.getPos(); + var lookAt = this.camera.getLookAt(); + GLU.gluLookAt(c.x, c.y, c.z, lookAt.x, lookAt.y, lookAt.z, 0, 1, 0); + this.cameraSetup = true; + } + + protected void resetCamera() { + this.cameraSetup = false; + // reset viewport + Minecraft minecraft = Minecraft.getMinecraft(); + GlStateManager.viewport(0, 0, minecraft.displayWidth, minecraft.displayHeight); + + // reset projection matrix + GlStateManager.matrixMode(GL11.GL_PROJECTION); + GlStateManager.popMatrix(); + + // reset modelview matrix + GlStateManager.matrixMode(GL11.GL_MODELVIEW); + GlStateManager.popMatrix(); + + GlStateManager.disableBlend(); + GlStateManager.disableDepth(); + + // reset attributes + // GlStateManager.popAttrib(); + } + + private int bindFBO() { + int lastID = GL11.glGetInteger(EXTFramebufferObject.GL_FRAMEBUFFER_BINDING_EXT); + this.framebuffer.setFramebufferColor(0.0F, 0.0F, 0.0F, 0.0F); + this.framebuffer.framebufferClear(); + this.framebuffer.bindFramebuffer(true); + GlStateManager.pushMatrix(); + return lastID; + } + + private void unbindFBO(int lastID) { + GlStateManager.popMatrix(); + this.framebuffer.unbindFramebufferTexture(); + OpenGlHelper.glBindFramebuffer(OpenGlHelper.GL_FRAMEBUFFER, lastID); + } + + private RayTraceResult rayTrace(Vector3f hitPos) { + Vec3d startPos = new Vec3d(this.camera.getPos().x, this.camera.getPos().y, this.camera.getPos().z); + hitPos.scale(2); // Double view range to ensure pos can be seen. + Vec3d endPos = new Vec3d((hitPos.x - startPos.x), (hitPos.y - startPos.y), (hitPos.z - startPos.z)); + return this.schema.getWorld().rayTraceBlocks(startPos, endPos); + } + + public boolean isCameraSetup() { + return cameraSetup; + } + + public interface IRayTracer { + + RayTraceResult rayTrace(int screenX, int screenY); + + RayTraceResult rayTraceMousePos(); + } + + public interface ICamera { + + void setupCamera(Vector3f cameraPos, Vector3f lookAt); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java new file mode 100644 index 000000000..a7b69bd3c --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java @@ -0,0 +1,88 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import org.apache.commons.lang3.tuple.MutablePair; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.Map; + +public class SchemaWorld extends DummyWorld implements ISchema { + + private final ObjectLinkedOpenHashSet blocks = new ObjectLinkedOpenHashSet<>(); + private final BlockPos.MutableBlockPos min = new BlockPos.MutableBlockPos(); + private final BlockPos.MutableBlockPos max = new BlockPos.MutableBlockPos(); + + @Override + public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newState, int flags) { + boolean b = super.setBlockState(pos, newState, flags); + if (newState.getBlock().isAir(newState, this, pos)) { + if (this.blocks.remove(pos) && BlockPosUtil.isOnBorder(min, max, pos)) { + if (this.blocks.isEmpty()) { + this.min.setPos(0, 0, 0); + this.max.setPos(0, 0, 0); + } else { + min.setPos(BlockPosUtil.MAX); + max.setPos(BlockPosUtil.MIN); + for (BlockPos pos1 : blocks) { + BlockPosUtil.setMin(min, pos1); + BlockPosUtil.setMax(max, pos1); + } + } + } + } else if (this.blocks.isEmpty()) { + this.blocks.add(pos); + this.min.setPos(pos); + this.max.setPos(pos); + } else if (this.blocks.add(pos)) { + BlockPosUtil.setMin(this.min, pos); + BlockPosUtil.setMax(this.max, pos); + } + return b; + } + + @Override + public World getWorld() { + return this; + } + + @Override + public Vec3d getFocus() { + return BlockPosUtil.getCenterD(this.min, this.max); + } + + @Override + public BlockPos getOrigin() { + return this.min; + } + + @NotNull + @Override + public Iterator> iterator() { + return new Iterator<>() { + + private final ObjectIterator it = blocks.iterator(); + private final BlockInfo.Mut info = new BlockInfo.Mut(); + private final MutablePair pair = new MutablePair<>(null, this.info); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Map.Entry next() { + BlockPos pos = it.next(); + this.info.set(SchemaWorld.this, pos); + this.pair.setLeft(pos); + return this.pair; + } + }; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java new file mode 100644 index 000000000..922f53bfb --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java @@ -0,0 +1,117 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import org.jetbrains.annotations.NotNull; + +import java.util.Iterator; +import java.util.Map; +import java.util.function.Function; + +public class SimpleSchema implements ISchema { + + private final World world; + private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); + private final BlockPos origin; + private final Vec3d center; + + public SimpleSchema(Map blocks) { + this.world = new DummyWorld(); + BlockPos.MutableBlockPos min = new BlockPos.MutableBlockPos(BlockPosUtil.MAX); + BlockPos.MutableBlockPos max = new BlockPos.MutableBlockPos(BlockPosUtil.MIN); + if (!blocks.isEmpty()) { + for (var entry : blocks.entrySet()) { + if (entry.getValue().getBlockState().getBlock() != Blocks.AIR) { + this.blocks.put(entry.getKey(), entry.getValue()); + entry.getValue().apply(this.world, entry.getKey()); + BlockPosUtil.setMin(min, entry.getKey()); + BlockPosUtil.setMax(max, entry.getKey()); + } + } + } else { + min.setPos(0, 0, 0); + max.setPos(0, 0, 0); + } + this.origin = min.toImmutable(); + this.center = BlockPosUtil.getCenterD(min, max); + } + + @Override + public World getWorld() { + return this.world; + } + + @Override + public Vec3d getFocus() { + return center; + } + + @Override + public BlockPos getOrigin() { + return origin; + } + + @NotNull + @Override + public Iterator> iterator() { + return new Iterator<>() { + + private final ObjectIterator> it = blocks.object2ObjectEntrySet().fastIterator(); + + @Override + public boolean hasNext() { + return it.hasNext(); + } + + @Override + public Map.Entry next() { + return it.next(); + } + }; + } + + public static class Builder { + + private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); + + public Builder add(BlockPos pos, IBlockState state) { + return add(pos, state, null); + } + + public Builder add(BlockPos pos, IBlockState state, TileEntity customTile) { + if (state.getBlock() == Blocks.AIR) return this; + this.blocks.put(pos, new BlockInfo(state, customTile)); + return this; + } + + public Builder add(BlockPos pos, BlockInfo blockInfo) { + this.blocks.put(pos, blockInfo.toImmutable()); + return this; + } + + public Builder add(Iterable posList, Function function) { + for (BlockPos pos : posList) { + BlockInfo info = function.apply(pos).toImmutable(); + add(pos, info); + } + return this; + } + + public Builder add(Map blocks) { + this.blocks.putAll(blocks); + return this; + } + + public SimpleSchema build() { + return new SimpleSchema(this.blocks); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java new file mode 100644 index 000000000..93555e5e9 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java @@ -0,0 +1,36 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import it.unimi.dsi.fastutil.chars.Char2ObjectMap; +import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +public class Structure { + + private final List matrix = new ArrayList<>(); + private final Char2ObjectMap> map = new Char2ObjectOpenHashMap<>(); + + public Structure() { + this.map.put(' ', air()); + } + + public Structure aisle(String... aisle) { + this.matrix.add(aisle); + return this; + } + + public Structure where(char c, Supplier blockInfo) { + this.map.put(c, blockInfo); + return this; + } + + public Structure where(char c, BlockInfo blockInfo) { + return where(c, () -> blockInfo); + } + + public static Supplier air() { + return () -> BlockInfo.EMPTY; + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java deleted file mode 100644 index 726a20a68..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/TrackedDummyWorld.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -import org.jetbrains.annotations.NotNull; -import org.lwjgl.util.vector.Vector3f; - -import java.util.Map; -import java.util.Set; -import java.util.function.Predicate; - -@SideOnly(Side.CLIENT) -public class TrackedDummyWorld extends DummyWorld { - - public final Set renderedBlocks = new ObjectOpenHashSet<>(); - private Predicate renderFilter; - private final World proxyWorld; - - private final Vector3f minPos = new Vector3f(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - private final Vector3f maxPos = new Vector3f(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); - - public void setRenderFilter(Predicate renderFilter) { - this.renderFilter = renderFilter; - } - - public TrackedDummyWorld() { - proxyWorld = null; - } - - public TrackedDummyWorld(World world) { - proxyWorld = world; - } - - public void addBlocks(Map renderedBlocks) { - renderedBlocks.forEach(this::addBlock); - } - - public void addBlock(BlockPos pos, BlockInfo blockInfo) { - if (blockInfo.getBlockState().getBlock() == Blocks.AIR) - return; - this.renderedBlocks.add(pos); - blockInfo.apply(this, pos); - } - - @Override - public TileEntity getTileEntity(@NotNull BlockPos pos) { - if (renderFilter != null && !renderFilter.test(pos)) - return null; - return proxyWorld != null ? proxyWorld.getTileEntity(pos) : super.getTileEntity(pos); - } - - @NotNull - @Override - public IBlockState getBlockState(@NotNull BlockPos pos) { - if (renderFilter != null && !renderFilter.test(pos)) - return Blocks.AIR.getDefaultState(); // return air if not rendering this block - return proxyWorld != null ? proxyWorld.getBlockState(pos) : super.getBlockState(pos); - } - - @Override - public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newState, int flags) { - minPos.setX(Math.min(minPos.getX(), pos.getX())); - minPos.setY(Math.min(minPos.getY(), pos.getY())); - minPos.setZ(Math.min(minPos.getZ(), pos.getZ())); - maxPos.setX(Math.max(maxPos.getX(), pos.getX())); - maxPos.setY(Math.max(maxPos.getY(), pos.getY())); - maxPos.setZ(Math.max(maxPos.getZ(), pos.getZ())); - return super.setBlockState(pos, newState, flags); - } - - public Vector3f getCenter() { - Vector3f center = (Vector3f) Vector3f.sub(maxPos, minPos, null).scale(0.5f); - return Vector3f.add(center, minPos, center); - } - - public float getMaxSize() { - return Math.max(maxPos.x - minPos.x, Math.max(maxPos.y - minPos.y, maxPos.z - minPos.z)); - } - - public Vector3f getSize() { - Vector3f result = new Vector3f(); - result.setX(maxPos.getX() - minPos.getX() + 1); - result.setY(maxPos.getY() - minPos.getY() + 1); - result.setZ(maxPos.getZ() - minPos.getZ() + 1); - return result; - } - - public Vector3f getMinPos() { - return minPos; - } - - public Vector3f getMaxPos() { - return maxPos; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java deleted file mode 100644 index 9cb754832..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/WorldSceneRenderer.java +++ /dev/null @@ -1,440 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import com.cleanroommc.modularui.utils.Color; - -import net.minecraft.block.Block; -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.texture.TextureMap; -import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.init.Blocks; -import net.minecraft.tileentity.TileEntity; -import net.minecraft.util.BlockRenderLayer; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.RayTraceResult; -import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; -import net.minecraftforge.client.ForgeHooksClient; -import net.minecraftforge.client.MinecraftForgeClient; -import net.minecraftforge.fml.relauncher.Side; -import net.minecraftforge.fml.relauncher.SideOnly; - -import org.apache.commons.lang3.tuple.Pair; -import org.lwjgl.opengl.GL11; -import org.lwjgl.util.glu.GLU; -import org.lwjgl.util.vector.Vector3f; - -import java.awt.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import java.nio.IntBuffer; -import java.util.*; -import java.util.List; -import java.util.function.Consumer; - -/** - * Abstract class, and extend a lot of features compared with the original one. - */ -@SideOnly(Side.CLIENT) -public abstract class WorldSceneRenderer { - - protected static final FloatBuffer MODELVIEW_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - protected static final FloatBuffer PROJECTION_MATRIX_BUFFER = ByteBuffer.allocateDirect(16 * 4) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - protected static final IntBuffer VIEWPORT_BUFFER = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()) - .asIntBuffer(); - protected static final FloatBuffer PIXEL_DEPTH_BUFFER = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()) - .asFloatBuffer(); - protected static final FloatBuffer OBJECT_POS_BUFFER = ByteBuffer.allocateDirect(3 * 4) - .order(ByteOrder.nativeOrder()).asFloatBuffer(); - - public final World world; - public final List, ISceneRenderHook>> renderedBlocksMap; - private Consumer beforeRender; - private Consumer afterRender; - private Consumer onLookingAt; - private int clearColor; - private RayTraceResult lastTraceResult; - private Vector3f eyePos = new Vector3f(0, 0, 10f); - private Vector3f lookAt = new Vector3f(0, 0, 0); - private Vector3f worldUp = new Vector3f(0, 1, 0); - - public WorldSceneRenderer(World world) { - this.world = world; - renderedBlocksMap = new ArrayList<>(); - } - - public WorldSceneRenderer setBeforeWorldRender(Consumer callback) { - this.beforeRender = callback; - return this; - } - - public WorldSceneRenderer setAfterWorldRender(Consumer callback) { - this.afterRender = callback; - return this; - } - - public WorldSceneRenderer addRenderedBlocks(Collection blocks, ISceneRenderHook renderHook) { - if (blocks != null) { - this.renderedBlocksMap.add(Pair.of(blocks, renderHook)); - } - return this; - } - - public WorldSceneRenderer setOnLookingAt(Consumer onLookingAt) { - this.onLookingAt = onLookingAt; - return this; - } - - public void setClearColor(int clearColor) { - this.clearColor = clearColor; - } - - public RayTraceResult getLastTraceResult() { - return lastTraceResult; - } - - public void render(float x, float y, float width, float height, int mouseX, int mouseY) { - // setupCamera - Rectangle positionedRect = getPositionedRect((int) x, (int) y, (int) width, (int) height); - Rectangle mouse = getPositionedRect(mouseX, mouseY, 0, 0); - mouseX = mouse.x; - mouseY = mouse.y; - setupCamera(positionedRect); - // render TrackedDummyWorld - drawWorld(); - // check lookingAt - this.lastTraceResult = null; - if (onLookingAt != null && mouseX > positionedRect.x && - mouseX < positionedRect.x + positionedRect.width && mouseY > positionedRect.y && - mouseY < positionedRect.y + positionedRect.height) { - Vector3f hitPos = unProject(mouseX, mouseY); - RayTraceResult result = rayTrace(hitPos); - if (result != null) { - this.lastTraceResult = null; - this.lastTraceResult = result; - onLookingAt.accept(result); - } - } - // resetCamera - resetCamera(); - } - - public Vector3f getEyePos() { - return eyePos; - } - - public Vector3f getLookAt() { - return lookAt; - } - - public Vector3f getWorldUp() { - return worldUp; - } - - public void setCameraLookAt(Vector3f eyePos, Vector3f lookAt, Vector3f worldUp) { - this.eyePos = eyePos; - this.lookAt = lookAt; - this.worldUp = worldUp; - } - - public void setCameraLookAt(Vector3f lookAt, double radius, double rotationPitch, double rotationYaw) { - this.lookAt = lookAt; - Vector3f pos = new Vector3f((float) Math.cos(rotationPitch), (float) 0, (float) Math.sin(rotationPitch)); - pos.y += (float) (Math.tan(rotationYaw) * pos.length()); - pos.normalise().scale((float) radius); - this.eyePos = pos.translate(lookAt.x, lookAt.y, lookAt.z); - } - - protected Rectangle getPositionedRect(int x, int y, int width, int height) { - return new Rectangle(x, y, width, height); - } - - protected void setupCamera(Rectangle rect) { - int x = rect.x; - int y = rect.y; - int width = rect.width; - int height = rect.height; - - GlStateManager.pushAttrib(); - - Minecraft.getMinecraft().entityRenderer.disableLightmap(); - GlStateManager.disableLighting(); - GlStateManager.enableDepth(); - GlStateManager.enableBlend(); - - // setup viewport and clear GL buffers - GlStateManager.viewport(x, y, width, height); - - clearView(x, y, width, height); - - // setup projection matrix to perspective - GlStateManager.matrixMode(GL11.GL_PROJECTION); - GlStateManager.pushMatrix(); - GlStateManager.loadIdentity(); - - float aspectRatio = width / (height * 1.0f); - GLU.gluPerspective(60.0f, aspectRatio, 0.1f, 10000.0f); - - // setup modelview matrix - GlStateManager.matrixMode(GL11.GL_MODELVIEW); - GlStateManager.pushMatrix(); - GlStateManager.loadIdentity(); - GLU.gluLookAt(eyePos.x, eyePos.y, eyePos.z, lookAt.x, lookAt.y, lookAt.z, worldUp.x, worldUp.y, worldUp.z); - } - - protected void clearView(int x, int y, int width, int height) { - Color.setGlColor(clearColor); - GlStateManager.clear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); - } - - protected static void resetCamera() { - // reset viewport - Minecraft minecraft = Minecraft.getMinecraft(); - GlStateManager.viewport(0, 0, minecraft.displayWidth, minecraft.displayHeight); - - // reset projection matrix - GlStateManager.matrixMode(GL11.GL_PROJECTION); - GlStateManager.popMatrix(); - - // reset modelview matrix - GlStateManager.matrixMode(GL11.GL_MODELVIEW); - GlStateManager.popMatrix(); - - GlStateManager.disableBlend(); - GlStateManager.disableDepth(); - - // reset attributes - GlStateManager.popAttrib(); - } - - protected void drawWorld() { - if (beforeRender != null) { - beforeRender.accept(this); - } - - Minecraft mc = Minecraft.getMinecraft(); - GlStateManager.enableCull(); - GlStateManager.enableRescaleNormal(); - RenderHelper.disableStandardItemLighting(); - mc.entityRenderer.disableLightmap(); - mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); - BlockRenderLayer oldRenderLayer = MinecraftForgeClient.getRenderLayer(); - GlStateManager.disableLighting(); - GlStateManager.enableTexture2D(); - GlStateManager.enableAlpha(); - - try { // render block in each layer - for (BlockRenderLayer layer : BlockRenderLayer.values()) { - ForgeHooksClient.setRenderLayer(layer); - int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; - - renderedBlocksMap.forEach(pair -> { - if (pair.getValue() != null) { - pair.getValue().apply(false, pass, layer); - } else { - setDefaultPassRenderState(pass); - } - - BufferBuilder buffer = Tessellator.getInstance().getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); - BlockRendererDispatcher blockrendererdispatcher = mc.getBlockRendererDispatcher(); - - for (BlockPos pos : pair.getKey()) { - IBlockState state = world.getBlockState(pos); - Block block = state.getBlock(); - if (block == Blocks.AIR) continue; - state = state.getActualState(world, pos); - if (block.canRenderInLayer(state, layer)) { - blockrendererdispatcher.renderBlock(state, pos, world, buffer); - } - } - - Tessellator.getInstance().draw(); - Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); - }); - } - } finally { - ForgeHooksClient.setRenderLayer(oldRenderLayer); - } - - RenderHelper.enableStandardItemLighting(); - GlStateManager.enableLighting(); - - // render TESR - for (int pass = 0; pass < 2; pass++) { - ForgeHooksClient.setRenderPass(pass); - int finalPass = pass; - renderedBlocksMap.forEach(pair -> { - if (pair.getValue() != null) { - pair.getValue().apply(true, finalPass, null); - } else { - setDefaultPassRenderState(finalPass); - } - for (BlockPos pos : pair.getKey()) { - TileEntity tile = world.getTileEntity(pos); - if (tile != null) { - if (tile.shouldRenderInPass(finalPass)) { - TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); - } - } - } - }); - } - ForgeHooksClient.setRenderPass(-1); - GlStateManager.enableDepth(); - GlStateManager.disableBlend(); - GlStateManager.depthMask(true); - - if (afterRender != null) { - afterRender.accept(this); - } - } - - public static void setDefaultPassRenderState(int pass) { - GlStateManager.color(1, 1, 1, 1); - if (pass == 0) { // SOLID - GlStateManager.enableDepth(); - GlStateManager.disableBlend(); - GlStateManager.depthMask(true); - } else { // TRANSLUCENT - GlStateManager.enableBlend(); - GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - GlStateManager.depthMask(false); - } - } - - public RayTraceResult rayTrace(Vector3f hitPos) { - Vec3d startPos = new Vec3d(this.eyePos.x, this.eyePos.y, this.eyePos.z); - hitPos.scale(2); // Double view range to ensure pos can be seen. - Vec3d endPos = new Vec3d((hitPos.x - startPos.x), (hitPos.y - startPos.y), (hitPos.z - startPos.z)); - return this.world.rayTraceBlocks(startPos, endPos); - } - - public static Vector3f project(BlockPos pos) { - // read current rendering parameters - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); - GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); - GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); - - // rewind buffers after write by OpenGL glGet calls - MODELVIEW_MATRIX_BUFFER.rewind(); - PROJECTION_MATRIX_BUFFER.rewind(); - VIEWPORT_BUFFER.rewind(); - - // call gluProject with retrieved parameters - GLU.gluProject(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f, MODELVIEW_MATRIX_BUFFER, - PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, OBJECT_POS_BUFFER); - - // rewind buffers after read by gluProject - VIEWPORT_BUFFER.rewind(); - PROJECTION_MATRIX_BUFFER.rewind(); - MODELVIEW_MATRIX_BUFFER.rewind(); - - // rewind buffer after write by gluProject - OBJECT_POS_BUFFER.rewind(); - - // obtain position in Screen - float winX = OBJECT_POS_BUFFER.get(); - float winY = OBJECT_POS_BUFFER.get(); - float winZ = OBJECT_POS_BUFFER.get(); - - // rewind buffer after read - OBJECT_POS_BUFFER.rewind(); - - return new Vector3f(winX, winY, winZ); - } - - public static Vector3f unProject(int mouseX, int mouseY) { - // read depth of pixel under mouse - GL11.glReadPixels(mouseX, mouseY, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, PIXEL_DEPTH_BUFFER); - - // rewind buffer after write by glReadPixels - PIXEL_DEPTH_BUFFER.rewind(); - - // retrieve depth from buffer (0.0-1.0f) - float pixelDepth = PIXEL_DEPTH_BUFFER.get(); - - // rewind buffer after read - PIXEL_DEPTH_BUFFER.rewind(); - - // read current rendering parameters - GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, MODELVIEW_MATRIX_BUFFER); - GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, PROJECTION_MATRIX_BUFFER); - GL11.glGetInteger(GL11.GL_VIEWPORT, VIEWPORT_BUFFER); - - // rewind buffers after write by OpenGL glGet calls - MODELVIEW_MATRIX_BUFFER.rewind(); - PROJECTION_MATRIX_BUFFER.rewind(); - VIEWPORT_BUFFER.rewind(); - - // call gluUnProject with retrieved parameters - GLU.gluUnProject(mouseX, mouseY, pixelDepth, MODELVIEW_MATRIX_BUFFER, PROJECTION_MATRIX_BUFFER, VIEWPORT_BUFFER, - OBJECT_POS_BUFFER); - - // rewind buffers after read by gluUnProject - VIEWPORT_BUFFER.rewind(); - PROJECTION_MATRIX_BUFFER.rewind(); - MODELVIEW_MATRIX_BUFFER.rewind(); - - // rewind buffer after write by gluUnProject - OBJECT_POS_BUFFER.rewind(); - - // obtain absolute position in world - float posX = OBJECT_POS_BUFFER.get(); - float posY = OBJECT_POS_BUFFER.get(); - float posZ = OBJECT_POS_BUFFER.get(); - - // rewind buffer after read - OBJECT_POS_BUFFER.rewind(); - - return new Vector3f(posX, posY, posZ); - } - - /*** - * For better performance, You'd better handle the event {@link #setOnLookingAt(Consumer)} or - * {@link #getLastTraceResult()} - * - * @param mouseX xPos in Texture - * @param mouseY yPos in Texture - * @return RayTraceResult Hit - */ - protected RayTraceResult screenPos2BlockPosFace(int mouseX, int mouseY, int x, int y, int width, int height) { - // render a frame - GlStateManager.enableDepth(); - setupCamera(getPositionedRect(x, y, width, height)); - - drawWorld(); - - Vector3f hitPos = unProject(mouseX, mouseY); - RayTraceResult result = rayTrace(hitPos); - - resetCamera(); - - return result; - } - - /*** - * For better performance, You'd better do project in {@link #setAfterWorldRender(Consumer)} - * - * @param pos BlockPos - * @param depth should pass Depth Test - * @return x, y, z - */ - protected Vector3f blockPos2ScreenPos(BlockPos pos, boolean depth, int x, int y, int width, int height) { - // render a frame - GlStateManager.enableDepth(); - setupCamera(getPositionedRect(x, y, width, height)); - - drawWorld(); - Vector3f winPos = project(pos); - - resetCamera(); - - return winPos; - } -} diff --git a/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java b/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java index 4b88b18e9..42f5ca012 100644 --- a/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java +++ b/src/main/java/com/cleanroommc/modularui/widget/sizer/Area.java @@ -14,6 +14,11 @@ */ public class Area extends Rectangle implements IUnResizeable { + public static boolean isInside(int x, int y, int w, int h, int px, int py) { + SHARED.set(x, y, w, h); + return SHARED.isInside(px, py); + } + public static final Area SHARED = new Area(); /** From 99ac876953fd610e3f8d082bb17c66250cdfc273 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sun, 31 Dec 2023 13:31:30 +0100 Subject: [PATCH 4/9] isometric rendering --- .../modularui/drawable/FakeWorld.java | 37 ------------------- .../modularui/test/ResizerTest.java | 7 ++-- .../utils/fakeworld/SchemaRenderer.java | 24 +++++++++++- 3 files changed, 26 insertions(+), 42 deletions(-) delete mode 100644 src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java diff --git a/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java b/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java deleted file mode 100644 index 62cf0248f..000000000 --- a/src/main/java/com/cleanroommc/modularui/drawable/FakeWorld.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.cleanroommc.modularui.drawable; - -import com.cleanroommc.modularui.api.drawable.IDrawable; -import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.theme.WidgetTheme; - -import net.minecraft.client.shader.Framebuffer; - -public class FakeWorld implements IDrawable { - - private static Framebuffer FBO; - private final WorldSceneRenderer renderer; - - public FakeWorld(WorldSceneRenderer renderer) { - this.renderer = renderer; - } - - public FakeWorld(TrackedDummyWorld world) { - if (FBO == null) { - FBO = new Framebuffer(1080, 1080, true); - } - this.renderer = new FBOWorldSceneRenderer(world, FBO); - double pitch = Math.PI / 4; - double yaw = Math.PI / 4; - this.renderer.setCameraLookAt(world.getCenter(), 10, pitch, yaw); - this.renderer.addRenderedBlocks(world.renderedBlocks, null); - - /*this.renderer.setBeforeWorldRender(renderer -> { - renderer.setCameraLookAt(world.getCenter(), world.getMaxSize() / 2 + 2, pitch, yaw); - });*/ - } - - @Override - public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { - this.renderer.render(x, y, width, height, context.getAbsMouseX(), context.getAbsMouseY()); - } -} diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index 6da2630ef..8a1c0a6f4 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -25,16 +25,17 @@ public class ResizerTest extends CustomModularScreen { .background(new SpriteDrawable(sprite)) .size(20) .align(Alignment.Center));*/ - TrackedDummyWorld world = new TrackedDummyWorld(); + /*TrackedDummyWorld world = new TrackedDummyWorld(); world.addBlock(new BlockPos(0, 0, 0), new BlockInfo(Blocks.DIAMOND_BLOCK.getDefaultState())); world.addBlock(new BlockPos(0, 1, 0), new BlockInfo(Blocks.BEDROCK.getDefaultState())); - world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState())); + world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState()));*/ double pitch = Math.PI / 4; double yaw = Math.PI / 4; return ModularPanel.defaultPanel("main") .size(150) .overlay(new SchemaRenderer(BoxSchema.of(Minecraft.getMinecraft().world, new BlockPos(Minecraft.getMinecraft().player), 5)) - .cameraFunc((camera, schema) -> camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 10, pitch, yaw)) + .cameraFunc((camera, schema) -> camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 20, pitch, yaw)) + .isometric(true) .asIcon().size(140)); } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java index cd3dfe79a..5760b9b9e 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java @@ -41,6 +41,7 @@ public class SchemaRenderer implements IDrawable { private Consumer afterRender; private BiConsumer cameraFunc; private int clearColor = 0; + private boolean isometric = false; public SchemaRenderer(ISchema schema, Framebuffer framebuffer) { this.schema = schema; @@ -66,6 +67,11 @@ public SchemaRenderer afterRender(Consumer consumer) { return this; } + public SchemaRenderer isometric(boolean isometric) { + this.isometric = isometric; + return this; + } + @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { render(x, y, width, height, context.getMouseX(), context.getMouseY()); @@ -206,13 +212,27 @@ protected void setupCamera(int width, int height) { GlStateManager.pushMatrix(); GlStateManager.loadIdentity(); - float aspectRatio = width / (height * 1.0f); - GLU.gluPerspective(60.0f, aspectRatio, 0.1f, 10000.0f); + float near = this.isometric ? 1f : 0.1f; + float far = 10000.0f; + float fovY = 60.0f; // Field of view in the Y direction + float aspect = (float) width / height; // width and height are the dimensions of your window + float top = near * (float) Math.tan(Math.toRadians(fovY) / 2.0); + float bottom = -top; + float left = aspect * bottom; + float right = aspect * top; + if (this.isometric) { + GL11.glOrtho(left, right, bottom, top, near, far); + } else { + GL11.glFrustum(left, right, bottom, top, near, far); + } // setup modelview matrix GlStateManager.matrixMode(GL11.GL_MODELVIEW); GlStateManager.pushMatrix(); GlStateManager.loadIdentity(); + if (this.isometric) { + GlStateManager.scale(0.1, 0.1, 0.1); + } var c = this.camera.getPos(); var lookAt = this.camera.getLookAt(); GLU.gluLookAt(c.x, c.y, c.z, lookAt.x, lookAt.y, lookAt.z, 0, 1, 0); From 039fce944206ae5423a676da98a6c222f8f6f22e Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sun, 31 Dec 2023 14:23:49 +0100 Subject: [PATCH 5/9] fix yaw/pitch --- .../cleanroommc/modularui/test/ResizerTest.java | 9 ++++++--- .../modularui/utils/fakeworld/Camera.java | 14 +++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index 8a1c0a6f4..fc965122f 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -29,12 +29,15 @@ public class ResizerTest extends CustomModularScreen { world.addBlock(new BlockPos(0, 0, 0), new BlockInfo(Blocks.DIAMOND_BLOCK.getDefaultState())); world.addBlock(new BlockPos(0, 1, 0), new BlockInfo(Blocks.BEDROCK.getDefaultState())); world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState()));*/ - double pitch = Math.PI / 4; - double yaw = Math.PI / 4; return ModularPanel.defaultPanel("main") .size(150) .overlay(new SchemaRenderer(BoxSchema.of(Minecraft.getMinecraft().world, new BlockPos(Minecraft.getMinecraft().player), 5)) - .cameraFunc((camera, schema) -> camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 20, pitch, yaw)) + .cameraFunc((camera, schema) -> { + double pitch = Math.PI / 4; + double T = 4000D; + double yaw = Minecraft.getSystemTime() % T / T * Math.PI * 2; + camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 20, yaw, pitch); + }) .isometric(true) .asIcon().size(140)); } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java index 6a9865c2c..2909a9c66 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java @@ -32,18 +32,18 @@ public Camera setPos(float x, float y, float z) { return this; } - public Camera setLookAt(Vector3f lookAt, double radius, double rotationPitch, double rotationYaw) { - return setLookAt(lookAt.x, lookAt.y, lookAt.z, radius, rotationPitch, rotationYaw); + public Camera setLookAt(Vector3f lookAt, double radius, double yaw, double pitch) { + return setLookAt(lookAt.x, lookAt.y, lookAt.z, radius, yaw, pitch); } - public Camera setLookAt(Vec3i lookAt, double radius, double rotationPitch, double rotationYaw) { - return setLookAt(lookAt.getX(), lookAt.getY(), lookAt.getZ(), radius, rotationPitch, rotationYaw); + public Camera setLookAt(Vec3i lookAt, double radius, double yaw, double pitch) { + return setLookAt(lookAt.getX(), lookAt.getY(), lookAt.getZ(), radius, yaw, pitch); } - public Camera setLookAt(float lookAtX, float lookAtY, float lookAtZ, double radius, double rotationPitch, double rotationYaw) { + public Camera setLookAt(float lookAtX, float lookAtY, float lookAtZ, double radius, double yaw, double pitch) { setLookAt(lookAtX, lookAtY, lookAtZ); - Vector3f pos = new Vector3f((float) Math.cos(rotationPitch), (float) 0, (float) Math.sin(rotationPitch)); - pos.y += (float) (Math.tan(rotationYaw) * pos.length()); + Vector3f pos = new Vector3f((float) Math.cos(yaw), (float) 0, (float) Math.sin(yaw)); + pos.y += (float) (Math.tan(pitch) * pos.length()); pos.normalise().scale((float) radius); this.pos.set(pos.translate(lookAtX, lookAtY, lookAtZ)); return this; From a79f530c72dbbd7c1fc4fcaa4c48275753e7a335 Mon Sep 17 00:00:00 2001 From: brachy84 <45517902+brachy84@users.noreply.github.com> Date: Tue, 21 May 2024 18:43:57 +0200 Subject: [PATCH 6/9] Fake world work (#59) --- .../modularui/test/ResizerTest.java | 43 ++++- .../modularui/utils/fakeworld/BoxSchema.java | 12 +- .../utils/fakeworld/IFilteredSchema.java | 19 ++ .../fakeworld/IMemorizingFilteredSchema.java | 17 ++ .../modularui/utils/fakeworld/ISchema.java | 3 + .../utils/fakeworld/PosListSchema.java | 22 ++- .../utils/fakeworld/SchemaRenderer.java | 58 +++++-- .../utils/fakeworld/SchemaWorld.java | 35 +++- .../utils/fakeworld/SimpleSchema.java | 55 +++++- .../modularui/utils/fakeworld/Structure.java | 85 +++++++-- .../modularui/widgets/SchemaWidget.java | 162 ++++++++++++++++++ 11 files changed, 460 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index fc965122f..462bf0b30 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -3,12 +3,15 @@ import com.cleanroommc.modularui.screen.CustomModularScreen; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; -import com.cleanroommc.modularui.utils.fakeworld.BoxSchema; -import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; +import com.cleanroommc.modularui.utils.fakeworld.*; + +import com.cleanroommc.modularui.widgets.SchemaWidget; import net.minecraft.client.Minecraft; import net.minecraft.init.Blocks; +import net.minecraft.init.MobEffects; +import net.minecraft.potion.Potion; +import net.minecraft.tileentity.TileEntityBeacon; import net.minecraft.util.math.BlockPos; import org.jetbrains.annotations.NotNull; @@ -29,7 +32,7 @@ public class ResizerTest extends CustomModularScreen { world.addBlock(new BlockPos(0, 0, 0), new BlockInfo(Blocks.DIAMOND_BLOCK.getDefaultState())); world.addBlock(new BlockPos(0, 1, 0), new BlockInfo(Blocks.BEDROCK.getDefaultState())); world.addBlock(new BlockPos(1, 0, 1), new BlockInfo(Blocks.GOLD_BLOCK.getDefaultState()));*/ - return ModularPanel.defaultPanel("main") +/* return ModularPanel.defaultPanel("main") .size(150) .overlay(new SchemaRenderer(BoxSchema.of(Minecraft.getMinecraft().world, new BlockPos(Minecraft.getMinecraft().player), 5)) .cameraFunc((camera, schema) -> { @@ -39,6 +42,34 @@ public class ResizerTest extends CustomModularScreen { camera.setLookAt(new BlockPos(Minecraft.getMinecraft().player), 20, yaw, pitch); }) .isometric(true) - .asIcon().size(140)); + .asIcon().size(140));*/ + + var plane = ModularPanel.defaultPanel("main").size(170); + + SimpleSchema world = new SimpleSchema.Builder() + .add(new BlockPos(0, 0, 0), Blocks.DIAMOND_BLOCK.getDefaultState()) + .add(new BlockPos(0, 1, 0), Blocks.BEDROCK.getDefaultState()) + .add(new BlockPos(0, 2, 0), Blocks.WOOL.getDefaultState()) + .add(new BlockPos(1, 0, 1), Blocks.GOLD_BLOCK.getDefaultState()) + .add(new BlockPos(0, 3, 0), Blocks.BEACON.getDefaultState()) + .build(); + + var schemaRenderer = new SchemaRenderer(world); + var layerUpDown = new SchemaWidget.LayerUpDown(); + world.setRenderFilter(layerUpDown.makeSchemaFilter()); + world.applyRenderFilter(); + + plane.child(layerUpDown.bottom(1).left(1).size(16)); + + var disableTESR = new SchemaWidget.DisableTESR().bottom(1).left(18).size(16); + schemaRenderer.disableTESR(disableTESR.makeSuppler()); + plane.child(disableTESR); + + + var shnemaW = new SchemaWidget(schemaRenderer).size(120); + plane.child(shnemaW); + + return plane; } -} + + } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java index 3cbfa74d9..5daa2a9bb 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BoxSchema.java @@ -4,18 +4,24 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import java.util.function.BiPredicate; + public class BoxSchema extends PosListSchema { public static BoxSchema of(World world, BlockPos center, int r) { - return new BoxSchema(world, center.add(-r, -r, -r), center.add(r, r, r)); + return new BoxSchema(world, center.add(-r, -r, -r), center.add(r, r, r), (blockPos, blockInfo) -> true); + } + + public static BoxSchema of(World world, BlockPos center, int r, BiPredicate renderFilter) { + return new BoxSchema(world, center.add(-r, -r, -r), center.add(r, r, r), renderFilter); } private final World world; private final BlockPos min, max; private final Vec3d center; - public BoxSchema(World world, BlockPos min, BlockPos max) { - super(world, BlockPosUtil.getAllInside(min, max, false)); + public BoxSchema(World world, BlockPos min, BlockPos max, BiPredicate renderFilter) { + super(world, BlockPosUtil.getAllInside(min, max, false), renderFilter); this.world = world; this.min = BlockPosUtil.getMin(min, max); this.max = BlockPosUtil.getMax(min, max); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java new file mode 100644 index 000000000..fa41b36d4 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java @@ -0,0 +1,19 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; + +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiPredicate; + +/** + * This schema gets filtered on insertion.

+ * If the filter changes, it will not get applied to ealready set blocks + */ +public interface IFilteredSchema extends ISchema { + + void setRenderFilter(@NotNull BiPredicate renderFilter); + + @NotNull BiPredicate getRenderFilter(); + +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java new file mode 100644 index 000000000..28c618d59 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java @@ -0,0 +1,17 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.util.math.BlockPos; + +import org.jetbrains.annotations.Unmodifiable; + +import java.util.Map; + +/** + * This schema gets filtered on insertion.

+ * If the filter changes, it will need to manually call {@link IMemorizingFilteredSchema#applyRenderFilter()} + */ +public interface IMemorizingFilteredSchema extends IFilteredSchema { + + @Unmodifiable Map getOriginalSchema(); + void applyRenderFilter(); +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java index 074dcdd29..065ecc63c 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java @@ -7,6 +7,8 @@ import org.apache.commons.lang3.tuple.Pair; import java.util.Map; +import java.util.function.BiPredicate; +import java.util.function.Consumer; public interface ISchema extends Iterable> { @@ -15,4 +17,5 @@ public interface ISchema extends Iterable> { Vec3d getFocus(); BlockPos getOrigin(); + } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java index b8a634251..db5dc0195 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java @@ -9,15 +9,28 @@ import java.util.Iterator; import java.util.Map; +import java.util.function.BiPredicate; -public abstract class PosListSchema implements ISchema { +public abstract class PosListSchema implements IFilteredSchema { private final World world; private final Iterable posList; + private BiPredicate renderFilter; - public PosListSchema(World world, Iterable posList) { + public PosListSchema(World world, Iterable posList, BiPredicate renderFilter) { this.world = world; this.posList = posList; + this.renderFilter = renderFilter; + } + + @Override + public void setRenderFilter(@NotNull BiPredicate renderFilter) { + this.renderFilter = renderFilter; + } + + @Override + public @NotNull BiPredicate getRenderFilter() { + return renderFilter; } @Override @@ -42,7 +55,10 @@ public boolean hasNext() { public Pair next() { BlockPos pos = posIt.next(); pair.setLeft(pos); - pair.setRight(BlockInfo.Mut.SHARED.set(PosListSchema.this.world, pos)); + BlockInfo.Mut.SHARED.set(PosListSchema.this.world, pos); + if (renderFilter.test(pos, BlockInfo.Mut.SHARED)) { + pair.setRight(BlockInfo.Mut.SHARED); + } else pair.setRight(BlockInfo.EMPTY); return pair; } }; diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java index 5760b9b9e..be3a36e68 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java @@ -24,10 +24,14 @@ import org.lwjgl.opengl.EXTFramebufferObject; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; +import org.lwjgl.util.vector.ReadableVector3f; import org.lwjgl.util.vector.Vector3f; +import java.util.Objects; import java.util.function.BiConsumer; +import java.util.function.BooleanSupplier; import java.util.function.Consumer; +import java.util.function.DoubleSupplier; public class SchemaRenderer implements IDrawable { @@ -37,6 +41,8 @@ public class SchemaRenderer implements IDrawable { private final Framebuffer framebuffer; private final Camera camera = new Camera(new Vector3f(), new Vector3f()); private boolean cameraSetup = false; + private DoubleSupplier scale; + private BooleanSupplier disableTESR; private Consumer onRayTrace; private Consumer afterRender; private BiConsumer cameraFunc; @@ -72,6 +78,24 @@ public SchemaRenderer isometric(boolean isometric) { return this; } + public SchemaRenderer scale(double scale) { + return scale(() -> scale); + } + + public SchemaRenderer scale(DoubleSupplier scale) { + this.scale = scale; + return this; + } + + public SchemaRenderer disableTESR(boolean disable) { + return disableTESR(() -> disable); + } + + public SchemaRenderer disableTESR(BooleanSupplier disable) { + this.disableTESR = disable; + return this; + } + @Override public void draw(GuiContext context, int x, int y, int width, int height, WidgetTheme widgetTheme) { render(x, y, width, height, context.getMouseX(), context.getMouseY()); @@ -81,6 +105,14 @@ public void render(int x, int y, int width, int height, int mouseX, int mouseY) if (this.cameraFunc != null) { this.cameraFunc.accept(this.camera, this.schema); } + if (Objects.nonNull(scale)) { + Vector3f cameraPos = camera.getPos(); + Vector3f looking = camera.getLookAt(); + Vector3f.sub(cameraPos, looking, cameraPos); + if (cameraPos.length() != 0.0f) cameraPos.normalise(); + cameraPos.scale((float) scale.getAsDouble()); + Vector3f.add(looking, cameraPos, cameraPos); + } int lastFbo = bindFBO(); setupCamera(this.framebuffer.framebufferWidth, this.framebuffer.framebufferHeight); renderWorld(); @@ -159,18 +191,20 @@ private void renderWorld() { GlStateManager.enableLighting(); // render TESR - for (int pass = 0; pass < 2; pass++) { - ForgeHooksClient.setRenderPass(pass); - int finalPass = pass; - GlStateManager.color(1, 1, 1, 1); - setDefaultPassRenderState(pass); - this.schema.forEach(pair -> { - BlockPos pos = pair.getKey(); - TileEntity tile = pair.getValue().getTileEntity(); - if (tile != null && tile.shouldRenderInPass(finalPass)) { - TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); - } - }); + if (disableTESR == null || !disableTESR.getAsBoolean()) { + for (int pass = 0; pass < 2; pass++) { + ForgeHooksClient.setRenderPass(pass); + int finalPass = pass; + GlStateManager.color(1, 1, 1, 1); + setDefaultPassRenderState(pass); + this.schema.forEach(pair -> { + BlockPos pos = pair.getKey(); + TileEntity tile = pair.getValue().getTileEntity(); + if (tile != null && tile.shouldRenderInPass(finalPass)) { + TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); + } + }); + } } ForgeHooksClient.setRenderPass(-1); GlStateManager.enableDepth(); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java index a7b69bd3c..4cbbaebbf 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java @@ -12,16 +12,42 @@ import java.util.Iterator; import java.util.Map; +import java.util.function.BiPredicate; -public class SchemaWorld extends DummyWorld implements ISchema { +public class SchemaWorld extends DummyWorld implements IFilteredSchema { private final ObjectLinkedOpenHashSet blocks = new ObjectLinkedOpenHashSet<>(); + private BiPredicate renderFilter; private final BlockPos.MutableBlockPos min = new BlockPos.MutableBlockPos(); private final BlockPos.MutableBlockPos max = new BlockPos.MutableBlockPos(); + public SchemaWorld() { + this((blockPos, blockInfo) -> true); + } + + public SchemaWorld(BiPredicate renderFilter) { + this.renderFilter = renderFilter; + } + + @Override + public void setRenderFilter(@NotNull BiPredicate renderFilter) { + this.renderFilter = renderFilter; + } + + @Override + public @NotNull BiPredicate getRenderFilter() { + return renderFilter; + } + @Override public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newState, int flags) { - boolean b = super.setBlockState(pos, newState, flags); + boolean renderTest; + boolean state; + if (renderFilter.test(pos, BlockInfo.of(this, pos))) { + renderTest = true; + state = super.setBlockState(pos, newState, flags); + } else renderTest = state = false; + if (newState.getBlock().isAir(newState, this, pos)) { if (this.blocks.remove(pos) && BlockPosUtil.isOnBorder(min, max, pos)) { if (this.blocks.isEmpty()) { @@ -37,14 +63,15 @@ public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newStat } } } else if (this.blocks.isEmpty()) { + if (!renderTest) return false; this.blocks.add(pos); this.min.setPos(pos); this.max.setPos(pos); - } else if (this.blocks.add(pos)) { + } else if (renderTest && this.blocks.add(pos)) { BlockPosUtil.setMin(this.min, pos); BlockPosUtil.setMax(this.max, pos); } - return b; + return renderTest && state; } @Override diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java index 922f53bfb..3490c91f8 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java @@ -12,25 +12,34 @@ import it.unimi.dsi.fastutil.objects.ObjectIterator; import org.jetbrains.annotations.NotNull; +import java.util.Collections; import java.util.Iterator; import java.util.Map; +import java.util.function.BiPredicate; import java.util.function.Function; -public class SimpleSchema implements ISchema { +public class SimpleSchema implements IMemorizingFilteredSchema { private final World world; - private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectOpenHashMap originalBlocks = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectOpenHashMap filteredBlocks = new Object2ObjectOpenHashMap<>(); + private BiPredicate renderFilter; private final BlockPos origin; private final Vec3d center; public SimpleSchema(Map blocks) { + this(blocks, (blockPos, blockInfo) -> true); + } + + public SimpleSchema(Map blocks, BiPredicate renderFilter) { this.world = new DummyWorld(); + this.renderFilter = renderFilter; BlockPos.MutableBlockPos min = new BlockPos.MutableBlockPos(BlockPosUtil.MAX); BlockPos.MutableBlockPos max = new BlockPos.MutableBlockPos(BlockPosUtil.MIN); if (!blocks.isEmpty()) { for (var entry : blocks.entrySet()) { if (entry.getValue().getBlockState().getBlock() != Blocks.AIR) { - this.blocks.put(entry.getKey(), entry.getValue()); + this.originalBlocks.put(entry.getKey(), entry.getValue()); entry.getValue().apply(this.world, entry.getKey()); BlockPosUtil.setMin(min, entry.getKey()); BlockPosUtil.setMax(max, entry.getKey()); @@ -42,6 +51,33 @@ public SimpleSchema(Map blocks) { } this.origin = min.toImmutable(); this.center = BlockPosUtil.getCenterD(min, max); + applyRenderFilter(); + } + + @Override + public Map getOriginalSchema() { + return Collections.unmodifiableMap(originalBlocks); + } + + @Override + public void applyRenderFilter() { + filteredBlocks.clear(); + originalBlocks.forEach((pos, bInfo) -> { + if (getRenderFilter().test(pos, bInfo)) { + bInfo.apply(getWorld(), pos); + filteredBlocks.put(pos, bInfo); + } else getWorld().setBlockToAir(pos); + }); + } + + @Override + public void setRenderFilter(@NotNull BiPredicate renderFilter) { + this.renderFilter = renderFilter; + } + + @Override + public @NotNull BiPredicate getRenderFilter() { + return renderFilter; } @Override @@ -64,7 +100,7 @@ public BlockPos getOrigin() { public Iterator> iterator() { return new Iterator<>() { - private final ObjectIterator> it = blocks.object2ObjectEntrySet().fastIterator(); + private final ObjectIterator> it = filteredBlocks.object2ObjectEntrySet().fastIterator(); @Override public boolean hasNext() { @@ -81,6 +117,7 @@ public Map.Entry next() { public static class Builder { private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); + private BiPredicate renderFilter; public Builder add(BlockPos pos, IBlockState state) { return add(pos, state, null); @@ -110,8 +147,16 @@ public Builder add(Map blocks) { return this; } + public Builder setRenderFilter(BiPredicate renderFilter) { + this.renderFilter = renderFilter; + return this; + } + public SimpleSchema build() { - return new SimpleSchema(this.blocks); + if (renderFilter == null) { + return new SimpleSchema(this.blocks); + } + return new SimpleSchema(this.blocks, renderFilter); } } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java index 93555e5e9..516229d69 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Structure.java @@ -1,36 +1,85 @@ package com.cleanroommc.modularui.utils.fakeworld; +import com.google.common.base.Predicate; +import com.google.common.collect.Lists; import it.unimi.dsi.fastutil.chars.Char2ObjectMap; import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.chars.CharArrayList; +import it.unimi.dsi.fastutil.chars.CharList; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; + +import net.minecraft.block.state.BlockWorldState; +import net.minecraft.util.math.BlockPos; + +import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.function.Supplier; +import java.util.stream.Collectors; public class Structure { + private Structure(){} - private final List matrix = new ArrayList<>(); - private final Char2ObjectMap> map = new Char2ObjectOpenHashMap<>(); - - public Structure() { - this.map.put(' ', air()); + public static StaticBuilder staticBuilder(){ + return new StaticBuilder(); } - public Structure aisle(String... aisle) { - this.matrix.add(aisle); - return this; - } + public static class StaticBuilder { + private final List matrix = new ObjectArrayList<>(); + private final Char2ObjectMap map = new Char2ObjectOpenHashMap<>(); - public Structure where(char c, Supplier blockInfo) { - this.map.put(c, blockInfo); - return this; - } - public Structure where(char c, BlockInfo blockInfo) { - return where(c, () -> blockInfo); - } + private StaticBuilder() { + this.map.put(' ', air()); + } + + public StaticBuilder aisle(String... aisle) { + this.matrix.add(aisle); + return this; + } + + public StaticBuilder where(char c, BlockInfo blockInfo) { + this.map.put(c, blockInfo); + return this; + } + + public static BlockInfo air() { + return BlockInfo.EMPTY; + } + + public Map buildPosMap() { + checkMissingPredicates(); + Map posMap = new Object2ObjectOpenHashMap<>(); + + for (int y = 0; y < matrix.size(); y++) { + var aisle = matrix.get(y); + for (int x = 0; x < aisle.length; x++) { + var aisleX = aisle[x].toCharArray(); + for (int z = 0; z < aisleX.length; z++) { + var aisleZ = aisleX[z]; + posMap.put(new BlockPos(x, y, z), map.get(aisleZ)); + } + } + } + + return posMap; + } + + private void checkMissingPredicates() { + CharList list = new CharArrayList(); + + for (Char2ObjectMap.Entry entry : map.char2ObjectEntrySet()) { + if (Objects.isNull(entry.getValue())) list.add(entry.getCharKey()); + } + + if (!list.isEmpty()) throw new IllegalStateException( + list.stream().map(Object::toString).collect(Collectors.joining(",", "Predicates for character(s) ", " are missing"))); + } - public static Supplier air() { - return () -> BlockInfo.EMPTY; } + } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java new file mode 100644 index 000000000..bb0e17299 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -0,0 +1,162 @@ +package com.cleanroommc.modularui.widgets; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.widget.Interactable; +import com.cleanroommc.modularui.drawable.GuiTextures; +import com.cleanroommc.modularui.drawable.keys.StringKey; +import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; +import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; +import com.cleanroommc.modularui.widget.Widget; + +import net.minecraft.util.math.BlockPos; + +import net.minecraft.util.math.MathHelper; + +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiPredicate; +import java.util.function.BooleanSupplier; + +public class SchemaWidget extends Widget implements Interactable { + + private final SchemaRenderer schema; + private boolean invertMouseScrollScaleAction = false; + private double scale = 10; + + + public SchemaWidget(SchemaRenderer schema) { + this.schema = schema; + schema.cameraFunc((camera, $schema) -> { + camera.setLookAt($schema.getOrigin(), scale, Math.toRadians(yaw), Math.toRadians(pitch)); + }); + } + + @Override + public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) { + switch (scrollDirection) { + case UP -> { + modifyScale(-((double) amount / 120)); + return true; + } + case DOWN -> { + modifyScale(((double) amount / 120)); + return true; + } + default -> { + return false; + } + } + } + + + public SchemaWidget modifyScale(double scale) { + this.scale += scale; + return this; + } + + public SchemaWidget invertMouseScrollScaleAction(boolean invert) { + invertMouseScrollScaleAction = invert; + return this; + } + + + private int lastMouseX; + private int lastMouseY; + + private float pitch; + private float yaw; + + @Override + public void onMouseDrag(int mouseButton, long timeSinceClick) { + int mouseX = getContext().getAbsMouseX(); + int mouseY = getContext().getAbsMouseY(); + + //timeSinceClick is sometimes greater than 0 even on the first click + if (timeSinceClick == 0) { + this.lastMouseX = mouseX; + this.lastMouseY = mouseY; + return; + } + + if (mouseButton == 0) { + yaw += mouseX - lastMouseX + 360; + yaw = yaw % 360; + pitch = (float) MathHelper.clamp(pitch + (mouseY - lastMouseY), -89.9, 89.9); + + this.lastMouseX = mouseX; + this.lastMouseY = mouseY; + } + + } + + @Override + public @Nullable IDrawable getOverlay() { + return schema; + } + + + public static class DisableTESR extends ButtonWidget { + + private boolean disable = false; + + public DisableTESR() { + this.background(GuiTextures.MC_BACKGROUND); + onMousePressed(mouseButton -> { + if (mouseButton == 0) { + disable = !disable; + return true; + } + + return false; + }); + } + + public BooleanSupplier makeSuppler() { + return () -> disable; + } + } + + public static class LayerUpDown extends ButtonWidget { + + private int minLayer = Integer.MIN_VALUE; + private int maxLayer = Integer.MAX_VALUE; + private int currentLayer = Integer.MIN_VALUE; + + public LayerUpDown() { + this.background(GuiTextures.MC_BACKGROUND); + + onMousePressed(mouseButton -> { + if (mouseButton == 0) { + currentLayer = Math.max(currentLayer + 1, maxLayer); + } + + if (mouseButton == 1) { + currentLayer = Math.max(currentLayer - 1, maxLayer); + } + + if (mouseButton == 0 || mouseButton == 1) { + overlay(new StringKey(Integer.toString(currentLayer))); + return true; + } + + return false; + }); + } + + public BiPredicate makeSchemaFilter() { + return (blockPos, blockInfo) -> { + maxLayer = Math.min(maxLayer, blockPos.getY()); + minLayer = Math.max(minLayer, blockPos.getY()); + if (currentLayer > blockPos.getY()) return false; + return true; + }; + } + + public LayerUpDown startLayer(int start) { + this.currentLayer = start; + return this; + } + } + +} From beba0fe540fa078fe253a6d81253202ce87ce2bb Mon Sep 17 00:00:00 2001 From: brachy84 Date: Tue, 21 May 2024 18:45:11 +0200 Subject: [PATCH 7/9] clean up, fix layer button & scroll wheel dragging --- .../modularui/test/ResizerTest.java | 3 +- .../modularui/utils/fakeworld/Camera.java | 2 - .../utils/fakeworld/IFilteredSchema.java | 19 --- .../fakeworld/IMemorizingFilteredSchema.java | 17 --- .../modularui/utils/fakeworld/ISchema.java | 12 +- .../utils/fakeworld/PosListSchema.java | 13 +- .../utils/fakeworld/RenderWorld.java | 77 ++++++++++ .../utils/fakeworld/SchemaRenderer.java | 8 +- .../utils/fakeworld/SchemaWorld.java | 13 +- .../utils/fakeworld/SimpleSchema.java | 52 +++---- .../modularui/widgets/SchemaWidget.java | 143 +++++++++--------- 11 files changed, 202 insertions(+), 157 deletions(-) delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java delete mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/RenderWorld.java diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index 462bf0b30..b1cfe6a47 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -55,9 +55,8 @@ public class ResizerTest extends CustomModularScreen { .build(); var schemaRenderer = new SchemaRenderer(world); - var layerUpDown = new SchemaWidget.LayerUpDown(); + var layerUpDown = new SchemaWidget.LayerUpDown(0, 3); world.setRenderFilter(layerUpDown.makeSchemaFilter()); - world.applyRenderFilter(); plane.child(layerUpDown.bottom(1).left(1).size(16)); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java index 2909a9c66..e11b7fcf3 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java @@ -6,8 +6,6 @@ public class Camera { - public static final double PI2 = 2 * Math.PI; - private final Vector3f pos; private final Vector3f lookAt; diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java deleted file mode 100644 index fa41b36d4..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IFilteredSchema.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import net.minecraft.util.math.BlockPos; - -import org.jetbrains.annotations.NotNull; - -import java.util.function.BiPredicate; - -/** - * This schema gets filtered on insertion.

- * If the filter changes, it will not get applied to ealready set blocks - */ -public interface IFilteredSchema extends ISchema { - - void setRenderFilter(@NotNull BiPredicate renderFilter); - - @NotNull BiPredicate getRenderFilter(); - -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java deleted file mode 100644 index 28c618d59..000000000 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/IMemorizingFilteredSchema.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.cleanroommc.modularui.utils.fakeworld; - -import net.minecraft.util.math.BlockPos; - -import org.jetbrains.annotations.Unmodifiable; - -import java.util.Map; - -/** - * This schema gets filtered on insertion.

- * If the filter changes, it will need to manually call {@link IMemorizingFilteredSchema#applyRenderFilter()} - */ -public interface IMemorizingFilteredSchema extends IFilteredSchema { - - @Unmodifiable Map getOriginalSchema(); - void applyRenderFilter(); -} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java index 065ecc63c..9904a230e 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java @@ -4,11 +4,12 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.util.vector.Vector3f; import java.util.Map; import java.util.function.BiPredicate; -import java.util.function.Consumer; public interface ISchema extends Iterable> { @@ -18,4 +19,11 @@ public interface ISchema extends Iterable> { BlockPos getOrigin(); + default Vector3f getOriginF() { + return new Vector3f(getOrigin().getX(), getOrigin().getY(), getOrigin().getZ()); + } + + void setRenderFilter(@Nullable BiPredicate renderFilter); + + @Nullable BiPredicate getRenderFilter(); } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java index db5dc0195..6faaf31e6 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/PosListSchema.java @@ -6,12 +6,13 @@ import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Iterator; import java.util.Map; import java.util.function.BiPredicate; -public abstract class PosListSchema implements IFilteredSchema { +public abstract class PosListSchema implements ISchema { private final World world; private final Iterable posList; @@ -24,12 +25,12 @@ public PosListSchema(World world, Iterable posList, BiPredic } @Override - public void setRenderFilter(@NotNull BiPredicate renderFilter) { + public void setRenderFilter(@Nullable BiPredicate renderFilter) { this.renderFilter = renderFilter; } @Override - public @NotNull BiPredicate getRenderFilter() { + public @Nullable BiPredicate getRenderFilter() { return renderFilter; } @@ -56,9 +57,11 @@ public Pair next() { BlockPos pos = posIt.next(); pair.setLeft(pos); BlockInfo.Mut.SHARED.set(PosListSchema.this.world, pos); - if (renderFilter.test(pos, BlockInfo.Mut.SHARED)) { + if (renderFilter == null || renderFilter.test(pos, BlockInfo.Mut.SHARED)) { pair.setRight(BlockInfo.Mut.SHARED); - } else pair.setRight(BlockInfo.EMPTY); + } else { + pair.setRight(BlockInfo.EMPTY); + } return pair; } }; diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/RenderWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/RenderWorld.java new file mode 100644 index 000000000..74bc0b889 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/RenderWorld.java @@ -0,0 +1,77 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraft.world.WorldType; +import net.minecraft.world.biome.Biome; + +import net.minecraft.world.chunk.Chunk; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class RenderWorld implements IBlockAccess { + + private final ISchema schema; + private final World world; + + public RenderWorld(ISchema schema) { + this.schema = schema; + this.world = schema.getWorld(); + } + + @Nullable + @Override + public TileEntity getTileEntity(@NotNull BlockPos pos) { + if (this.schema == null) return this.world.getTileEntity(pos); + BlockInfo.Mut.SHARED.set(this.world, pos); + return this.schema.getRenderFilter().test(pos, BlockInfo.Mut.SHARED) ? BlockInfo.Mut.SHARED.getTileEntity() : null; + } + + @Override + public int getCombinedLight(@NotNull BlockPos pos, int lightValue) { + return this.world.getCombinedLight(pos, lightValue); + } + + @Override + public @NotNull IBlockState getBlockState(@NotNull BlockPos pos) { + if (this.schema == null) return this.world.getBlockState(pos); + BlockInfo.Mut.SHARED.set(this.world, pos); + return this.schema.getRenderFilter().test(pos, BlockInfo.Mut.SHARED) ? BlockInfo.Mut.SHARED.getBlockState() : Blocks.AIR.getDefaultState(); + } + + @Override + public boolean isAirBlock(@NotNull BlockPos pos) { + IBlockState state = getBlockState(pos); + return state.getBlock().isAir(state, this, pos); + } + + @Override + public @NotNull Biome getBiome(@NotNull BlockPos pos) { + return this.world.getBiome(pos); + } + + @Override + public int getStrongPower(@NotNull BlockPos pos, @NotNull EnumFacing direction) { + return this.world.getStrongPower(pos, direction); + } + + @Override + public @NotNull WorldType getWorldType() { + return this.world.getWorldType(); + } + + @Override + public boolean isSideSolid(@NotNull BlockPos pos, @NotNull EnumFacing side, boolean _default) { + if (!world.isValid(pos)) return _default; + + Chunk chunk = world.getChunk(pos); + if (chunk == null || chunk.isEmpty()) return _default; + return getBlockState(pos).isSideSolid(this, pos, side); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java index be3a36e68..1c29d700d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java @@ -18,13 +18,13 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.IBlockAccess; import net.minecraftforge.client.ForgeHooksClient; import net.minecraftforge.client.MinecraftForgeClient; import org.lwjgl.opengl.EXTFramebufferObject; import org.lwjgl.opengl.GL11; import org.lwjgl.util.glu.GLU; -import org.lwjgl.util.vector.ReadableVector3f; import org.lwjgl.util.vector.Vector3f; import java.util.Objects; @@ -38,6 +38,7 @@ public class SchemaRenderer implements IDrawable { private static final Framebuffer FBO = new Framebuffer(1080, 1080, true); private final ISchema schema; + private final IBlockAccess renderWorld; private final Framebuffer framebuffer; private final Camera camera = new Camera(new Vector3f(), new Vector3f()); private boolean cameraSetup = false; @@ -52,6 +53,7 @@ public class SchemaRenderer implements IDrawable { public SchemaRenderer(ISchema schema, Framebuffer framebuffer) { this.schema = schema; this.framebuffer = framebuffer; + this.renderWorld = new RenderWorld(schema); } public SchemaRenderer(ISchema schema) { @@ -176,8 +178,8 @@ private void renderWorld() { this.schema.forEach(pair -> { BlockPos pos = pair.getKey(); IBlockState state = pair.getValue().getBlockState(); - if (!state.getBlock().isAir(state, this.schema.getWorld(), pos) && state.getBlock().canRenderInLayer(state, layer)) { - blockrendererdispatcher.renderBlock(state, pos, this.schema.getWorld(), buffer); + if (!state.getBlock().isAir(state, this.renderWorld, pos) && state.getBlock().canRenderInLayer(state, layer)) { + blockrendererdispatcher.renderBlock(state, pos, this.renderWorld, buffer); } }); Tessellator.getInstance().draw(); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java index 4cbbaebbf..b3bb0c4d6 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java @@ -9,12 +9,13 @@ import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; import org.apache.commons.lang3.tuple.MutablePair; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Iterator; import java.util.Map; import java.util.function.BiPredicate; -public class SchemaWorld extends DummyWorld implements IFilteredSchema { +public class SchemaWorld extends DummyWorld implements ISchema{ private final ObjectLinkedOpenHashSet blocks = new ObjectLinkedOpenHashSet<>(); private BiPredicate renderFilter; @@ -30,12 +31,12 @@ public SchemaWorld(BiPredicate renderFilter) { } @Override - public void setRenderFilter(@NotNull BiPredicate renderFilter) { + public void setRenderFilter(@Nullable BiPredicate renderFilter) { this.renderFilter = renderFilter; } @Override - public @NotNull BiPredicate getRenderFilter() { + public @Nullable BiPredicate getRenderFilter() { return renderFilter; } @@ -43,10 +44,12 @@ public void setRenderFilter(@NotNull BiPredicate renderFilt public boolean setBlockState(@NotNull BlockPos pos, @NotNull IBlockState newState, int flags) { boolean renderTest; boolean state; - if (renderFilter.test(pos, BlockInfo.of(this, pos))) { + if (renderFilter == null || renderFilter.test(pos, BlockInfo.of(this, pos))) { renderTest = true; state = super.setBlockState(pos, newState, flags); - } else renderTest = state = false; + } else { + renderTest = state = false; + } if (newState.getBlock().isAir(newState, this, pos)) { if (this.blocks.remove(pos) && BlockPosUtil.isOnBorder(min, max, pos)) { diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java index 3490c91f8..8a958701d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java @@ -7,10 +7,12 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import com.google.common.collect.AbstractIterator; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectIterator; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.Iterator; @@ -18,17 +20,16 @@ import java.util.function.BiPredicate; import java.util.function.Function; -public class SimpleSchema implements IMemorizingFilteredSchema { +public class SimpleSchema implements ISchema { private final World world; - private final Object2ObjectOpenHashMap originalBlocks = new Object2ObjectOpenHashMap<>(); - private final Object2ObjectOpenHashMap filteredBlocks = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); private BiPredicate renderFilter; private final BlockPos origin; private final Vec3d center; public SimpleSchema(Map blocks) { - this(blocks, (blockPos, blockInfo) -> true); + this(blocks, null); } public SimpleSchema(Map blocks, BiPredicate renderFilter) { @@ -39,7 +40,7 @@ public SimpleSchema(Map blocks, BiPredicate blocks, BiPredicate getOriginalSchema() { - return Collections.unmodifiableMap(originalBlocks); - } - - @Override - public void applyRenderFilter() { - filteredBlocks.clear(); - originalBlocks.forEach((pos, bInfo) -> { - if (getRenderFilter().test(pos, bInfo)) { - bInfo.apply(getWorld(), pos); - filteredBlocks.put(pos, bInfo); - } else getWorld().setBlockToAir(pos); - }); - } - - @Override - public void setRenderFilter(@NotNull BiPredicate renderFilter) { + public void setRenderFilter(@Nullable BiPredicate renderFilter) { this.renderFilter = renderFilter; } @Override - public @NotNull BiPredicate getRenderFilter() { + public @Nullable BiPredicate getRenderFilter() { return renderFilter; } @@ -98,18 +82,20 @@ public BlockPos getOrigin() { @NotNull @Override public Iterator> iterator() { - return new Iterator<>() { + return new AbstractIterator<>() { - private final ObjectIterator> it = filteredBlocks.object2ObjectEntrySet().fastIterator(); + private final ObjectIterator> it = blocks.object2ObjectEntrySet().fastIterator(); @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public Map.Entry next() { - return it.next(); + protected Map.Entry computeNext() { + while (it.hasNext()) { + Map.Entry entry = it.next(); + if (renderFilter == null || renderFilter.test(entry.getKey(), entry.getValue())) { + return entry; + } + } + endOfData(); + return null; } }; } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java index bb0e17299..dfc2b09bb 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -1,93 +1,95 @@ package com.cleanroommc.modularui.widgets; import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.api.drawable.IKey; import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.GuiTextures; -import com.cleanroommc.modularui.drawable.keys.StringKey; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; import com.cleanroommc.modularui.widget.Widget; import net.minecraft.util.math.BlockPos; - import net.minecraft.util.math.MathHelper; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.lwjgl.util.vector.Vector3f; import java.util.function.BiPredicate; import java.util.function.BooleanSupplier; public class SchemaWidget extends Widget implements Interactable { + public static final float PI = (float) Math.PI; + public static final float PI2 = 2 * PI; + private final SchemaRenderer schema; private boolean invertMouseScrollScaleAction = false; private double scale = 10; + private int lastMouseX; + private int lastMouseY; + private float pitch = (float) (Math.PI / 4f); + private float yaw = (float) (Math.PI / 4f); + private final Vector3f offset = new Vector3f(); public SchemaWidget(SchemaRenderer schema) { this.schema = schema; schema.cameraFunc((camera, $schema) -> { - camera.setLookAt($schema.getOrigin(), scale, Math.toRadians(yaw), Math.toRadians(pitch)); + camera.setLookAt($schema.getOriginF().translate(offset.x, offset.y, offset.z), scale, yaw, pitch); }); } @Override public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) { - switch (scrollDirection) { - case UP -> { - modifyScale(-((double) amount / 120)); - return true; - } - case DOWN -> { - modifyScale(((double) amount / 120)); - return true; - } - default -> { - return false; - } - } + modifyScale(-scrollDirection.modifier * amount / 120.0); + return true; } - - public SchemaWidget modifyScale(double scale) { - this.scale += scale; - return this; - } - - public SchemaWidget invertMouseScrollScaleAction(boolean invert) { - invertMouseScrollScaleAction = invert; - return this; + @Override + public @NotNull Result onMousePressed(int mouseButton) { + this.lastMouseX = getContext().getAbsMouseX(); + this.lastMouseY = getContext().getAbsMouseY(); + return Result.SUCCESS; } - - private int lastMouseX; - private int lastMouseY; - - private float pitch; - private float yaw; - @Override public void onMouseDrag(int mouseButton, long timeSinceClick) { int mouseX = getContext().getAbsMouseX(); int mouseY = getContext().getAbsMouseY(); - - //timeSinceClick is sometimes greater than 0 even on the first click - if (timeSinceClick == 0) { - this.lastMouseX = mouseX; - this.lastMouseY = mouseY; - return; - } - + int dx = mouseX - lastMouseX; + int dy = mouseY - lastMouseY; if (mouseButton == 0) { - yaw += mouseX - lastMouseX + 360; - yaw = yaw % 360; - pitch = (float) MathHelper.clamp(pitch + (mouseY - lastMouseY), -89.9, 89.9); - - this.lastMouseX = mouseX; - this.lastMouseY = mouseY; + float moveScale = 0.025f; + yaw = (yaw + dx * moveScale + PI2) % PI2; + pitch = MathHelper.clamp(pitch + dy * moveScale, -PI2 / 4 + 0.001f, PI2 / 4 - 0.001f); + } else if (mouseButton == 2) { + // the idea is to construct a vector which points upwards from the camerae pov (y-axis on screen) + // but force dy we force x = y = 0 + float y = (float) Math.cos(pitch); + float moveScale = 0.06f; + // with this the offset can be moved by dy + offset.translate(0, dy * y * moveScale, 0); + // to respect dx we need a new vector which is perpendicular on the previous vector (x-axis on screen) + // y = 0 => mouse movement in x does not move y + float phi = (yaw + PI / 2) % PI2; + float x = (float) Math.cos(phi); + float z = (float) Math.sin(phi); + offset.translate(dx * x * moveScale, 0, dx * z * moveScale); } + this.lastMouseX = mouseX; + this.lastMouseY = mouseY; + } + public SchemaWidget modifyScale(double scale) { + this.scale += scale; + return this; + } + + public SchemaWidget invertMouseScrollScaleAction(boolean invert) { + invertMouseScrollScaleAction = invert; + return this; } @Override @@ -95,13 +97,13 @@ public void onMouseDrag(int mouseButton, long timeSinceClick) { return schema; } - public static class DisableTESR extends ButtonWidget { private boolean disable = false; public DisableTESR() { - this.background(GuiTextures.MC_BACKGROUND); + background(GuiTextures.MC_BACKGROUND); + overlay(IKey.str("TESR").scale(0.5f)); onMousePressed(mouseButton -> { if (mouseButton == 0) { disable = !disable; @@ -119,38 +121,42 @@ public BooleanSupplier makeSuppler() { public static class LayerUpDown extends ButtonWidget { - private int minLayer = Integer.MIN_VALUE; - private int maxLayer = Integer.MAX_VALUE; + private final int minLayer; + private final int maxLayer; private int currentLayer = Integer.MIN_VALUE; - public LayerUpDown() { - this.background(GuiTextures.MC_BACKGROUND); + public LayerUpDown(int minLayer, int maxLayer) { + this.minLayer = minLayer; + this.maxLayer = maxLayer; + background(GuiTextures.MC_BACKGROUND); + overlay(IKey.dynamic(() -> currentLayer > Integer.MIN_VALUE ? Integer.toString(currentLayer) : "ALL").scale(0.5f)); onMousePressed(mouseButton -> { - if (mouseButton == 0) { - currentLayer = Math.max(currentLayer + 1, maxLayer); - } - - if (mouseButton == 1) { - currentLayer = Math.max(currentLayer - 1, maxLayer); - } - if (mouseButton == 0 || mouseButton == 1) { - overlay(new StringKey(Integer.toString(currentLayer))); + if (mouseButton == 0) { + if (currentLayer == Integer.MIN_VALUE) { + currentLayer = minLayer; + } else { + currentLayer++; + } + } else { + if (currentLayer == Integer.MIN_VALUE) { + currentLayer = maxLayer; + } else { + currentLayer--; + } + } + if (currentLayer > maxLayer || currentLayer < minLayer) { + currentLayer = Integer.MIN_VALUE; + } return true; } - return false; }); } public BiPredicate makeSchemaFilter() { - return (blockPos, blockInfo) -> { - maxLayer = Math.min(maxLayer, blockPos.getY()); - minLayer = Math.max(minLayer, blockPos.getY()); - if (currentLayer > blockPos.getY()) return false; - return true; - }; + return (blockPos, blockInfo) -> currentLayer == Integer.MIN_VALUE || currentLayer >= blockPos.getY(); } public LayerUpDown startLayer(int start) { @@ -158,5 +164,4 @@ public LayerUpDown startLayer(int start) { return this; } } - } From a95f3063372d75075bffaeaa6beaeec55d8cfb1d Mon Sep 17 00:00:00 2001 From: brachy84 Date: Tue, 28 May 2024 18:20:38 +0200 Subject: [PATCH 8/9] ArraySchema & fixes --- .../modularui/test/ResizerTest.java | 55 ++-- .../modularui/utils/VectorUtil.java | 52 ++++ .../utils/fakeworld/ArraySchema.java | 235 ++++++++++++++++++ .../utils/fakeworld/BlockPosUtil.java | 4 + .../modularui/utils/fakeworld/ISchema.java | 6 - .../{SimpleSchema.java => MapSchema.java} | 16 +- .../utils/fakeworld/SchemaWorld.java | 30 +-- .../modularui/widgets/SchemaWidget.java | 66 ++--- 8 files changed, 369 insertions(+), 95 deletions(-) create mode 100644 src/main/java/com/cleanroommc/modularui/utils/VectorUtil.java create mode 100644 src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java rename src/main/java/com/cleanroommc/modularui/utils/fakeworld/{SimpleSchema.java => MapSchema.java} (90%) diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index 8e0b78b2f..a07162b43 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -3,25 +3,10 @@ import com.cleanroommc.modularui.screen.CustomModularScreen; import com.cleanroommc.modularui.screen.ModularPanel; import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.utils.fakeworld.*; - +import com.cleanroommc.modularui.utils.fakeworld.ArraySchema; +import com.cleanroommc.modularui.utils.fakeworld.ISchema; import com.cleanroommc.modularui.widgets.SchemaWidget; -import net.minecraft.client.Minecraft; -import net.minecraft.init.Blocks; -import net.minecraft.init.MobEffects; -import net.minecraft.potion.Potion; -import net.minecraft.tileentity.TileEntityBeacon; -import net.minecraft.util.math.BlockPos; -import com.cleanroommc.modularui.api.drawable.IKey; -import com.cleanroommc.modularui.api.layout.IViewportStack; -import com.cleanroommc.modularui.drawable.GuiTextures; -import com.cleanroommc.modularui.screen.CustomModularScreen; -import com.cleanroommc.modularui.screen.ModularPanel; -import com.cleanroommc.modularui.screen.viewport.GuiContext; -import com.cleanroommc.modularui.widget.Widget; - -import net.minecraft.client.Minecraft; import org.jetbrains.annotations.NotNull; public class ResizerTest extends CustomModularScreen { @@ -52,30 +37,34 @@ public class ResizerTest extends CustomModularScreen { .isometric(true) .asIcon().size(140));*/ - var plane = ModularPanel.defaultPanel("main").size(170); - SimpleSchema world = new SimpleSchema.Builder() + /*MapSchema world = new MapSchema.Builder() .add(new BlockPos(0, 0, 0), Blocks.DIAMOND_BLOCK.getDefaultState()) .add(new BlockPos(0, 1, 0), Blocks.BEDROCK.getDefaultState()) .add(new BlockPos(0, 2, 0), Blocks.WOOL.getDefaultState()) .add(new BlockPos(1, 0, 1), Blocks.GOLD_BLOCK.getDefaultState()) .add(new BlockPos(0, 3, 0), Blocks.BEACON.getDefaultState()) - .build(); + .build();*/ - var schemaRenderer = new SchemaRenderer(world); - var layerUpDown = new SchemaWidget.LayerUpDown(0, 3); - world.setRenderFilter(layerUpDown.makeSchemaFilter()); - - plane.child(layerUpDown.bottom(1).left(1).size(16)); - - var disableTESR = new SchemaWidget.DisableTESR().bottom(1).left(18).size(16); - schemaRenderer.disableTESR(disableTESR.makeSuppler()); - plane.child(disableTESR); + ISchema schema = ArraySchema.builder() + .layer("D D", " ", " ", " ") + .layer(" DDD ", " E E ", " ", " ") + .layer(" DDD ", " E ", " G ", " B ") + .layer(" DDD ", " E E ", " ", " ") + .layer("D D", " ", " ", " ") + .where('D', "minecraft:gold_block") + .where('E', "minecraft:emerald_block") + .where('G', "minecraft:diamond_block") + .where('B', "minecraft:beacon") + .build(); - var shnemaW = new SchemaWidget(schemaRenderer).size(120); - plane.child(shnemaW); + var layerUpDown = new SchemaWidget.LayerUpDown(0, 3); + schema.setRenderFilter(layerUpDown.makeSchemaFilter()); - return plane; + var panel = ModularPanel.defaultPanel("main").size(170); + panel.child(new SchemaWidget(schema).full()) + .child(layerUpDown.bottom(1).left(1).size(16)); + return panel; } - } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/VectorUtil.java b/src/main/java/com/cleanroommc/modularui/utils/VectorUtil.java new file mode 100644 index 000000000..efe7d1094 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/VectorUtil.java @@ -0,0 +1,52 @@ +package com.cleanroommc.modularui.utils; + +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.util.vector.Vector3f; + +public class VectorUtil { + + public static Vector3f set(Vector3f target, float x, float y, float z) { + if (target == null) target = new Vector3f(); + target.set(x, y, z); + return target; + } + + @NotNull + public static Vector3f set(@Nullable Vector3f target, Vec3d vec) { + return set(target, (float) vec.x, (float) vec.y, (float) vec.z); + } + + @NotNull + public static Vector3f set(@Nullable Vector3f target, Vec3i vec) { + return set(target, vec.getX(), vec.getY(), vec.getZ()); + } + + public static Vector3f vec3f(Vec3d vec3d) { + return set(null, vec3d); + } + + public static Vector3f vec3f(Vec3i vec3i) { + return set(null, vec3i); + } + + public static Vector3f vec3fAdd(Vector3f source, Vector3f target, float x, float y, float z) { + if (target == null) target = new Vector3f(); + if (source == null) return set(target, x, y, z); + if (target != source) target.set(source); + return target.translate(x, y, z); + } + + @NotNull + public static Vector3f vec3fAdd(Vector3f source, @Nullable Vector3f target, Vec3i vec) { + return vec3fAdd(source, target, vec.getX(), vec.getY(), vec.getZ()); + } + + @NotNull + public static Vector3f vec3fAdd(Vector3f source, @Nullable Vector3f target, Vec3d vec) { + return vec3fAdd(source, target, (float) vec.x, (float) vec.y, (float) vec.z); + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java new file mode 100644 index 000000000..95f263104 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java @@ -0,0 +1,235 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.ModularUI; + +import it.unimi.dsi.fastutil.chars.Char2ObjectMap; + +import it.unimi.dsi.fastutil.chars.Char2ObjectOpenHashMap; + +import it.unimi.dsi.fastutil.chars.CharArraySet; +import it.unimi.dsi.fastutil.chars.CharSet; + +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +import com.google.common.collect.AbstractIterator; + +import net.minecraftforge.fml.common.registry.ForgeRegistries; + +import org.apache.commons.lang3.tuple.MutablePair; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.function.BiPredicate; + +public class ArraySchema implements ISchema { + + public static Builder builder() { + return new Builder(); + } + + private final World world; + private final BlockInfo[][][] blocks; + private BiPredicate renderFilter; + private final Vec3d center; + + public ArraySchema(BlockInfo[][][] blocks) { + this.blocks = blocks; + this.world = new DummyWorld(); + BlockPos.MutableBlockPos current = new BlockPos.MutableBlockPos(); + BlockPos.MutableBlockPos max = new BlockPos.MutableBlockPos(BlockPosUtil.MIN); + for (int x = 0; x < blocks.length; x++) { + for (int y = 0; y < blocks[x].length; y++) { + for (int z = 0; z < blocks[x][y].length; z++) { + BlockInfo block = blocks[x][y][z]; + if (block == null) continue; + current.setPos(x, y, z); + BlockPosUtil.setMax(max, current); + block.apply(this.world, current); + } + } + } + this.center = BlockPosUtil.getCenterD(BlockPos.ORIGIN, BlockPosUtil.add(max, 1, 1, 1)); + } + + @Override + public World getWorld() { + return world; + } + + @Override + public Vec3d getFocus() { + return center; + } + + @Override + public BlockPos getOrigin() { + return BlockPos.ORIGIN; + } + + @Override + public void setRenderFilter(@Nullable BiPredicate renderFilter) { + this.renderFilter = renderFilter; + } + + @Override + public @Nullable BiPredicate getRenderFilter() { + return renderFilter; + } + + @NotNull + @Override + public Iterator> iterator() { + return new AbstractIterator<>() { + + private final BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + private final MutablePair pair = new MutablePair<>(pos, null); + private int x = 0, y = 0, z = -1; + + @Override + protected Map.Entry computeNext() { + BlockInfo info; + while (true) { + if (++z >= blocks[x][y].length) { + z = 0; + if (++y >= blocks[x].length) { + y = 0; + if (++x >= blocks.length) { + return endOfData(); + } + } + } + pos.setPos(x, y, z); + info = blocks[x][y][z]; + if (info != null && renderFilter.test(pos, info)) { + pair.setRight(info); + return pair; + } + } + } + }; + } + + public static class Builder { + + private final List tensor = new ArrayList<>(); + private final Char2ObjectMap blockMap = new Char2ObjectOpenHashMap<>(); + + public Builder() { + blockMap.put(' ', BlockInfo.EMPTY); + blockMap.put('#', BlockInfo.EMPTY); + } + + public Builder layer(String... layer) { + this.tensor.add(layer); + return this; + } + + public Builder where(char c, BlockInfo info) { + this.blockMap.put(c, info); + return this; + } + + public Builder whereAir(char c) { + return where(c, BlockInfo.EMPTY); + } + + public Builder where(char c, IBlockState blockState) { + return where(c, new BlockInfo(blockState)); + } + + public Builder where(char c, IBlockState blockState, TileEntity tile) { + return where(c, new BlockInfo(blockState, tile)); + } + + public Builder where(char c, Block block) { + return where(c, new BlockInfo(block)); + } + + public Builder where(char c, ResourceLocation registryName, int stateMeta) { + Block block = ForgeRegistries.BLOCKS.getValue(registryName); + if (block == null) throw new IllegalArgumentException("Block with name " + registryName + " doesn't exist!"); + IBlockState state = block.getStateFromMeta(stateMeta); + return where(c, new BlockInfo(state)); + } + + public Builder where(char c, ResourceLocation registryName) { + return where(c, registryName, 0); + } + + public Builder where(char c, String registryName, int stateMeta) { + return where(c, new ResourceLocation(registryName), stateMeta); + } + + public Builder where(char c, String registryName) { + return where(c, new ResourceLocation(registryName), 0); + } + + private void validate() { + if (this.tensor.isEmpty()) { + throw new IllegalArgumentException("no block matrix defined"); + } + List errors = new ArrayList<>(); + CharSet checkedChars = new CharArraySet(); + int layerSize = this.tensor.get(0).length; + for (int x = 0; x < this.tensor.size(); x++) { + String[] xLayer = this.tensor.get(x); + if (xLayer.length == 0) { + errors.add(String.format("Layer %s is empty. This is not right", x + 1)); + } else if (xLayer.length != layerSize) { + errors.add(String.format("Invalid x-layer size. Expected %s, but got %s at layer %s", layerSize, xLayer.length, x + 1)); + } + int rowSize = xLayer[0].length(); + for (int y = 0; y < xLayer.length; y++) { + String yRow = xLayer[y]; + if (yRow.isEmpty()) { + errors.add(String.format("Row %s in layer %s is empty. This is not right", y + 1, x + 1)); + } else if (yRow.length() != rowSize) { + errors.add(String.format("Invalid x-layer size. Expected %s, but got %s at row %s in layer %s", layerSize, xLayer.length, y + 1, x + 1)); + } + for (int z = 0; z < yRow.length(); z++) { + char zChar = yRow.charAt(z); + if (!checkedChars.contains(zChar)) { + if (!this.blockMap.containsKey(zChar)) { + errors.add(String.format("Found char '%s' at char %s in row %s in layer %s, but character was not found in map!", zChar, z + 1, y + 1, x + 1)); + } + checkedChars.add(zChar); + } + } + } + } + if (!errors.isEmpty()) { + ModularUI.LOGGER.error("Error validating ArrayScheme BlockArray:"); + for (String e : errors) ModularUI.LOGGER.error(" - {}", e); + throw new IllegalArgumentException("The ArraySchema builder was misconfigured. See message above."); + } + } + + public ArraySchema build() { + validate(); + BlockInfo[][][] blocks = new BlockInfo[this.tensor.size()][this.tensor.get(0).length][this.tensor.get(0)[0].length()]; + for (int x = 0; x < this.tensor.size(); x++) { + String[] xLayer = this.tensor.get(x); + for (int y = 0; y < xLayer.length; y++) { + String yRow = xLayer[y]; + for (int z = 0; z < yRow.length(); z++) { + char zChar = yRow.charAt(z); + BlockInfo info = this.blockMap.get(zChar); + if (info == null || info == BlockInfo.EMPTY) continue; // null -> any allowed -> don't need to check + blocks[x][y][z] = info; + } + } + } + return new ArraySchema(blocks); + } + } +} diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java index b7c72f52b..5a48cbf5a 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java @@ -77,4 +77,8 @@ public static boolean isOnBorder(BlockPos boxMin, BlockPos boxMax, BlockPos p) { p.getY() == boxMin.getY() || p.getY() == boxMax.getY() || p.getZ() == boxMin.getZ() || p.getZ() == boxMax.getZ(); } + + public static BlockPos.MutableBlockPos add(BlockPos.MutableBlockPos pos, int x, int y, int z) { + return pos.setPos(pos.getX() + x, pos.getY() + y, pos.getZ() + z); + } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java index 9904a230e..7f2847d97 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ISchema.java @@ -4,9 +4,7 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.lwjgl.util.vector.Vector3f; import java.util.Map; import java.util.function.BiPredicate; @@ -19,10 +17,6 @@ public interface ISchema extends Iterable> { BlockPos getOrigin(); - default Vector3f getOriginF() { - return new Vector3f(getOrigin().getX(), getOrigin().getY(), getOrigin().getZ()); - } - void setRenderFilter(@Nullable BiPredicate renderFilter); @Nullable BiPredicate getRenderFilter(); diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/MapSchema.java similarity index 90% rename from src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java rename to src/main/java/com/cleanroommc/modularui/utils/fakeworld/MapSchema.java index 8a958701d..e4781aa3e 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SimpleSchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/MapSchema.java @@ -14,13 +14,12 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.function.BiPredicate; import java.util.function.Function; -public class SimpleSchema implements ISchema { +public class MapSchema implements ISchema { private final World world; private final Object2ObjectOpenHashMap blocks = new Object2ObjectOpenHashMap<>(); @@ -28,11 +27,11 @@ public class SimpleSchema implements ISchema { private final BlockPos origin; private final Vec3d center; - public SimpleSchema(Map blocks) { + public MapSchema(Map blocks) { this(blocks, null); } - public SimpleSchema(Map blocks, BiPredicate renderFilter) { + public MapSchema(Map blocks, BiPredicate renderFilter) { this.world = new DummyWorld(); this.renderFilter = renderFilter; BlockPos.MutableBlockPos min = new BlockPos.MutableBlockPos(BlockPosUtil.MAX); @@ -94,8 +93,7 @@ protected Map.Entry computeNext() { return entry; } } - endOfData(); - return null; + return endOfData(); } }; } @@ -138,11 +136,11 @@ public Builder setRenderFilter(BiPredicate renderFilter) { return this; } - public SimpleSchema build() { + public MapSchema build() { if (renderFilter == null) { - return new SimpleSchema(this.blocks); + return new MapSchema(this.blocks); } - return new SimpleSchema(this.blocks, renderFilter); + return new MapSchema(this.blocks, renderFilter); } } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java index b3bb0c4d6..969cffd22 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaWorld.java @@ -5,8 +5,9 @@ import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import it.unimi.dsi.fastutil.objects.ObjectIterator; +import com.google.common.collect.AbstractIterator; import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet; +import it.unimi.dsi.fastutil.objects.ObjectListIterator; import org.apache.commons.lang3.tuple.MutablePair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,7 +16,7 @@ import java.util.Map; import java.util.function.BiPredicate; -public class SchemaWorld extends DummyWorld implements ISchema{ +public class SchemaWorld extends DummyWorld implements ISchema { private final ObjectLinkedOpenHashSet blocks = new ObjectLinkedOpenHashSet<>(); private BiPredicate renderFilter; @@ -95,23 +96,22 @@ public BlockPos getOrigin() { @NotNull @Override public Iterator> iterator() { - return new Iterator<>() { - - private final ObjectIterator it = blocks.iterator(); + return new AbstractIterator<>() { + private final ObjectListIterator it = blocks.iterator(); private final BlockInfo.Mut info = new BlockInfo.Mut(); private final MutablePair pair = new MutablePair<>(null, this.info); @Override - public boolean hasNext() { - return it.hasNext(); - } - - @Override - public Map.Entry next() { - BlockPos pos = it.next(); - this.info.set(SchemaWorld.this, pos); - this.pair.setLeft(pos); - return this.pair; + protected Map.Entry computeNext() { + while (it.hasNext()) { + var pos = it.next(); + this.info.set(SchemaWorld.this, pos); + this.pair.setLeft(pos); + if (renderFilter == null || renderFilter.test(pos, info)) { + return this.pair; + } + } + return endOfData(); } }; } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java index dfc2b09bb..169879213 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -5,7 +5,9 @@ import com.cleanroommc.modularui.api.widget.Interactable; import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.ModularScreen; +import com.cleanroommc.modularui.utils.VectorUtil; import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; +import com.cleanroommc.modularui.utils.fakeworld.ISchema; import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; import com.cleanroommc.modularui.widget.Widget; @@ -17,7 +19,6 @@ import org.lwjgl.util.vector.Vector3f; import java.util.function.BiPredicate; -import java.util.function.BooleanSupplier; public class SchemaWidget extends Widget implements Interactable { @@ -25,26 +26,35 @@ public class SchemaWidget extends Widget implements Interactable { public static final float PI2 = 2 * PI; private final SchemaRenderer schema; - private boolean invertMouseScrollScaleAction = false; - private double scale = 10; - + private boolean enableRotation = true; + private boolean enableTranslation = true; + private boolean enableScaling = true; private int lastMouseX; private int lastMouseY; + private double scale = 10; private float pitch = (float) (Math.PI / 4f); private float yaw = (float) (Math.PI / 4f); private final Vector3f offset = new Vector3f(); + public SchemaWidget(ISchema schema) { + this(new SchemaRenderer(schema)); + } + public SchemaWidget(SchemaRenderer schema) { this.schema = schema; schema.cameraFunc((camera, $schema) -> { - camera.setLookAt($schema.getOriginF().translate(offset.x, offset.y, offset.z), scale, yaw, pitch); + Vector3f focus = VectorUtil.vec3fAdd(this.offset, null, $schema.getFocus()); + camera.setLookAt(focus, scale, yaw, pitch); }); } @Override public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) { - modifyScale(-scrollDirection.modifier * amount / 120.0); - return true; + if (this.enableScaling) { + modifyScale(-scrollDirection.modifier * amount / 120.0); + return true; + } + return false; } @Override @@ -60,11 +70,11 @@ public void onMouseDrag(int mouseButton, long timeSinceClick) { int mouseY = getContext().getAbsMouseY(); int dx = mouseX - lastMouseX; int dy = mouseY - lastMouseY; - if (mouseButton == 0) { + if (mouseButton == 0 && this.enableRotation) { float moveScale = 0.025f; yaw = (yaw + dx * moveScale + PI2) % PI2; pitch = MathHelper.clamp(pitch + dy * moveScale, -PI2 / 4 + 0.001f, PI2 / 4 - 0.001f); - } else if (mouseButton == 2) { + } else if (mouseButton == 2 && this.enableTranslation) { // the idea is to construct a vector which points upwards from the camerae pov (y-axis on screen) // but force dy we force x = y = 0 float y = (float) Math.cos(pitch); @@ -87,36 +97,28 @@ public SchemaWidget modifyScale(double scale) { return this; } - public SchemaWidget invertMouseScrollScaleAction(boolean invert) { - invertMouseScrollScaleAction = invert; + public SchemaWidget enableDragRotation(boolean enable) { + this.enableRotation = enable; return this; } - @Override - public @Nullable IDrawable getOverlay() { - return schema; + public SchemaWidget enableDragTranslation(boolean enable) { + this.enableTranslation = enable; + return this; } - public static class DisableTESR extends ButtonWidget { - - private boolean disable = false; - - public DisableTESR() { - background(GuiTextures.MC_BACKGROUND); - overlay(IKey.str("TESR").scale(0.5f)); - onMousePressed(mouseButton -> { - if (mouseButton == 0) { - disable = !disable; - return true; - } + public SchemaWidget enableScrollScaling(boolean enable) { + this.enableScaling = enable; + return this; + } - return false; - }); - } + public SchemaWidget enableAllInteraction(boolean enable) { + return enableDragRotation(enable).enableDragTranslation(enable).enableScrollScaling(enable); + } - public BooleanSupplier makeSuppler() { - return () -> disable; - } + @Override + public @Nullable IDrawable getOverlay() { + return schema; } public static class LayerUpDown extends ButtonWidget { From a68f3f4f728661f121b7a2ed4597d17f08fe25d7 Mon Sep 17 00:00:00 2001 From: brachy84 Date: Sun, 23 Jun 2024 15:30:54 +0200 Subject: [PATCH 9/9] minor changes --- .../modularui/test/ResizerTest.java | 12 ++--- .../modularui/widgets/SchemaWidget.java | 44 ++++++++++++------- 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java index a07162b43..73702fd38 100644 --- a/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java +++ b/src/main/java/com/cleanroommc/modularui/test/ResizerTest.java @@ -58,13 +58,13 @@ public class ResizerTest extends CustomModularScreen { .where('B', "minecraft:beacon") .build(); - - var layerUpDown = new SchemaWidget.LayerUpDown(0, 3); - schema.setRenderFilter(layerUpDown.makeSchemaFilter()); - var panel = ModularPanel.defaultPanel("main").size(170); - panel.child(new SchemaWidget(schema).full()) - .child(layerUpDown.bottom(1).left(1).size(16)); + panel.child(new SchemaWidget(schema) + .full()) + .child(new SchemaWidget.LayerButton(schema, 0, 3) + .bottom(1) + .left(1) + .size(16)); return panel; } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java index 169879213..6808ea564 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -6,20 +6,16 @@ import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.screen.ModularScreen; import com.cleanroommc.modularui.utils.VectorUtil; -import com.cleanroommc.modularui.utils.fakeworld.BlockInfo; import com.cleanroommc.modularui.utils.fakeworld.ISchema; import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; import com.cleanroommc.modularui.widget.Widget; -import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.lwjgl.util.vector.Vector3f; -import java.util.function.BiPredicate; - public class SchemaWidget extends Widget implements Interactable { public static final float PI = (float) Math.PI; @@ -51,7 +47,7 @@ public SchemaWidget(SchemaRenderer schema) { @Override public boolean onMouseScroll(ModularScreen.UpOrDown scrollDirection, int amount) { if (this.enableScaling) { - modifyScale(-scrollDirection.modifier * amount / 120.0); + scale(-scrollDirection.modifier * amount / 120.0); return true; } return false; @@ -76,7 +72,7 @@ public void onMouseDrag(int mouseButton, long timeSinceClick) { pitch = MathHelper.clamp(pitch + dy * moveScale, -PI2 / 4 + 0.001f, PI2 / 4 - 0.001f); } else if (mouseButton == 2 && this.enableTranslation) { // the idea is to construct a vector which points upwards from the camerae pov (y-axis on screen) - // but force dy we force x = y = 0 + // this vector determines the amount of z offset from mouse movement in y float y = (float) Math.cos(pitch); float moveScale = 0.06f; // with this the offset can be moved by dy @@ -92,11 +88,26 @@ public void onMouseDrag(int mouseButton, long timeSinceClick) { this.lastMouseY = mouseY; } - public SchemaWidget modifyScale(double scale) { + public SchemaWidget scale(double scale) { this.scale += scale; return this; } + public SchemaWidget pitch(float pitch) { + this.pitch += pitch; + return this; + } + + public SchemaWidget yaw(float yaw) { + this.yaw += yaw; + return this; + } + + public SchemaWidget offset(float x, float y, float z) { + this.offset.set(x, y, z); + return this; + } + public SchemaWidget enableDragRotation(boolean enable) { this.enableRotation = enable; return this; @@ -112,8 +123,14 @@ public SchemaWidget enableScrollScaling(boolean enable) { return this; } + public SchemaWidget enableInteraction(boolean rotation, boolean translation, boolean scaling) { + return enableDragRotation(rotation) + .enableDragTranslation(translation) + .enableScrollScaling(scaling); + } + public SchemaWidget enableAllInteraction(boolean enable) { - return enableDragRotation(enable).enableDragTranslation(enable).enableScrollScaling(enable); + return enableInteraction(enable, enable, enable); } @Override @@ -121,13 +138,13 @@ public SchemaWidget enableAllInteraction(boolean enable) { return schema; } - public static class LayerUpDown extends ButtonWidget { + public static class LayerButton extends ButtonWidget { private final int minLayer; private final int maxLayer; private int currentLayer = Integer.MIN_VALUE; - public LayerUpDown(int minLayer, int maxLayer) { + public LayerButton(ISchema schema, int minLayer, int maxLayer) { this.minLayer = minLayer; this.maxLayer = maxLayer; background(GuiTextures.MC_BACKGROUND); @@ -155,13 +172,10 @@ public LayerUpDown(int minLayer, int maxLayer) { } return false; }); + schema.setRenderFilter((blockPos, blockInfo) -> currentLayer == Integer.MIN_VALUE || currentLayer >= blockPos.getY()); } - public BiPredicate makeSchemaFilter() { - return (blockPos, blockInfo) -> currentLayer == Integer.MIN_VALUE || currentLayer >= blockPos.getY(); - } - - public LayerUpDown startLayer(int start) { + public LayerButton startLayer(int start) { this.currentLayer = start; return this; }