From 8271d4987ee8fd6928ee993b5ed942bfb8dfd153 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 12:31:48 +0300 Subject: [PATCH 1/9] Fix facades' models when facing +X/+Y/+Z --- .../client/renderer/cover/FacadeCoverRenderer.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 2d88b38e102..82b31aa390c 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -57,17 +57,16 @@ public class FacadeCoverRenderer extends BaseBakedModel implements ICoverRendere // the second row of pixels of the block's texture and to combat Z-fighting. AABB facadePlane = switch (dir) { case DOWN -> FACADE_PLANE.setMaxY(FACADE_PLANE_BACK); - case UP -> FACADE_PLANE.setMinY(FACADE_PLANE_BACK); + case UP -> FACADE_PLANE.setMinY(1.0 - FACADE_PLANE_BACK); case NORTH -> FACADE_PLANE.setMaxZ(FACADE_PLANE_BACK); - case SOUTH -> FACADE_PLANE.setMinZ(FACADE_PLANE_BACK); + case SOUTH -> FACADE_PLANE.setMinZ(1.0 - FACADE_PLANE_BACK); case WEST -> FACADE_PLANE.setMaxX(FACADE_PLANE_BACK); - case EAST -> FACADE_PLANE.setMinX(FACADE_PLANE_BACK); + case EAST -> FACADE_PLANE.setMinX(1.0 - FACADE_PLANE_BACK); }; map.put(dir, GTQuadTransformers.clamp(facadePlane)); } }); - private static final IQuadTransformer FACADE_ITEM_PLANE_TRANSFORMER = FACADE_PLANE_TRANSFORMERS.get(Direction.NORTH); // spotless:on public static final FacadeCoverRenderer INSTANCE = new FacadeCoverRenderer(); @@ -169,7 +168,7 @@ public void renderCover(List quads, @Nullable Direction side, RandomS quad = GTQuadTransformers.copy(quad); } - // clamp the quad's vertex positions to fit into the facade plane + // clamp the quad's vertices into the facade plane clamper.processInPlace(quad); quads.add(quad); @@ -293,6 +292,7 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction ModelData facadeData = modelData.get(GTModelProperties.CHILD_MODEL_DATA); if (facadeData == null) facadeData = ModelData.EMPTY; + IQuadTransformer clamper = FACADE_PLANE_TRANSFORMERS.get(Direction.NORTH); ItemColors itemColors = Minecraft.getInstance().getItemColors(); for (var model : facadeModel.getRenderPasses(this.facadeStack, true)) { @@ -326,8 +326,8 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction quad = GTQuadTransformers.copy(quad); } - // clamp the quad's vertex positions to fit into the facade plane - FACADE_ITEM_PLANE_TRANSFORMER.processInPlace(quad); + // clamp the quad's vertices into the facade plane + clamper.processInPlace(quad); quads.add(quad); } From 6964e659479a2407d1136b4f368b0e99ee3fc501 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 12:46:13 +0300 Subject: [PATCH 2/9] Fix facaded models' UVs --- .../renderer/cover/FacadeCoverRenderer.java | 12 +- .../transformers/InterpolationHelper.java | 127 +++++++++++ .../quad/transformers/QuadReInterpolator.java | 200 ++++++++++++++++++ 3 files changed, 338 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/InterpolationHelper.java create mode 100644 src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadReInterpolator.java diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 82b31aa390c..b26295f10f9 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -5,7 +5,7 @@ import com.gregtechceu.gtceu.client.model.GTModelProperties; import com.gregtechceu.gtceu.client.util.GTQuadTransformers; import com.gregtechceu.gtceu.client.util.ModelUtils; -import com.gregtechceu.gtceu.client.util.StaticFaceBakery; +import com.gregtechceu.gtceu.client.util.quad.transformers.QuadReInterpolator; import com.gregtechceu.gtceu.common.cover.FacadeCover; import com.gregtechceu.gtceu.common.item.behavior.FacadeItemBehaviour; import com.gregtechceu.gtceu.utils.GTUtil; @@ -142,6 +142,7 @@ public void renderCover(List quads, @Nullable Direction side, RandomS } IQuadTransformer clamper = FACADE_PLANE_TRANSFORMERS.get(attachedSide); + QuadReInterpolator interpolator = new QuadReInterpolator(); BlockColors blockColors = Minecraft.getInstance().getBlockColors(); // always add unculled faces @@ -158,6 +159,8 @@ public void renderCover(List quads, @Nullable Direction side, RandomS } for (BakedQuad quad : facadeQuads) { + interpolator.setInputQuad(quad); + // bake the quad's colors into its vertices if (quad.isTinted()) { // if the quad has a tint index set, bake the tint into the vertex @@ -170,6 +173,8 @@ public void renderCover(List quads, @Nullable Direction side, RandomS // clamp the quad's vertices into the facade plane clamper.processInPlace(quad); + // fix the quad's UVs based on the original & clamped vertices + interpolator.processInPlace(quad); quads.add(quad); } @@ -293,6 +298,7 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction if (facadeData == null) facadeData = ModelData.EMPTY; IQuadTransformer clamper = FACADE_PLANE_TRANSFORMERS.get(Direction.NORTH); + QuadReInterpolator interpolator = new QuadReInterpolator(); ItemColors itemColors = Minecraft.getInstance().getItemColors(); for (var model : facadeModel.getRenderPasses(this.facadeStack, true)) { @@ -315,6 +321,8 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction // clamp all 'facaded' quads into a box and bake their tint color into the vertices for (BakedQuad quad : facadeQuads) { + interpolator.setInputQuad(quad); + // bake the quad's colors into its vertices if (quad.isTinted()) { // if the quad has a tint index set, bake the tint into the vertex color @@ -328,6 +336,8 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction // clamp the quad's vertices into the facade plane clamper.processInPlace(quad); + // fix the quad's UVs based on the original & clamped vertices + interpolator.processInPlace(quad); quads.add(quad); } diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/InterpolationHelper.java b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/InterpolationHelper.java new file mode 100644 index 00000000000..ae92336d060 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/InterpolationHelper.java @@ -0,0 +1,127 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with CodeChickenLib. If not, see . + */ +package com.gregtechceu.gtceu.client.util.quad.transformers; + +import net.minecraft.util.Mth; + +import org.jetbrains.annotations.ApiStatus; + +/** + * @author covers1624 + */ +public class InterpolationHelper { + + private final float[][] posCache = new float[4][2]; + private final float[] valCache = new float[4]; + + private float x0; + private float x1; + private float y0; + private float y1; + + private float rX; + private float rY; + + private int p00; + private int p10; + private int p11; + private int p01; + + /** + * Resets the interpolation helper with the given quad. Does not care what order the vertices are in. + */ + public void reset(float dx0, float dy0, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) { + float[] vec0 = this.posCache[0]; + float[] vec1 = this.posCache[1]; + float[] vec2 = this.posCache[2]; + float[] vec3 = this.posCache[3]; + + vec0[0] = dx0; + vec1[0] = dx1; + vec2[0] = dx2; + vec3[0] = dx3; + + vec0[1] = dy0; + vec1[1] = dy1; + vec2[1] = dy2; + vec3[1] = dy3; + } + + @ApiStatus.Internal + public float[][] getInternalPosCache() { + return this.posCache; + } + + /** + * Call when you are ready to use the interpolation helper. + */ + public void setup() { + this.p00 = 0; // Bottom Left is always first. + this.x0 = this.posCache[this.p00][0]; + this.y0 = this.posCache[this.p00][1]; + + for (int i = 1; i < 4; i++) { + float x = this.posCache[i][0]; + float y = this.posCache[i][1]; + + if (this.y0 == y) { + // Bottom right. + this.p10 = i; + this.x1 = x; + } else if (this.x0 == x) { + // Top left. + this.p01 = i; + this.y1 = y; + } else { + // Top right. + this.p11 = i; + } + } + } + + /** + * Computes the coefficients for the interpolation. + * + * @param x X interpolation location. + * @param y Y interpolation location. + */ + public void locate(float x, float y) { + this.rX = Mth.inverseLerp(x, this.x0, this.x1); + this.rY = Mth.inverseLerp(y, this.y0, this.y1); + } + + /** + * Interpolates using the already computed coefficients. + * + * @param q0 Value at dx0 dy0 + * @param q1 Value at dx1 dy1 + * @param q2 Value at dx2 dy2 + * @param q3 Value at dx3 dy3 + * @return The result. + */ + public float interpolate(float q0, float q1, float q2, float q3) { + this.valCache[0] = q0; + this.valCache[1] = q1; + this.valCache[2] = q2; + this.valCache[3] = q3; + float f0 = Mth.lerp(this.rX, this.valCache[this.p00], this.valCache[this.p10]); + float f1 = Mth.lerp(this.rX, this.valCache[this.p01], this.valCache[this.p11]); + + return Mth.lerp(this.rY, f0, f1); + } +} diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadReInterpolator.java b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadReInterpolator.java new file mode 100644 index 00000000000..6a91b592935 --- /dev/null +++ b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadReInterpolator.java @@ -0,0 +1,200 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with CodeChickenLib. If not, see . + */ +package com.gregtechceu.gtceu.client.util.quad.transformers; + +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.core.Direction; +import net.minecraftforge.client.model.IQuadTransformer; + +/** + * This transformer Re-Interpolates the Color, UV's and LightMaps. Use this after all transformations that translate + * vertices in the pipeline. + *

