diff --git a/src/main/java/com/cleanroommc/modularui/factory/GuiData.java b/src/main/java/com/cleanroommc/modularui/factory/GuiData.java index adf62be15..5aea6ae7d 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/GuiData.java +++ b/src/main/java/com/cleanroommc/modularui/factory/GuiData.java @@ -4,6 +4,7 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; +import net.minecraft.world.World; import java.util.Objects; @@ -27,6 +28,10 @@ public EntityPlayer getPlayer() { return this.player; } + public World getWorld() { + return this.player.getEntityWorld(); + } + public boolean isClient() { return NetworkUtils.isClient(this.player); } diff --git a/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java b/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java index 5c5afab09..5afd7669d 100644 --- a/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java +++ b/src/main/java/com/cleanroommc/modularui/factory/PosGuiData.java @@ -4,7 +4,6 @@ import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.World; /** * See {@link GuiData} for an explanation for what this is for. @@ -22,10 +21,6 @@ public PosGuiData(EntityPlayer player, int x, int y, int z) { this.z = z; } - public World getWorld() { - return getPlayer().world; - } - public int getX() { return this.x; } diff --git a/src/main/java/com/cleanroommc/modularui/test/TestTile.java b/src/main/java/com/cleanroommc/modularui/test/TestTile.java index e9c0649ea..07c84d929 100644 --- a/src/main/java/com/cleanroommc/modularui/test/TestTile.java +++ b/src/main/java/com/cleanroommc/modularui/test/TestTile.java @@ -3,11 +3,13 @@ import com.cleanroommc.modularui.api.IGuiHolder; import com.cleanroommc.modularui.api.IPanelHandler; import com.cleanroommc.modularui.api.drawable.IKey; +import com.cleanroommc.modularui.api.widget.IWidget; import com.cleanroommc.modularui.drawable.Circle; import com.cleanroommc.modularui.drawable.GuiTextures; import com.cleanroommc.modularui.drawable.ItemDrawable; import com.cleanroommc.modularui.drawable.Rectangle; import com.cleanroommc.modularui.drawable.text.AnimatedText; +import com.cleanroommc.modularui.factory.GuiData; import com.cleanroommc.modularui.factory.PosGuiData; import com.cleanroommc.modularui.network.NetworkUtils; import com.cleanroommc.modularui.screen.ModularPanel; @@ -18,6 +20,9 @@ import com.cleanroommc.modularui.utils.Alignment; import com.cleanroommc.modularui.utils.Color; import com.cleanroommc.modularui.utils.Interpolation; +import com.cleanroommc.modularui.utils.fakeworld.ArraySchema; +import com.cleanroommc.modularui.utils.fakeworld.BlockHighlight; +import com.cleanroommc.modularui.utils.fakeworld.SchemaRenderer; import com.cleanroommc.modularui.value.BoolValue; import com.cleanroommc.modularui.value.IntValue; import com.cleanroommc.modularui.value.StringValue; @@ -40,6 +45,7 @@ import com.cleanroommc.modularui.widgets.PageButton; import com.cleanroommc.modularui.widgets.PagedWidget; import com.cleanroommc.modularui.widgets.ProgressWidget; +import com.cleanroommc.modularui.widgets.SchemaWidget; import com.cleanroommc.modularui.widgets.ScrollingTextWidget; import com.cleanroommc.modularui.widgets.SliderWidget; import com.cleanroommc.modularui.widgets.SlotGroupWidget; @@ -174,7 +180,10 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI .tab(GuiTextures.TAB_TOP, 0)) .child(new PageButton(3, tabController) .tab(GuiTextures.TAB_TOP, 0) - .overlay(new ItemDrawable(Blocks.CHEST).asIcon()))) + .overlay(new ItemDrawable(Blocks.CHEST).asIcon())) + .child(new PageButton(4, tabController) + .tab(GuiTextures.TAB_TOP, 0) + .overlay(new ItemDrawable(Items.ENDER_EYE).asIcon()))) .child(new Expandable() .debugName("expandable") .top(0) @@ -438,7 +447,8 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI .child(new DynamicSyncedWidget<>() .widthRel(1f) .syncHandler(dynamicSyncHandler))) - ))) + ) + .addPage(createSchemaPage(guiData)))) .child(SlotGroupWidget.playerInventory(false)) ); /*panel.child(new ButtonWidget<>() @@ -454,6 +464,19 @@ public ModularPanel buildUI(PosGuiData guiData, PanelSyncManager syncManager, UI return panel; } + private IWidget createSchemaPage(GuiData data) { + ParentWidget page = new ParentWidget<>(); + page.debugName("page 5 schema"); + page.sizeRel(1f); + page.child(IKey.str("schema").asWidget()); + if (world.isRemote) + page.child(new SchemaWidget(new SchemaRenderer(ArraySchema.of(data.getPlayer(), 5)) + .highlightRenderer(new BlockHighlight(Color.withAlpha(Color.GREEN.brighter(1), 0.9f), 1/32f))) + .pos(20, 20) + .size(100, 100)); + return page; + } + public ModularPanel openSecondWindow(PanelSyncManager syncManager, IPanelHandler syncHandler) { ModularPanel panel = new Dialog<>("second_window", null) .setDisablePanelsBelow(false) diff --git a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java index 67ee57d20..5c2c5cc9a 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java +++ b/src/main/java/com/cleanroommc/modularui/utils/MathUtils.java @@ -1,10 +1,17 @@ package com.cleanroommc.modularui.utils; +import net.minecraft.util.math.MathHelper; + import org.mariuszgromada.math.mxparser.Constant; import org.mariuszgromada.math.mxparser.Expression; public class MathUtils { + public static final float PI = (float) Math.PI; + public static final float PI2 = 2f * PI; + public static final float PI_HALF = PI / 2f; + public static final float PI_QUART = PI / 4f; + // SI prefixes public static final Constant k = new Constant("k", 1e3); public static final Constant M = new Constant("M", 1e6); @@ -154,4 +161,16 @@ public static int wrapDegrees(int angle) { if (angle < -180) angle += 360; return angle; } + + public static float sin(float v) { + return MathHelper.sin(v); + } + + public static float cos(float v) { + return MathHelper.cos(v); + } + + public static float tan(float v) { + return MathHelper.sin(v) / MathHelper.cos(v); + } } diff --git a/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java b/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java index 1153caebd..8c5305e34 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java +++ b/src/main/java/com/cleanroommc/modularui/utils/Vector3f.java @@ -1,9 +1,23 @@ package com.cleanroommc.modularui.utils; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + import java.nio.FloatBuffer; +import java.util.Objects; public class Vector3f { + public static final Vector3f UNIT_X = new Vector3f(1.0f, 0.0f, 0.0f); + public static final Vector3f UNIT_Y = new Vector3f(0.0f, 1.0f, 0.0f); + public static final Vector3f UNIT_Z = new Vector3f(0.0f, 0.0f, 1.0f); + + public static void resetUnitVectors() { + UNIT_X.set(1.0f, 0.0f, 0.0f); + UNIT_Y.set(0.0f, 1.0f, 0.0f); + UNIT_Z.set(0.0f, 0.0f, 1.0f); + } + public float x, y, z; /** @@ -20,6 +34,10 @@ public Vector3f(Vector3f src) { set(src); } + public Vector3f(Vec3d src) { + set(src); + } + /** * Constructor */ @@ -44,6 +62,12 @@ public void set(float x, float y, float z) { this.z = z; } + public void set(double x, double y, double z) { + this.x = (float) x; + this.y = (float) y; + this.z = (float) z; + } + /** * Load from another Vec3f * @@ -57,6 +81,11 @@ public Vector3f set(Vector3f src) { return this; } + public Vector3f set(Vec3d src) { + set(src.x, src.y, src.z); + return this; + } + /** * @return the length squared of the vector */ @@ -82,6 +111,10 @@ public Vector3f translate(float x, float y, float z) { return this; } + public Vector3f translate(double x, double y, double z) { + return translate((float) x, (float) y, (float) z); + } + /** * Add a vector to another vector and place the result in a destination * vector. @@ -100,6 +133,20 @@ public static Vector3f add(Vector3f left, Vector3f right, Vector3f dest) { } } + public static Vector3f add(Vector3f a, Vector3f b, Vector3f c, Vector3f dest) { + if (dest == null) dest = new Vector3f(); + dest.set(a.x + b.x + c.x, a.y + b.y + c.y, a.z + b.z + c.z); + return dest; + } + + public Vector3f add(Vector3f v) { + return add(v, this, this); + } + + public Vector3f add(Vector3f v, Vector3f dest) { + return add(this, v, dest); + } + /** * Subtract a vector from another vector and place the result in a destination * vector. @@ -134,7 +181,6 @@ public static Vector3f cross(Vector3f left, Vector3f right, Vector3f dest) { return dest; } - /** * Negate a vector * @@ -154,14 +200,16 @@ public Vector3f negate() { * @return the negated vector */ public Vector3f negate(Vector3f dest) { - if (dest == null) - dest = new Vector3f(); + if (dest == null) dest = new Vector3f(); dest.x = -x; dest.y = -y; dest.z = -z; return dest; } + public Vector3f normalise() { + return normalise(this); + } /** * Normalise this vector and place the result in another vector. @@ -170,13 +218,11 @@ public Vector3f negate(Vector3f dest) { * @return the normalised vector */ public Vector3f normalise(Vector3f dest) { - float l = length(); - - if (dest == null) - dest = new Vector3f(x / l, y / l, z / l); - else - dest.set(x / l, y / l, z / l); - + if (dest == null) dest = new Vector3f(); + float lsq = lengthSquared(); + if (lsq == 1) return dest.set(this); + float f = (float) MathHelper.fastInvSqrt(lsq); + dest.set(x * f, y * f, z * f); return dest; } @@ -222,42 +268,68 @@ public Vector3f load(FloatBuffer buf) { * @see org.lwjgl.vector.Vec#scale(float) */ public Vector3f scale(float scale) { - x *= scale; y *= scale; z *= scale; - return this; - } /* (non-Javadoc) * @see org.lwjgl.vector.Vec#store(FloatBuffer) */ public Vector3f store(FloatBuffer buf) { - buf.put(x); buf.put(y); buf.put(z); + return this; + } + + public float distanceTo(Vector3f vec) { + return MathHelper.sqrt(squareDistanceTo(vec)); + } + + public float squareDistanceTo(Vector3f v) { + return squareDistanceTo(v.x, v.y, v.z); + } + + public float squareDistanceTo(float xIn, float yIn, float zIn) { + float d0 = xIn - this.x; + float d1 = yIn - this.y; + float d2 = zIn - this.z; + return d0 * d0 + d1 * d1 + d2 * d2; + } + + public Vector3f rotatePitch(float pitch) { + float cos = MathHelper.cos(pitch); + float sin = MathHelper.sin(pitch); + float x = this.x; + float y = this.y * cos + this.z * sin; + float z = this.z * cos - this.y * sin; + set(x, y, z); + return this; + } + public Vector3f rotateYaw(float yaw) { + float sin = MathHelper.cos(yaw); + float cos = MathHelper.sin(yaw); + float x = this.x * sin + this.z * cos; + float y = this.y; + float z = this.z * sin - this.x * cos; + set(x, y, z); return this; } + public Vector3f copy() { + return new Vector3f(x, y, z); + } + + public Vec3d toVec3d() { + return new Vec3d(x, y, z); + } + /* (non-Javadoc) * @see java.lang.Object#toString() */ - public String toString() { - StringBuilder sb = new StringBuilder(64); - - sb.append("Vec3f["); - sb.append(x); - sb.append(", "); - sb.append(y); - sb.append(", "); - sb.append(z); - sb.append(']'); - return sb.toString(); - } /** * @return x @@ -307,15 +379,21 @@ public float getZ() { return z; } + public String toString() { + return "Vec3f[" + x + ", " + y + ", " + z + ']'; + } + + @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Vector3f other = (Vector3f) obj; - - if (x == other.x && y == other.y && z == other.z) return true; - - return false; + return x == other.x && y == other.y && z == other.z; } + @Override + public int hashCode() { + return Objects.hash(x, y, 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 index 95f263104..935c51e8d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/ArraySchema.java @@ -2,25 +2,22 @@ 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.entity.Entity; 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.util.math.Vec3i; import net.minecraft.world.World; - -import com.google.common.collect.AbstractIterator; - import net.minecraftforge.fml.common.registry.ForgeRegistries; +import com.google.common.collect.AbstractIterator; +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 org.apache.commons.lang3.tuple.MutablePair; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -37,9 +34,44 @@ public static Builder builder() { return new Builder(); } + public static ArraySchema of(Entity entity, int radius) { + return of(entity.world, entity.getPosition(), radius); + } + + public static ArraySchema of(World world, BlockPos center, int radius) { + int s = 2 * radius + 1; + BlockInfo[][][] blocks = new BlockInfo[s][s][s]; + BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(); + BlockPosUtil.add(pos.setPos(center), -radius, -radius, -radius); + for (int x = 0; x < s; x++) { + for (int y = 0; y < s; y++) { + for (int z = 0; z < s; z++) { + blocks[x][y][z] = BlockInfo.of(world, pos); + BlockPosUtil.add(pos, 0, 0, 1); + } + BlockPosUtil.add(pos, 0, 1, -s); + } + BlockPosUtil.add(pos, 1, -s, 0); + } + return new ArraySchema(blocks); + } + + public static ArraySchema of(World world, Vec3i p1, Vec3i p2) { + 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()); + x0--; + y0--; + z0--; + BlockInfo[][][] blocks = new BlockInfo[x1 - x0][y1 - y0][z1 - z0]; + for (BlockPos.MutableBlockPos pos : BlockPos.getAllInBoxMutable(x0, y0, z0, x1, y1, z1)) { + blocks[pos.getX() - x0][pos.getY() - y0][pos.getZ() - z0] = BlockInfo.of(world, pos); + } + return new ArraySchema(blocks); + } + private final World world; private final BlockInfo[][][] blocks; - private BiPredicate renderFilter; + private BiPredicate renderFilter = (__, ___) -> true; private final Vec3d center; public ArraySchema(BlockInfo[][][] blocks) { diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BaseSchemaRenderer.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BaseSchemaRenderer.java new file mode 100644 index 000000000..df4009a89 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BaseSchemaRenderer.java @@ -0,0 +1,332 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.api.drawable.IDrawable; +import com.cleanroommc.modularui.drawable.Icon; +import com.cleanroommc.modularui.screen.viewport.GuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.Platform; +import com.cleanroommc.modularui.utils.Vector3f; +import com.cleanroommc.modularui.widget.sizer.Area; +import com.cleanroommc.modularui.widgets.SchemaWidget; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.Tessellator; +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.world.IBlockAccess; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.MinecraftForgeClient; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.EXTFramebufferObject; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; + +public class BaseSchemaRenderer 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(); + private RayTraceResult lastRayTrace = null; + + public BaseSchemaRenderer(ISchema schema, Framebuffer framebuffer) { + this.schema = schema; + this.framebuffer = framebuffer; + this.renderWorld = new RenderWorld(schema); + } + + public BaseSchemaRenderer(ISchema schema) { + this(schema, FBO); + } + + @Nullable + public RayTraceResult getLastRayTrace() { + return lastRayTrace; + } + + public ISchema getSchema() { + return schema; + } + + public Camera getCamera() { + return camera; + } + + @Override + public SchemaWidget asWidget() { + return new SchemaWidget(this); + } + + @Override + public Icon asIcon() { + return IDrawable.super.asIcon().size(50); + } + + @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()); + } + + protected void render(int x, int y, int width, int height, int mouseX, int mouseY) { + onSetupCamera(); + int lastFbo = bindFBO(); + setupCamera(this.framebuffer.framebufferWidth, this.framebuffer.framebufferHeight); + renderWorld(); + if (doRayTrace()) { + RayTraceResult result = null; + if (Area.isInside(x, y, width, height, mouseX, mouseY)) { + result = rayTrace(mouseX, mouseY, width, height); + } + if (result == null) { + if (this.lastRayTrace != null) { + onRayTraceFailed(); + } + } else { + onSuccessfulRayTrace(result); + } + this.lastRayTrace = result; + } + onRendered(); + Platform.setupDrawTex(); + 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 + Platform.startDrawing(Platform.DrawMode.QUADS, Platform.VertexFormat.POS_TEX, bufferBuilder -> { + 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(); + }); + GlStateManager.bindTexture(lastFbo); + } + + protected RayTraceResult rayTrace(int mouseX, int mouseY, int width, int height) { + final float halfPI = (float) (Math.PI / 2); + Vector3f cameraPos = camera.getPos(); + float yaw = camera.getYaw(); + float pitch = camera.getPitch(); + + Vector3f mouseXShift = new Vector3f(1, 0, 0) + .rotatePitch(pitch) + .rotateYaw(-yaw + halfPI) + .scale(mouseX - width / 2f) + .scale(1 / 32f); + Vector3f mouseYShift = new Vector3f(0, -1, 0) + .rotatePitch(pitch) + .rotateYaw(-yaw + halfPI) + .scale(mouseY - height / 2f) + .scale(1 / 32f); + Vector3f mousePos = Vector3f.add(cameraPos, mouseXShift, mouseYShift, null); + Vector3f focus = camera.getLookAt(); + float perspectiveCompensation = isIsometric() ? 1 : cameraPos.distanceTo(focus) / 3 * width / 100; + Vector3f underMousePos = focus.add(mouseXShift.scale(perspectiveCompensation), null).add(mouseYShift.scale(perspectiveCompensation)); + Vector3f look = Vector3f.sub(underMousePos, mousePos, null).scale(10); + Vector3f.add(mousePos, look, underMousePos); + return schema.getWorld().rayTraceBlocks(mousePos.toVec3d(), underMousePos.toVec3d(), true); + } + + private void renderWorld() { + Minecraft mc = Minecraft.getMinecraft(); + Platform.setupDrawTex(); + GlStateManager.enableCull(); + GlStateManager.enableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + mc.entityRenderer.disableLightmap(); + mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + BlockRenderLayer oldRenderLayer = MinecraftForgeClient.getRenderLayer(); + GlStateManager.disableLighting(); + Platform.setupDrawGradient(); // needed for ambient occlusion + + 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.renderWorld, pos) && state.getBlock().canRenderInLayer(state, layer)) { + blockrendererdispatcher.renderBlock(state, pos, this.renderWorld, buffer); + } + }); + Tessellator.getInstance().draw(); + Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); + } + } finally { + ForgeHooksClient.setRenderLayer(oldRenderLayer); + } + + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableLighting(); + + // render TESR + if (isTesrEnabled()) { + 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); + } + }); + } + } + Platform.endDrawGradient(); + ForgeHooksClient.setRenderPass(-1); + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + } + + 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 final 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(getClearColor()); + 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 near = isIsometric() ? 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 (isIsometric()) { + 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 (isIsometric()) { + 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); + } + + protected final 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(); + } + + 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); + } + + @ApiStatus.OverrideOnly + protected void onSetupCamera() {} + + @ApiStatus.OverrideOnly + protected void onRendered() {} + + @ApiStatus.OverrideOnly + protected void onSuccessfulRayTrace(@NotNull RayTraceResult result) {} + + @ApiStatus.OverrideOnly + protected void onRayTraceFailed() {} + + public boolean doRayTrace() { + return false; + } + + public int getClearColor() { + return Color.withAlpha(0, 0.5f); + } + + public boolean isIsometric() { + return false; + } + + public boolean isTesrEnabled() { + return true; + } +} + + + diff --git a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockHighlight.java b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockHighlight.java new file mode 100644 index 000000000..f41528188 --- /dev/null +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockHighlight.java @@ -0,0 +1,183 @@ +package com.cleanroommc.modularui.utils.fakeworld; + +import com.cleanroommc.modularui.utils.Color; +import com.cleanroommc.modularui.utils.Platform; +import com.cleanroommc.modularui.utils.Vector3f; + +import net.minecraft.client.renderer.BufferBuilder; +import net.minecraft.client.renderer.GlStateManager; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; + +import org.jetbrains.annotations.Nullable; + +public class BlockHighlight { + + // this is magic + protected static final float[][] vertices = new float[6][12]; + + static { + int[][] intVertices = { + {1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0}, + {0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0}, + {0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0}, + {0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1}, + {0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0}, + {1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0} + }; + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 12; j++) { + int v = intVertices[i][j]; + vertices[i][j] = v == 1 ? 1.005f : -0.005f; + } + } + } + + private int color; + private boolean allSides; + private float frameThickness; + + public BlockHighlight(int color) { + this(color, true, 0.0F); + } + + public BlockHighlight(int color, float frameThickness) { + this(color, true, frameThickness); + } + + public BlockHighlight(int color, boolean allSides) { + this(color, allSides, 0.0F); + } + + public BlockHighlight(int color, boolean allSides, float frameThickness) { + this.color = color; + this.allSides = allSides; + this.frameThickness = frameThickness; + } + + public final void renderHighlight(RayTraceResult result, Vector3f camera) { + if (result != null && result.typeOfHit == RayTraceResult.Type.BLOCK) { + renderHighlight(result.getBlockPos(), result.sideHit, camera); + } + } + + public final void renderHighlight(BlockPos pos, EnumFacing side, Vector3f camera) { + Platform.setupDrawColor(); + GlStateManager.disableLighting(); + Color.setGlColor(this.color); + GlStateManager.pushMatrix(); + GlStateManager.translate(pos.getX(), pos.getY(), pos.getZ()); + float dist = camera.squareDistanceTo(pos.getX() + 0.5f, pos.getY() + 0.5f, pos.getZ() + 0.5f); + doRender(side, dist); + GlStateManager.popMatrix(); + } + + protected void doRender(EnumFacing side, float sqDistance) { + if (this.allSides) side = null; + if (this.frameThickness >= 0) { + // scale thickness with distance to block + float d = (float) (this.frameThickness * (1 + Math.max(0, Math.sqrt(sqDistance) - 3) / 5)); + renderFrame(side, d); + } else { + renderSolid(side); + } + } + + public float getFrameThickness() { + return frameThickness; + } + + public int getColor() { + return color; + } + + public boolean isAllSides() { + return allSides; + } + + public void setFrameThickness(float frameThickness) { + this.frameThickness = frameThickness; + } + + public void setColor(int color) { + this.color = color; + } + + public void setAllSides(boolean allSides) { + this.allSides = allSides; + } + + protected static void renderSolid(@Nullable EnumFacing side) { + Platform.startDrawing(Platform.DrawMode.QUADS, Platform.VertexFormat.POS, builder -> { + if (side == null) { + for (int i = 0; i < 6; i++) { + buildFace(builder, EnumFacing.VALUES[i]); + } + } else { + buildFace(builder, side); + } + }); + } + + protected static void renderFrame(@Nullable EnumFacing side, float d) { + if (side == null) { + for (int i = 0; i < 6; i++) { + buildFrameFace(EnumFacing.VALUES[i], d); + } + } else { + buildFrameFace(side, d); + } + } + + protected static void buildFrameFace(EnumFacing facing, float d) { + float[] vert = vertices[facing.getIndex()]; + // draw faces individually do avoid weird lines between faces + Platform.startDrawing(Platform.DrawMode.TRIANGLE_STRIP, Platform.VertexFormat.POS, builder -> { + buildVertex(builder, vert, 9); + buildInnerVertex(builder, vert, 9, facing, d); + buildVertex(builder, vert, 6); + buildInnerVertex(builder, vert, 6, facing, d); + buildVertex(builder, vert, 3); + buildInnerVertex(builder, vert, 3, facing, d); + buildVertex(builder, vert, 0); + buildInnerVertex(builder, vert, 0, facing, d); + buildVertex(builder, vert, 9); + buildInnerVertex(builder, vert, 9, facing, d); + }); + } + + protected static void buildFace(BufferBuilder builder, EnumFacing facing) { + float[] vert = vertices[facing.getIndex()]; + buildVertex(builder, vert, 0); + buildVertex(builder, vert, 3); + buildVertex(builder, vert, 6); + buildVertex(builder, vert, 9); + } + + protected static void buildVertex(BufferBuilder builder, float[] vertices, int i) { + float x = vertices[i]; + float y = vertices[i + 1]; + float z = vertices[i + 2]; + builder.pos(x, y, z).endVertex(); + } + + private static void buildInnerVertex(BufferBuilder builder, float[] vertices, int i, EnumFacing side, float d) { + float x = vertices[i]; + float y = vertices[i + 1]; + float z = vertices[i + 2]; + if (side.getAxis() != EnumFacing.Axis.X) { + if (x >= 1) x -= d; + else x += d; + } + if (side.getAxis() != EnumFacing.Axis.Y) { + if (y >= 1) y -= d; + else y += d; + } + if (side.getAxis() != EnumFacing.Axis.Z) { + if (z >= 1) z -= d; + else z += d; + } + builder.pos(x, y, z).endVertex(); + } +} 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 44e4effa7..699bc8bc1 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockInfo.java @@ -29,11 +29,24 @@ public static BlockInfo of(IBlockAccess world, BlockPos pos) { } TileEntity tile = null; if (blockState.getBlock().hasTileEntity(blockState)) { - tile = world.getTileEntity(pos); + TileEntity realTile = world.getTileEntity(pos); + tile = fixRealTileWorldCorrupting(realTile, blockState); } return new BlockInfo(blockState, tile); } + @Nullable + private static TileEntity fixRealTileWorldCorrupting(TileEntity realTile, IBlockState blockState) { + TileEntity fakeTile = null; + if (realTile != null) { + fakeTile = blockState.getBlock().createTileEntity(null, blockState); + if (fakeTile != null) { + fakeTile.deserializeNBT(realTile.serializeNBT()); + } + } + return fakeTile; + } + private IBlockState blockState; private TileEntity tileEntity; 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 5a48cbf5a..39607038d 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/BlockPosUtil.java @@ -54,7 +54,7 @@ public static Vec3d getCenterD(BlockPos p1, BlockPos 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()); + return new Vec3d(xs / 2.0 + origin.getX(), ys / 2.0 + origin.getY(), zs / 2.0 + origin.getZ()); } public static Iterable getAllInside(BlockPos p1, BlockPos p2, boolean includeBorder) { 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 e11b7fcf3..e12b5110f 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/Camera.java @@ -1,57 +1,152 @@ package com.cleanroommc.modularui.utils.fakeworld; +import com.cleanroommc.modularui.utils.MathUtils; +import com.cleanroommc.modularui.utils.Vector3f; + +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3i; -import org.lwjgl.util.vector.Vector3f; +import org.jetbrains.annotations.Nullable; public class Camera { - private final Vector3f pos; - private final Vector3f lookAt; + private final Vector3f pos = new Vector3f(); + private final Vector3f lookAt = new Vector3f(); + private final Vector3f temp = new Vector3f(); + private float yaw, pitch, dist; + + public Camera() {} + + public Camera setPosAndLookAt(Vector3f pos, Vector3f lookAt) { + return setPosAndLookAt(pos.x, pos.y, pos.z, lookAt.x, lookAt.y, lookAt.z); + } + + public Camera setPosAndLookAt(float xPos, float yPos, float zPos, Vector3f lookAt) { + return setPosAndLookAt(xPos, yPos, zPos, lookAt.x, lookAt.y, lookAt.z); + } - public Camera(Vector3f pos, Vector3f lookAt) { - this.pos = pos; - this.lookAt = lookAt; + public Camera setPosAndLookAt(Vector3f pos, float xLook, float yLook, float zLook) { + return setPosAndLookAt(pos.x, pos.y, pos.z, xLook, yLook, zLook); } - public Camera setLookAt(Vector3f pos, Vector3f lookAt) { - this.pos.set(pos); - this.lookAt.set(lookAt); + public Camera setPosAndLookAt(float xPos, float yPos, float zPos, float xLook, float yLook, float zLook) { + this.pos.set(xPos, yPos, zPos); + this.lookAt.set(xLook, yLook, zLook); + Vector3f look = getLookVec(this.temp); + Vector3f.resetUnitVectors(); + this.pitch = Vector3f.angle(look, Vector3f.UNIT_Y) + MathUtils.PI_HALF; + look.setY(0); + this.yaw = Vector3f.angle(look, Vector3f.UNIT_X); + this.dist = this.pos.distanceTo(this.lookAt); return this; } - public Camera setLookAt(float x, float y, float z) { - this.lookAt.set(x, y, z); + public Camera setLookAtKeepPos(float x, float y, float z) { + setPosAndLookAt(this.pos, x, y, z); return this; } - public Camera setPos(float x, float y, float z) { - this.lookAt.set(x, y, z); + public Camera setLookAtKeepAngle(float x, float y, float z) { + // just calculate the offset + this.temp.set(x, y, z); + Vector3f.sub(this.temp, this.lookAt, this.temp); + this.pos.add(this.temp); + this.lookAt.set(this.temp); return this; } - public Camera setLookAt(Vector3f lookAt, double radius, double yaw, double pitch) { - return setLookAt(lookAt.x, lookAt.y, lookAt.z, radius, yaw, pitch); + public Camera setPosKeepLookAt(float x, float y, float z) { + return setPosAndLookAt(x, y, z, this.lookAt); + } + + public Camera setPosKeepAngle(float x, float y, float z) { + // just calculate the offset + this.temp.set(x, y, z); + Vector3f.sub(this.temp, this.pos, this.temp); + this.lookAt.add(this.temp); + this.pos.set(this.temp); + return this; + } + + public Camera setAngleKeepLookAt(float radius, float yaw, float pitch) { + return setLookAtAndAngle(this.lookAt, radius, yaw, pitch); + } + + public Camera setLookAtAndAngle(Vector3f lookAt, float radius, float yaw, float pitch) { + return setLookAtAndAngle(lookAt.x, lookAt.y, lookAt.z, radius, yaw, pitch); + } + + public Camera setLookAtAndAngle(Vec3i lookAt, float radius, float yaw, float pitch) { + return setLookAtAndAngle(lookAt.getX(), lookAt.getY(), lookAt.getZ(), radius, yaw, pitch); } - public Camera setLookAt(Vec3i lookAt, double radius, double yaw, double pitch) { - return setLookAt(lookAt.getX(), lookAt.getY(), lookAt.getZ(), radius, yaw, pitch); + public Camera setLookAtAndAngle(float lookAtX, float lookAtY, float lookAtZ, float dist, float yaw, float pitch) { + this.lookAt.set(lookAtX, lookAtY, lookAtZ); + this.yaw = yaw; + this.pitch = pitch; + this.dist = dist; + Vector3f v = this.temp; + v.set(MathHelper.cos(yaw), 0, MathHelper.sin(yaw)); + v.y = MathUtils.tan(pitch) * v.length(); + v.normalise().scale(dist); + this.pos.set(v.translate(lookAtX, lookAtY, lookAtZ)); + return this; } - 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(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)); + public Camera setPosAndAngle(float posX, float posY, float posZ, float dist, float yaw, float pitch) { + this.pos.set(posX, posY, posZ); + this.yaw = yaw; + this.pitch = pitch; + this.dist = dist; + Vector3f v = this.temp; + v.set(MathUtils.cos(MathUtils.PI_HALF - yaw), 0, MathUtils.sin(MathUtils.PI_HALF - yaw)); + v.y = MathUtils.tan(MathUtils.PI_HALF - pitch) * v.length(); + v.normalise().scale(dist); + this.lookAt.set(v).add(this.pos); return this; } + public void setDistanceKeepLookAt(float dist) { + if (dist == this.dist) return; + this.dist = dist; + Vector3f.sub(this.pos, this.lookAt, this.temp); + this.temp.normalise().scale(dist); + Vector3f.add(this.lookAt, this.temp, this.pos); + } + + public void scaleDistanceKeepLookAt(float dist) { + if (dist == 1) return; + this.dist *= dist; + Vector3f.sub(this.pos, this.lookAt, this.temp); + this.temp.scale(dist); + Vector3f.add(this.lookAt, this.temp, this.pos); + } + public Vector3f getPos() { - return pos; + return pos.copy(); } public Vector3f getLookAt() { - return lookAt; + return lookAt.copy(); + } + + public Vector3f getLookVec() { + return getLookVec(null); + } + + public Vector3f getLookVec(@Nullable Vector3f dest) { + return Vector3f.sub(lookAt, pos, dest); + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public float getDist() { + return dist; } } 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 71eb3070c..2cf07ba13 100644 --- a/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java +++ b/src/main/java/com/cleanroommc/modularui/utils/fakeworld/SchemaRenderer.java @@ -1,69 +1,31 @@ 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.utils.Platform; -import com.cleanroommc.modularui.widget.sizer.Area; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.BlockRendererDispatcher; -import net.minecraft.client.renderer.BufferBuilder; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.OpenGlHelper; -import net.minecraft.client.renderer.RenderHelper; -import net.minecraft.client.renderer.Tessellator; -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.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.Vector3f; +import org.jetbrains.annotations.NotNull; -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 { - - private static final Framebuffer FBO = new Framebuffer(1080, 1080, true); +public class SchemaRenderer extends BaseSchemaRenderer { - 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; private DoubleSupplier scale; private BooleanSupplier disableTESR; - private Consumer onRayTrace; - private Consumer afterRender; + private Consumer afterRender; private BiConsumer cameraFunc; - private int clearColor = 0; private boolean isometric = false; + private boolean rayTracing = false; + private BlockHighlight highlight; public SchemaRenderer(ISchema schema, Framebuffer framebuffer) { - this.schema = schema; - this.framebuffer = framebuffer; - this.renderWorld = new RenderWorld(schema); + super(schema, framebuffer); } public SchemaRenderer(ISchema schema) { - this(schema, FBO); + super(schema); } public SchemaRenderer cameraFunc(BiConsumer camera) { @@ -71,12 +33,7 @@ public SchemaRenderer cameraFunc(BiConsumer camera) { return this; } - public SchemaRenderer onRayTrace(Consumer consumer) { - this.onRayTrace = consumer; - return this; - } - - public SchemaRenderer afterRender(Consumer consumer) { + public SchemaRenderer afterRender(Consumer consumer) { this.afterRender = consumer; return this; } @@ -104,235 +61,52 @@ public SchemaRenderer disableTESR(BooleanSupplier 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()); + public SchemaRenderer rayTracing(boolean rayTracing) { + this.rayTracing = rayTracing; + return this; } - 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(); - 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 - Platform.startDrawing(Platform.DrawMode.QUADS, Platform.VertexFormat.POS_TEX, bufferBuilder -> { - 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(); - }); - GlStateManager.bindTexture(lastFbo); + public SchemaRenderer highlightRenderer(BlockHighlight highlight) { + this.highlight = highlight; + return rayTracing(true); } - 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.renderWorld, pos) && state.getBlock().canRenderInLayer(state, layer)) { - blockrendererdispatcher.renderBlock(state, pos, this.renderWorld, buffer); - } - }); - Tessellator.getInstance().draw(); - Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); - } - } finally { - ForgeHooksClient.setRenderLayer(oldRenderLayer); - } - - RenderHelper.enableStandardItemLighting(); - GlStateManager.enableLighting(); - - // render TESR - 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); - } - }); - } + @Override + protected void onSetupCamera() { + if (this.scale != null) { + getCamera().scaleDistanceKeepLookAt((float) this.scale.getAsDouble()); } - ForgeHooksClient.setRenderPass(-1); - GlStateManager.enableDepth(); - GlStateManager.disableBlend(); - GlStateManager.depthMask(true); - if (this.afterRender != null) { - this.afterRender.accept(Projection.INSTANCE); + if (this.cameraFunc != null) { + this.cameraFunc.accept(getCamera(), getSchema()); } } - 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); + @Override + protected void onRendered() { + if (this.afterRender != null) { + this.afterRender.accept(this); } } - 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 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); + @Override + protected void onSuccessfulRayTrace(@NotNull RayTraceResult result) { + if (this.highlight != null) { + this.highlight.renderHighlight(result.getBlockPos(), result.sideHit, getCamera().getPos()); } - 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; + @Override + public boolean doRayTrace() { + return this.rayTracing; } - public interface IRayTracer { - - RayTraceResult rayTrace(int screenX, int screenY); - - RayTraceResult rayTraceMousePos(); + @Override + public boolean isTesrEnabled() { + return this.disableTESR == null || !this.disableTESR.getAsBoolean(); } - public interface ICamera { - - void setupCamera(Vector3f cameraPos, Vector3f lookAt); + @Override + public boolean isIsometric() { + return isometric; } } diff --git a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java index 7d9c03f4c..c635c5b71 100644 --- a/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java +++ b/src/main/java/com/cleanroommc/modularui/widgets/SchemaWidget.java @@ -1,52 +1,59 @@ package com.cleanroommc.modularui.widgets; import com.cleanroommc.modularui.api.UpOrDown; -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.screen.viewport.ModularGuiContext; +import com.cleanroommc.modularui.theme.WidgetTheme; import com.cleanroommc.modularui.utils.MathUtils; -import com.cleanroommc.modularui.utils.VectorUtil; +import com.cleanroommc.modularui.utils.Vector3f; +import com.cleanroommc.modularui.utils.fakeworld.BaseSchemaRenderer; 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.RayTraceResult; +import net.minecraft.util.math.Vec3d; + import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.util.vector.Vector3f; public class SchemaWidget extends Widget implements Interactable { public static final float PI = (float) Math.PI; - public static final float PI2 = 2 * PI; + public static final float PI2 = 2f * PI; + public static final float PI_HALF = PI / 2f; + public static final float PI_QUART = PI / 4f; - private final SchemaRenderer schema; + private final BaseSchemaRenderer schema; 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 float scale = 10; + private float pitch = PI_QUART; + private float yaw = 0; private final Vector3f offset = new Vector3f(); public SchemaWidget(ISchema schema) { - this(new SchemaRenderer(schema)); + this(new BaseSchemaRenderer(schema)); } - public SchemaWidget(SchemaRenderer schema) { + public SchemaWidget(BaseSchemaRenderer schema) { this.schema = schema; - schema.cameraFunc((camera, $schema) -> { - Vector3f focus = VectorUtil.vec3fAdd(this.offset, null, $schema.getFocus()); - camera.setLookAt(focus, scale, yaw, pitch); - }); + } + + @Override + public void draw(ModularGuiContext context, WidgetTheme widgetTheme) { + Vec3d f = this.schema.getSchema().getFocus(); + this.schema.getCamera().setLookAtAndAngle((float) (f.x + this.offset.x), (float) (f.y + this.offset.y), (float) (f.z + this.offset.z), scale, yaw, pitch); + this.schema.drawAtZero(context, getArea(), widgetTheme); } @Override public boolean onMouseScroll(UpOrDown scrollDirection, int amount) { if (this.enableScaling) { - scale(-scrollDirection.modifier * amount / 120.0); + incrementScale(-scrollDirection.modifier * amount / 120.0f); return true; } return false; @@ -66,39 +73,38 @@ public void onMouseDrag(int mouseButton, long timeSinceClick) { int dx = mouseX - lastMouseX; int dy = mouseY - lastMouseY; if (mouseButton == 0 && this.enableRotation) { - float moveScale = 0.025f; - yaw = (yaw + dx * moveScale + PI2) % PI2; - pitch = MathUtils.clamp(pitch + dy * moveScale, -PI2 / 4 + 0.001f, PI2 / 4 - 0.001f); + float moveScale = 0.03f; + yaw(this.yaw + dx * moveScale); + pitch(this.pitch + dy * moveScale); } else if (mouseButton == 2 && this.enableTranslation) { - // the idea is to construct a vector which points upwards from the camerae pov (y-axis on screen) - // 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 - 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); + float moveScale = 0.09f; + Vector3f.resetUnitVectors(); + Vector3f look = this.schema.getCamera().getLookVec().normalise(); // direction camera is looking + Vector3f right = Vector3f.cross(look, Vector3f.UNIT_Y, null).normalise(); // right relative to screen + Vector3f up = Vector3f.cross(right, look, null); // up relative to screen + this.offset.add(right.scale(-dx * moveScale)).add(up.scale(dy * moveScale)); } this.lastMouseX = mouseX; this.lastMouseY = mouseY; } - public SchemaWidget scale(double scale) { - this.scale += scale; + public void incrementScale(float amount) { + this.scale += amount; + } + + public SchemaWidget scale(float scale) { + this.scale = scale; return this; } public SchemaWidget pitch(float pitch) { - this.pitch += pitch; + this.pitch = MathUtils.clamp(pitch, -PI_HALF + 0.001f, PI_HALF - 0.001f); + ; return this; } public SchemaWidget yaw(float yaw) { - this.yaw += yaw; + this.yaw = (yaw + PI2) % PI2; return this; } @@ -132,9 +138,8 @@ public SchemaWidget enableAllInteraction(boolean enable) { return enableInteraction(enable, enable, enable); } - @Override - public @Nullable IDrawable getOverlay() { - return schema; + public RayTraceResult getBlockUnderMouse() { + return schema.getLastRayTrace(); } public static class LayerButton extends ButtonWidget { diff --git a/src/test/java/com/cleanroommc/modularui/CameraTest.java b/src/test/java/com/cleanroommc/modularui/CameraTest.java new file mode 100644 index 000000000..20c1acbca --- /dev/null +++ b/src/test/java/com/cleanroommc/modularui/CameraTest.java @@ -0,0 +1,62 @@ +package com.cleanroommc.modularui; + +import com.cleanroommc.modularui.utils.Vector3f; +import com.cleanroommc.modularui.utils.fakeworld.Camera; + +import org.junit.jupiter.api.AssertionFailureBuilder; +import org.junit.jupiter.api.Test; + +public class CameraTest { + + static final float PI = (float) Math.PI; + static final float PI2 = 2f * PI; + static final float PI_HALF = PI / 2f; + static final float PI_QUART = PI / 4f; + static final Vector3f temp = new Vector3f(); + + @Test + void testCamera() { + Camera a = new Camera(); + Camera b = new Camera(); + a.setAngleKeepLookAt(1, PI_HALF, 0); + assertPos(a, 0, 0, 1); + a.setAngleKeepLookAt(1, PI, 0); + assertPos(a, -1, 0, 0); + a.setAngleKeepLookAt(1, PI + PI_HALF, 0); + assertPos(a, 0, 0, -1); + a.setAngleKeepLookAt(1, PI2, 0); + assertPos(a, 1, 0, 0); + } + + void assertPos(Camera c, float x, float y, float z) { + temp.set(x, y, z); + Vector3f v = c.getPos(); + if (!areEqual(v, temp)) { + AssertionFailureBuilder.assertionFailure() + .message("Camera pos does not match") + .actual(v) + .expected(temp) + .buildAndThrow(); + } + } + + void assertLookAt(Camera c, float x, float y, float z) { + temp.set(x, y, z); + Vector3f v = c.getLookAt(); + if (!areEqual(v, temp)) { + AssertionFailureBuilder.assertionFailure() + .message("Camera look at does not match") + .actual(v) + .expected(temp) + .buildAndThrow(); + } + } + + boolean areEqual(Vector3f a, Vector3f b) { + return areFloatEqual(a.x, b.x) && areFloatEqual(a.y, b.y) && areFloatEqual(a.z, b.z); + } + + boolean areFloatEqual(float a, float b) { + return Math.abs(a - b) < 0.00000001f; + } +}