diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/ctm/CTMMeshBuilder.java b/src/main/java/com/gregtechceu/gtceu/client/model/ctm/CTMMeshBuilder.java index cf0f19c3656..7de4618781a 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/ctm/CTMMeshBuilder.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/ctm/CTMMeshBuilder.java @@ -62,11 +62,11 @@ public static List buildCTMQuads(TextureConnections connections, List emitter.computeGeometry(); emitter.populateMissingNormals(); - result.add(emitter.toBakedQuad(ctmSprite)); emitter.emit(); } } } + meshBuilder.build().asBlockBakedQuads(result::add); return result; } 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..c8637cbb7ef 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,9 @@ 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/model/quad/Mesh.java b/src/main/java/com/gregtechceu/gtceu/client/model/quad/Mesh.java index 29403d1af8a..657a3a73470 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/quad/Mesh.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/quad/Mesh.java @@ -63,4 +63,12 @@ public List toBlockBakedQuads() { forEach(qv -> result.add(qv.toBakedQuad(finder.find(qv)))); return result; } + + @SuppressWarnings("deprecation") + public void asBlockBakedQuads(Consumer consumer) { + SpriteFinder finder = SpriteFinder.get(Minecraft.getInstance().getModelManager() + .getAtlas(TextureAtlas.LOCATION_BLOCKS)); + + forEach(qv -> consumer.accept(qv.toBakedQuad(finder.find(qv)))); + } } diff --git a/src/main/java/com/gregtechceu/gtceu/client/model/quad/StaticFaceBakery.java b/src/main/java/com/gregtechceu/gtceu/client/model/quad/StaticFaceBakery.java index 43910d2f8c6..7629e3b948f 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/model/quad/StaticFaceBakery.java +++ b/src/main/java/com/gregtechceu/gtceu/client/model/quad/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; @@ -53,13 +49,27 @@ 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, @@ -68,6 +78,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); @@ -90,8 +105,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, 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 799fb61a06f..80476ac896c 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 @@ -3,9 +3,13 @@ import com.gregtechceu.gtceu.api.cover.CoverBehavior; import com.gregtechceu.gtceu.client.model.BaseBakedModel; import com.gregtechceu.gtceu.client.model.GTModelProperties; +import com.gregtechceu.gtceu.client.model.quad.MeshBuilder; import com.gregtechceu.gtceu.client.model.quad.StaticFaceBakery; +import com.gregtechceu.gtceu.client.model.quad.transform.QuadTransform; import com.gregtechceu.gtceu.client.util.RenderUtil; -import com.gregtechceu.gtceu.client.util.quad.transformers.GTQuadTransformers; +import com.gregtechceu.gtceu.client.util.quad.transformers.QuadPositionForcer; +import com.gregtechceu.gtceu.client.util.quad.transformers.QuadReInterpolator; +import com.gregtechceu.gtceu.client.util.quad.transformers.QuadTinter; import com.gregtechceu.gtceu.common.cover.FacadeCover; import com.gregtechceu.gtceu.common.item.behavior.FacadeItemBehaviour; import com.gregtechceu.gtceu.utils.GTUtil; @@ -34,7 +38,6 @@ import net.minecraftforge.client.ChunkRenderTypeSet; import net.minecraftforge.client.model.BakedModelWrapper; import net.minecraftforge.client.model.IDynamicBakedModel; -import net.minecraftforge.client.model.IQuadTransformer; import net.minecraftforge.client.model.data.ModelData; import com.mojang.blaze3d.vertex.PoseStack; @@ -48,26 +51,23 @@ 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); - // spotless:off - private static final Map FACADE_PLANE_TRANSFORMERS = Util.make(new EnumMap<>(Direction.class), map -> { + 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(FACADE_PLANE_BACK); - case NORTH -> FACADE_PLANE.setMaxZ(FACADE_PLANE_BACK); - case SOUTH -> FACADE_PLANE.setMinZ(FACADE_PLANE_BACK); - case WEST -> FACADE_PLANE.setMaxX(FACADE_PLANE_BACK); - case EAST -> FACADE_PLANE.setMinX(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, new QuadPositionForcer(facadePlane)); } }); - private static final IQuadTransformer FACADE_ITEM_PLANE_TRANSFORMER = FACADE_PLANE_TRANSFORMERS.get(Direction.NORTH); // spotless:on public static final FacadeCoverRenderer INSTANCE = new FacadeCoverRenderer(); @@ -116,7 +116,7 @@ public List getRenderPasses(ItemStack stack, boolean fabulous) { @Override @OnlyIn(Dist.CLIENT) - public void renderCover(List quads, @Nullable Direction side, RandomSource rand, + public void renderCover(List quads, @Nullable Direction cullFace, RandomSource rand, CoverBehavior coverBehavior, BlockPos pos, BlockAndTintGetter level, ModelData modelData, @Nullable RenderType renderType) { if (!(coverBehavior instanceof FacadeCover facadeCover)) { @@ -138,42 +138,62 @@ public void renderCover(List quads, @Nullable Direction side, RandomS } Direction attachedSide = coverBehavior.attachedSide; - if (side != attachedSide && (side != null || !coverBehavior.coverHolder.shouldRenderBackSide())) { + if (cullFace != attachedSide && (cullFace != null || !coverBehavior.coverHolder.shouldRenderBackSide())) { return; } - IQuadTransformer clamper = FACADE_PLANE_TRANSFORMERS.get(attachedSide); + MeshBuilder meshBuilder = MeshBuilder.getInstance(); + var emitter = meshBuilder.getEmitter(); + + QuadTransform clamper = FACADE_PLANE_TRANSFORMERS.get(attachedSide); + QuadReInterpolator interpolator = new QuadReInterpolator(); BlockColors blockColors = Minecraft.getInstance().getBlockColors(); // always add unculled faces - List facadeQuads = new LinkedList<>( - facadeModel.getQuads(facadeState, null, rand, facadeData, renderType)); - if (side != null) { + Map> facadeQuads = new IdentityHashMap<>(); + facadeQuads.put(null, facadeModel.getQuads(facadeState, null, rand, facadeData, renderType)); + if (cullFace != null) { // if a cullface is given, only draw that + unculled faces - facadeQuads.addAll(facadeModel.getQuads(facadeState, side, rand, facadeData, renderType)); + facadeQuads.put(cullFace, facadeModel.getQuads(facadeState, cullFace, rand, facadeData, renderType)); } else { // add all culled faces if no cullface is given - for (Direction cullFace : GTUtil.DIRECTIONS) { - facadeQuads.addAll(facadeModel.getQuads(facadeState, cullFace, rand, facadeData, renderType)); + for (Direction face : GTUtil.DIRECTIONS) { + facadeQuads.put(face, facadeModel.getQuads(facadeState, face, rand, facadeData, renderType)); } } + // clamp all 'facaded' quads into a box and bake their tint color into the vertices + for (var entry : facadeQuads.entrySet()) { + Direction face = entry.getKey(); + List cullfaceQuads = entry.getValue(); + if (cullfaceQuads.isEmpty()) continue; + + for (BakedQuad quad : cullfaceQuads) { + // skip quads that aren't oriented correctly + if (quad.getDirection() != attachedSide && (coverBehavior.shouldRenderPlate() || + !coverBehavior.coverHolder.shouldRenderBackSide())) { + continue; + } - for (BakedQuad quad : facadeQuads) { - // 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 - int color = blockColors.getColor(facadeState, level, pos, quad.getTintIndex()); - quad = GTQuadTransformers.setColor(quad, color, true); - } else { - // otherwise just copy the quad so we don't mutate the original model with the overlay offset - quad = GTQuadTransformers.copy(quad); - } + emitter.fromVanilla(quad, face); + interpolator.setInputQuad(emitter); - // clamp the quad's vertex positions to fit into the facade plane - clamper.processInPlace(quad); + // bake the quad's colors into its vertices + if (emitter.tintIndex() != -1) { + // if the quad has a tint index set, bake the tint into the vertex + int color = blockColors.getColor(facadeState, level, pos, quad.getTintIndex()); + QuadTinter tinter = new QuadTinter(color); + tinter.transform(emitter); + } - quads.add(quad); + // clamp the quad's vertices into the facade plane + clamper.transform(emitter); + // fix the quad's UVs based on the original & clamped vertices + interpolator.transform(emitter); + + emitter.emit(); + } } + meshBuilder.build().asBlockBakedQuads(quads::add); } @Override @@ -274,13 +294,13 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction } @Override - public List getQuads(@Nullable BlockState state, @Nullable Direction side, + public List getQuads(@Nullable BlockState state, @Nullable Direction cullFace, RandomSource rand, ModelData modelData, @Nullable RenderType renderType) { - if (this.quads.containsKey(side)) { - return this.quads.get(side); + if (this.quads.containsKey(cullFace)) { + return this.quads.get(cullFace); } List quads = new LinkedList<>(); - this.quads.put(side, quads); + this.quads.put(cullFace, quads); if (facadeState.getRenderShape() != RenderShape.MODEL) { return quads; @@ -293,6 +313,11 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction ModelData facadeData = modelData.get(GTModelProperties.CHILD_MODEL_DATA); if (facadeData == null) facadeData = ModelData.EMPTY; + MeshBuilder meshBuilder = MeshBuilder.getInstance(); + var emitter = meshBuilder.getEmitter(); + + QuadTransform clamper = FACADE_PLANE_TRANSFORMERS.get(Direction.NORTH); + QuadReInterpolator interpolator = new QuadReInterpolator(); ItemColors itemColors = Minecraft.getInstance().getItemColors(); for (var model : facadeModel.getRenderPasses(this.facadeStack, true)) { @@ -301,38 +326,52 @@ public List getQuads(@Nullable BlockState state, @Nullable Direction } // always add unculled faces - List facadeQuads = new LinkedList<>( - model.getQuads(this.facadeState, null, rand, facadeData, renderType)); - if (side != null) { + Map> facadeQuads = new IdentityHashMap<>(); + facadeQuads.put(null, model.getQuads(this.facadeState, null, rand, facadeData, renderType)); + if (cullFace != null) { // if a cullface is given, only draw that + unculled faces - facadeQuads.addAll(model.getQuads(this.facadeState, side, rand, facadeData, renderType)); + facadeQuads.put(cullFace, model.getQuads(this.facadeState, cullFace, rand, facadeData, renderType)); } else { // add all culled faces if no cullface is given - for (Direction cullFace : GTUtil.DIRECTIONS) { - facadeQuads.addAll(model.getQuads(this.facadeState, cullFace, rand, facadeData, renderType)); + for (Direction face : GTUtil.DIRECTIONS) { + facadeQuads.put(face, model.getQuads(this.facadeState, face, rand, facadeData, renderType)); } } // clamp all 'facaded' quads into a box and bake their tint color into the vertices - for (BakedQuad quad : facadeQuads) { - // 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 - int color = itemColors.getColor(this.facadeStack, quad.getTintIndex()); - // this also copies the quad - quad = GTQuadTransformers.setColor(quad, color, true); - } else { - // otherwise just copy the quad so we don't mutate the original model with the clamping - quad = GTQuadTransformers.copy(quad); + for (var entry : facadeQuads.entrySet()) { + Direction face = entry.getKey(); + List cullfaceQuads = entry.getValue(); + if (cullfaceQuads.isEmpty()) continue; + + for (BakedQuad quad : cullfaceQuads) { + // skip quads that aren't oriented correctly + if (quad.getDirection() != Direction.NORTH) { + continue; + } + + emitter.fromVanilla(quad, face); + interpolator.setInputQuad(emitter); + + // bake the quad's colors into its vertices + if (emitter.tintIndex() != -1) { + // if the quad has a tint index set, bake the tint into the vertex + int color = itemColors.getColor(this.facadeStack, emitter.tintIndex()); + QuadTinter tinter = new QuadTinter(color); + tinter.transform(emitter); + } + + // clamp the quad's vertices into the facade plane + clamper.transform(emitter); + // fix the quad's UVs based on the original & clamped vertices + interpolator.transform(emitter); + + emitter.emit(); } - - // clamp the quad's vertex positions to fit into the facade plane - FACADE_ITEM_PLANE_TRANSFORMER.processInPlace(quad); - - quads.add(quad); } } + meshBuilder.build().asBlockBakedQuads(quads::add); return quads; } 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 2469b6ab994..eb38fd677f5 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; - double LESS_THIN_OFFSET = 0.005; @OnlyIn(Dist.CLIENT) static void initSprites(TextureAtlas atlas) { @@ -48,36 +46,46 @@ 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) { - 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()) { - 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); - - 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 - quads.add(StaticFaceBakery.bakeFace(cube, side, COVER_BACK_PLATE[0])); + if (coverable.hasCover(face)) { + coverMask |= 1 << face.ordinal(); + } + } + + for (Direction face : GTUtil.DIRECTIONS) { + 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 (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) { + 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 + 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], true)); } - } - } - 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); } } } @@ -106,6 +114,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); diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/GTQuadTransformers.java b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/GTQuadTransformers.java index de1099e49b5..ddb1df65648 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/GTQuadTransformers.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/GTQuadTransformers.java @@ -5,7 +5,6 @@ import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.core.Direction; import net.minecraft.util.Mth; -import net.minecraft.world.phys.AABB; import net.minecraftforge.client.model.IQuadTransformer; import net.minecraftforge.client.model.QuadTransformers; @@ -47,24 +46,6 @@ public static IQuadTransformer offset(float xOffset, float yOffset, float zOffse }; } - public static IQuadTransformer clamp(AABB bounds) { - return quad -> { - int[] vertices = quad.getVertices(); - - for (int i = 0; i < 4; i++) { - int offset = i * IQuadTransformer.STRIDE + IQuadTransformer.POSITION; - - 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)); - } - }; - } - public static BakedQuad setSprite(BakedQuad quad, TextureAtlasSprite sprite) { TextureAtlasSprite oldSprite = quad.getSprite(); int[] vertices = quad.getVertices().clone(); 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 index f1b1d5404fa..ae92336d060 100644 --- 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 @@ -19,6 +19,8 @@ import net.minecraft.util.Mth; +import org.jetbrains.annotations.ApiStatus; + /** * @author covers1624 */ @@ -60,6 +62,11 @@ public void reset(float dx0, float dy0, float dx1, float dy1, float dx2, float d vec3[1] = dy3; } + @ApiStatus.Internal + public float[][] getInternalPosCache() { + return this.posCache; + } + /** * Call when you are ready to use the interpolation helper. */ diff --git a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadClamper.java b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadPositionForcer.java similarity index 61% rename from src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadClamper.java rename to src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadPositionForcer.java index 9ca6980a74d..d9bc4284b25 100644 --- a/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadClamper.java +++ b/src/main/java/com/gregtechceu/gtceu/client/util/quad/transformers/QuadPositionForcer.java @@ -1,20 +1,3 @@ -/* - * 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 com.gregtechceu.gtceu.client.model.quad.MutableQuadView; @@ -27,27 +10,27 @@ import org.joml.Vector3f; /** - * This transformer simply clamps the vertices inside the provided box.
+ * This transformer simply sets the vertices to be on the edges of the provided box.
* You probably want to Re-Interpolate the UV's, Color, and Lightmap. For that, see {@link QuadReInterpolator}. * * @see QuadReInterpolator - * @author covers1624 + * @author screret */ -public class QuadClamper implements QuadTransform { +public class QuadPositionForcer implements QuadTransform { - private final AABB clampBounds; + private final AABB bounds; private final Vector3f pos = new Vector3f(); - public QuadClamper(AABB clampBounds) { - this.clampBounds = clampBounds; + public QuadPositionForcer(AABB bounds) { + this.bounds = bounds; } @Override public boolean transform(MutableQuadView quad) { Direction.Axis axis = quad.nominalFace().getAxis(); - clamp(quad, this.clampBounds); + setPosition(quad, this.bounds); // Check if the quad would be invisible and cull it. float x1 = quad.posByIndex(0, xCoord(axis)); @@ -66,12 +49,16 @@ public boolean transform(MutableQuadView quad) { return !flag1 && !flag2; } - private void clamp(MutableQuadView quad, AABB bb) { + private void setPosition(MutableQuadView quad, AABB bb) { + 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++) { quad.copyPos(i, pos); - pos.set((float) Mth.clamp(pos.x(), bb.minX, bb.maxX), - (float) Mth.clamp(pos.y(), bb.minY, bb.maxY), - (float) Mth.clamp(pos.z(), bb.minZ, bb.maxZ)); + pos.set(middleX - pos.x() > Mth.EPSILON ? minX : maxX, + middleY - pos.y() > Mth.EPSILON ? minY : maxY, + middleZ - pos.z() > Mth.EPSILON ? minZ : maxZ); quad.pos(i, pos); } } 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 index bc3cf275201..37533cd2da5 100644 --- 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 @@ -41,9 +41,7 @@ public class QuadReInterpolator implements QuadTransform { private final float[] originalSpriteV = new float[4]; private final int[] originalSpriteLightmap = new int[4]; - public QuadReInterpolator() { - super(); - } + public QuadReInterpolator() {} public void setInputQuad(QuadView quad) { Direction.Axis axis = quad.nominalFace().getAxis(); @@ -63,6 +61,12 @@ public void setInputQuad(QuadView quad) { originalSpriteV[v] = quad.v(v); originalSpriteLightmap[v] = quad.lightmap(v); } + + // 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 @@ -166,7 +170,7 @@ public void interpolateLightmapFrom(MutableQuadView quad, int vertexIndex) { * @return The x coordinate. */ private static int xCoord(Direction.Axis axis) { - if (axis == Direction.Axis.Y) { + if (axis != Direction.Axis.X) { return 0; } else { return 2;