+ * This Transformation can only be used in the BakedPipeline. + * + * @author covers1624 + */ +@SuppressWarnings("PointlessArithmeticExpression") +public class QuadReInterpolator implements IQuadTransformer { + + private final InterpolationHelper interpolationHelper = new InterpolationHelper(); + + private final int[] originalSpriteColor = new int[4]; + private final float[] originalSpriteU = new float[4]; + private final float[] originalSpriteV = new float[4]; + private final int[] originalSpriteLightmap = new int[4]; + + public QuadReInterpolator() {} + + public void setInputQuad(BakedQuad quad) { + Direction.Axis axis = quad.getDirection().getAxis(); + int xIndex = xCoord(axis); + int yIndex = yCoord(axis); + + int[] vertices = quad.getVertices(); + float[][] posCache = interpolationHelper.getInternalPosCache(); + + // Save the original properties of the quad's vertices + for (int v = 0; v < 4; v++) { + int offset = v * IQuadTransformer.STRIDE; + + // same as calling interpolationHelper.reset with the array contents + posCache[v][0] = Float.intBitsToFloat(vertices[offset + POSITION + xIndex]); + posCache[v][1] = Float.intBitsToFloat(vertices[offset + POSITION + yIndex]); + + originalSpriteColor[v] = vertices[offset + COLOR]; + originalSpriteU[v] = Float.intBitsToFloat(vertices[offset + UV0 + 0]); + originalSpriteV[v] = Float.intBitsToFloat(vertices[offset + UV0 + 1]); + originalSpriteLightmap[v] = vertices[offset + UV2]; + } + +// interpolationHelper.reset( +// posCache[0][0], posCache[0][1], +// posCache[1][0], posCache[1][1], +// posCache[2][0], posCache[2][1], +// posCache[3][0], posCache[3][1]); + } + + @Override + public void processInPlace(BakedQuad quad) { + int[] vertices = quad.getVertices(); + Direction.Axis axis = quad.getDirection().getAxis(); + int xIndex = xCoord(axis); + int yIndex = yCoord(axis); + + this.interpolationHelper.setup(); + for (int v = 0; v < 4; v++) { + int offset = v * STRIDE + POSITION; + float x = Float.intBitsToFloat(vertices[offset + xIndex]); + float y = Float.intBitsToFloat(vertices[offset + yIndex]); + this.interpolationHelper.locate(x, y); + + interpolateColorFrom(quad, v); + interpolateUVFrom(quad, v); + interpolateLightmapFrom(quad, v); + } + } + + /** + * Interpolates the new color values for this Vertex using the others as a reference. + */ + public void interpolateColorFrom(BakedQuad quad, int vertexIndex) { + int p1 = this.originalSpriteColor[0]; + int p2 = this.originalSpriteColor[1]; + int p3 = this.originalSpriteColor[2]; + int p4 = this.originalSpriteColor[3]; + if (p1 == p2 && p2 == p3 && p3 == p4) { + return; // Don't bother for uniformly colored quads + } + + // Interpolate each color component separately + int color = 0; + int mask = 0xFF; + for (int i = 0; i < 4; i++) { + int p1c = p1 & mask; + int p2c = p2 & mask; + int p3c = p3 & mask; + int p4c = p4 & mask; + int interpolated = (int) interpolationHelper.interpolate(p1c, p2c, p3c, p4c); + color |= interpolated & mask; + mask <<= 8; + } + + quad.getVertices()[vertexIndex * STRIDE + COLOR] = color; + } + + /** + * Interpolates the new UV values for this Vertex using the others as a reference. + */ + public void interpolateUVFrom(BakedQuad quad, int vertexIndex) { + float p1 = originalSpriteU[0]; + float p2 = originalSpriteU[1]; + float p3 = originalSpriteU[2]; + float p4 = originalSpriteU[3]; + float u = interpolationHelper.interpolate(p1, p2, p3, p4); + + p1 = originalSpriteV[0]; + p2 = originalSpriteV[1]; + p3 = originalSpriteV[2]; + p4 = originalSpriteV[3]; + float v = interpolationHelper.interpolate(p1, p2, p3, p4); + + int offset = vertexIndex * STRIDE + UV0; + quad.getVertices()[offset + 0] = Float.floatToIntBits(u); + quad.getVertices()[offset + 1] = Float.floatToIntBits(v); + } + + /** + * Interpolates the new LightMap values for this Vertex using the others as a reference. + * + * @return The same Vertex. + */ + public void interpolateLightmapFrom(BakedQuad quad, int vertexIndex) { + int p1 = originalSpriteLightmap[0]; + int p2 = originalSpriteLightmap[1]; + int p3 = originalSpriteLightmap[2]; + int p4 = originalSpriteLightmap[3]; + if (p1 == p2 && p2 == p3 && p3 == p4) { + return; // Don't bother for uniformly lit quads + } + + // Interpolate both lightmap components separately + int p1l = LightTexture.block(p1); + int p2l = LightTexture.block(p2); + int p3l = LightTexture.block(p3); + int p4l = LightTexture.block(p4); + int block = (int) interpolationHelper.interpolate(p1l, p2l, p3l, p4l); + + p1l = LightTexture.sky(p1); + p2l = LightTexture.sky(p2); + p3l = LightTexture.sky(p3); + p4l = LightTexture.sky(p4); + int sky = (int) interpolationHelper.interpolate(p1l, p2l, p3l, p4l); + + quad.getVertices()[vertexIndex * STRIDE + UV2] = LightTexture.pack(block, sky); + } + + /** + * Gets the 2d X coordinate for the given axis. + * + * @param axis The axis. side >> 1 + * @return The x coordinate. + */ + private static int xCoord(Direction.Axis axis) { + if (axis != Direction.Axis.X) { + return 0; + } else { + return 2; + } + } + + /** + * Gets the 2d Y coordinate for the given axis. + * + * @param axis The axis. + * @return The y coordinate. + */ + private static int yCoord(Direction.Axis axis) { + if (axis != Direction.Axis.Y) { + return 1; + } else { + return 2; + } + } +} From 4b3e5c427405a64aba69346055b5a548ea7045b3 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 12:47:58 +0300 Subject: [PATCH 3/9] Fix cover plates Z-fighting --- .../client/renderer/cover/FacadeCoverRenderer.java | 9 +++++++++ .../client/renderer/cover/ICoverableRenderer.java | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index b26295f10f9..1b91b171871 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -157,8 +157,17 @@ public void renderCover(List quads, @Nullable Direction side, RandomS facadeQuads.addAll(facadeModel.getQuads(facadeState, cullFace, rand, facadeData, renderType)); } } + if (facadeQuads.isEmpty()) { + return; + } for (BakedQuad quad : facadeQuads) { + // skip quads that aren't oriented correctly + if (quad.getDirection() != attachedSide && coverBehavior.shouldRenderPlate() || + !coverBehavior.coverHolder.shouldRenderBackSide()) { + continue; + } + interpolator.setInputQuad(quad); // bake the quad's colors into its vertices diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index 489f4e43e0c..3f4910da0eb 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -48,6 +48,13 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos Map coverModelData = modelData.get(GTModelProperties.COVER_MODEL_DATA); double thickness = coverable.getCoverPlateThickness(); + byte coverMask = 0; + for (Direction face : GTUtil.DIRECTIONS) { + if (coverable.hasCover(face)) { + coverMask |= 1 << face.ordinal(); + } + } + for (Direction face : GTUtil.DIRECTIONS) { var cover = coverable.getCoverAtSide(face); if (cover != null) { @@ -70,7 +77,8 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos if (coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { if (side == null) { // render back quads.add(StaticFaceBakery.bakeFace(cube, face.getOpposite(), COVER_BACK_PLATE[0])); - } else if (side != face.getOpposite()) { // render sides + } else if (side != face.getOpposite() && + (((coverMask >> side.ordinal()) & 1) == 0 || side == face)) { // render sides quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0])); } } From f0733c0f2dbf0a2e7a6d8fc1a8fbb6da0d5fb869 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 13:00:44 +0300 Subject: [PATCH 4/9] cleaner back plate AABB code --- .../renderer/cover/FacadeCoverRenderer.java | 2 +- .../renderer/cover/ICoverableRenderer.java | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 1b91b171871..6f02b47cbfe 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -48,7 +48,7 @@ public class FacadeCoverRenderer extends BaseBakedModel implements ICoverRendere private static final double FACADE_PLANE_BACK = 1.0 / 16; - private static final AABB FACADE_PLANE = StaticFaceBakery.BLOCK.deflate(ICoverableRenderer.THIN_OFFSET); + private static final AABB FACADE_PLANE = ICoverableRenderer.COVER_PLATE_BOX; // spotless:off private static final Map FACADE_PLANE_TRANSFORMERS = Util.make(new EnumMap<>(Direction.class), map -> { diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index 3f4910da0eb..bc1109d2eb5 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -34,7 +34,7 @@ public interface ICoverableRenderer { @OnlyIn(Dist.CLIENT) TextureAtlasSprite[] COVER_BACK_PLATE = new TextureAtlasSprite[1]; double THIN_OFFSET = 0.002; - double LESS_THIN_OFFSET = 0.005; + AABB COVER_PLATE_BOX = StaticFaceBakery.BLOCK.deflate(THIN_OFFSET); @OnlyIn(Dist.CLIENT) static void initSprites(TextureAtlas atlas) { @@ -63,16 +63,16 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos ICoverRenderer coverRenderer = cover.getCoverRenderer().get(); if (thickness > 0 && cover.shouldRenderPlate()) { - double min = thickness + 0.01; - double max = 0.99 - thickness; - var normal = face.getNormal(); - var cube = new AABB( - normal.getX() > 0 ? max : LESS_THIN_OFFSET, - normal.getY() > 0 ? max : LESS_THIN_OFFSET, - normal.getZ() > 0 ? max : LESS_THIN_OFFSET, - normal.getX() >= 0 ? 1.0 - LESS_THIN_OFFSET : min, - normal.getY() >= 0 ? 1.0 - LESS_THIN_OFFSET : min, - normal.getZ() >= 0 ? 1.0 - LESS_THIN_OFFSET : min); + // All faces are slightly under a full block's size to never show the beginning of + // the second row of pixels of the block's texture and to combat Z-fighting. + AABB cube = switch (face) { + case DOWN -> COVER_PLATE_BOX.setMaxY(thickness); + case UP -> COVER_PLATE_BOX.setMinY(1.0 - thickness); + case NORTH -> COVER_PLATE_BOX.setMaxZ(thickness); + case SOUTH -> COVER_PLATE_BOX.setMinZ(1.0 - thickness); + case WEST -> COVER_PLATE_BOX.setMaxX(thickness); + case EAST -> COVER_PLATE_BOX.setMinX(1.0 - thickness); + }; if (coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { if (side == null) { // render back From 1bb0e487ee50de999d03a9e13644fe133305b519 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 13:33:14 +0300 Subject: [PATCH 5/9] Formatting changes --- .../renderer/cover/ICoverableRenderer.java | 56 +++++++++---------- .../gtceu/client/util/StaticFaceBakery.java | 12 ++-- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index bc1109d2eb5..a516d2f9b0d 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -56,37 +56,35 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos } for (Direction face : GTUtil.DIRECTIONS) { - var cover = coverable.getCoverAtSide(face); - if (cover != null) { - // it won't ever be null on the client - // noinspection DataFlowIssue - ICoverRenderer coverRenderer = cover.getCoverRenderer().get(); - - if (thickness > 0 && cover.shouldRenderPlate()) { - // All faces are slightly under a full block's size to never show the beginning of - // the second row of pixels of the block's texture and to combat Z-fighting. - AABB cube = switch (face) { - case DOWN -> COVER_PLATE_BOX.setMaxY(thickness); - case UP -> COVER_PLATE_BOX.setMinY(1.0 - thickness); - case NORTH -> COVER_PLATE_BOX.setMaxZ(thickness); - case SOUTH -> COVER_PLATE_BOX.setMinZ(1.0 - thickness); - case WEST -> COVER_PLATE_BOX.setMaxX(thickness); - case EAST -> COVER_PLATE_BOX.setMinX(1.0 - thickness); - }; - - if (coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { - if (side == null) { // render back - quads.add(StaticFaceBakery.bakeFace(cube, face.getOpposite(), COVER_BACK_PLATE[0])); - } else if (side != face.getOpposite() && - (((coverMask >> side.ordinal()) & 1) == 0 || side == face)) { // render sides - quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0])); - } - } + CoverBehavior cover = coverable.getCoverAtSide(face); + if (cover == null) continue; + // it won't ever be null on the client + // noinspection DataFlowIssue + ICoverRenderer coverRenderer = cover.getCoverRenderer().get(); + + if (thickness > 0 && cover.shouldRenderPlate() && coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { + // All faces are slightly under a full block's size to never show the beginning of + // the second row of pixels of the block's texture and to combat Z-fighting. + AABB cube = switch (face) { + case DOWN -> COVER_PLATE_BOX.setMaxY(thickness); + case UP -> COVER_PLATE_BOX.setMinY(1.0 - thickness); + case NORTH -> COVER_PLATE_BOX.setMaxZ(thickness); + case SOUTH -> COVER_PLATE_BOX.setMinZ(1.0 - thickness); + case WEST -> COVER_PLATE_BOX.setMaxX(thickness); + case EAST -> COVER_PLATE_BOX.setMinX(1.0 - thickness); + }; + + if (side == null) { // render back + quads.add(StaticFaceBakery.bakeFace(cube, face.getOpposite(), COVER_BACK_PLATE[0])); + } else if (side != face.getOpposite() && + (((coverMask >> side.ordinal()) & 1) == 0 || side == face)) { // render sides + quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0])); } - coverRenderer.renderCover(quads, side, rand, cover, pos, level, - coverModelData != null ? coverModelData.getOrDefault(face, ModelData.EMPTY) : ModelData.EMPTY, - renderType); } + + coverRenderer.renderCover(quads, side, rand, cover, pos, level, + coverModelData != null ? coverModelData.getOrDefault(face, ModelData.EMPTY) : ModelData.EMPTY, + renderType); } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java index 7b963ba6b52..793086799ce 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java @@ -24,14 +24,10 @@ public class StaticFaceBakery { public static final AABB BLOCK = new AABB(0, 0, 0, 1, 1, 1); - public static final AABB SLIGHTLY_OVER_BLOCK = new AABB(-0.001f, -0.001f, -0.001f, - 1.001f, 1.001f, 1.001f); - public static final AABB OUTPUT_OVERLAY = new AABB(-.006f, -.006f, -.006f, - 1.006f, 1.006f, 1.006f); - public static final AABB AUTO_OUTPUT_OVERLAY = new AABB(-.008f, -.008f, -.008f, - 1.008f, 1.008f, 1.008f); - public static final AABB COVER_OVERLAY = new AABB(-.008f, -.008f, -.008f, - 1.008f, 1.008f, 1.008f); + public static final AABB SLIGHTLY_OVER_BLOCK = BLOCK.inflate(0.001); + public static final AABB OUTPUT_OVERLAY = BLOCK.inflate(0.006); + public static final AABB AUTO_OUTPUT_OVERLAY = BLOCK.inflate(0.008); + public static final AABB COVER_OVERLAY = BLOCK.inflate(0.002); private static final int VERTEX_INT_SIZE = 8; private static final float RESCALE_22_5 = 1.0F / (float) Math.cos((float) (Math.PI / 8)) - 1.0F; From 19cc64e2fb059d7f608955960f6351095914910c Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 14:03:49 +0300 Subject: [PATCH 6/9] Render covers with the render type(s) they request instead of all of them --- .../client/renderer/cover/ICoverableRenderer.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index a516d2f9b0d..d203646a937 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -62,7 +62,8 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos // noinspection DataFlowIssue ICoverRenderer coverRenderer = cover.getCoverRenderer().get(); - if (thickness > 0 && cover.shouldRenderPlate() && coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { + if (renderType == RenderType.cutoutMipped() && thickness > 0 && + cover.shouldRenderPlate() && coverRenderer.shouldRenderBackPlateForSide(cover, pos, level, side)) { // All faces are slightly under a full block's size to never show the beginning of // the second row of pixels of the block's texture and to combat Z-fighting. AABB cube = switch (face) { @@ -82,9 +83,12 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos } } - coverRenderer.renderCover(quads, side, rand, cover, pos, level, - coverModelData != null ? coverModelData.getOrDefault(face, ModelData.EMPTY) : ModelData.EMPTY, - renderType); + ModelData coverData = coverModelData != null ? coverModelData.getOrDefault(face, ModelData.EMPTY) : + ModelData.EMPTY; + ChunkRenderTypeSet coverRenderTypes = coverRenderer.getRenderTypes(cover, pos, level, rand, coverData); + if (renderType == null || coverRenderTypes.contains(renderType)) { + coverRenderer.renderCover(quads, side, rand, cover, pos, level, coverData, renderType); + } } } @@ -112,6 +116,7 @@ default ChunkRenderTypeSet getCoverRenderTypes(ICoverable coverable, BlockPos po Map coverModelData = modelData.get(GTModelProperties.COVER_MODEL_DATA); Set renderTypeSets = new HashSet<>(); + renderTypeSets.add(ChunkRenderTypeSet.of(RenderType.cutoutMipped())); for (Direction side : GTUtil.DIRECTIONS) { CoverBehavior cover = coverable.getCoverAtSide(side); From a536246669febd2bc99fb1ed3ff9050150c6e171 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 14:42:01 +0300 Subject: [PATCH 7/9] Make cover plates slightly _larger_ than the block they're on, rather than smaller --- .../renderer/cover/FacadeCoverRenderer.java | 17 ++++++++--------- .../renderer/cover/ICoverableRenderer.java | 14 ++++++-------- .../gtceu/client/util/GTQuadTransformers.java | 12 ++++++++---- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 6f02b47cbfe..157dddaf510 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -5,6 +5,7 @@ import com.gregtechceu.gtceu.client.model.GTModelProperties; import com.gregtechceu.gtceu.client.util.GTQuadTransformers; import com.gregtechceu.gtceu.client.util.ModelUtils; +import com.gregtechceu.gtceu.client.util.StaticFaceBakery; import com.gregtechceu.gtceu.client.util.quad.transformers.QuadReInterpolator; import com.gregtechceu.gtceu.common.cover.FacadeCover; import com.gregtechceu.gtceu.common.item.behavior.FacadeItemBehaviour; @@ -48,23 +49,21 @@ public class FacadeCoverRenderer extends BaseBakedModel implements ICoverRendere private static final double FACADE_PLANE_BACK = 1.0 / 16; - private static final AABB FACADE_PLANE = ICoverableRenderer.COVER_PLATE_BOX; - // spotless:off private static final Map FACADE_PLANE_TRANSFORMERS = Util.make(new EnumMap<>(Direction.class), map -> { for (Direction dir : GTUtil.DIRECTIONS) { // All faces are slightly under a full block's size to never show the beginning of // the second row of pixels of the block's texture and to combat Z-fighting. AABB facadePlane = switch (dir) { - case DOWN -> FACADE_PLANE.setMaxY(FACADE_PLANE_BACK); - case UP -> FACADE_PLANE.setMinY(1.0 - FACADE_PLANE_BACK); - case NORTH -> FACADE_PLANE.setMaxZ(FACADE_PLANE_BACK); - case SOUTH -> FACADE_PLANE.setMinZ(1.0 - FACADE_PLANE_BACK); - case WEST -> FACADE_PLANE.setMaxX(FACADE_PLANE_BACK); - case EAST -> FACADE_PLANE.setMinX(1.0 - FACADE_PLANE_BACK); + case DOWN -> StaticFaceBakery.COVER_OVERLAY.setMaxY(FACADE_PLANE_BACK); + case UP -> StaticFaceBakery.COVER_OVERLAY.setMinY(1.0 - FACADE_PLANE_BACK); + case NORTH -> StaticFaceBakery.COVER_OVERLAY.setMaxZ(FACADE_PLANE_BACK); + case SOUTH -> StaticFaceBakery.COVER_OVERLAY.setMinZ(1.0 - FACADE_PLANE_BACK); + case WEST -> StaticFaceBakery.COVER_OVERLAY.setMaxX(FACADE_PLANE_BACK); + case EAST -> StaticFaceBakery.COVER_OVERLAY.setMinX(1.0 - FACADE_PLANE_BACK); }; - map.put(dir, GTQuadTransformers.clamp(facadePlane)); + map.put(dir, GTQuadTransformers.forcePositionTo(facadePlane)); } }); // spotless:on diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index d203646a937..550a6f05ad0 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -33,8 +33,6 @@ public interface ICoverableRenderer { @OnlyIn(Dist.CLIENT) TextureAtlasSprite[] COVER_BACK_PLATE = new TextureAtlasSprite[1]; - double THIN_OFFSET = 0.002; - AABB COVER_PLATE_BOX = StaticFaceBakery.BLOCK.deflate(THIN_OFFSET); @OnlyIn(Dist.CLIENT) static void initSprites(TextureAtlas atlas) { @@ -67,12 +65,12 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos // All faces are slightly under a full block's size to never show the beginning of // the second row of pixels of the block's texture and to combat Z-fighting. AABB cube = switch (face) { - case DOWN -> COVER_PLATE_BOX.setMaxY(thickness); - case UP -> COVER_PLATE_BOX.setMinY(1.0 - thickness); - case NORTH -> COVER_PLATE_BOX.setMaxZ(thickness); - case SOUTH -> COVER_PLATE_BOX.setMinZ(1.0 - thickness); - case WEST -> COVER_PLATE_BOX.setMaxX(thickness); - case EAST -> COVER_PLATE_BOX.setMinX(1.0 - thickness); + case DOWN -> StaticFaceBakery.COVER_OVERLAY.setMaxY(thickness); + case UP -> StaticFaceBakery.COVER_OVERLAY.setMinY(1.0 - thickness); + case NORTH -> StaticFaceBakery.COVER_OVERLAY.setMaxZ(thickness); + case SOUTH -> StaticFaceBakery.COVER_OVERLAY.setMinZ(1.0 - thickness); + case WEST -> StaticFaceBakery.COVER_OVERLAY.setMaxX(thickness); + case EAST -> StaticFaceBakery.COVER_OVERLAY.setMinX(1.0 - thickness); }; if (side == null) { // render back diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/GTQuadTransformers.java b/src/main/java/com/gregtechceu/gtceu/client/util/GTQuadTransformers.java index 42e0bb9b62b..c83a4d33fee 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/GTQuadTransformers.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/GTQuadTransformers.java @@ -45,9 +45,12 @@ public static IQuadTransformer offset(float xOffset, float yOffset, float zOffse }; } - public static IQuadTransformer clamp(AABB bounds) { + public static IQuadTransformer forcePositionTo(AABB bounds) { return quad -> { int[] vertices = quad.getVertices(); + float minX = (float) bounds.minX, minY = (float) bounds.minY, minZ = (float) bounds.minZ; + float maxX = (float) bounds.maxX, maxY = (float) bounds.maxY, maxZ = (float) bounds.maxZ; + float middleX = (minX + maxX) / 2f, middleY = (minY + maxY) / 2f, middleZ = (minZ + maxZ) / 2f; for (int i = 0; i < 4; i++) { int offset = i * IQuadTransformer.STRIDE + IQuadTransformer.POSITION; @@ -55,10 +58,11 @@ public static IQuadTransformer clamp(AABB bounds) { float x = Float.intBitsToFloat(vertices[offset]); float y = Float.intBitsToFloat(vertices[offset + 1]); float z = Float.intBitsToFloat(vertices[offset + 2]); + // noinspection PointlessArithmeticExpression looks nicer - vertices[offset + 0] = Float.floatToRawIntBits(Mth.clamp(x, (float) bounds.minX, (float) bounds.maxX)); - vertices[offset + 1] = Float.floatToRawIntBits(Mth.clamp(y, (float) bounds.minY, (float) bounds.maxY)); - vertices[offset + 2] = Float.floatToRawIntBits(Mth.clamp(z, (float) bounds.minZ, (float) bounds.maxZ)); + vertices[offset + 0] = Float.floatToRawIntBits(middleX - x > Mth.EPSILON ? minX : maxX); + vertices[offset + 1] = Float.floatToRawIntBits(middleY - y > Mth.EPSILON ? minY : maxY); + vertices[offset + 2] = Float.floatToRawIntBits(middleZ - z > Mth.EPSILON ? minZ : maxZ); } }; } From 356aacb2627bb3f004f3f0faaf22a6568b4f7267 Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 14:42:26 +0300 Subject: [PATCH 8/9] Fix quad orientation filtering --- .../gtceu/client/renderer/cover/FacadeCoverRenderer.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java index 157dddaf510..f8b7f27c77a 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/FacadeCoverRenderer.java @@ -162,8 +162,8 @@ public void renderCover(List quads, @Nullable Direction side, RandomS for (BakedQuad quad : facadeQuads) { // skip quads that aren't oriented correctly - if (quad.getDirection() != attachedSide && coverBehavior.shouldRenderPlate() || - !coverBehavior.coverHolder.shouldRenderBackSide()) { + if (quad.getDirection() != attachedSide && (coverBehavior.shouldRenderPlate() || + !coverBehavior.coverHolder.shouldRenderBackSide())) { continue; } @@ -329,6 +329,11 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction // clamp all 'facaded' quads into a box and bake their tint color into the vertices for (BakedQuad quad : facadeQuads) { + // skip quads that aren't oriented correctly + if (quad.getDirection() != Direction.NORTH) { + continue; + } + interpolator.setInputQuad(quad); // bake the quad's colors into its vertices From c8f0d1c2001459f404474c55672b90114ea752ca Mon Sep 17 00:00:00 2001 From: screret <68943070+screret@users.noreply.github.com> Date: Wed, 20 May 2026 14:47:07 +0300 Subject: [PATCH 9/9] Add element based UV calculation to StaticFaceBakery.bakeFace --- .../client/model/pipe/ActivablePipeModel.java | 23 +++- .../gtceu/client/model/pipe/PipeModel.java | 107 +++++++++--------- .../renderer/cover/ICoverableRenderer.java | 4 +- .../gtceu/client/util/StaticFaceBakery.java | 38 +++++-- 4 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/pipe/ActivablePipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/model/pipe/ActivablePipeModel.java index 46331b3fb90..6cee9decda6 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/pipe/ActivablePipeModel.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/pipe/ActivablePipeModel.java @@ -151,15 +151,15 @@ protected BlockModelBuilder makeActiveElementModel(ResourceLocation name, @Nulla ResourceLocation sideOverlay = this.sideOverlayActive != null ? this.sideOverlayActive : this.sideOverlay; ResourceLocation endOverlay = this.endOverlayActive != null ? this.endOverlayActive : this.endOverlay; - makePartModelElement(model, endFace, false, faceEndpoints, 0.0f, 0, 1, + makePartModelElement(model, endFace, false, 0.0f, 0, 1, x1, y1, z1, x2, y2, z2, side, end, SIDE_KEY, END_KEY, this.sideActive != null, this.endActive != null); - makePartModelElement(model, endFace, true, faceEndpoints, 0.001f, 0, 1, + makePartModelElement(model, endFace, true, 0.001f, 0, 1, x1, y1, z1, x2, y2, z2, sideSecondary, endSecondary, SIDE_SECONDARY_KEY, END_SECONDARY_KEY, this.sideSecondaryActive != null, this.endSecondaryActive != null); - makePartModelElement(model, endFace, true, faceEndpoints, 0.002f, 2, 2, + makePartModelElement(model, endFace, true, 0.002f, 2, 2, x1, y1, z1, x2, y2, z2, sideOverlay, endOverlay, SIDE_OVERLAY_KEY, END_OVERLAY_KEY, this.sideOverlayActive != null, this.endOverlayActive != null); @@ -168,7 +168,6 @@ protected BlockModelBuilder makeActiveElementModel(ResourceLocation name, @Nulla protected > void makePartModelElement(T model, @Nullable Direction endFace, boolean useEndWithFullCube, - Reference2FloatMap faceEndpoints, float offset, int sideTintIndex, int endTintIndex, final float x1, final float y1, final float z1, final float x2, final float y2, final float z2, @@ -176,7 +175,21 @@ protected > void makePartModelElement(T model, @Nullab @Nullable ResourceLocation endTexture, String sideKey, String endKey, boolean sideEmissive, boolean endEmissive) { - this.makePartModelElement(model, endFace, useEndWithFullCube, faceEndpoints, offset, + this.makePartModelElement(model, endFace, useEndWithFullCube, false, offset, + sideTintIndex, endTintIndex, x1, y1, z1, x2, y2, z2, + sideTexture, endTexture, sideKey, endKey, sideEmissive, endEmissive); + } + + protected > void makePartModelElement(T model, @Nullable Direction endFace, + boolean useEndWithFullCube, boolean alwaysAddEnd, + float offset, int sideTintIndex, int endTintIndex, + final float x1, final float y1, final float z1, + final float x2, final float y2, final float z2, + @Nullable ResourceLocation sideTexture, + @Nullable ResourceLocation endTexture, + String sideKey, String endKey, + boolean sideEmissive, boolean endEmissive) { + this.makePartModelElement(model, endFace, useEndWithFullCube, alwaysAddEnd, offset, sideTintIndex, endTintIndex, x1, y1, z1, x2, y2, z2, sideTexture, endTexture, sideKey, endKey, (face, textureKey, builder) -> { if (activeEmissivity == 0) { diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/pipe/PipeModel.java b/src/main/java/com/gregtechceu/gtceu/client/model/pipe/PipeModel.java index 5f440b0214f..e1ada315ad3 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/pipe/PipeModel.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/pipe/PipeModel.java @@ -180,18 +180,18 @@ public void initModels() { */ @ApiStatus.OverrideOnly protected BlockModelBuilder getOrCreateBlockModel() { - if (this.blockModel != null) { - return this.blockModel; + if (this.blockModel == null) { + // spotless:off + this.blockModel = this.provider.models().getBuilder(this.blockId.toString()) + // make the "default" model be based on the center part's model + .parent(this.getOrCreateCenterElement()) + .customLoader(PipeModelBuilder.begin(this.thickness, this.provider)) + .centerModels(this.getOrCreateCenterElement().getLocation()) + .connectionModels(this.getOrCreateConnectionElement().getLocation()) + .end(); + // spotless:on } - // spotless:off - return this.blockModel = this.provider.models().getBuilder(this.blockId.toString()) - // make the "default" model be based on the center part's model - .parent(this.getOrCreateCenterElement()) - .customLoader(PipeModelBuilder.begin(this.thickness, this.provider)) - .centerModels(this.getOrCreateCenterElement().getLocation()) - .connectionModels(this.getOrCreateConnectionElement().getLocation()) - .end(); - // spotless:on + return this.blockModel; } /** @@ -203,11 +203,11 @@ protected BlockModelBuilder getOrCreateBlockModel() { */ @ApiStatus.OverrideOnly protected BlockModelBuilder getOrCreateCenterElement() { - if (this.centerElement != null) { - return this.centerElement; + if (this.centerElement == null) { + this.centerElement = makeElementModel(this.blockId.withPath(path -> "block/pipe/" + path + "/center"), + null, minCoord, minCoord, minCoord, maxCoord, maxCoord, maxCoord); } - return this.centerElement = makeElementModel(this.blockId.withPath(path -> "block/pipe/" + path + "/center"), - null, minCoord, minCoord, minCoord, maxCoord, maxCoord, maxCoord); + return this.centerElement; } /** @@ -222,12 +222,12 @@ protected BlockModelBuilder getOrCreateCenterElement() { */ @ApiStatus.OverrideOnly protected BlockModelBuilder getOrCreateConnectionElement() { - if (this.connectionElement != null) { - return this.connectionElement; + if (this.connectionElement == null) { + this.connectionElement = makeElementModel( + this.blockId.withPath(path -> "block/pipe/" + path + "/connection"), + Direction.DOWN, minCoord, 0, minCoord, maxCoord, minCoord, maxCoord); } - return this.connectionElement = makeElementModel( - this.blockId.withPath(path -> "block/pipe/" + path + "/connection"), - Direction.DOWN, minCoord, 0, minCoord, maxCoord, minCoord, maxCoord); + return this.connectionElement; } /** @@ -239,10 +239,10 @@ protected BlockModelBuilder getOrCreateConnectionElement() { */ @ApiStatus.OverrideOnly protected ItemModelBuilder getOrCreateItemModel() { - if (this.itemModel != null) { - return this.itemModel; + if (this.itemModel == null) { + this.itemModel = createItemModel(this.blockId, this.minCoord, this.maxCoord); } - return this.itemModel = createItemModel(this.blockId, this.minCoord, this.maxCoord); + return this.itemModel; } /** @@ -275,22 +275,14 @@ public IGeneratedBlockState createBlockState() { * @return An item model builder. */ protected ItemModelBuilder createItemModel(ResourceLocation name, float min, float max) { - Reference2FloatMap faceEndpoints = new Reference2FloatOpenHashMap<>(); - faceEndpoints.put(Direction.DOWN, min); - faceEndpoints.put(Direction.UP, max); - faceEndpoints.put(Direction.NORTH, 0); - faceEndpoints.put(Direction.SOUTH, 16); - faceEndpoints.put(Direction.WEST, min); - faceEndpoints.put(Direction.EAST, max); - ItemModelBuilder model = this.provider.itemModels().getBuilder(name.toString()) .parent(this.getOrCreateCenterElement()); - makePartModelElement(model, Direction.NORTH, false, faceEndpoints, 0.0f, 0, 1, + makePartModelElement(model, Direction.NORTH, false, true, 0.0f, 0, 1, min, min, 0, max, max, 16, this.side, this.end, SIDE_KEY, END_KEY); - makePartModelElement(model, Direction.NORTH, true, faceEndpoints, 0.001f, 0, 1, + makePartModelElement(model, Direction.NORTH, true, true, 0.001f, 0, 1, min, min, 0, max, max, 16, this.sideSecondary, this.endSecondary, SIDE_SECONDARY_KEY, END_SECONDARY_KEY); - makePartModelElement(model, Direction.NORTH, true, faceEndpoints, 0.002f, 2, 2, + makePartModelElement(model, Direction.NORTH, true, true, 0.002f, 2, 2, min, min, 0, max, max, 16, this.sideOverlay, this.endOverlay, SIDE_OVERLAY_KEY, END_OVERLAY_KEY); return model; } @@ -311,38 +303,47 @@ protected ItemModelBuilder createItemModel(ResourceLocation name, float min, flo protected BlockModelBuilder makeElementModel(ResourceLocation name, @Nullable Direction endFace, final float x1, final float y1, final float z1, final float x2, final float y2, final float z2) { - Reference2FloatMap faceEndpoints = makeFaceEndpointMap(x1, y1, z1, x2, y2, z2); - BlockModelBuilder model = this.provider.models().getBuilder(name.toString()) .parent(new ModelFile.UncheckedModelFile("block/block")) .texture("particle", "#" + (this.side != null ? SIDE_KEY : END_KEY)) .renderType(RENDERTYPE_CUTOUT_MIPPED); - makePartModelElement(model, endFace, false, faceEndpoints, 0.0f, 0, 1, + makePartModelElement(model, endFace, false, 0.0f, 0, 1, x1, y1, z1, x2, y2, z2, this.side, this.end, SIDE_KEY, END_KEY); - makePartModelElement(model, endFace, true, faceEndpoints, 0.001f, 0, 1, + makePartModelElement(model, endFace, true, 0.001f, 0, 1, x1, y1, z1, x2, y2, z2, this.sideSecondary, this.endSecondary, "side_secondary", "end_secondary"); - makePartModelElement(model, endFace, true, faceEndpoints, 0.002f, 2, 2, + makePartModelElement(model, endFace, true, 0.002f, 2, 2, x1, y1, z1, x2, y2, z2, this.sideOverlay, this.endOverlay, "side_overlay", "end_overlay"); return model; } protected > void makePartModelElement(T model, @Nullable Direction endFace, boolean useEndWithFullCube, - Reference2FloatMap faceEndpoints, float offset, int sideTintIndex, int endTintIndex, final float x1, final float y1, final float z1, final float x2, final float y2, final float z2, @Nullable ResourceLocation sideTexture, @Nullable ResourceLocation endTexture, String sideKey, String endKey) { - makePartModelElement(model, endFace, useEndWithFullCube, faceEndpoints, + makePartModelElement(model, endFace, useEndWithFullCube, false, + offset, sideTintIndex, endTintIndex, x1, y1, z1, x2, y2, z2, + sideTexture, endTexture, sideKey, endKey); + } + + protected > void makePartModelElement(T model, @Nullable Direction endFace, + boolean useEndWithFullCube, boolean alwaysAddEnd, + float offset, int sideTintIndex, int endTintIndex, + final float x1, final float y1, final float z1, + final float x2, final float y2, final float z2, + @Nullable ResourceLocation sideTexture, + @Nullable ResourceLocation endTexture, + String sideKey, String endKey) { + makePartModelElement(model, endFace, useEndWithFullCube, alwaysAddEnd, offset, sideTintIndex, endTintIndex, x1, y1, z1, x2, y2, z2, sideTexture, endTexture, sideKey, endKey, (face, texture, builder) -> {}); } protected > void makePartModelElement(T model, @Nullable Direction endFace, - boolean useEndWithFullCube, - Reference2FloatMap faceEndpoints, + boolean useEndWithFullCube, boolean alwaysAddEnd, float offset, int sideTintIndex, int endTintIndex, final float x1, final float y1, final float z1, final float x2, final float y2, final float z2, @@ -365,20 +366,18 @@ protected > void makePartModelElement(T model, @Nullab .to(x2 + offset, y2 + offset, z2 + offset); for (Direction dir : GTUtil.DIRECTIONS) { - ModelBuilder.ElementBuilder.FaceBuilder face = null; - boolean isEnd = !fullCube && endFace != null && (endFace == dir || endFace == dir.getOpposite()); + if (!alwaysAddEnd && endFace == dir.getOpposite()) { + // skip the inside face + continue; + } + boolean isEnd = !fullCube && endFace == dir; if (isEnd && endTexture != null) { - face = element.face(dir).texture("#" + endKey).tintindex(endTintIndex); + var face = element.face(dir).cullface(dir).texture("#" + endKey).tintindex(endTintIndex); faceConfigurator.accept(dir, endKey, face); } else if (!isEnd && sideTexture != null) { - face = element.face(dir).texture("#" + sideKey).tintindex(sideTintIndex); + var face = element.face(dir).texture("#" + sideKey).tintindex(sideTintIndex); faceConfigurator.accept(dir, sideKey, face); } - - float faceEnd = faceEndpoints.getFloat(dir); - if (face != null && (faceEnd >= 16.0f || faceEnd <= 0.0f)) { - face.cullface(dir); - } } } @@ -427,9 +426,7 @@ public interface FaceConfigurator> { * @param texture The texture of the face, usually in {@code #reference} format. * Note that the String does NOT begin with {@code #}. * @param builder The face builder. - * @see ActivablePipeModel#makePartModelElement(ModelBuilder, Direction, boolean, Reference2FloatMap, float, - * int, int, float, float, float, float, float, float, ResourceLocation, ResourceLocation, String, String, - * boolean, boolean) ActivablePipeModel.makePartModelElement + * @see #makePartModelElement(ModelBuilder, Direction, boolean, float, int, int, float, float, float, float, float, float, ResourceLocation, ResourceLocation, String, String) ActivablePipeModel.makePartModelElement */ void accept(Direction face, String texture, ModelBuilder.ElementBuilder.FaceBuilder builder); } diff --git a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java index 550a6f05ad0..809e057dc3a 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java +++ b/src/main/java/com/gregtechceu/gtceu/client/renderer/cover/ICoverableRenderer.java @@ -74,10 +74,10 @@ default void renderCovers(List quads, ICoverable coverable, BlockPos }; if (side == null) { // render back - quads.add(StaticFaceBakery.bakeFace(cube, face.getOpposite(), COVER_BACK_PLATE[0])); + quads.add(StaticFaceBakery.bakeFace(cube, face.getOpposite(), COVER_BACK_PLATE[0], true)); } else if (side != face.getOpposite() && (((coverMask >> side.ordinal()) & 1) == 0 || side == face)) { // render sides - quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0])); + quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0], true)); } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java index 793086799ce..740b6163189 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/StaticFaceBakery.java @@ -49,13 +49,28 @@ public class StaticFaceBakery { * @param cull whether cull the face * @param shade whether shade the face */ - public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite sprite, ModelState rotation, - int tintIndex, int emissivity, boolean cull, boolean shade) { - return bakeQuad( - new Vector3f((float) cube.minX * 16f, (float) cube.minY * 16f, (float) cube.minZ * 16f), - new Vector3f((float) cube.maxX * 16f, (float) cube.maxY * 16f, (float) cube.maxZ * 16f), + public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite sprite, boolean cubeUV, + ModelState rotation, int tintIndex, int emissivity, boolean cull, boolean shade) { + Vector3f posFrom = new Vector3f((float) cube.minX * 16f, (float) cube.minY * 16f, (float) cube.minZ * 16f); + Vector3f posTo = new Vector3f((float) cube.maxX * 16f, (float) cube.maxY * 16f, (float) cube.maxZ * 16f); + float[] uv; + if (cubeUV) { + uv = switch (face) { + case UP -> new float[] { posFrom.x(), posFrom.z(), posTo.x(), posTo.z() }; + case DOWN -> new float[] { posFrom.x(), posTo.z(), posTo.x(), posFrom.z() }; + case NORTH -> new float[] { posTo.x(), posTo.y(), posFrom.x(), posFrom.y() }; + case SOUTH -> new float[] { posFrom.x(), posTo.y(), posTo.x(), posFrom.y() }; + case WEST -> new float[] { posFrom.z(), posTo.y(), posTo.z(), posFrom.y() }; + case EAST -> new float[] { posTo.z(), posTo.y(), posFrom.z(), posFrom.y() }; + }; + } + else { + uv = new float[] { 0.0F, 0.0F, 16.0F, 16.0F }; + } + + return bakeQuad(posFrom, posTo, new BlockElementFace(cull ? face : null, tintIndex, sprite.contents().name().toString(), - new BlockFaceUV(new float[] { 0.0F, 0.0F, 16.0F, 16.0F }, 0)), + new BlockFaceUV(uv, 0)), sprite, face, rotation, @@ -64,6 +79,11 @@ public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite s emissivity); } + public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite sprite, ModelState rotation, + int tintIndex, int emissivity, boolean cull, boolean shade) { + return bakeFace(cube, face, sprite, false, rotation, tintIndex, emissivity, cull, shade); + } + public static BakedQuad bakeFace(Direction face, TextureAtlasSprite sprite, ModelState rotation, int tintIndex, int emissivity, boolean cull, boolean shade) { return bakeFace(BLOCK, face, sprite, rotation, tintIndex, emissivity, cull, shade); @@ -86,8 +106,12 @@ public static BakedQuad bakeFace(Direction face, TextureAtlasSprite sprite) { return bakeFace(face, sprite, BlockModelRotation.X0_Y0); } + public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite sprite, boolean cubeUV) { + return bakeFace(cube, face, sprite, cubeUV, BlockModelRotation.X0_Y0, -1, 0, true, true); + } + public static BakedQuad bakeFace(AABB cube, Direction face, TextureAtlasSprite sprite) { - return bakeFace(cube, face, sprite, BlockModelRotation.X0_Y0, -1, 0, true, true); + return bakeFace(cube, face, sprite, false); } public static BakedQuad bakeQuad(Vector3f posFrom,