diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 7487575c..51db1a46 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -31,10 +31,8 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); - /** 当前帧的 PoseStack,由 GuiShow mixin 注入 */ public static PoseStack context; - /** 由 HUD 渲染 mixin 调用,缓存当前帧的 PoseStack */ public static void update(PoseStack matrices) { context = matrices; } diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 75a27682..79e8192a 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -113,9 +113,10 @@ public class CoreRenderTarget extends TextFrameBuffer { if (texts.isEmpty()) { return; } - if (line >= texts.size() || line >= entries.size()) { + if (line >= texts.size()) { return; } + if (line >= entries.size()) return; PoseStack matrices = AllMusicClient.context; if (matrices == null) return; Font font = Minecraft.getInstance().font; @@ -153,11 +154,12 @@ public class CoreRenderTarget extends TextFrameBuffer { if (maxWidth != -1 && entry.width > maxWidth) { float maxOffset = entry.width - maxWidth; - int texOffset = (int) (maxOffset * state); + float texOffset = maxOffset * state; int revealWidth = (int) (maxWidth * state); + enableScissor(drawX, drawY, maxWidth, entry.height); enableScissor(drawX, drawY, revealWidth, entry.height); - drawString(font, matrices, entry, drawX - texOffset, drawY, finalColor); + drawString(font, matrices, entry, drawX - (int) texOffset, drawY, finalColor); disableScissor(); } else { int revealWidth = (int) (entry.width * state); diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/PicRender.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/PicRender.java index 799ba0ae..1fc20769 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/PicRender.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/PicRender.java @@ -48,8 +48,7 @@ public class PicRender extends PictureFrameBuffer { public void draw(boolean rotate, int size, float x, float y, int ang, HudPosType dir, float alpha) { Point2f point = AllMusicHud.getPos(size, size, x, y, dir); - RenderSystem.setShaderTexture(0, rotate ? rotateTexture.getId() : sourceTexture.getId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); + RenderSystem.bindTexture(rotate ? rotateTexture.getId() : sourceTexture.getId()); RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.depthFunc(GL30.GL_ALWAYS); diff --git a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/TexRender.java b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/TexRender.java index ad798b68..74522ea3 100644 --- a/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/TexRender.java +++ b/client/fabric_1_16_5/src/main/java/com/coloryr/allmusic/client/TexRender.java @@ -18,6 +18,7 @@ import net.minecraft.client.renderer.texture.SimpleTexture; import net.minecraft.client.resources.metadata.texture.TextureMetadataSection; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ResourceManager; +import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL30; import java.io.IOException; @@ -42,8 +43,7 @@ public class TexRender extends TextureRender { @Override public void drawPic(float x, float y, float alpha) { - RenderSystem.setShaderTexture(0, sourceTexture.getId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); + RenderSystem.bindTexture(sourceTexture.getId()); RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.depthFunc(GL30.GL_ALWAYS); @@ -64,7 +64,7 @@ public class TexRender extends TextureRender { float v1 = 1; BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); - bufferBuilder.begin(7, DefaultVertexFormat.POSITION_COLOR_TEX); + bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); bufferBuilder.vertex(matrix, x0, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v1).endVertex(); bufferBuilder.vertex(matrix, x1, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v1).endVertex(); bufferBuilder.vertex(matrix, x1, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v0).endVertex(); @@ -78,8 +78,7 @@ public class TexRender extends TextureRender { @Override public void drawPic(float x, float y, float width, float alpha) { - RenderSystem.setShaderTexture(0, sourceTexture.getId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); + RenderSystem.bindTexture(sourceTexture.getId()); RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.depthFunc(GL30.GL_ALWAYS); @@ -117,8 +116,7 @@ public class TexRender extends TextureRender { public void drawPic(float x, float y, float width, float height, HudPosType dir, float alpha) { Point2f point = AllMusicHud.getPos(width, height, x, y, dir); - RenderSystem.setShaderTexture(0, sourceTexture.getId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); + RenderSystem.bindTexture(sourceTexture.getId()); RenderSystem.depthMask(false); RenderSystem.enableBlend(); RenderSystem.depthFunc(GL30.GL_ALWAYS); diff --git a/client/fabric_1_16_5/src/main/resources/fabric.mod.json b/client/fabric_1_16_5/src/main/resources/fabric.mod.json index 2a2837a3..f7bff6d6 100644 --- a/client/fabric_1_16_5/src/main/resources/fabric.mod.json +++ b/client/fabric_1_16_5/src/main/resources/fabric.mod.json @@ -23,7 +23,7 @@ "allmusic_client.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "fabric": "*", "minecraft": ">=1.16" }, diff --git a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index f53ea051..21bd7243 100644 --- a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -26,10 +26,10 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { public static final ResourceLocation ID = new ResourceLocation("allmusic", "channel"); public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); - public static GuiGraphics graphics; + public static GuiGraphics context; - public static void update(GuiGraphics graphics) { - AllMusicClient.graphics = graphics; + public static void update(GuiGraphics context) { + AllMusicClient.context = context; AllMusicCore.hudUpdate(); } @@ -72,7 +72,7 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index a4f3f31b..450f1f7b 100644 --- a/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,301 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import org.joml.Matrix4f; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private static final RenderBuffers renderBuffers = new RenderBuffers(); + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final RenderTarget target; - private Matrix4f matrix4f; + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } - public CoreRenderTarget() { - target = new TextureTarget(800, 200, false, false); - target.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = (int) (width * window.getGuiScale()); - nowHeight = (int) (height * window.getGuiScale()); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight, false); - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } else if (matrix4f == null) { - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.clear(false); - target.bindWrite(true); - - RenderSystem.backupProjectionMatrix(); - if (matrix4f != null) { - RenderSystem.setProjectionMatrix(matrix4f, VertexSorting.ORTHOGRAPHIC_Z); - } + entries.clear(); } @Override public void unUse() { isDraw = false; - - RenderSystem.restoreProjectionMatrix(); - - RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); - main.bindWrite(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - - if (font.width(component) == 0) { + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - int width = font.drawInBatch(component, 0, y, color, shadow, new Matrix4f(), renderBuffers.bufferSource(), Font.DisplayMode.NORMAL, 0, 15728880); - RenderSystem.disableDepthTest(); - renderBuffers.bufferSource().endBatch(); - RenderSystem.enableDepthTest(); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - RenderSystem.setShaderTexture(0, target.getColorTextureId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); - - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - RenderSystem.depthFunc(GL30.GL_ALWAYS); - - float w = (width / 2); - float h = (height / 2); - - Matrix4f matrix = new Matrix4f().translation(x + w, y + h, 0); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); - bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); - bufferBuilder.vertex(matrix, x0, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v0).endVertex(); - bufferBuilder.vertex(matrix, x0, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v0).endVertex(); - - BufferUploader.drawWithShader(bufferBuilder.end()); - - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - - target.unbindRead(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -306,19 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } - - target.unbindRead(); + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } -} +} \ No newline at end of file diff --git a/client/fabric_1_20_1/src/main/resources/fabric.mod.json b/client/fabric_1_20_1/src/main/resources/fabric.mod.json index c2c860cb..33e4cc0f 100644 --- a/client/fabric_1_20_1/src/main/resources/fabric.mod.json +++ b/client/fabric_1_20_1/src/main/resources/fabric.mod.json @@ -23,10 +23,9 @@ "allmusic_client.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "fabric-api": "*", - "minecraft": ">=1.20.1", - "java": ">=17" + "minecraft": ">=1.20.1" }, "suggests": { } diff --git a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index cbc61ef6..8e87d66d 100644 --- a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -27,7 +27,7 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { public static final ResourceLocation ID = ResourceLocation.fromNamespaceAndPath("allmusic", "channel"); public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); - private static GuiGraphics context; + public static GuiGraphics context; public static void update(GuiGraphics draw) { context = draw; @@ -50,12 +50,6 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { return Minecraft.getInstance().font.lineHeight; } - public void drawText(String item, int x, int y, int color, boolean shadow) { - var hud = Minecraft.getInstance().font; - Component component = MiniMessage.parse(item); - context.drawString(hud, component, x, y, color, shadow); - } - public void sendMessage(String data) { data = "[AllMusic Client]" + data; LOGGER.warn(data); @@ -79,7 +73,7 @@ public class AllMusicClient implements ClientModInitializer, AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index fa6eec91..450f1f7b 100644 --- a/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,213 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import org.joml.Matrix4f; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private static final RenderBuffers renderBuffers = new RenderBuffers(Runtime.getRuntime().availableProcessors()); + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final RenderTarget target; - private Matrix4f matrix4f; + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } - public CoreRenderTarget() { - target = new TextureTarget(800, 200, false, false); - target.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = (int) (width * window.getGuiScale()); - nowHeight = (int) (height * window.getGuiScale()); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight, false); - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } else if (matrix4f == null) { - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.clear(false); - target.bindWrite(true); - - RenderSystem.backupProjectionMatrix(); - if (matrix4f != null) { - RenderSystem.setProjectionMatrix(matrix4f, VertexSorting.ORTHOGRAPHIC_Z); - } + entries.clear(); } @Override public void unUse() { isDraw = false; - - RenderSystem.restoreProjectionMatrix(); - - RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); - main.bindWrite(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - - if (font.width(component) == 0) { + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - int width = font.drawInBatch(component, 0, y, color, shadow, new Matrix4f(), renderBuffers.bufferSource(), Font.DisplayMode.NORMAL, 0, 15728880); - RenderSystem.disableDepthTest(); - renderBuffers.bufferSource().endBatch(); - RenderSystem.enableDepthTest(); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - RenderSystem.setShaderTexture(0, target.getColorTextureId()); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); - - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - RenderSystem.depthFunc(GL30.GL_ALWAYS); - - float w = (width / 2); - float h = (height / 2); - - Matrix4f matrix = new Matrix4f().translation(x + w, y + h, 0); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - BufferBuilder bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - bufferBuilder.addVertex(matrix, x0, y1, z).setUv(u0, v1).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x1, y1, z).setUv(u1, v1).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x1, y0, z).setUv(u1, v0).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x0, y0, z).setUv(u0, v0).setColor(1.0f, 1.0f, 1.0f, alpha); - - BufferUploader.drawWithShader(bufferBuilder.buildOrThrow()); - - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - - target.unbindRead(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -218,19 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } - - target.unbindRead(); + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } -} +} \ No newline at end of file diff --git a/client/fabric_1_21/src/main/resources/fabric.mod.json b/client/fabric_1_21/src/main/resources/fabric.mod.json index 13ca553c..ba8bcbd4 100644 --- a/client/fabric_1_21/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21/src/main/resources/fabric.mod.json @@ -26,9 +26,8 @@ "allmusic_client.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 700736fd..450f1f7b 100644 --- a/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,218 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.gui.render.state.GlyphRenderState; -import net.minecraft.client.gui.render.state.GuiElementRenderState; -import net.minecraft.client.gui.render.state.GuiRenderState; -import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; -import net.minecraft.client.renderer.MappableRingBuffer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.network.chat.Component; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.jspecify.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.drawString(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -223,280 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst( - Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom) - .thenComparing(ScreenRectangle::left) - .thenComparing(ScreenRectangle::right) - ); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true); - private final CachedOrthoProjectionMatrixBuffer itemsProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("items", -1000.0F, 1000.0F, true); - private @Nullable ScreenRectangle previousScissorArea = null; - private @Nullable RenderPipeline previousPipeline = null; - private @Nullable TextureSetup previousTextureSetup = null; - private @Nullable BufferBuilder bufferBuilder = null; - - public GuiRenderer(GuiRenderState state) { - this.renderState = state; - } - - public void render(GpuBufferSlice bufferSlice, CoreRenderTarget target) { - this.renderState.forEachText(state -> { - var matrix3x2fc = state.pose; - var screenrectangle = state.scissor; - state.ensurePrepared().visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(TextRenderable.Styled styled) { - this.accept(styled); - } - - @Override - public void acceptEffect(TextRenderable textRenderable) { - this.accept(textRenderable); - } - - private void accept(TextRenderable textRenderable) { - renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2fc, textRenderable, screenrectangle)); - } - }); - }); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.bufferBuilder = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - var map = new Object2IntOpenHashMap(); - - for (var draw : this.meshesToDraw) { - var state = draw.mesh.drawState(); - var vertexformat = state.format(); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize()); - } - - for (var entry : map.object2IntEntrySet()) { - var vertexformat = entry.getKey(); - int i = entry.getIntValue(); - var mappableringbuffer = this.vertexBuffers.get(vertexformat); - if (mappableringbuffer == null || mappableringbuffer.size() < i) { - if (mappableringbuffer != null) { - mappableringbuffer.close(); - } - - this.vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "GUI vertex buffer for " + vertexformat, 34, i)); - } - } - - var commandencoder = RenderSystem.getDevice().createCommandEncoder(); - map = new Object2IntOpenHashMap<>(); - - for (MeshToDraw draw : this.meshesToDraw) { - var meshdata = draw.mesh; - var state = meshdata.drawState(); - var vertexformat = state.format(); - var mappableringbuffer = this.vertexBuffers.get(vertexformat); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - ByteBuffer bytebuffer = meshdata.vertexBuffer(); - int i = bytebuffer.remaining(); - int j = map.getInt(vertexformat); - - try (GpuBuffer.MappedView view = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) { - MemoryUtil.memCopy(bytebuffer, view.data()); - } - - map.put(vertexformat, j + i); - this.draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(), - state.indexCount(), draw.pipeline, draw.textureSetup, draw.scissorArea)); - draw.close(); - } - - this.draw(bufferSlice, target); - - for (var mappableringbuffer : this.vertexBuffers.values()) { - mappableringbuffer.rotate(); - } - - this.draws.clear(); - this.meshesToDraw.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - } - - private void draw(GpuBufferSlice bufferSlice, CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var window = minecraft.getWindow(); - RenderSystem.setProjectionMatrix( - this.guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(), (float) target.target.height / window.getGuiScale()), - ProjectionType.ORTHOGRAPHIC - ); - var rendertarget = target.target; - int i = 0; - - for (var item : this.draws) { - if (item.indexCount > i) { - i = item.indexCount; - } - } - - var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var gpubuffer = buffer.getBuffer(i); - var indexType = buffer.type(); - var gpubufferslice = RenderSystem.getDynamicUniforms() - .writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f()); - if (!this.meshesToDraw.isEmpty()) { - this.executeDrawRange(() -> "GUI before blur", rendertarget, bufferSlice, gpubufferslice, gpubuffer, - indexType, Math.min(this.meshesToDraw.size(), this.draws.size())); - } - } - } - - private void executeDrawRange(Supplier name, RenderTarget target, GpuBufferSlice bufferSlice, GpuBufferSlice bufferSlice1, - GpuBuffer buffer, VertexFormat.IndexType indexType, int count) { - try (var renderpass = RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(), - target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderpass); - renderpass.setUniform("Fog", bufferSlice); - renderpass.setUniform("DynamicTransforms", bufferSlice1); - - for (int i = 0; i < count; i++) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderpass, buffer, indexType); - } - } - } - - private void addElementToMesh(GuiElementRenderState state) { - var pipeline = state.pipeline(); - var texturesetup = state.textureSetup(); - var screenrectangle = state.scissorArea(); - if (pipeline != this.previousPipeline - || this.scissorChanged(screenrectangle, this.previousScissorArea) - || !texturesetup.equals(this.previousTextureSetup)) { - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - this.bufferBuilder = new BufferBuilder(this.byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - this.previousPipeline = pipeline; - this.previousTextureSetup = texturesetup; - this.previousScissorArea = screenrectangle; - } - - state.buildVertices(this.bufferBuilder); - } - - private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle rectangle) { - var meshdata = builder.build(); - if (meshdata != null) { - this.meshesToDraw.add(new MeshToDraw(meshdata, pipeline, textureSetup, rectangle)); - } - } - - private void executeDraw(Draw draw, RenderPass pass, GpuBuffer buffer, VertexFormat.IndexType index) { - var renderpipeline = draw.pipeline(); - pass.setPipeline(renderpipeline); - pass.setVertexBuffer(0, draw.vertexBuffer); - var screenrectangle = draw.scissorArea(); - if (screenrectangle != null) { - Window window = Minecraft.getInstance().getWindow(); - int i = window.getHeight(); - int j = window.getGuiScale(); - double d0 = screenrectangle.left() * j; - double d1 = i - screenrectangle.bottom() * j; - double d2 = screenrectangle.width() * j; - double d3 = screenrectangle.height() * j; - pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3)); - } else { - pass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - pass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - pass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - pass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - pass.setIndexBuffer(buffer, index); - pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(@Nullable ScreenRectangle rectangle, @Nullable ScreenRectangle rectangle1) { - if (rectangle == rectangle1) { - return false; - } else { - return rectangle != null ? !rectangle.equals(rectangle1) : true; - } - } - - @Override - public void close() { - this.byteBufferBuilder.close(); - this.guiProjectionMatrixBuffer.close(); - this.itemsProjectionMatrixBuffer.close(); - - for (MappableRingBuffer mappableringbuffer : this.vertexBuffers.values()) { - mappableringbuffer.close(); - } - } - - record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - } - - record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) implements AutoCloseable { - @Override - public void close() { - this.mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/fabric_1_21_11/src/main/resources/fabric.mod.json b/client/fabric_1_21_11/src/main/resources/fabric.mod.json index 50201477..1766ba56 100644 --- a/client/fabric_1_21_11/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21_11/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.18.4", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21.11", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 02b4db80..450f1f7b 100644 --- a/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -170,7 +170,9 @@ public class CoreRenderTarget extends TextFrameBuffer { } private static int applyAlpha(int color, float alpha) { - int a = Math.clamp((int) (alpha * 255), 0, 255); + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; return (color & 0x00FFFFFF) | (a << 24); } } \ No newline at end of file diff --git a/client/fabric_1_21_3/src/main/resources/fabric.mod.json b/client/fabric_1_21_3/src/main/resources/fabric.mod.json index e773d87b..693c18e3 100644 --- a/client/fabric_1_21_3/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21_3/src/main/resources/fabric.mod.json @@ -28,7 +28,6 @@ "depends": { "fabricloader": ">=0.19.3", "minecraft": ">=1.21.3", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 43cbccde..450f1f7b 100644 --- a/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -63,7 +63,6 @@ public class CoreRenderTarget extends TextFrameBuffer { Component component = MiniMessage.parse(text); Font font = Minecraft.getInstance().font; int width = font.width(component); - if (width == 0) { return; } @@ -171,7 +170,9 @@ public class CoreRenderTarget extends TextFrameBuffer { } private static int applyAlpha(int color, float alpha) { - int a = Math.clamp((int) (alpha * 255), 0, 255); + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; return (color & 0x00FFFFFF) | (a << 24); } } \ No newline at end of file diff --git a/client/fabric_1_21_4/src/main/resources/fabric.mod.json b/client/fabric_1_21_4/src/main/resources/fabric.mod.json index cc59c6bb..0ff73eeb 100644 --- a/client/fabric_1_21_4/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21_4/src/main/resources/fabric.mod.json @@ -28,7 +28,6 @@ "depends": { "fabricloader": ">=0.19.3", "minecraft": ">=1.21.4", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index df88968d..450f1f7b 100644 --- a/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,212 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.font.glyphs.BakedGlyph; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.gui.render.state.GlyphEffectRenderState; -import net.minecraft.client.gui.render.state.GlyphRenderState; -import net.minecraft.client.gui.render.state.GuiElementRenderState; -import net.minecraft.client.gui.render.state.GuiRenderState; -import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; -import net.minecraft.client.renderer.MappableRingBuffer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.network.chat.Component; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState); - color = color | 0xFF000000; - graphics.drawString(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -217,274 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst( - Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom) - .thenComparing(ScreenRectangle::left) - .thenComparing(ScreenRectangle::right) - ); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true); - private ScreenRectangle previousScissorArea; - private RenderPipeline previousPipeline; - private TextureSetup previousTextureSetup; - private BufferBuilder bufferBuilder; - - public GuiRenderer(GuiRenderState state) { - renderState = state; - } - - public void render(GpuBufferSlice gpu, CoreRenderTarget target) { - renderState.forEachText(state -> { - var matrix3x2f = state.pose; - var screenrectangle = state.scissor; - state.ensurePrepared().visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(BakedGlyph.GlyphInstance glyph) { - if (glyph.glyph().textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2f, glyph, screenrectangle)); - } - } - - @Override - public void acceptEffect(BakedGlyph glyph, BakedGlyph.Effect effect) { - if (glyph.textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphEffectRenderState(matrix3x2f, glyph, effect, screenrectangle)); - } - } - }); - }); - - renderState.sortElements(ELEMENT_SORT_COMPARATOR); - - previousScissorArea = null; - previousPipeline = null; - previousTextureSetup = null; - bufferBuilder = null; - renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - recordDraws(); - - if (!draws.isEmpty()) { - Minecraft minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - RenderSystem.setProjectionMatrix( - guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(), - (float) target.target.height / window.getGuiScale()), - ProjectionType.ORTHOGRAPHIC - ); - var rendertarget = target.target; - int i = 0; - - for (Draw guirenderer$draw : draws) { - if (guirenderer$draw.indexCount > i) { - i = guirenderer$draw.indexCount; - } - } - - var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var gpubuffer = buffer.getBuffer(i); - var index = buffer.type(); - var gpubufferslice = RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), - new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f(), 0.0F); - if (!meshesToDraw.isEmpty()) { - executeDrawRange(() -> "before blur", rendertarget, gpu, gpubufferslice, gpubuffer, - index, Math.min(meshesToDraw.size(), draws.size()), target); - } - } - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.rotate(); - } - - draws.clear(); - meshesToDraw.clear(); - renderState.reset(); - } - - private void executeDrawRange(Supplier name, RenderTarget target, GpuBufferSlice buffer1, GpuBufferSlice buffer2, - GpuBuffer buffer3, VertexFormat.IndexType index, int count, CoreRenderTarget target1) { - try (RenderPass renderpass = RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(), - target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderpass); - renderpass.setUniform("Fog", buffer1); - renderpass.setUniform("DynamicTransforms", buffer2); - - for (int i = 0; i < count; i++) { - var item = draws.get(i); - executeDraw(item, renderpass, target1, buffer3, index); - } - } - } - - private void addElementToMesh(GuiElementRenderState state, int range) { - var pipeline = state.pipeline(); - var setup = state.textureSetup(); - var screen = state.scissorArea(); - if (pipeline != previousPipeline || scissorChanged(screen, previousScissorArea) || !setup.equals(previousTextureSetup)) { - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - bufferBuilder = new BufferBuilder(byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - previousPipeline = pipeline; - previousTextureSetup = setup; - previousScissorArea = screen; - } - - state.buildVertices(bufferBuilder, 0.0F + range * 0.01F); - } - - private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup texture, ScreenRectangle rectangle) { - var meshdata = builder.buildOrThrow(); - meshesToDraw.add(new MeshToDraw(meshdata, pipeline, texture, rectangle)); - } - - private void recordDraws() { - var map = new Object2IntOpenHashMap(); - - for (MeshToDraw item : meshesToDraw) { - var state = item.mesh.drawState(); - var vertexformat = state.format(); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize()); - } - - for (var entry : map.object2IntEntrySet()) { - var vertexformat = entry.getKey(); - int i = entry.getIntValue(); - var buffer = vertexBuffers.get(vertexformat); - if (buffer == null || buffer.size() < i) { - if (buffer != null) { - buffer.close(); - } - - vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "vertex buffer for " + vertexformat, 34, i)); - } - } - - var commandencoder = RenderSystem.getDevice().createCommandEncoder(); - map = new Object2IntOpenHashMap<>(); - - for (MeshToDraw item : meshesToDraw) { - var meshdata = item.mesh; - var state = meshdata.drawState(); - var vertexformat = state.format(); - var mappableringbuffer = vertexBuffers.get(vertexformat); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - ByteBuffer bytebuffer = meshdata.vertexBuffer(); - int i = bytebuffer.remaining(); - int j = map.getInt(vertexformat); - - try (var item1 = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) { - MemoryUtil.memCopy(bytebuffer, item1.data()); - } - - map.put(vertexformat, j + i); - draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(), - state.indexCount(), item.pipeline, item.textureSetup, item.scissorArea)); - item.close(); - } - } - - private void executeDraw(Draw draw, RenderPass pass, CoreRenderTarget target, GpuBuffer buffer, VertexFormat.IndexType index) { - var renderpipeline = draw.pipeline(); - pass.setPipeline(renderpipeline); - pass.setVertexBuffer(0, draw.vertexBuffer); - var area = draw.scissorArea(); - if (area != null) { - Window window = Minecraft.getInstance().getWindow(); - - int i = target.target.height; - int j = window.getGuiScale(); - double d0 = area.left() * j; - double d1 = i - area.bottom() * j; - double d2 = area.width() * j; - double d3 = area.height() * j; - pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3)); - } else { - pass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - pass.bindSampler("Sampler0", draw.textureSetup.texure0()); - } - - if (draw.textureSetup.texure1() != null) { - pass.bindSampler("Sampler1", draw.textureSetup.texure1()); - } - - if (draw.textureSetup.texure2() != null) { - pass.bindSampler("Sampler2", draw.textureSetup.texure2()); - } - - pass.setIndexBuffer(buffer, index); - pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(ScreenRectangle screen1, ScreenRectangle screen2) { - if (screen1 == screen2) { - return false; - } else { - return screen1 == null || !screen1.equals(screen2); - } - } - - @Override - public void close() { - byteBufferBuilder.close(); - - guiProjectionMatrixBuffer.close(); - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.close(); - } - } - - record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, ScreenRectangle scissorArea) { - } - - record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - ScreenRectangle scissorArea) implements AutoCloseable { - @Override - public void close() { - mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/fabric_1_21_6/src/main/resources/fabric.mod.json b/client/fabric_1_21_6/src/main/resources/fabric.mod.json index a6a09df2..c4651986 100644 --- a/client/fabric_1_21_6/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21_6/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.16.14", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21.6", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index df88968d..450f1f7b 100644 --- a/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_1_21_8/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,212 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.font.glyphs.BakedGlyph; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.gui.render.state.GlyphEffectRenderState; -import net.minecraft.client.gui.render.state.GlyphRenderState; -import net.minecraft.client.gui.render.state.GuiElementRenderState; -import net.minecraft.client.gui.render.state.GuiRenderState; -import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; -import net.minecraft.client.renderer.MappableRingBuffer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.network.chat.Component; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState); - color = color | 0xFF000000; - graphics.drawString(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.submitGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -217,274 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst( - Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom) - .thenComparing(ScreenRectangle::left) - .thenComparing(ScreenRectangle::right) - ); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true); - private ScreenRectangle previousScissorArea; - private RenderPipeline previousPipeline; - private TextureSetup previousTextureSetup; - private BufferBuilder bufferBuilder; - - public GuiRenderer(GuiRenderState state) { - renderState = state; - } - - public void render(GpuBufferSlice gpu, CoreRenderTarget target) { - renderState.forEachText(state -> { - var matrix3x2f = state.pose; - var screenrectangle = state.scissor; - state.ensurePrepared().visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(BakedGlyph.GlyphInstance glyph) { - if (glyph.glyph().textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2f, glyph, screenrectangle)); - } - } - - @Override - public void acceptEffect(BakedGlyph glyph, BakedGlyph.Effect effect) { - if (glyph.textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphEffectRenderState(matrix3x2f, glyph, effect, screenrectangle)); - } - } - }); - }); - - renderState.sortElements(ELEMENT_SORT_COMPARATOR); - - previousScissorArea = null; - previousPipeline = null; - previousTextureSetup = null; - bufferBuilder = null; - renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - recordDraws(); - - if (!draws.isEmpty()) { - Minecraft minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - RenderSystem.setProjectionMatrix( - guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(), - (float) target.target.height / window.getGuiScale()), - ProjectionType.ORTHOGRAPHIC - ); - var rendertarget = target.target; - int i = 0; - - for (Draw guirenderer$draw : draws) { - if (guirenderer$draw.indexCount > i) { - i = guirenderer$draw.indexCount; - } - } - - var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var gpubuffer = buffer.getBuffer(i); - var index = buffer.type(); - var gpubufferslice = RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), - new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f(), 0.0F); - if (!meshesToDraw.isEmpty()) { - executeDrawRange(() -> "before blur", rendertarget, gpu, gpubufferslice, gpubuffer, - index, Math.min(meshesToDraw.size(), draws.size()), target); - } - } - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.rotate(); - } - - draws.clear(); - meshesToDraw.clear(); - renderState.reset(); - } - - private void executeDrawRange(Supplier name, RenderTarget target, GpuBufferSlice buffer1, GpuBufferSlice buffer2, - GpuBuffer buffer3, VertexFormat.IndexType index, int count, CoreRenderTarget target1) { - try (RenderPass renderpass = RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(), - target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderpass); - renderpass.setUniform("Fog", buffer1); - renderpass.setUniform("DynamicTransforms", buffer2); - - for (int i = 0; i < count; i++) { - var item = draws.get(i); - executeDraw(item, renderpass, target1, buffer3, index); - } - } - } - - private void addElementToMesh(GuiElementRenderState state, int range) { - var pipeline = state.pipeline(); - var setup = state.textureSetup(); - var screen = state.scissorArea(); - if (pipeline != previousPipeline || scissorChanged(screen, previousScissorArea) || !setup.equals(previousTextureSetup)) { - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - bufferBuilder = new BufferBuilder(byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - previousPipeline = pipeline; - previousTextureSetup = setup; - previousScissorArea = screen; - } - - state.buildVertices(bufferBuilder, 0.0F + range * 0.01F); - } - - private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup texture, ScreenRectangle rectangle) { - var meshdata = builder.buildOrThrow(); - meshesToDraw.add(new MeshToDraw(meshdata, pipeline, texture, rectangle)); - } - - private void recordDraws() { - var map = new Object2IntOpenHashMap(); - - for (MeshToDraw item : meshesToDraw) { - var state = item.mesh.drawState(); - var vertexformat = state.format(); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize()); - } - - for (var entry : map.object2IntEntrySet()) { - var vertexformat = entry.getKey(); - int i = entry.getIntValue(); - var buffer = vertexBuffers.get(vertexformat); - if (buffer == null || buffer.size() < i) { - if (buffer != null) { - buffer.close(); - } - - vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "vertex buffer for " + vertexformat, 34, i)); - } - } - - var commandencoder = RenderSystem.getDevice().createCommandEncoder(); - map = new Object2IntOpenHashMap<>(); - - for (MeshToDraw item : meshesToDraw) { - var meshdata = item.mesh; - var state = meshdata.drawState(); - var vertexformat = state.format(); - var mappableringbuffer = vertexBuffers.get(vertexformat); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - ByteBuffer bytebuffer = meshdata.vertexBuffer(); - int i = bytebuffer.remaining(); - int j = map.getInt(vertexformat); - - try (var item1 = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) { - MemoryUtil.memCopy(bytebuffer, item1.data()); - } - - map.put(vertexformat, j + i); - draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(), - state.indexCount(), item.pipeline, item.textureSetup, item.scissorArea)); - item.close(); - } - } - - private void executeDraw(Draw draw, RenderPass pass, CoreRenderTarget target, GpuBuffer buffer, VertexFormat.IndexType index) { - var renderpipeline = draw.pipeline(); - pass.setPipeline(renderpipeline); - pass.setVertexBuffer(0, draw.vertexBuffer); - var area = draw.scissorArea(); - if (area != null) { - Window window = Minecraft.getInstance().getWindow(); - - int i = target.target.height; - int j = window.getGuiScale(); - double d0 = area.left() * j; - double d1 = i - area.bottom() * j; - double d2 = area.width() * j; - double d3 = area.height() * j; - pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3)); - } else { - pass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - pass.bindSampler("Sampler0", draw.textureSetup.texure0()); - } - - if (draw.textureSetup.texure1() != null) { - pass.bindSampler("Sampler1", draw.textureSetup.texure1()); - } - - if (draw.textureSetup.texure2() != null) { - pass.bindSampler("Sampler2", draw.textureSetup.texure2()); - } - - pass.setIndexBuffer(buffer, index); - pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(ScreenRectangle screen1, ScreenRectangle screen2) { - if (screen1 == screen2) { - return false; - } else { - return screen1 == null || !screen1.equals(screen2); - } - } - - @Override - public void close() { - byteBufferBuilder.close(); - - guiProjectionMatrixBuffer.close(); - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.close(); - } - } - - record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, ScreenRectangle scissorArea) { - } - - record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - ScreenRectangle scissorArea) implements AutoCloseable { - @Override - public void close() { - mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/fabric_1_21_8/src/main/resources/fabric.mod.json b/client/fabric_1_21_8/src/main/resources/fabric.mod.json index a6a09df2..c4651986 100644 --- a/client/fabric_1_21_8/src/main/resources/fabric.mod.json +++ b/client/fabric_1_21_8/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.16.14", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21.6", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index b76b81e1..5f3122e6 100644 --- a/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -3,221 +3,160 @@ package com.coloryr.allmusic.client; import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.client.others.ModernUI; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.fog.FogRenderer; -import net.minecraft.client.renderer.state.WindowRenderState; -import net.minecraft.client.renderer.state.gui.GlyphRenderState; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import net.minecraft.client.renderer.state.gui.GuiRenderState; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.util.profiling.Profiler; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.jspecify.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphicsExtractor graphics = new GuiGraphicsExtractor(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.text(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -225,285 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right)); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)).thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final Projection guiProjection = new Projection(); - private final ProjectionMatrixBuffer guiProjectionMatrixBuffer = new ProjectionMatrixBuffer("gui"); - private final CubeMap cubeMap = new CubeMap(Identifier.withDefaultNamespace("textures/gui/title/background/panorama")); - private ScreenRectangle previousScissorArea = null; - private RenderPipeline previousPipeline = null; - private TextureSetup previousTextureSetup = null; - private BufferBuilder bufferBuilder = null; - - public GuiRenderer(GuiRenderState renderState) { - this.renderState = renderState; - } - - public void render(GpuBufferSlice fogBuffer, CoreRenderTarget target) { - var profiler = Profiler.get(); - if (this.renderState.panoramaRenderState != null) { - this.cubeMap.render(10.0F, this.renderState.panoramaRenderState.spin()); - } - - profiler.push("allmusic text prepare"); - this.renderState.forEachText((text) -> { - var pose = text.pose; - var scissor = text.scissor; - Font.PreparedText preparedText = text.ensurePrepared(); - if (AllMusicClient.modui) { - ModernUI.text(preparedText, renderState, pose, scissor); - } else { - preparedText.visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(TextRenderable.Styled glyph) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, glyph, scissor)); - } - - @Override - public void acceptEffect(TextRenderable glyph) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, glyph, scissor)); - } - }); - } - }); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.bufferBuilder = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - this.ensureVertexBufferSizes(); - var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); - var offsets = new Object2IntOpenHashMap(); - - for (var meshToDraw : this.meshesToDraw) { - var mesh = meshToDraw.mesh; - var drawState = mesh.drawState(); - var format = drawState.format(); - var vertexBuffer = this.vertexBuffers.get(format); - if (!offsets.containsKey(format)) { - offsets.put(format, 0); - } - - var meshVertexBuffer = mesh.vertexBuffer(); - int meshBufferSize = meshVertexBuffer.remaining(); - int offset = offsets.getInt(format); - - try (var mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(offset, meshBufferSize), false, true)) { - MemoryUtil.memCopy(meshVertexBuffer, mappedView.data()); - } - - offsets.put(format, offset + meshBufferSize); - this.draws.add(new Draw(vertexBuffer.currentBuffer(), offset / format.getVertexSize(), - drawState.mode(), drawState.indexCount(), meshToDraw.pipeline, meshToDraw.textureSetup, meshToDraw.scissorArea)); - meshToDraw.close(); - } - profiler.popPush("allmusic text draw"); - this.draw(fogBuffer, target); - profiler.popPush("allmusic text vertexBufferRotate"); - - for (MappableRingBuffer buffer : this.vertexBuffers.values()) { - buffer.rotate(); - } - - profiler.pop(); - this.draws.clear(); - this.meshesToDraw.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - } - - private void draw(GpuBufferSlice fogBuffer, CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var windowState = minecraft.gameRenderer.getGameRenderState().windowRenderState; - this.guiProjection.setupOrtho(1000.0F, 11000.0F, (float) target.target.width / (float) windowState.guiScale, - (float) target.target.height / (float) windowState.guiScale, true); - RenderSystem.setProjectionMatrix(this.guiProjectionMatrixBuffer.getBuffer(this.guiProjection), ProjectionType.ORTHOGRAPHIC); - var mainRenderTarget = target.target; - int maxIndexCount = 0; - - for (var draw : this.draws) { - if (draw.indexCount > maxIndexCount) { - maxIndexCount = draw.indexCount; - } - } - - var autoIndices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var indexBuffer = autoIndices.getBuffer(maxIndexCount); - var indexType = autoIndices.type(); - var dynamicTransforms = RenderSystem.getDynamicUniforms().writeTransform((new Matrix4f()).setTranslation(0.0F, 0.0F, -11000.0F), - new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f()); - this.executeDrawRange(() -> "GUI before blur", mainRenderTarget, fogBuffer, dynamicTransforms, - indexBuffer, indexType, Math.min(this.meshesToDraw.size(), this.draws.size())); - } - } - - private void executeDrawRange(Supplier label, RenderTarget mainRenderTarget, GpuBufferSlice fogBuffer, - GpuBufferSlice dynamicTransforms, GpuBuffer indexBuffer, VertexFormat.IndexType indexType, int endIndex) { - try (var renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(label, mainRenderTarget.getColorTextureView(), - OptionalInt.empty(), mainRenderTarget.useDepth ? mainRenderTarget.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderPass); - renderPass.setUniform("Fog", fogBuffer); - renderPass.setUniform("DynamicTransforms", dynamicTransforms); - - for (int i = 0; i < endIndex; ++i) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderPass, indexBuffer, indexType); - } - } - } - - private void addElementToMesh(GuiElementRenderState elementState) { - var pipeline = elementState.pipeline(); - var textureSetup = elementState.textureSetup(); - var scissorArea = elementState.scissorArea(); - if (pipeline != this.previousPipeline || this.scissorChanged(scissorArea, this.previousScissorArea) || !textureSetup.equals(this.previousTextureSetup)) { - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - this.bufferBuilder = new BufferBuilder(this.byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - this.previousPipeline = pipeline; - this.previousTextureSetup = textureSetup; - this.previousScissorArea = scissorArea; - } - - elementState.buildVertices(this.bufferBuilder); - } - - private void recordMesh(BufferBuilder bufferBuilder, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - var mesh = bufferBuilder.build(); - if (mesh != null) { - this.meshesToDraw.add(new MeshToDraw(mesh, pipeline, textureSetup, scissorArea)); - } - - } - - private void ensureVertexBufferSizes() { - var requiredSizes = new Object2IntOpenHashMap(); - - for (var meshToDraw : this.meshesToDraw) { - var drawState = meshToDraw.mesh.drawState(); - var format = drawState.format(); - if (!requiredSizes.containsKey(format)) { - requiredSizes.put(format, 0); - } - - requiredSizes.put(format, requiredSizes.getInt(format) + drawState.vertexCount() * format.getVertexSize()); - } - - for (var entry : requiredSizes.object2IntEntrySet()) { - var vertexFormat = entry.getKey(); - int requiredSize = entry.getIntValue(); - var vertexBuffer = this.vertexBuffers.get(vertexFormat); - if (vertexBuffer == null || vertexBuffer.size() < requiredSize) { - if (vertexBuffer != null) { - vertexBuffer.close(); - } - - this.vertexBuffers.put(vertexFormat, new MappableRingBuffer(() -> "GUI vertex buffer for " + vertexFormat, 34, requiredSize)); - } - } - } - - private void executeDraw(Draw draw, RenderPass renderPass, GpuBuffer indexBuffer, VertexFormat.IndexType indexType) { - var pipeline = draw.pipeline(); - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, draw.vertexBuffer); - var scissorArea = draw.scissorArea(); - if (scissorArea != null) { - this.enableScissor(scissorArea, renderPass); - } else { - renderPass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - renderPass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - renderPass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - renderPass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - renderPass.setIndexBuffer(indexBuffer, indexType); - renderPass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(@Nullable ScreenRectangle newScissor, @Nullable ScreenRectangle oldScissor) { - if (newScissor == oldScissor) { - return false; - } else if (newScissor != null) { - return !newScissor.equals(oldScissor); - } else { - return true; - } - } - - private void enableScissor(ScreenRectangle rectangle, RenderPass renderPass) { - WindowRenderState windowState = Minecraft.getInstance().gameRenderer.getGameRenderState().windowRenderState; - int windowHeight = windowState.height; - int guiScale = windowState.guiScale; - double left = rectangle.left() * guiScale; - double bottom = windowHeight - rectangle.bottom() * guiScale; - double width = rectangle.width() * guiScale; - double height = rectangle.height() * guiScale; - renderPass.enableScissor((int) left, (int) bottom, Math.max(0, (int) width), Math.max(0, (int) height)); - } - - public void close() { - this.byteBufferBuilder.close(); - this.guiProjectionMatrixBuffer.close(); - - for (MappableRingBuffer buffer : this.vertexBuffers.values()) { - buffer.close(); - } - - this.cubeMap.close(); - } - - private record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - } - - private record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) implements AutoCloseable { - public void close() { - this.mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/fabric_26_1/src/main/resources/fabric.mod.json b/client/fabric_26_1/src/main/resources/fabric.mod.json index ce840880..e2d6e371 100644 --- a/client/fabric_26_1/src/main/resources/fabric.mod.json +++ b/client/fabric_26_1/src/main/resources/fabric.mod.json @@ -32,9 +32,8 @@ } ], "depends": { - "fabricloader": ">=0.18.3", + "fabricloader": ">=0.19.3", "minecraft": ">=26.1", - "java": ">=25", "fabric-api": "*" }, "suggests": { diff --git a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index dec9853e..5f3122e6 100644 --- a/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/fabric_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,207 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.GpuFormat; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.state.gui.*; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.util.profiling.Profiler; -import org.joml.*; -import org.jspecify.annotations.Nullable; -import java.lang.Math; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false, GpuFormat.RGBA8_UNORM); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), new Vector4f(0)); + entries.clear(); } @Override public void unUse() { - renderer.render(this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphicsExtractor graphics = new GuiGraphicsExtractor(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.text(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.guiRenderState.addGuiElement(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.scissorStack.peek())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -212,200 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right)); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR; - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final StagedVertexBuffer vertexBuffer = new StagedVertexBuffer(() -> "GUI Vertex Buffer", 786432); - private final Projection guiProjection = new Projection(); - private final ProjectionMatrixBuffer guiProjectionMatrixBuffer = new ProjectionMatrixBuffer("gui"); - private final CubeMap cubeMap = new CubeMap(Identifier.withDefaultNamespace("textures/gui/title/background/panorama")); - private @Nullable ScreenRectangle previousScissorArea = null; - private @Nullable RenderPipeline previousPipeline = null; - private @Nullable TextureSetup previousTextureSetup = null; - private StagedVertexBuffer.@Nullable Draw previousDraw; - - public GuiRenderer(final GuiRenderState renderState) { - this.renderState = renderState; - } - - public void render(CoreRenderTarget target) { - var profiler = Profiler.get(); - if (this.renderState.panoramaRenderState != null) { - this.cubeMap.render(10.0F, this.renderState.panoramaRenderState.spin()); - } - - profiler.push("prepare"); - this.prepare(); - profiler.popPush("upload"); - this.vertexBuffer.upload(); - profiler.popPush("draw"); - this.draw(target); - profiler.popPush("endFrame"); - this.vertexBuffer.endDraw(); - this.vertexBuffer.endFrame(); - this.draws.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - - profiler.pop(); - } - - private void prepare() { - this.prepareText(); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - this.addElementsToMeshes(); - } - - private void addElementsToMeshes() { - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.previousDraw = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - } - - private void draw(CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var windowState = minecraft.gameRenderer.gameRenderState().windowRenderState; - this.guiProjection.setupOrtho(1000.0F, 11000.0F, (float) target.target.width / (float) windowState.guiScale, - (float) target.target.height / (float) windowState.guiScale, true); - RenderSystem.setProjectionMatrix(this.guiProjectionMatrixBuffer.getBuffer(this.guiProjection), ProjectionType.ORTHOGRAPHIC); - var mainRenderTarget = target.target; - var dynamicTransforms = RenderSystem.getDynamicUniforms().writeTransform((new Matrix4f()).setTranslation(0.0F, 0.0F, -11000.0F)); - if (!this.draws.isEmpty()) { - this.executeDrawRange(() -> "GUI before blur", mainRenderTarget, dynamicTransforms, this.draws.size()); - } - } - } - - private void executeDrawRange(Supplier label, RenderTarget mainRenderTarget, GpuBufferSlice dynamicTransforms, int endIndex) { - try (var renderPass = RenderSystem.getDevice().createCommandEncoder() - .createRenderPass(label, mainRenderTarget.getColorTextureView(), - Optional.empty(), mainRenderTarget.useDepth ? mainRenderTarget.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderPass); - renderPass.setUniform("DynamicTransforms", dynamicTransforms); - - for (int i = 0; i < endIndex; ++i) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderPass); - } - } - } - - private void addElementToMesh(final GuiElementRenderState elementState) { - var pipeline = elementState.pipeline(); - var textureSetup = elementState.textureSetup(); - var scissorArea = elementState.scissorArea(); - if (this.previousDraw == null || pipeline != this.previousPipeline || this.scissorChanged(scissorArea, this.previousScissorArea) || !textureSetup.equals(this.previousTextureSetup)) { - this.previousPipeline = pipeline; - this.previousTextureSetup = textureSetup; - this.previousScissorArea = scissorArea; - this.previousDraw = this.vertexBuffer.appendDraw(pipeline.getVertexFormatBinding(0), pipeline.getPrimitiveTopology()); - this.draws.add(new Draw(this.previousDraw, pipeline, textureSetup, scissorArea)); - } - - elementState.buildVertices(this.vertexBuffer.getVertexBuilder(Objects.requireNonNull(this.previousDraw))); - } - - private void prepareText() { - this.renderState.forEachText((text) -> { - var pose = text.pose; - var scissor = text.scissor; - text.ensurePrepared().visit(new Font.GlyphVisitor() { - public void acceptRenderable(final TextRenderable renderable) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, renderable, scissor)); - } - }); - }); - } - - private void executeDraw(final Draw draw, final RenderPass renderPass) { - var executeInfo = this.vertexBuffer.getExecuteInfo(draw.draw); - if (executeInfo != null) { - var pipeline = draw.pipeline(); - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, executeInfo.vertexBuffer().slice()); - var scissorArea = draw.scissorArea(); - if (scissorArea != null) { - this.enableScissor(scissorArea, renderPass); - } else { - renderPass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - renderPass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - renderPass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - renderPass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - renderPass.setIndexBuffer(executeInfo.indexBuffer(), executeInfo.indexType()); - renderPass.drawIndexed(executeInfo.indexCount(), 1, executeInfo.firstIndex(), executeInfo.baseVertex(), 0); - } - } - - private boolean scissorChanged(final @Nullable ScreenRectangle newScissor, final @Nullable ScreenRectangle oldScissor) { - if (newScissor == oldScissor) { - return false; - } else if (newScissor != null) { - return !newScissor.equals(oldScissor); - } else { - return true; - } - } - - private void enableScissor(final ScreenRectangle rectangle, final RenderPass renderPass) { - var window = Minecraft.getInstance().gameRenderer.gameRenderState().windowRenderState; - int guiScale = window.guiScale; - double left = (rectangle.left() * guiScale); - double top = (rectangle.top() * guiScale); - double right = Math.min(rectangle.right() * guiScale, window.width); - double bottom = Math.min(rectangle.bottom() * guiScale, window.height); - renderPass.enableScissor((int) left, window.height - (int) bottom, Math.max(0, (int) (right - left)), Math.max(0, (int) (bottom - top))); - } - - public void close() { - this.vertexBuffer.close(); - this.guiProjectionMatrixBuffer.close(); - this.cubeMap.close(); - } - - static { - ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - } - - @Environment(EnvType.CLIENT) - private record Draw(StagedVertexBuffer.Draw draw, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) { - } - } -} +} \ No newline at end of file diff --git a/client/fabric_26_2/src/main/resources/fabric.mod.json b/client/fabric_26_2/src/main/resources/fabric.mod.json index 054f4e54..99bd6f13 100644 --- a/client/fabric_26_2/src/main/resources/fabric.mod.json +++ b/client/fabric_26_2/src/main/resources/fabric.mod.json @@ -34,7 +34,6 @@ "depends": { "fabricloader": ">=0.19.3", "minecraft": ">=26.2", - "java": ">=25", "fabric-api": "*" }, "suggests": { diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 863ea33e..96a21281 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -130,7 +130,7 @@ public class AllMusicClient implements AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 709b3cf5..0dd2be81 100644 --- a/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/forge_1_12_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -6,288 +6,162 @@ import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.renderer.vertex.DefaultVertexFormats; -import net.minecraft.client.shader.Framebuffer; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final Framebuffer target; + private static class TextEntry { + final String text; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - public CoreRenderTarget() { - target = new Framebuffer(800, 200, false); - target.setFramebufferColor(0.0f, 0.0f, 0.0f, 0.0f); + TextEntry(String text, int y, int color, boolean shadow, int width, int height) { + this.text = text; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); - - nowWidth = (width * scaledresolution.getScaledWidth()); - nowHeight = (height * scaledresolution.getScaledHeight()); - - if (nowWidth > target.framebufferWidth || nowHeight > target.framebufferHeight) { - target.createFramebuffer(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.framebufferClear(); - target.bindFramebuffer(true); - -// GlStateManager.ortho(0.0F, target.framebufferWidth, target.framebufferHeight, 0.0F, 1000.0F, 3000.0D); + entries.clear(); } @Override public void unUse() { isDraw = false; - - Minecraft minecraft = Minecraft.getMinecraft(); - -// GlStateManager.ortho(0.0D, minecraft.displayWidth, minecraft.displayHeight, 0.0D, 1000.0D, 3000.0D); - - Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Minecraft minecraft = Minecraft.getMinecraft(); - - ScaledResolution scaledresolution = new ScaledResolution(Minecraft.getMinecraft()); - - FontRenderer font = minecraft.fontRenderer; - - int width; - - if (shadow) { - width = font.drawStringWithShadow(text, 0, 0, color); - } else { - width = font.drawString(text, 0, 0, color); - } - - TextItem item = new TextItem(width, font.FONT_HEIGHT + (shadow ? 1 : 0), y, (float) scaledresolution.getScaledWidth()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - GlStateManager.bindTexture(target.framebufferTexture); - GlStateManager.depthMask(false); - GlStateManager.enableBlend(); - - float w = (width / 2); - float h = (height / 2); - - GL11.glPushMatrix(); - GL11.glTranslatef(x + w, y + h, 0.0f); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.framebufferWidth; - float v0 = 1 - (texY * scale / target.framebufferHeight); - float u1 = (texX + width) * scale / target.framebufferWidth; - float v1 = 1 - ((texY + height) * scale / target.framebufferHeight); - - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder bufferbuilder = tessellator.getBuffer(); - - bufferbuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX_COLOR); - bufferbuilder.pos(x0, y1, z).tex(u0, v1).color(1.0f, 1.0f, 1.0f, alpha).endVertex(); - bufferbuilder.pos(x1, y1, z).tex(u1, v1).color(1.0f, 1.0f, 1.0f, alpha).endVertex(); - bufferbuilder.pos(x1, y0, z).tex(u1, v0).color(1.0f, 1.0f, 1.0f, alpha).endVertex(); - bufferbuilder.pos(x0, y0, z).tex(u0, v0).color(1.0f, 1.0f, 1.0f, alpha).endVertex(); - tessellator.draw(); - - GlStateManager.disableBlend(); - GlStateManager.depthMask(true); - - GL11.glPopMatrix(); - - target.unbindFramebufferTexture(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); + color = (color & 0x00FFFFFF) | 0xFF000000; + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + int width = font.getStringWidth(text); + if (width == 0) { return; } - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); + int height = font.FONT_HEIGHT + (shadow ? 1 : 0); + if (isState) { + y = 0; } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(text, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + GlStateManager.pushMatrix(); + GlStateManager.translate(drawX, drawY, 0); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + enableScissor(drawX, drawY, maxWidth, entry.height); + drawString(font, entry, -scrollOffset, 0, finalColor); + if (scrollOffset > 0) { + drawString(font, entry, -scrollOffset + entry.width, 0, finalColor); + } + disableScissor(); + } else { + drawString(font, entry, 0, 0, finalColor); + } + + GlStateManager.popMatrix(); } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + TextEntry entry = entries.get(line); + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y + entry.y, 0); + drawString(font, entry, 0, 0, applyAlpha(entry.color, alpha)); + GlStateManager.popMatrix(); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + GlStateManager.pushMatrix(); + GlStateManager.translate(drawX, drawY, 0); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + enableScissor(drawX, drawY, maxWidth, entry.height); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, entry, -(int) texOffset, 0, finalColor); + disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, entry, 0, 0, finalColor); + disableScissor(); + } + + GlStateManager.popMatrix(); + } } @Override @@ -295,17 +169,41 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); + private static void drawString(FontRenderer font, TextEntry entry, int x, int y, int color) { + if (entry.shadow) { + font.drawStringWithShadow(entry.text, x, y, color); + } else { + font.drawString(entry.text, x, y, color); } } + + private static void enableScissor(int x, int y, int width, int height) { + Minecraft mc = Minecraft.getMinecraft(); + int scale = mc.gameSettings.guiScale; + if (scale == 0) { + scale = Math.max(1, Math.min(mc.displayWidth, mc.displayHeight) / 320); + } + int sx = x * scale; + int sy = mc.displayHeight - (y + height) * scale; + int sw = width * scale; + int sh = height * scale; + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(sx, sy, sw, sh); + } + + private static void disableScissor() { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } } diff --git a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 6c42bc8d..72341bb3 100644 --- a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -5,6 +5,7 @@ import com.coloryr.allmusic.client.core.AllMusicCore; import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.client.core.render.TextureRender; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.TextComponent; import net.minecraft.resources.ResourceLocation; @@ -40,6 +41,12 @@ public class AllMusicClient implements AllMusicBridge { public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); private static final ResourceLocation channel = new ResourceLocation("allmusic", "channel"); + public static PoseStack context; + + public static void update(PoseStack matrices) { + context = matrices; + } + public AllMusicClient() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); @@ -112,6 +119,7 @@ public class AllMusicClient implements AllMusicBridge { @SubscribeEvent public void onRenderOverlay(RenderGameOverlayEvent.Pre e) { if (e.getType() == RenderGameOverlayEvent.ElementType.PORTAL) { + update(e.getMatrixStack()); AllMusicCore.hudUpdate(); } } @@ -139,7 +147,7 @@ public class AllMusicClient implements AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index dae7470e..79e8192a 100644 --- a/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/forge_1_16_5/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,321 +4,200 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.pipeline.RenderTarget; import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; -import com.mojang.math.Matrix4f; +import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.RenderBuffers; import net.minecraft.network.chat.Component; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private static final RenderBuffers renderBuffers = new RenderBuffers(); + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final RenderTarget target; + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } - public CoreRenderTarget() { - target = new RenderTarget(800, 200, false, false); - target.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + private final List entries = new ArrayList<>(); + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = (int) (width * window.getGuiScale()); - nowHeight = (int) (height * window.getGuiScale()); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight, false); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.clear(false); - target.bindWrite(true); - - Window window = Minecraft.getInstance().getWindow(); - - RenderSystem.ortho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); + entries.clear(); } @Override public void unUse() { isDraw = false; - - Window window = Minecraft.getInstance().getWindow(); - - RenderSystem.ortho(0.0F, (double) window.getWidth() / window.getGuiScale(), (double) window.getHeight() / window.getGuiScale(), 0.0F, 1000.0F, 3000.0F); - - RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); - main.bindWrite(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - Font font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - - if (font.width(component) == 0) { + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - // 使用 drawInBatch 将文字渲染到 CoreRenderTarget 自己的 buffer, - // 而不是 Font 内部的 buffer。这样 endBatch() 才能正确 flush 到当前 RenderTarget(FBO)。 - int width = font.drawInBatch(component, 0, y, color, shadow, - new Matrix4f(), renderBuffers.bufferSource(), - false, 0, 15728880); - - RenderSystem.disableDepthTest(); - renderBuffers.bufferSource().endBatch(); - RenderSystem.enableDepthTest(); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - RenderSystem.bindTexture(target.getColorTextureId()); - RenderSystem.enableTexture(); - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - RenderSystem.depthFunc(GL30.GL_ALWAYS); - - float w = (width / 2); - float h = (height / 2); - - Matrix4f matrix = Matrix4f.createTranslateMatrix(x + w, y + h, 0); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - // 注意:1.16.5 的 FBO 纹理不需要 Y 轴翻转,纹理原点在左上角 - float u0 = texX * scale / target.width; - float v0 = texY * scale / target.height; - float u1 = (texX + width) * scale / target.width; - float v1 = (texY + height) * scale / target.height; - - BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); - bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); - bufferBuilder.vertex(matrix, x0, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v0).endVertex(); - bufferBuilder.vertex(matrix, x0, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v0).endVertex(); - bufferBuilder.end(); - BufferUploader.end(bufferBuilder); - - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - - target.unbindRead(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + PoseStack matrices = AllMusicClient.context; + if (matrices == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + enableScissor(drawX, drawY, maxWidth, entry.height); + drawString(font, matrices, entry, drawX - scrollOffset, drawY, finalColor); + if (scrollOffset > 0) { + drawString(font, matrices, entry, drawX - scrollOffset + entry.width, drawY, finalColor); + } + disableScissor(); + } else { + drawString(font, matrices, entry, drawX, drawY, finalColor); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + PoseStack matrices = AllMusicClient.context; + if (matrices == null) return; + Font font = Minecraft.getInstance().font; + TextEntry entry = entries.get(line); + drawString(font, matrices, entry, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha)); } @Override public Point2f getLine(int line) { - if (line >= texts.size()) { + if (line >= texts.size() || line >= entries.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - @Override public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); + if (texts.isEmpty()) { + return; } + PoseStack matrices = AllMusicClient.context; + if (matrices == null) return; + Font font = Minecraft.getInstance().font; - target.unbindRead(); + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + enableScissor(drawX, drawY, maxWidth, entry.height); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, matrices, entry, drawX - (int) texOffset, drawY, finalColor); + disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, matrices, entry, drawX, drawY, finalColor); + disableScissor(); + } + } + } + + private static void drawString(Font font, PoseStack matrices, TextEntry entry, + int x, int y, int color) { + if (entry.shadow) { + font.drawShadow(matrices, entry.component, x, y, color); + } else { + font.draw(matrices, entry.component, x, (float) y, color); + } + } + + private static void enableScissor(int x, int y, int width, int height) { + Window window = Minecraft.getInstance().getWindow(); + double scale = window.getGuiScale(); + int sx = (int) (x * scale); + int sy = (int) (window.getHeight() - (y + height) * scale); + int sw = (int) (width * scale); + int sh = (int) (height * scale); + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(sx, sy, sw, sh); + } + + private static void disableScissor() { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } } diff --git a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index 8f845b2f..0bb23ba8 100644 --- a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -6,6 +6,7 @@ import com.coloryr.allmusic.client.core.render.PictureFrameBuffer; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.client.core.render.TextureRender; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; @@ -37,6 +38,8 @@ public class AllMusicClient implements AllMusicBridge { public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); private static final ResourceLocation channel = new ResourceLocation("allmusic", "channel"); + public static GuiGraphics context; + public AllMusicClient() { IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus(); @@ -124,6 +127,7 @@ public class AllMusicClient implements AllMusicBridge { @SubscribeEvent public void onRenderOverlay(RenderGuiOverlayEvent.Pre e) { if (e.getOverlay().id() == VanillaGuiOverlay.PORTAL.id()) { + context = e.getGuiGraphics(); AllMusicCore.hudUpdate(); } } @@ -135,7 +139,7 @@ public class AllMusicClient implements AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index e8236a1a..450f1f7b 100644 --- a/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/forge_1_20_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,300 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import org.joml.Matrix4f; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private static final RenderBuffers renderBuffers = new RenderBuffers(); + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final RenderTarget target; - private Matrix4f matrix4f; + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } - public CoreRenderTarget() { - target = new TextureTarget(800, 200, false, false); - target.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = (int) (width * window.getGuiScale()); - nowHeight = (int) (height * window.getGuiScale()); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight, false); - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } else if (matrix4f == null) { - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.clear(false); - target.bindWrite(true); - - RenderSystem.backupProjectionMatrix(); - if (matrix4f != null) { - RenderSystem.setProjectionMatrix(matrix4f, VertexSorting.ORTHOGRAPHIC_Z); - } + entries.clear(); } @Override public void unUse() { isDraw = false; - - RenderSystem.restoreProjectionMatrix(); - - RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); - main.bindWrite(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - if (font.width(component) == 0) { + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - int width = font.drawInBatch(component, 0, y, color, shadow, new Matrix4f(), renderBuffers.bufferSource(), Font.DisplayMode.NORMAL, 0, 15728880); - RenderSystem.disableDepthTest(); - renderBuffers.bufferSource().endBatch(); - RenderSystem.enableDepthTest(); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - RenderSystem.setShaderTexture(0, target.getColorTextureId()); - RenderSystem.setShader(GameRenderer::getPositionColorTexShader); - - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - RenderSystem.depthFunc(GL30.GL_ALWAYS); - - float w = (width / 2); - float h = (height / 2); - - Matrix4f matrix = new Matrix4f().translation(x + w, y + h, 0); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - BufferBuilder bufferBuilder = Tesselator.getInstance().getBuilder(); - bufferBuilder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR_TEX); - bufferBuilder.vertex(matrix, x0, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y1, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v1).endVertex(); - bufferBuilder.vertex(matrix, x1, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u1, v0).endVertex(); - bufferBuilder.vertex(matrix, x0, y0, z).color(1.0f, 1.0f, 1.0f, alpha).uv(u0, v0).endVertex(); - - BufferUploader.drawWithShader(bufferBuilder.end()); - - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - - target.unbindRead(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -305,19 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } - - target.unbindRead(); + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } -} +} \ No newline at end of file diff --git a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java index b0000cc4..27aa30e4 100644 --- a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java +++ b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/AllMusic.java @@ -142,7 +142,7 @@ public class AllMusic implements AllMusicBridge { @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override diff --git a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 6ee73771..86b989cf 100644 --- a/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/forge_1_7_10/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -6,280 +6,161 @@ import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.FontRenderer; -import net.minecraft.client.gui.ScaledResolution; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.client.shader.Framebuffer; import org.lwjgl.opengl.GL11; -public class CoreRenderTarget extends TextFrameBuffer { - private final Framebuffer target; +import java.util.ArrayList; +import java.util.List; - public CoreRenderTarget() { - target = new Framebuffer(800, 200, false); - target.setFramebufferColor(0.0f, 0.0f, 0.0f, 0.0f); +public class CoreRenderTarget extends TextFrameBuffer { + private static class TextEntry { + final String text; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; + + TextEntry(String text, int y, int color, boolean shadow, int width, int height) { + this.text = text; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Minecraft mc = Minecraft.getMinecraft(); - ScaledResolution scaledresolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - - nowWidth = (width * scaledresolution.getScaledWidth()); - nowHeight = (height * scaledresolution.getScaledHeight()); - - if (nowWidth > target.framebufferWidth || nowHeight > target.framebufferHeight) { - target.createFramebuffer(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.framebufferClear(); - target.bindFramebuffer(true); + entries.clear(); } @Override public void unUse() { isDraw = false; - - Minecraft minecraft = Minecraft.getMinecraft(); - - Minecraft.getMinecraft().getFramebuffer().bindFramebuffer(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Minecraft minecraft = Minecraft.getMinecraft(); - - ScaledResolution scaledresolution = new ScaledResolution(minecraft, minecraft.displayWidth, minecraft.displayHeight); - - FontRenderer font = minecraft.fontRenderer; - - int width; - - if (shadow) { - width = font.drawStringWithShadow(text, 0, 0, color); - } else { - width = font.drawString(text, 0, 0, color); - } - - TextItem item = new TextItem(width, font.FONT_HEIGHT + (shadow ? 1 : 0), y, (float) scaledresolution.getScaledWidth()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - GL11.glBindTexture(GL11.GL_TEXTURE_2D, target.framebufferTexture); - GL11.glDepthMask(false); - GL11.glEnable(GL11.GL_BLEND); - - float w = (width / 2); - float h = (height / 2); - - GL11.glPushMatrix(); - GL11.glTranslatef(x + w, y + h, 0.0f); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.framebufferWidth; - float v0 = 1 - (texY * scale / target.framebufferHeight); - float u1 = (texX + width) * scale / target.framebufferWidth; - float v1 = 1 - ((texY + height) * scale / target.framebufferHeight); - - Tessellator tessellator = Tessellator.instance; - tessellator.startDrawingQuads(); - tessellator.setColorRGBA_F(1.0f, 1.0f, 1.0f, alpha); - tessellator.addVertexWithUV(x0, y1, z, u0, v1); - tessellator.addVertexWithUV(x1, y1, z, u1, v1); - tessellator.addVertexWithUV(x1, y0, z, u1, v0); - tessellator.addVertexWithUV(x0, y0, z, u0, v0); - tessellator.draw(); - - GL11.glDisable(GL11.GL_BLEND); - GL11.glDepthMask(true); - - GL11.glPopMatrix(); - - GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); + color = (color & 0x00FFFFFF) | 0xFF000000; + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + int width = font.getStringWidth(text); + if (width == 0) { return; } - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); + int height = font.FONT_HEIGHT + (shadow ? 1 : 0); + if (isState) { + y = 0; } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(text, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + GL11.glPushMatrix(); + GL11.glTranslatef(drawX, drawY, 0); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + enableScissor(drawX, drawY, maxWidth, entry.height); + drawString(font, entry, -scrollOffset, 0, finalColor); + if (scrollOffset > 0) { + drawString(font, entry, -scrollOffset + entry.width, 0, finalColor); + } + disableScissor(); + } else { + drawString(font, entry, 0, 0, finalColor); + } + + GL11.glPopMatrix(); } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + TextEntry entry = entries.get(line); + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + GL11.glPushMatrix(); + GL11.glTranslatef((float) x, (float) (y + entry.y), 0); + drawString(font, entry, 0, 0, applyAlpha(entry.color, alpha)); + GL11.glPopMatrix(); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + FontRenderer font = Minecraft.getMinecraft().fontRenderer; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + GL11.glPushMatrix(); + GL11.glTranslatef(drawX, drawY, 0); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + enableScissor(drawX, drawY, maxWidth, entry.height); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, entry, -(int) texOffset, 0, finalColor); + disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + enableScissor(drawX, drawY, revealWidth, entry.height); + drawString(font, entry, 0, 0, finalColor); + disableScissor(); + } + + GL11.glPopMatrix(); + } } @Override @@ -287,17 +168,42 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (TextItem item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); + private static void drawString(FontRenderer font, TextEntry entry, int x, int y, int color) { + if (entry.shadow) { + font.drawStringWithShadow(entry.text, x, y, color); + } else { + font.drawString(entry.text, x, y, color); } } + + private static void enableScissor(int x, int y, int width, int height) { + Minecraft mc = Minecraft.getMinecraft(); + int scale = mc.gameSettings.guiScale; + if (scale == 0) { + int k = Math.min(mc.displayWidth, mc.displayHeight); + scale = Math.max(1, k / 320); + } + int sx = x * scale; + int sy = mc.displayHeight - (y + height) * scale; + int sw = width * scale; + int sh = height * scale; + GL11.glEnable(GL11.GL_SCISSOR_TEST); + GL11.glScissor(sx, sy, sw, sh); + } + + private static void disableScissor() { + GL11.glDisable(GL11.GL_SCISSOR_TEST); + } + + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); + } } diff --git a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java index d0b54bb2..fe5d1faa 100644 --- a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java +++ b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/AllMusicClient.java @@ -8,6 +8,7 @@ import com.coloryr.allmusic.client.core.render.TextureRender; import com.coloryr.allmusic.comm.AllMusicInit; import com.coloryr.allmusic.comm.MusicCodec; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.Resource; @@ -37,6 +38,8 @@ public class AllMusicClient implements IPayloadHandler, AllMusicBrid public static final String MODID = "allmusic_client"; public static final Logger LOGGER = LogManager.getLogger("AllMusic Client"); + public static GuiGraphics context; + @SubscribeEvent public static void setup(final FMLClientSetupEvent event) { AllMusicClient client = new AllMusicClient(); @@ -98,7 +101,7 @@ public class AllMusicClient implements IPayloadHandler, AllMusicBrid @Override public TextFrameBuffer makeTextRender(String name) { - return new CoreRenderTarget(); + return new CoreRenderTarget(name); } @Override @@ -169,6 +172,7 @@ public class AllMusicClient implements IPayloadHandler, AllMusicBrid @SubscribeEvent public static void onRenderOverlay(RenderGuiLayerEvent.Post e) { if (e.getName().equals(VanillaGuiLayers.CAMERA_OVERLAYS)) { + context= e.getGuiGraphics(); AllMusicCore.hudUpdate(); } } diff --git a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index d77a244b..450f1f7b 100644 --- a/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_1_21/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,299 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.*; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; -import net.minecraft.client.renderer.GameRenderer; -import net.minecraft.client.renderer.RenderBuffers; +import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; -import org.joml.Matrix4f; -import org.lwjgl.opengl.GL30; + +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private static final RenderBuffers renderBuffers = new RenderBuffers(Runtime.getRuntime().availableProcessors()); + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final RenderTarget target; - private Matrix4f matrix4f; + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } - public CoreRenderTarget() { - target = new TextureTarget(800, 200, false, false); - target.setClearColor(0.0f, 0.0f, 0.0f, 0.0f); + private final List entries = new ArrayList<>(); + + private final boolean isState; + + public CoreRenderTarget(String name) { + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = (int) (width * window.getGuiScale()); - nowHeight = (int) (height * window.getGuiScale()); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight, false); - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } else if (matrix4f == null) { - matrix4f = new Matrix4f().setOrtho(0.0F, (float) (target.width / window.getGuiScale()), - (float) (target.height / window.getGuiScale()), 0.0F, 1000.0F, 21000.0F); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - target.clear(false); - target.bindWrite(true); - - RenderSystem.backupProjectionMatrix(); - if (matrix4f != null) { - RenderSystem.setProjectionMatrix(matrix4f, VertexSorting.ORTHOGRAPHIC_Z); - } + entries.clear(); } @Override public void unUse() { isDraw = false; - - RenderSystem.restoreProjectionMatrix(); - - RenderTarget main = Minecraft.getInstance().getMainRenderTarget(); - main.bindWrite(true); } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - if (font.width(component) == 0) { + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - int width = font.drawInBatch(component, 0, y, color, shadow, new Matrix4f(), renderBuffers.bufferSource(), Font.DisplayMode.NORMAL, 0, 15728880); - RenderSystem.disableDepthTest(); - renderBuffers.bufferSource().endBatch(); - RenderSystem.enableDepthTest(); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - RenderSystem.setShaderTexture(0, target.getColorTextureId()); - RenderSystem.setShader(GameRenderer::getPositionTexColorShader); - - RenderSystem.depthMask(false); - RenderSystem.enableBlend(); - RenderSystem.depthFunc(GL30.GL_ALWAYS); - - float w = (width / 2); - float h = (height / 2); - - Matrix4f matrix = new Matrix4f().translation(x + w, y + h, 0); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - float z = 0; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - BufferBuilder bufferBuilder = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR); - bufferBuilder.addVertex(matrix, x0, y1, z).setUv(u0, v1).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x1, y1, z).setUv(u1, v1).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x1, y0, z).setUv(u1, v0).setColor(1.0f, 1.0f, 1.0f, alpha); - bufferBuilder.addVertex(matrix, x0, y0, z).setUv(u0, v0).setColor(1.0f, 1.0f, 1.0f, alpha); - - BufferUploader.drawWithShader(bufferBuilder.buildOrThrow()); - - RenderSystem.disableBlend(); - RenderSystem.depthMask(true); - - target.unbindRead(); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, (int) (texWidth * percent), texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - int texOffset = (int) (maxOffset * percent); - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); - } - - /** - * 正向百分比显示(从左开始显示,右边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示前面40%的内容 - */ - public void drawByForwardPercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移为0(靠左显示) - int screenOffset = 0; - - // 贴图偏移为0(从左边开始) - int texOffset = 0; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); - } - - /** - * 反向百分比显示(从右开始显示,左边空白) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),表示显示的内容比例,例如0.4表示显示后面40%的内容 - */ - public void drawByReversePercent(float alpha, int startX, int startY, - int texX, int texY, - int texWidth, int texHeight, - int maxWidth, float percent, float scale) { - // 限制百分比范围 - percent = Math.min(1.0f, Math.max(0.0f, percent)); - - // 如果要显示的内容为0,直接返回 - if (percent <= 0) { - return; - } - - // 计算要显示的内容宽度 - int contentWidth = (int) (texWidth * percent); - - // 实际渲染宽度 - int renderWidth = Math.min(contentWidth, maxWidth); - - // 屏幕偏移(靠右显示) - int screenOffset = maxWidth - renderWidth; - - // 贴图偏移(显示后面部分) - int texOffset = texWidth - contentWidth; - - // 渲染 - draw(alpha, startX + screenOffset, startY, - renderWidth, texHeight, - texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -304,19 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } - - target.unbindRead(); + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } -} +} \ No newline at end of file diff --git a/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index cd6f0166..450f1f7b 100644 --- a/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_1_21_11/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,219 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.gui.render.state.GlyphRenderState; -import net.minecraft.client.gui.render.state.GuiElementRenderState; -import net.minecraft.client.gui.render.state.GuiRenderState; -import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; -import net.minecraft.client.renderer.MappableRingBuffer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.network.chat.Component; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.jspecify.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.drawString(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -224,280 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst( - Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom) - .thenComparing(ScreenRectangle::left) - .thenComparing(ScreenRectangle::right) - ); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true); - private final CachedOrthoProjectionMatrixBuffer itemsProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("items", -1000.0F, 1000.0F, true); - private @Nullable ScreenRectangle previousScissorArea = null; - private @Nullable RenderPipeline previousPipeline = null; - private @Nullable TextureSetup previousTextureSetup = null; - private @Nullable BufferBuilder bufferBuilder = null; - - public GuiRenderer(GuiRenderState state) { - this.renderState = state; - } - - public void render(GpuBufferSlice bufferSlice, CoreRenderTarget target) { - this.renderState.forEachText(state -> { - var matrix3x2fc = state.pose; - var screenrectangle = state.scissor; - state.ensurePrepared().visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(TextRenderable.Styled styled) { - this.accept(styled); - } - - @Override - public void acceptEffect(TextRenderable textRenderable) { - this.accept(textRenderable); - } - - private void accept(TextRenderable textRenderable) { - renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2fc, textRenderable, screenrectangle)); - } - }); - }); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.bufferBuilder = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - var map = new Object2IntOpenHashMap(); - - for (var draw : this.meshesToDraw) { - var state = draw.mesh.drawState(); - var vertexformat = state.format(); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize()); - } - - for (var entry : map.object2IntEntrySet()) { - var vertexformat = entry.getKey(); - int i = entry.getIntValue(); - var mappableringbuffer = this.vertexBuffers.get(vertexformat); - if (mappableringbuffer == null || mappableringbuffer.size() < i) { - if (mappableringbuffer != null) { - mappableringbuffer.close(); - } - - this.vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "GUI vertex buffer for " + vertexformat, 34, i)); - } - } - - var commandencoder = RenderSystem.getDevice().createCommandEncoder(); - map = new Object2IntOpenHashMap<>(); - - for (MeshToDraw draw : this.meshesToDraw) { - var meshdata = draw.mesh; - var state = meshdata.drawState(); - var vertexformat = state.format(); - var mappableringbuffer = this.vertexBuffers.get(vertexformat); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - ByteBuffer bytebuffer = meshdata.vertexBuffer(); - int i = bytebuffer.remaining(); - int j = map.getInt(vertexformat); - - try (GpuBuffer.MappedView view = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) { - MemoryUtil.memCopy(bytebuffer, view.data()); - } - - map.put(vertexformat, j + i); - this.draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(), - state.indexCount(), draw.pipeline, draw.textureSetup, draw.scissorArea)); - draw.close(); - } - - this.draw(bufferSlice, target); - - for (var mappableringbuffer : this.vertexBuffers.values()) { - mappableringbuffer.rotate(); - } - - this.draws.clear(); - this.meshesToDraw.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - } - - private void draw(GpuBufferSlice bufferSlice, CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var window = minecraft.getWindow(); - RenderSystem.setProjectionMatrix( - this.guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(), (float) target.target.height / window.getGuiScale()), - ProjectionType.ORTHOGRAPHIC - ); - var rendertarget = target.target; - int i = 0; - - for (var item : this.draws) { - if (item.indexCount > i) { - i = item.indexCount; - } - } - - var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var gpubuffer = buffer.getBuffer(i); - var indexType = buffer.type(); - var gpubufferslice = RenderSystem.getDynamicUniforms() - .writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f()); - if (!this.meshesToDraw.isEmpty()) { - this.executeDrawRange(() -> "GUI before blur", rendertarget, bufferSlice, gpubufferslice, gpubuffer, - indexType, Math.min(this.meshesToDraw.size(), this.draws.size())); - } - } - } - - private void executeDrawRange(Supplier name, RenderTarget target, GpuBufferSlice bufferSlice, GpuBufferSlice bufferSlice1, - GpuBuffer buffer, VertexFormat.IndexType indexType, int count) { - try (var renderpass = RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(), - target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderpass); - renderpass.setUniform("Fog", bufferSlice); - renderpass.setUniform("DynamicTransforms", bufferSlice1); - - for (int i = 0; i < count; i++) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderpass, buffer, indexType); - } - } - } - - private void addElementToMesh(GuiElementRenderState state) { - var pipeline = state.pipeline(); - var texturesetup = state.textureSetup(); - var screenrectangle = state.scissorArea(); - if (pipeline != this.previousPipeline - || this.scissorChanged(screenrectangle, this.previousScissorArea) - || !texturesetup.equals(this.previousTextureSetup)) { - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - this.bufferBuilder = new BufferBuilder(this.byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - this.previousPipeline = pipeline; - this.previousTextureSetup = texturesetup; - this.previousScissorArea = screenrectangle; - } - - state.buildVertices(this.bufferBuilder); - } - - private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle rectangle) { - var meshdata = builder.build(); - if (meshdata != null) { - this.meshesToDraw.add(new MeshToDraw(meshdata, pipeline, textureSetup, rectangle)); - } - } - - private void executeDraw(Draw draw, RenderPass pass, GpuBuffer buffer, VertexFormat.IndexType index) { - var renderpipeline = draw.pipeline(); - pass.setPipeline(renderpipeline); - pass.setVertexBuffer(0, draw.vertexBuffer); - var screenrectangle = draw.scissorArea(); - if (screenrectangle != null) { - Window window = Minecraft.getInstance().getWindow(); - int i = window.getHeight(); - int j = window.getGuiScale(); - double d0 = screenrectangle.left() * j; - double d1 = i - screenrectangle.bottom() * j; - double d2 = screenrectangle.width() * j; - double d3 = screenrectangle.height() * j; - pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3)); - } else { - pass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - pass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - pass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - pass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - pass.setIndexBuffer(buffer, index); - pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(@Nullable ScreenRectangle rectangle, @Nullable ScreenRectangle rectangle1) { - if (rectangle == rectangle1) { - return false; - } else { - return rectangle != null ? !rectangle.equals(rectangle1) : true; - } - } - - @Override - public void close() { - this.byteBufferBuilder.close(); - this.guiProjectionMatrixBuffer.close(); - this.itemsProjectionMatrixBuffer.close(); - - for (MappableRingBuffer mappableringbuffer : this.vertexBuffers.values()) { - mappableringbuffer.close(); - } - } - - record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - } - - record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) implements AutoCloseable { - @Override - public void close() { - this.mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 02b4db80..450f1f7b 100644 --- a/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_1_21_3/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -170,7 +170,9 @@ public class CoreRenderTarget extends TextFrameBuffer { } private static int applyAlpha(int color, float alpha) { - int a = Math.clamp((int) (alpha * 255), 0, 255); + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; return (color & 0x00FFFFFF) | (a << 24); } } \ No newline at end of file diff --git a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 43cbccde..450f1f7b 100644 --- a/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_1_21_4/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -63,7 +63,6 @@ public class CoreRenderTarget extends TextFrameBuffer { Component component = MiniMessage.parse(text); Font font = Minecraft.getInstance().font; int width = font.width(component); - if (width == 0) { return; } @@ -171,7 +170,9 @@ public class CoreRenderTarget extends TextFrameBuffer { } private static int applyAlpha(int color, float alpha) { - int a = Math.clamp((int) (alpha * 255), 0, 255); + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; return (color & 0x00FFFFFF) | (a << 24); } } \ No newline at end of file diff --git a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 57a888bf..450f1f7b 100644 --- a/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_1_21_6/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,212 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.font.glyphs.BakedGlyph; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.gui.render.state.GlyphEffectRenderState; -import net.minecraft.client.gui.render.state.GlyphRenderState; -import net.minecraft.client.gui.render.state.GuiElementRenderState; -import net.minecraft.client.gui.render.state.GuiRenderState; -import net.minecraft.client.renderer.CachedOrthoProjectionMatrixBuffer; -import net.minecraft.client.renderer.MappableRingBuffer; -import net.minecraft.client.renderer.RenderPipelines; -import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.network.chat.Component; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.lwjgl.system.MemoryUtil; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override public void unUse() { - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - Window window = Minecraft.getInstance().getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - GuiGraphics graphics = new GuiGraphics(Minecraft.getInstance(), renderState); - color = color | 0xFF000000; - graphics.drawString(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView()), matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.drawString(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.drawString(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphics gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.drawString(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -217,274 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst( - Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom) - .thenComparing(ScreenRectangle::left) - .thenComparing(ScreenRectangle::right) - ); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final CachedOrthoProjectionMatrixBuffer guiProjectionMatrixBuffer = new CachedOrthoProjectionMatrixBuffer("gui", 1000.0F, 11000.0F, true); - private ScreenRectangle previousScissorArea; - private RenderPipeline previousPipeline; - private TextureSetup previousTextureSetup; - private BufferBuilder bufferBuilder; - - public GuiRenderer(GuiRenderState state) { - renderState = state; - } - - public void render(GpuBufferSlice gpu, CoreRenderTarget target) { - renderState.forEachText(state -> { - var matrix3x2f = state.pose; - var screenrectangle = state.scissor; - state.ensurePrepared().visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(BakedGlyph.GlyphInstance glyph) { - if (glyph.glyph().textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphRenderState(matrix3x2f, glyph, screenrectangle)); - } - } - - @Override - public void acceptEffect(BakedGlyph glyph, BakedGlyph.Effect effect) { - if (glyph.textureView() != null) { - renderState.submitGlyphToCurrentLayer(new GlyphEffectRenderState(matrix3x2f, glyph, effect, screenrectangle)); - } - } - }); - }); - - renderState.sortElements(ELEMENT_SORT_COMPARATOR); - - previousScissorArea = null; - previousPipeline = null; - previousTextureSetup = null; - bufferBuilder = null; - renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - recordDraws(); - - if (!draws.isEmpty()) { - Minecraft minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - RenderSystem.setProjectionMatrix( - guiProjectionMatrixBuffer.getBuffer((float) target.target.width / window.getGuiScale(), - (float) target.target.height / window.getGuiScale()), - ProjectionType.ORTHOGRAPHIC - ); - var rendertarget = target.target; - int i = 0; - - for (Draw guirenderer$draw : draws) { - if (guirenderer$draw.indexCount > i) { - i = guirenderer$draw.indexCount; - } - } - - var buffer = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var gpubuffer = buffer.getBuffer(i); - var index = buffer.type(); - var gpubufferslice = RenderSystem.getDynamicUniforms().writeTransform(new Matrix4f().setTranslation(0.0F, 0.0F, -11000.0F), - new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f(), 0.0F); - if (!meshesToDraw.isEmpty()) { - executeDrawRange(() -> "before blur", rendertarget, gpu, gpubufferslice, gpubuffer, - index, Math.min(meshesToDraw.size(), draws.size()), target); - } - } - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.rotate(); - } - - draws.clear(); - meshesToDraw.clear(); - renderState.reset(); - } - - private void executeDrawRange(Supplier name, RenderTarget target, GpuBufferSlice buffer1, GpuBufferSlice buffer2, - GpuBuffer buffer3, VertexFormat.IndexType index, int count, CoreRenderTarget target1) { - try (RenderPass renderpass = RenderSystem.getDevice() - .createCommandEncoder() - .createRenderPass(name, target.getColorTextureView(), OptionalInt.empty(), - target.useDepth ? target.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderpass); - renderpass.setUniform("Fog", buffer1); - renderpass.setUniform("DynamicTransforms", buffer2); - - for (int i = 0; i < count; i++) { - var item = draws.get(i); - executeDraw(item, renderpass, target1, buffer3, index); - } - } - } - - private void addElementToMesh(GuiElementRenderState state, int range) { - var pipeline = state.pipeline(); - var setup = state.textureSetup(); - var screen = state.scissorArea(); - if (pipeline != previousPipeline || scissorChanged(screen, previousScissorArea) || !setup.equals(previousTextureSetup)) { - if (bufferBuilder != null) { - recordMesh(bufferBuilder, previousPipeline, previousTextureSetup, previousScissorArea); - } - - bufferBuilder = new BufferBuilder(byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - previousPipeline = pipeline; - previousTextureSetup = setup; - previousScissorArea = screen; - } - - state.buildVertices(bufferBuilder, 0.0F + range * 0.01F); - } - - private void recordMesh(BufferBuilder builder, RenderPipeline pipeline, TextureSetup texture, ScreenRectangle rectangle) { - var meshdata = builder.buildOrThrow(); - meshesToDraw.add(new MeshToDraw(meshdata, pipeline, texture, rectangle)); - } - - private void recordDraws() { - var map = new Object2IntOpenHashMap(); - - for (MeshToDraw item : meshesToDraw) { - var state = item.mesh.drawState(); - var vertexformat = state.format(); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - map.put(vertexformat, map.getInt(vertexformat) + state.vertexCount() * vertexformat.getVertexSize()); - } - - for (var entry : map.object2IntEntrySet()) { - var vertexformat = entry.getKey(); - int i = entry.getIntValue(); - var buffer = vertexBuffers.get(vertexformat); - if (buffer == null || buffer.size() < i) { - if (buffer != null) { - buffer.close(); - } - - vertexBuffers.put(vertexformat, new MappableRingBuffer(() -> "vertex buffer for " + vertexformat, 34, i)); - } - } - - var commandencoder = RenderSystem.getDevice().createCommandEncoder(); - map = new Object2IntOpenHashMap<>(); - - for (MeshToDraw item : meshesToDraw) { - var meshdata = item.mesh; - var state = meshdata.drawState(); - var vertexformat = state.format(); - var mappableringbuffer = vertexBuffers.get(vertexformat); - if (!map.containsKey(vertexformat)) { - map.put(vertexformat, 0); - } - - ByteBuffer bytebuffer = meshdata.vertexBuffer(); - int i = bytebuffer.remaining(); - int j = map.getInt(vertexformat); - - try (var item1 = commandencoder.mapBuffer(mappableringbuffer.currentBuffer().slice(j, i), false, true)) { - MemoryUtil.memCopy(bytebuffer, item1.data()); - } - - map.put(vertexformat, j + i); - draws.add(new Draw(mappableringbuffer.currentBuffer(), j / vertexformat.getVertexSize(), state.mode(), - state.indexCount(), item.pipeline, item.textureSetup, item.scissorArea)); - item.close(); - } - } - - private void executeDraw(Draw draw, RenderPass pass, CoreRenderTarget target, GpuBuffer buffer, VertexFormat.IndexType index) { - var renderpipeline = draw.pipeline(); - pass.setPipeline(renderpipeline); - pass.setVertexBuffer(0, draw.vertexBuffer); - var area = draw.scissorArea(); - if (area != null) { - Window window = Minecraft.getInstance().getWindow(); - - int i = target.target.height; - int j = window.getGuiScale(); - double d0 = area.left() * j; - double d1 = i - area.bottom() * j; - double d2 = area.width() * j; - double d3 = area.height() * j; - pass.enableScissor((int) d0, (int) d1, Math.max(0, (int) d2), Math.max(0, (int) d3)); - } else { - pass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - pass.bindSampler("Sampler0", draw.textureSetup.texure0()); - } - - if (draw.textureSetup.texure1() != null) { - pass.bindSampler("Sampler1", draw.textureSetup.texure1()); - } - - if (draw.textureSetup.texure2() != null) { - pass.bindSampler("Sampler2", draw.textureSetup.texure2()); - } - - pass.setIndexBuffer(buffer, index); - pass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(ScreenRectangle screen1, ScreenRectangle screen2) { - if (screen1 == screen2) { - return false; - } else { - return screen1 == null || !screen1.equals(screen2); - } - } - - @Override - public void close() { - byteBufferBuilder.close(); - - guiProjectionMatrixBuffer.close(); - - for (MappableRingBuffer mappableringbuffer : vertexBuffers.values()) { - mappableringbuffer.close(); - } - } - - record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, ScreenRectangle scissorArea) { - } - - record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - ScreenRectangle scissorArea) implements AutoCloseable { - @Override - public void close() { - mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index 45b3fe31..5f3122e6 100644 --- a/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_26_1/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -3,82 +3,53 @@ package com.coloryr.allmusic.client; import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; -import com.coloryr.allmusic.client.others.ModernUI; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBuffer; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import com.mojang.blaze3d.vertex.BufferBuilder; -import com.mojang.blaze3d.vertex.ByteBufferBuilder; -import com.mojang.blaze3d.vertex.MeshData; -import com.mojang.blaze3d.vertex.VertexFormat; -import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.fog.FogRenderer; -import net.minecraft.client.renderer.state.WindowRenderState; -import net.minecraft.client.renderer.state.gui.GlyphRenderState; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import net.minecraft.client.renderer.state.gui.GuiRenderState; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.util.profiling.Profiler; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector3f; -import org.joml.Vector4f; -import org.jspecify.annotations.Nullable; -import org.lwjgl.system.MemoryUtil; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final FogRenderer fogRenderer = new FogRenderer(); - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), 0); + entries.clear(); } @Override @@ -88,133 +59,104 @@ public class CoreRenderTarget extends TextFrameBuffer { @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphicsExtractor graphics = new GuiGraphicsExtractor(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.text(font, component, 0, y, color, shadow); - - renderer.render(fogRenderer.getBuffer(FogRenderer.FogMode.NONE), this); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); + Font font = Minecraft.getInstance().font; + int width = font.width(component); + if (width == 0) { return; } - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -222,285 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right)); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)).thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final List meshesToDraw = new ArrayList<>(); - private final ByteBufferBuilder byteBufferBuilder = new ByteBufferBuilder(786432); - private final Map vertexBuffers = new Object2ObjectOpenHashMap<>(); - private final Projection guiProjection = new Projection(); - private final ProjectionMatrixBuffer guiProjectionMatrixBuffer = new ProjectionMatrixBuffer("gui"); - private final CubeMap cubeMap = new CubeMap(Identifier.withDefaultNamespace("textures/gui/title/background/panorama")); - private ScreenRectangle previousScissorArea = null; - private RenderPipeline previousPipeline = null; - private TextureSetup previousTextureSetup = null; - private BufferBuilder bufferBuilder = null; - - public GuiRenderer(GuiRenderState renderState) { - this.renderState = renderState; - } - - public void render(GpuBufferSlice fogBuffer, CoreRenderTarget target) { - var profiler = Profiler.get(); - if (this.renderState.panoramaRenderState != null) { - this.cubeMap.render(10.0F, this.renderState.panoramaRenderState.spin()); - } - - profiler.push("allmusic text prepare"); - this.renderState.forEachText((text) -> { - var pose = text.pose; - var scissor = text.scissor; - Font.PreparedText preparedText = text.ensurePrepared(); - if (AllMusicClient.modui) { - ModernUI.text(preparedText, renderState, pose, scissor); - } else { - preparedText.visit(new Font.GlyphVisitor() { - @Override - public void acceptGlyph(TextRenderable.Styled glyph) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, glyph, scissor)); - } - - @Override - public void acceptEffect(TextRenderable glyph) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, glyph, scissor)); - } - }); - } - }); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.bufferBuilder = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - this.ensureVertexBufferSizes(); - var commandEncoder = RenderSystem.getDevice().createCommandEncoder(); - var offsets = new Object2IntOpenHashMap(); - - for (var meshToDraw : this.meshesToDraw) { - var mesh = meshToDraw.mesh; - var drawState = mesh.drawState(); - var format = drawState.format(); - var vertexBuffer = this.vertexBuffers.get(format); - if (!offsets.containsKey(format)) { - offsets.put(format, 0); - } - - var meshVertexBuffer = mesh.vertexBuffer(); - int meshBufferSize = meshVertexBuffer.remaining(); - int offset = offsets.getInt(format); - - try (var mappedView = commandEncoder.mapBuffer(vertexBuffer.currentBuffer().slice(offset, meshBufferSize), false, true)) { - MemoryUtil.memCopy(meshVertexBuffer, mappedView.data()); - } - - offsets.put(format, offset + meshBufferSize); - this.draws.add(new Draw(vertexBuffer.currentBuffer(), offset / format.getVertexSize(), - drawState.mode(), drawState.indexCount(), meshToDraw.pipeline, meshToDraw.textureSetup, meshToDraw.scissorArea)); - meshToDraw.close(); - } - profiler.popPush("allmusic text draw"); - this.draw(fogBuffer, target); - profiler.popPush("allmusic text vertexBufferRotate"); - - for (MappableRingBuffer buffer : this.vertexBuffers.values()) { - buffer.rotate(); - } - - profiler.pop(); - this.draws.clear(); - this.meshesToDraw.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - } - - private void draw(GpuBufferSlice fogBuffer, CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var windowState = minecraft.gameRenderer.getGameRenderState().windowRenderState; - this.guiProjection.setupOrtho(1000.0F, 11000.0F, (float) target.target.width / (float) windowState.guiScale, - (float) target.target.height / (float) windowState.guiScale, true); - RenderSystem.setProjectionMatrix(this.guiProjectionMatrixBuffer.getBuffer(this.guiProjection), ProjectionType.ORTHOGRAPHIC); - var mainRenderTarget = target.target; - int maxIndexCount = 0; - - for (var draw : this.draws) { - if (draw.indexCount > maxIndexCount) { - maxIndexCount = draw.indexCount; - } - } - - var autoIndices = RenderSystem.getSequentialBuffer(VertexFormat.Mode.QUADS); - var indexBuffer = autoIndices.getBuffer(maxIndexCount); - var indexType = autoIndices.type(); - var dynamicTransforms = RenderSystem.getDynamicUniforms().writeTransform((new Matrix4f()).setTranslation(0.0F, 0.0F, -11000.0F), - new Vector4f(1.0F, 1.0F, 1.0F, 1.0F), new Vector3f(), new Matrix4f()); - this.executeDrawRange(() -> "GUI before blur", mainRenderTarget, fogBuffer, dynamicTransforms, - indexBuffer, indexType, Math.min(this.meshesToDraw.size(), this.draws.size())); - } - } - - private void executeDrawRange(Supplier label, RenderTarget mainRenderTarget, GpuBufferSlice fogBuffer, - GpuBufferSlice dynamicTransforms, GpuBuffer indexBuffer, VertexFormat.IndexType indexType, int endIndex) { - try (var renderPass = RenderSystem.getDevice().createCommandEncoder().createRenderPass(label, mainRenderTarget.getColorTextureView(), - OptionalInt.empty(), mainRenderTarget.useDepth ? mainRenderTarget.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderPass); - renderPass.setUniform("Fog", fogBuffer); - renderPass.setUniform("DynamicTransforms", dynamicTransforms); - - for (int i = 0; i < endIndex; ++i) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderPass, indexBuffer, indexType); - } - } - } - - private void addElementToMesh(GuiElementRenderState elementState) { - var pipeline = elementState.pipeline(); - var textureSetup = elementState.textureSetup(); - var scissorArea = elementState.scissorArea(); - if (pipeline != this.previousPipeline || this.scissorChanged(scissorArea, this.previousScissorArea) || !textureSetup.equals(this.previousTextureSetup)) { - if (this.bufferBuilder != null) { - this.recordMesh(this.bufferBuilder, this.previousPipeline, this.previousTextureSetup, this.previousScissorArea); - } - - this.bufferBuilder = new BufferBuilder(this.byteBufferBuilder, pipeline.getVertexFormatMode(), pipeline.getVertexFormat()); - this.previousPipeline = pipeline; - this.previousTextureSetup = textureSetup; - this.previousScissorArea = scissorArea; - } - - elementState.buildVertices(this.bufferBuilder); - } - - private void recordMesh(BufferBuilder bufferBuilder, RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - var mesh = bufferBuilder.build(); - if (mesh != null) { - this.meshesToDraw.add(new MeshToDraw(mesh, pipeline, textureSetup, scissorArea)); - } - - } - - private void ensureVertexBufferSizes() { - var requiredSizes = new Object2IntOpenHashMap(); - - for (var meshToDraw : this.meshesToDraw) { - var drawState = meshToDraw.mesh.drawState(); - var format = drawState.format(); - if (!requiredSizes.containsKey(format)) { - requiredSizes.put(format, 0); - } - - requiredSizes.put(format, requiredSizes.getInt(format) + drawState.vertexCount() * format.getVertexSize()); - } - - for (var entry : requiredSizes.object2IntEntrySet()) { - var vertexFormat = entry.getKey(); - int requiredSize = entry.getIntValue(); - var vertexBuffer = this.vertexBuffers.get(vertexFormat); - if (vertexBuffer == null || vertexBuffer.size() < requiredSize) { - if (vertexBuffer != null) { - vertexBuffer.close(); - } - - this.vertexBuffers.put(vertexFormat, new MappableRingBuffer(() -> "GUI vertex buffer for " + vertexFormat, 34, requiredSize)); - } - } - } - - private void executeDraw(Draw draw, RenderPass renderPass, GpuBuffer indexBuffer, VertexFormat.IndexType indexType) { - var pipeline = draw.pipeline(); - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, draw.vertexBuffer); - var scissorArea = draw.scissorArea(); - if (scissorArea != null) { - this.enableScissor(scissorArea, renderPass); - } else { - renderPass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - renderPass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - renderPass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - renderPass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - renderPass.setIndexBuffer(indexBuffer, indexType); - renderPass.drawIndexed(draw.baseVertex, 0, draw.indexCount, 1); - } - - private boolean scissorChanged(@Nullable ScreenRectangle newScissor, @Nullable ScreenRectangle oldScissor) { - if (newScissor == oldScissor) { - return false; - } else if (newScissor != null) { - return !newScissor.equals(oldScissor); - } else { - return true; - } - } - - private void enableScissor(ScreenRectangle rectangle, RenderPass renderPass) { - WindowRenderState windowState = Minecraft.getInstance().gameRenderer.getGameRenderState().windowRenderState; - int windowHeight = windowState.height; - int guiScale = windowState.guiScale; - double left = rectangle.left() * guiScale; - double bottom = windowHeight - rectangle.bottom() * guiScale; - double width = rectangle.width() * guiScale; - double height = rectangle.height() * guiScale; - renderPass.enableScissor((int) left, (int) bottom, Math.max(0, (int) width), Math.max(0, (int) height)); - } - - public void close() { - this.byteBufferBuilder.close(); - this.guiProjectionMatrixBuffer.close(); - - for (MappableRingBuffer buffer : this.vertexBuffers.values()) { - buffer.close(); - } - - this.cubeMap.close(); - } - - private record Draw(GpuBuffer vertexBuffer, int baseVertex, VertexFormat.Mode mode, int indexCount, - RenderPipeline pipeline, TextureSetup textureSetup, @Nullable ScreenRectangle scissorArea) { - } - - private record MeshToDraw(MeshData mesh, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) implements AutoCloseable { - public void close() { - this.mesh.close(); - } - } - } -} +} \ No newline at end of file diff --git a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java index e883eaf7..5f3122e6 100644 --- a/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java +++ b/client/neoforge_26_2/src/main/java/com/coloryr/allmusic/client/CoreRenderTarget.java @@ -4,208 +4,159 @@ import com.coloryr.allmusic.client.core.AllMusicHud; import com.coloryr.allmusic.client.core.Point2f; import com.coloryr.allmusic.client.core.render.TextFrameBuffer; import com.coloryr.allmusic.codec.HudPosType; -import com.mojang.blaze3d.GpuFormat; -import com.mojang.blaze3d.ProjectionType; -import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.pipeline.TextureTarget; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.systems.RenderPass; -import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.textures.FilterMode; -import net.minecraft.SharedConstants; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.client.gui.font.TextRenderable; -import net.minecraft.client.gui.navigation.ScreenRectangle; -import net.minecraft.client.gui.render.TextureSetup; -import net.minecraft.client.renderer.*; -import net.minecraft.client.renderer.state.gui.GlyphRenderState; -import net.minecraft.client.renderer.state.gui.GuiElementRenderState; -import net.minecraft.client.renderer.state.gui.GuiRenderState; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; -import net.minecraft.util.profiling.Profiler; -import org.joml.Matrix3x2f; -import org.joml.Matrix4f; -import org.joml.Vector4f; -import org.jspecify.annotations.Nullable; -import java.util.*; -import java.util.function.Supplier; +import java.util.ArrayList; +import java.util.List; public class CoreRenderTarget extends TextFrameBuffer { - private final RenderTarget target; - private final String name; + private static class TextEntry { + final Component component; + final int y; + final int color; + final boolean shadow; + final int width; + final int height; - private final GuiRenderer renderer; - private final GuiRenderState renderState = new GuiRenderState(); + TextEntry(Component component, int y, int color, boolean shadow, int width, int height) { + this.component = component; + this.y = y; + this.color = color; + this.shadow = shadow; + this.width = width; + this.height = height; + } + } + + private final List entries = new ArrayList<>(); + + private final boolean isState; public CoreRenderTarget(String name) { - this.name = name; - target = new TextureTarget(name, 800, 200, false, GpuFormat.RGBA8_UNORM); - renderer = new GuiRenderer(renderState); + isState = name.equals("state"); } @Override public void resize(int width, int height) { - Window window = Minecraft.getInstance().getWindow(); - - nowWidth = width * window.getGuiScale(); - nowHeight = height * window.getGuiScale(); - - if (nowWidth > target.width || nowHeight > target.height) { - target.resize(nowWidth, nowHeight); - } + nowWidth = width; + nowHeight = height; } @Override public void use() { isDraw = true; - clear(); - RenderSystem.getDevice().createCommandEncoder().clearColorTexture(target.getColorTexture(), new Vector4f(0)); + entries.clear(); } @Override public void unUse() { - renderer.render(this); isDraw = false; } @Override public void drawText(String text, int y, int color, boolean shadow) { - var minecraft = Minecraft.getInstance(); - Window window = minecraft.getWindow(); - - var font = Minecraft.getInstance().font; + color = (color & 0x00FFFFFF) | 0xFF000000; Component component = MiniMessage.parse(text); - int width = font.width(component.getVisualOrderText()); - + Font font = Minecraft.getInstance().font; + int width = font.width(component); if (width == 0) { return; } - int i = (int) minecraft.mouseHandler.getScaledXPos(window); - int j = (int) minecraft.mouseHandler.getScaledYPos(window); - - GuiGraphicsExtractor graphics = new GuiGraphicsExtractor(Minecraft.getInstance(), renderState, i, j); - color = color | 0xFF000000; - graphics.text(font, component, 0, y, color, shadow); - - TextItem item = new TextItem(width, font.lineHeight + (shadow ? 1 : 0), y, (float) window.getGuiScale()); - texts.add(item); - } - - /** - * 渲染贴图的一部分到屏幕指定位置 - * - * @param alpha 透明度 - * @param x 屏幕X坐标(左上角) - * @param y 屏幕Y坐标(左上角) - * @param width 需要渲染的宽度 - * @param height 需要渲染的高度 - * @param texX 贴图左上角X坐标 - * @param texY 贴图左上角Y坐标 - * @param scale 贴图缩放 - */ - private void draw(float alpha, float x, float y, float width, float height, float texX, float texY, float scale) { - float w = width / 2; - float h = height / 2; - - Matrix3x2f matrix = new Matrix3x2f().translation(x + w, y + h); - - float x0 = -w; - float x1 = w; - float y0 = -h; - float y1 = h; - - // 计算贴图区域UV - float u0 = texX * scale / target.width; - float v0 = 1 - (texY * scale / target.height); - float u1 = (texX + width) * scale / target.width; - float v1 = 1 - ((texY + height) * scale / target.height); - - int color = 0xFFFFFF00 + (int) (255 * alpha); - - AllMusicClient.context.submitGuiElementRenderState(new FloatRenderState(RenderPipelines.GUI_TEXTURED, - TextureSetup.singleTexture(target.getColorTextureView(), RenderSystem.getSamplerCache().getRepeat(FilterMode.NEAREST)), - matrix, x0, y0, x1, y1, u0, u1, v0, v1, color, AllMusicClient.context.peekScissorStack())); - } - - public void drawLoop(float alpha, float x, float y, - float texX, float texY, - float textWidth, float textHeight, - int maxWidth, float offsetX, float scale) { - - // 如果宽度不大于最大宽度,直接全部渲染 - if (maxWidth == -1 || textWidth <= maxWidth) { - draw(alpha, x, y, textWidth, textHeight, texX, texY, scale); - return; + int height = font.lineHeight + (shadow ? 1 : 0); + if (isState) { + y = 0; } - - if (textWidth - offsetX < maxWidth) { - float nowWith = textWidth - offsetX; - draw(alpha, x, y, nowWith, textHeight, offsetX, texY, scale); - draw(alpha, x + nowWith, y, maxWidth - nowWith, textHeight, 0, texY, scale); - } else { - draw(alpha, x, y, maxWidth, textHeight, offsetX, texY, scale); - } - } - - /** - * 居中百分比显示(根据百分比选择显示贴图的中间部分) - * - * @param alpha 透明度 - * @param startX 起始X坐标(屏幕左上角) - * @param startY 起始Y坐标(屏幕左上角) - * @param texX 贴图左上角X - * @param texY 贴图左上角Y - * @param texWidth 贴图总宽度 - * @param texHeight 贴图高度 - * @param maxWidth 最大渲染宽度 - * @param percent 百分比(0.0-1.0),0%显示左边,100%显示右边 - */ - public void drawByPercent(float alpha, float startX, float startY, - float texX, float texY, - float texWidth, float texHeight, - int maxWidth, float percent, float scale) { - - // 限制百分比范围 - percent = Math.clamp(percent, 0.0f, 1.0f); - - // 如果贴图宽度小于等于最大宽度,直接全部显示 - if (texWidth <= maxWidth) { - draw(alpha, startX, startY, texWidth * percent, texHeight, texX, texY, scale); - return; - } - - // 计算贴图的起始位置(根据百分比) - float maxOffset = texWidth - maxWidth; - float texOffset = maxOffset * percent; - - // 渲染 - draw(alpha, startX, startY, maxWidth, texHeight, texX + texOffset, texY, scale); + entries.add(new TextEntry(component, y, color, shadow, width, height)); + texts.add(new TextItem(width, height, y, 1.0f)); } @Override public void draw(float alpha, int x, int y, int maxWidth, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); + if (texts.isEmpty()) { + return; + } - drawLoop(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, getOffset(item) % item.textWidth, item.scale); + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + int scrollOffset = (int) (offsetX % entry.width); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - scrollOffset, drawY, finalColor, entry.shadow); + if (scrollOffset > 0) { + gui.text(font, entry.component, drawX - scrollOffset + entry.width, drawY, finalColor, entry.shadow); + } + gui.disableScissor(); + } else { + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + } } } @Override public void drawLine(float x, float y, float alpha, int line) { + if (texts.isEmpty()) { + return; + } if (line >= texts.size()) { return; } - TextItem item = texts.get(line); - draw(alpha, x, y, item.textWidth, item.textHeight, 0, item.y, item.scale); + if (line >= entries.size()) return; + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + TextEntry entry = entries.get(line); + gui.text(Minecraft.getInstance().font, entry.component, + (int) x, (int) (y + entry.y), + applyAlpha(entry.color, alpha), entry.shadow); + } + + @Override + public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { + if (texts.isEmpty()) { + return; + } + GuiGraphicsExtractor gui = AllMusicClient.context; + if (gui == null) return; + Font font = Minecraft.getInstance().font; + + for (TextEntry entry : entries) { + int displayWidth = maxWidth != -1 ? Math.min(entry.width, maxWidth) : entry.width; + Point2f point = AllMusicHud.getPos(displayWidth, entry.height, x, y, dir); + + int drawX = (int) point.x; + int drawY = (int) (point.y + entry.y); + int finalColor = applyAlpha(entry.color, alpha); + + if (maxWidth != -1 && entry.width > maxWidth) { + float maxOffset = entry.width - maxWidth; + float texOffset = maxOffset * state; + int revealWidth = (int) (maxWidth * state); + + gui.enableScissor(drawX, Math.max(0, drawY), drawX + maxWidth, drawY + entry.height); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX - (int) texOffset, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } else { + int revealWidth = (int) (entry.width * state); + gui.enableScissor(drawX, Math.max(0, drawY), drawX + revealWidth, drawY + entry.height); + gui.text(font, entry.component, drawX, drawY, finalColor, entry.shadow); + gui.disableScissor(); + } + } } @Override @@ -213,199 +164,15 @@ public class CoreRenderTarget extends TextFrameBuffer { if (line >= texts.size()) { return new Point2f(0, 0); } - TextItem item = texts.get(line); - return new Point2f(item.textWidth, item.textHeight); + if (line >= entries.size()) return new Point2f(0, 0); + TextEntry entry = entries.get(line); + return new Point2f(entry.width, entry.height); } - - @Override - public void drawWithState(float alpha, int x, int y, int maxWidth, float state, HudPosType dir) { - for (var item : texts) { - Point2f point = AllMusicHud.getPos(Math.min(maxWidth, item.textWidth), item.textHeight, x, y, dir); - - drawByPercent(alpha, point.x, point.y + item.y, 0, item.y, item.textWidth, item.textHeight, maxWidth, state, item.scale); - } + private static int applyAlpha(int color, float alpha) { + int a = (int) (alpha * 255); + if (a < 0) a = 0; + if (a > 255) a = 255; + return (color & 0x00FFFFFF) | (a << 24); } - - public static class GuiRenderer implements AutoCloseable { - private static final Comparator SCISSOR_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(ScreenRectangle::top) - .thenComparing(ScreenRectangle::bottom).thenComparing(ScreenRectangle::left).thenComparing(ScreenRectangle::right)); - private static final Comparator TEXTURE_COMPARATOR = Comparator.nullsFirst(Comparator.comparing(TextureSetup::getSortKey)); - private static final Comparator ELEMENT_SORT_COMPARATOR; - private final GuiRenderState renderState; - private final List draws = new ArrayList<>(); - private final StagedVertexBuffer vertexBuffer = new StagedVertexBuffer(() -> "GUI Vertex Buffer", 786432); - private final Projection guiProjection = new Projection(); - private final ProjectionMatrixBuffer guiProjectionMatrixBuffer = new ProjectionMatrixBuffer("gui"); - private final CubeMap cubeMap = new CubeMap(Identifier.withDefaultNamespace("textures/gui/title/background/panorama")); - private @Nullable ScreenRectangle previousScissorArea = null; - private @Nullable RenderPipeline previousPipeline = null; - private @Nullable TextureSetup previousTextureSetup = null; - private StagedVertexBuffer.@Nullable Draw previousDraw; - - public GuiRenderer(final GuiRenderState renderState) { - this.renderState = renderState; - } - - public void render(CoreRenderTarget target) { - var profiler = Profiler.get(); - if (this.renderState.panoramaRenderState != null) { - this.cubeMap.render(10.0F, this.renderState.panoramaRenderState.spin()); - } - - profiler.push("prepare"); - this.prepare(); - profiler.popPush("upload"); - this.vertexBuffer.upload(); - profiler.popPush("draw"); - this.draw(target); - profiler.popPush("endFrame"); - this.vertexBuffer.endDraw(); - this.vertexBuffer.endFrame(); - this.draws.clear(); - this.renderState.reset(); - if (SharedConstants.DEBUG_SHUFFLE_UI_RENDERING_ORDER) { - RenderPipeline.updateSortKeySeed(); - TextureSetup.updateSortKeySeed(); - } - - profiler.pop(); - } - - private void prepare() { - this.prepareText(); - this.renderState.sortElements(ELEMENT_SORT_COMPARATOR); - this.addElementsToMeshes(); - } - - private void addElementsToMeshes() { - this.previousScissorArea = null; - this.previousPipeline = null; - this.previousTextureSetup = null; - this.previousDraw = null; - this.renderState.forEachElement(this::addElementToMesh, GuiRenderState.TraverseRange.BEFORE_BLUR); - } - - private void draw(CoreRenderTarget target) { - if (!this.draws.isEmpty()) { - var minecraft = Minecraft.getInstance(); - var windowState = minecraft.gameRenderer.gameRenderState().windowRenderState; - this.guiProjection.setupOrtho(1000.0F, 11000.0F, (float) target.target.width / (float) windowState.guiScale, - (float) target.target.height / (float) windowState.guiScale, true); - RenderSystem.setProjectionMatrix(this.guiProjectionMatrixBuffer.getBuffer(this.guiProjection), ProjectionType.ORTHOGRAPHIC); - var mainRenderTarget = target.target; - var dynamicTransforms = RenderSystem.getDynamicUniforms().writeTransform((new Matrix4f()).setTranslation(0.0F, 0.0F, -11000.0F)); - if (!this.draws.isEmpty()) { - this.executeDrawRange(() -> "GUI before blur", mainRenderTarget, dynamicTransforms, this.draws.size()); - } - } - } - - private void executeDrawRange(Supplier label, RenderTarget mainRenderTarget, GpuBufferSlice dynamicTransforms, int endIndex) { - try (var renderPass = RenderSystem.getDevice().createCommandEncoder() - .createRenderPass(label, mainRenderTarget.getColorTextureView(), - Optional.empty(), mainRenderTarget.useDepth ? mainRenderTarget.getDepthTextureView() : null, OptionalDouble.empty())) { - RenderSystem.bindDefaultUniforms(renderPass); - renderPass.setUniform("DynamicTransforms", dynamicTransforms); - - for (int i = 0; i < endIndex; ++i) { - Draw draw = this.draws.get(i); - this.executeDraw(draw, renderPass); - } - } - } - - private void addElementToMesh(final GuiElementRenderState elementState) { - var pipeline = elementState.pipeline(); - var textureSetup = elementState.textureSetup(); - var scissorArea = elementState.scissorArea(); - if (this.previousDraw == null || pipeline != this.previousPipeline || this.scissorChanged(scissorArea, this.previousScissorArea) || !textureSetup.equals(this.previousTextureSetup)) { - this.previousPipeline = pipeline; - this.previousTextureSetup = textureSetup; - this.previousScissorArea = scissorArea; - this.previousDraw = this.vertexBuffer.appendDraw(pipeline.getVertexFormatBinding(0), pipeline.getPrimitiveTopology()); - this.draws.add(new Draw(this.previousDraw, pipeline, textureSetup, scissorArea)); - } - - elementState.buildVertices(this.vertexBuffer.getVertexBuilder(Objects.requireNonNull(this.previousDraw))); - } - - private void prepareText() { - this.renderState.forEachText((text) -> { - var pose = text.pose; - var scissor = text.scissor; - text.ensurePrepared().visit(new Font.GlyphVisitor() { - public void acceptRenderable(final TextRenderable renderable) { - renderState.addGlyphToCurrentLayer(new GlyphRenderState(pose, renderable, scissor)); - } - }); - }); - } - - private void executeDraw(final Draw draw, final RenderPass renderPass) { - var executeInfo = this.vertexBuffer.getExecuteInfo(draw.draw); - if (executeInfo != null) { - var pipeline = draw.pipeline(); - renderPass.setPipeline(pipeline); - renderPass.setVertexBuffer(0, executeInfo.vertexBuffer().slice()); - var scissorArea = draw.scissorArea(); - if (scissorArea != null) { - this.enableScissor(scissorArea, renderPass); - } else { - renderPass.disableScissor(); - } - - if (draw.textureSetup.texure0() != null) { - renderPass.bindTexture("Sampler0", draw.textureSetup.texure0(), draw.textureSetup.sampler0()); - } - - if (draw.textureSetup.texure1() != null) { - renderPass.bindTexture("Sampler1", draw.textureSetup.texure1(), draw.textureSetup.sampler1()); - } - - if (draw.textureSetup.texure2() != null) { - renderPass.bindTexture("Sampler2", draw.textureSetup.texure2(), draw.textureSetup.sampler2()); - } - - renderPass.setIndexBuffer(executeInfo.indexBuffer(), executeInfo.indexType()); - renderPass.drawIndexed(executeInfo.indexCount(), 1, executeInfo.firstIndex(), executeInfo.baseVertex(), 0); - } - } - - private boolean scissorChanged(final @Nullable ScreenRectangle newScissor, final @Nullable ScreenRectangle oldScissor) { - if (newScissor == oldScissor) { - return false; - } else if (newScissor != null) { - return !newScissor.equals(oldScissor); - } else { - return true; - } - } - - private void enableScissor(final ScreenRectangle rectangle, final RenderPass renderPass) { - var window = Minecraft.getInstance().gameRenderer.gameRenderState().windowRenderState; - int guiScale = window.guiScale; - double left = (rectangle.left() * guiScale); - double top = (rectangle.top() * guiScale); - double right = Math.min(rectangle.right() * guiScale, window.width); - double bottom = Math.min(rectangle.bottom() * guiScale, window.height); - renderPass.enableScissor((int) left, window.height - (int) bottom, Math.max(0, (int) (right - left)), Math.max(0, (int) (bottom - top))); - } - - public void close() { - this.vertexBuffer.close(); - this.guiProjectionMatrixBuffer.close(); - this.cubeMap.close(); - } - - static { - ELEMENT_SORT_COMPARATOR = Comparator.comparing(GuiElementRenderState::scissorArea, SCISSOR_COMPARATOR) - .thenComparing(GuiElementRenderState::pipeline, Comparator.comparing(RenderPipeline::getSortKey)) - .thenComparing(GuiElementRenderState::textureSetup, TEXTURE_COMPARATOR); - } - - private record Draw(StagedVertexBuffer.Draw draw, RenderPipeline pipeline, TextureSetup textureSetup, - @Nullable ScreenRectangle scissorArea) { - } - } -} +} \ No newline at end of file diff --git a/onejar/fabric_1_20_1/build.gradle.kts b/onejar/fabric_1_20_1/build.gradle.kts new file mode 100644 index 00000000..649bb783 --- /dev/null +++ b/onejar/fabric_1_20_1/build.gradle.kts @@ -0,0 +1,59 @@ +plugins { + id("fabric-loom") version Versions.fabricLoom +} + +// 直接引用 client 和 server 模块的源码目录,无需手动复制 +val clientSourceDir = project(":client:fabric_1_20_1").projectDir.resolve("src/main/java") +val serverSourceDir = project(":server:fabric_1_20_1").projectDir.resolve("src/main/java") +val clientResDir = project(":client:fabric_1_20_1").projectDir.resolve("src/main/resources") +val serverResDir = project(":server:fabric_1_20_1").projectDir.resolve("src/main/resources") + +sourceSets { + main { + java { + setSrcDirs(listOf(clientSourceDir, serverSourceDir)) + } + resources { + setSrcDirs(listOf( + file("src/main/resources"), // onejar 自己的合并资源(fabric.mod.json) + clientResDir, + serverResDir + )) + } + } +} + +dependencies { + minecraft("com.mojang:minecraft:1.20.1") + mappings(loom.officialMojangMappings()) + modImplementation("net.fabricmc:fabric-loader:0.16.10") + + modImplementation("net.fabricmc.fabric-api:fabric-api:0.92.3+1.20.1") + + modImplementation(include("net.kyori:adventure-platform-fabric:5.9.0")!!) +} + +tasks { + processResources { + // onejar 自己的 fabric.mod.json 优先(排在 srcDirs 第一位), + // client/server 的 fabric.mod.json 作为重复项被排除 + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + + filesMatching("fabric.mod.json") { + expand( + "version" to project.version + ) + } + } + + remapJar { + inputFile.set(shadowJar.get().archiveFile) + + archiveFileName.set("[fabric-1.20.1]AllMusic-${project.version}.jar") + destinationDirectory.set(file("${parent!!.projectDir}/../build")) + } + + build { + dependsOn(remapJar) + } +} diff --git a/onejar/fabric_1_20_1/src/main/resources/assets/allmusic/icon.png b/onejar/fabric_1_20_1/src/main/resources/assets/allmusic/icon.png new file mode 100644 index 00000000..b2b5fa79 Binary files /dev/null and b/onejar/fabric_1_20_1/src/main/resources/assets/allmusic/icon.png differ diff --git a/onejar/fabric_1_20_1/src/main/resources/fabric.mod.json b/onejar/fabric_1_20_1/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..22b23486 --- /dev/null +++ b/onejar/fabric_1_20_1/src/main/resources/fabric.mod.json @@ -0,0 +1,35 @@ +{ + "schemaVersion": 1, + "id": "allmusic_client", + "version": "${version}", + "name": "AllMusic Client", + "description": "AllMusic Client Mod", + "authors": [ + "Color_yr" + ], + "contact": { + "homepage": "https://github.com/Coloryr/AllMusic", + "sources": "https://github.com/Coloryr/AllMusic" + }, + "license": "GPL-3.0", + "icon": "assets/allmusic/icon.png", + "environment": "*", + "entrypoints": { + "main": [ + "com.coloryr.allmusic.server.AllMusicServer" + ], + "client": [ + "com.coloryr.allmusic.client.AllMusicClient" + ] + }, + "mixins": [ + "allmusic_server.mixins.json", + "allmusic_client.mixins.json" + ], + "depends": { + "fabricloader": ">=0.19.3", + "fabric": "*", + "minecraft": ">=1.16" + }, + "suggests": {} +} diff --git a/server/fabric_1_16_5/src/main/resources/fabric.mod.json b/server/fabric_1_16_5/src/main/resources/fabric.mod.json index 0c02766e..ee75526a 100644 --- a/server/fabric_1_16_5/src/main/resources/fabric.mod.json +++ b/server/fabric_1_16_5/src/main/resources/fabric.mod.json @@ -29,7 +29,7 @@ "adventure-platform-fabric.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "fabric": "*", "minecraft": ">=1.16" }, diff --git a/server/fabric_1_20_1/src/main/resources/fabric.mod.json b/server/fabric_1_20_1/src/main/resources/fabric.mod.json index c5b0b45b..b06238f8 100644 --- a/server/fabric_1_20_1/src/main/resources/fabric.mod.json +++ b/server/fabric_1_20_1/src/main/resources/fabric.mod.json @@ -23,10 +23,9 @@ "allmusic_server.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "fabric-api": "*", - "minecraft": "~1.20", - "java": ">=17" + "minecraft": "~1.20" }, "suggests": { } diff --git a/server/fabric_1_21/src/main/resources/fabric.mod.json b/server/fabric_1_21/src/main/resources/fabric.mod.json index eefebd6e..fe591676 100644 --- a/server/fabric_1_21/src/main/resources/fabric.mod.json +++ b/server/fabric_1_21/src/main/resources/fabric.mod.json @@ -26,9 +26,8 @@ "allmusic_server.mixins.json" ], "depends": { - "fabricloader": ">=0.15.11", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/server/fabric_1_21_11/src/main/resources/fabric.mod.json b/server/fabric_1_21_11/src/main/resources/fabric.mod.json index c6001cf3..b77d1c66 100644 --- a/server/fabric_1_21_11/src/main/resources/fabric.mod.json +++ b/server/fabric_1_21_11/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.18.4", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21.11", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/server/fabric_1_21_6/src/main/resources/fabric.mod.json b/server/fabric_1_21_6/src/main/resources/fabric.mod.json index 17208733..4953f884 100644 --- a/server/fabric_1_21_6/src/main/resources/fabric.mod.json +++ b/server/fabric_1_21_6/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.16.14", + "fabricloader": ">=0.19.3", "minecraft": ">=1.21.6", - "java": ">=21", "fabric-api": "*" }, "suggests": { diff --git a/server/fabric_26_1/src/main/resources/fabric.mod.json b/server/fabric_26_1/src/main/resources/fabric.mod.json index 4f152853..648c6821 100644 --- a/server/fabric_26_1/src/main/resources/fabric.mod.json +++ b/server/fabric_26_1/src/main/resources/fabric.mod.json @@ -29,9 +29,8 @@ } ], "depends": { - "fabricloader": ">=0.18.3", + "fabricloader": ">=0.19.3", "minecraft": ">=26.1", - "java": ">=25", "fabric-api": "*" }, "suggests": { diff --git a/server/fabric_26_2/src/main/resources/fabric.mod.json b/server/fabric_26_2/src/main/resources/fabric.mod.json index fc00108e..6ac18ec3 100644 --- a/server/fabric_26_2/src/main/resources/fabric.mod.json +++ b/server/fabric_26_2/src/main/resources/fabric.mod.json @@ -31,7 +31,6 @@ "depends": { "fabricloader": ">=0.19.3", "minecraft": ">=26.2", - "java": ">=25", "fabric-api": "*" }, "suggests": { diff --git a/settings.gradle.kts b/settings.gradle.kts index 01c5b8dc..844f0048 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -58,6 +58,7 @@ include(":server:velocity") include(":onejar") include(":onejar:fabric_1_16_5") +include(":onejar:fabric_1_20_1") pluginManagement { repositories {