diff --git a/Common/src/main/java/at/petrak/hexcasting/client/render/PatternTextureManager.java b/Common/src/main/java/at/petrak/hexcasting/client/render/PatternTextureManager.java index afec858fc..3f33bd9a4 100644 --- a/Common/src/main/java/at/petrak/hexcasting/client/render/PatternTextureManager.java +++ b/Common/src/main/java/at/petrak/hexcasting/client/render/PatternTextureManager.java @@ -29,6 +29,7 @@ import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.List; +import java.util.concurrent.*; import java.util.stream.Collectors; public class PatternTextureManager { @@ -37,6 +38,7 @@ public class PatternTextureManager { public static boolean useTextures = true; public static int repaintIndex = 0; public static int resolutionScaler = 4; + public static int fastRenderScaleFactor = 8; // e.g. this is 8, resolution is 1024, so render at 1024/8 = 128 public static int resolutionByBlockSize = 128 * resolutionScaler; public static int paddingByBlockSize = 16 * resolutionScaler; @@ -55,6 +57,11 @@ public static void setResolutionScaler(int resolutionScaler) { otherLineWidth = 10 * resolutionScaler; } + private static final ConcurrentMap patternTexturesToAdd = new ConcurrentHashMap<>(); + // basically newCachedThreadPool, but with a max pool size + private static final ExecutorService executor = new ThreadPoolExecutor(0, 16, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); + + private static final HashMap patternTextures = new HashMap<>(); public static String getPointsKey(List zappyPoints) @@ -217,17 +224,34 @@ private static void vertex(Matrix4f mat, Matrix3f normal, int light, VertexConsu .endVertex(); } - public static ResourceLocation getTexture(List points, String pointsKey, int blockSize, boolean showsStrokeOrder, int lineWidth, boolean useFullSize, Color innerColor, Color outerColor) { - if (patternTextures.containsKey(pointsKey)) - return patternTextures.get(pointsKey); - return createTexture(points, pointsKey, blockSize, showsStrokeOrder, lineWidth, useFullSize, innerColor, outerColor); + public static ResourceLocation getTexture(List points, String pointsKey, int blockSize, boolean showsStrokeOrder, float lineWidth, boolean useFullSize, Color innerColor, Color outerColor) { + if (patternTexturesToAdd.containsKey(pointsKey)) + return patternTexturesToAdd.get(pointsKey); + + // render a higher-resolution texture in a background thread so it eventually becomes all nice nice and pretty + executor.submit(() -> { + var slowTexture = createTexture(points, blockSize, showsStrokeOrder, lineWidth, useFullSize, innerColor, outerColor, false); + + // TextureManager#register doesn't look very thread-safe, so move back to the main thread after the slow part is done + Minecraft.getInstance().execute(() -> registerTexture(points, pointsKey, slowTexture)); + }); + + // quickly create and cache a low-resolution texture so the client has something to look at + var fastTexture = createTexture(points, blockSize, showsStrokeOrder, lineWidth, useFullSize, innerColor, outerColor, true); + return registerTexture(points, pointsKey, fastTexture); } - public static ResourceLocation createTexture(List points, String pointsKey, int blockSize, boolean showsStrokeOrder, int lineWidth, boolean useFullSize, Color innerColor, Color outerColor) + private static DynamicTexture createTexture(List points, int blockSize, boolean showsStrokeOrder, float lineWidth, boolean useFullSize, Color innerColor, Color outerColor, boolean fastRender) { int resolution = resolutionByBlockSize * blockSize; int padding = paddingByBlockSize * blockSize; + if (fastRender) { + resolution /= fastRenderScaleFactor; + padding /= fastRenderScaleFactor; + lineWidth /= (float)fastRenderScaleFactor; + } + double minX = Double.MAX_VALUE, maxX = Double.MIN_VALUE, minY = Double.MAX_VALUE, maxY = Double.MIN_VALUE; for (Vec2 point : points) { @@ -251,6 +275,7 @@ public static ResourceLocation createTexture(List points, String pointsKey BufferedImage img = new BufferedImage(resolution, resolution, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = img.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setColor(outerColor); g2d.setStroke(new BasicStroke((blockSize * 5f / 3f) * lineWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); @@ -275,9 +300,13 @@ public static ResourceLocation createTexture(List points, String pointsKey for (int x = 0; x < img.getWidth(); x++) nativeImage.setPixelRGBA(x, y, img.getRGB(x, y)); - DynamicTexture dynamicTexture = new DynamicTexture(nativeImage); - ResourceLocation resourceLocation = Minecraft.getInstance().getTextureManager().register("hex_pattern_texture_" + points.hashCode() + "_" + repaintIndex + ".png", dynamicTexture); - patternTextures.put(pointsKey, resourceLocation); + return new DynamicTexture(nativeImage); + } + + private static ResourceLocation registerTexture(List points, String pointsKey, DynamicTexture dynamicTexture) { + String name = "hex_pattern_texture_" + points.hashCode() + "_" + repaintIndex + ".png"; + ResourceLocation resourceLocation = Minecraft.getInstance().getTextureManager().register(name, dynamicTexture); + patternTexturesToAdd.put(pointsKey, resourceLocation); return resourceLocation; } @@ -311,6 +340,6 @@ private static void drawHexagon(Graphics2D g2d, int x, int y, int radius) { public static void repaint() { repaintIndex++; - patternTextures.clear(); + patternTexturesToAdd.clear(); } } \ No newline at end of file