package com.chuangzhou.vivid2D.render; import com.chuangzhou.vivid2D.render.systems.RenderSystem; import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder; import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator; import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement; import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram; import org.joml.Vector2f; import org.joml.Vector4f; import org.lwjgl.opengl.*; import org.lwjgl.stb.STBTTAlignedQuad; import org.lwjgl.stb.STBTTBakedChar; import org.lwjgl.system.MemoryStack; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import static org.lwjgl.stb.STBTruetype.*; /** * OpenGL 文字渲染器实例类 * 支持多字体、多实例管理,每个实例维护独立的字符数据与纹理 * * @author tzdwindows 7 */ public final class TextRenderer { private static final Logger logger = LoggerFactory.getLogger(TextRenderer.class); private final int bitmapWidth; private final int bitmapHeight; private final int firstChar; private final int charCount; private STBTTBakedChar.Buffer charData; private int fontTextureId; private boolean initialized = false; /** * 构造函数 * * @param bitmapWidth 字符纹理宽度 * @param bitmapHeight 字符纹理高度 * @param firstChar 字符起始码 * @param charCount 字符数量 */ public TextRenderer(int bitmapWidth, int bitmapHeight, int firstChar, int charCount) { this.bitmapWidth = bitmapWidth; this.bitmapHeight = bitmapHeight; this.firstChar = firstChar; this.charCount = charCount; } /** * 初始化字体渲染器 * * @param fontData TTF 字体文件内容 * @param fontHeight 字体像素高度 */ public void initialize(ByteBuffer fontData, float fontHeight) { if (initialized) return; ShaderProgram shader = ShaderManagement.getShaderProgram("TextShader"); shader.use(); // 验证输入参数 if (fontData == null || fontData.capacity() == 0) { logger.error("Invalid font data provided to TextRenderer"); return; } if (fontHeight <= 0) { logger.error("Invalid font height: {}", fontHeight); return; } if (bitmapWidth <= 0 || bitmapHeight <= 0) { logger.error("Invalid bitmap dimensions: {}x{}", bitmapWidth, bitmapHeight); return; } try { charData = STBTTBakedChar.malloc(charCount); // 分配位图内存 int bitmapSize = bitmapWidth * bitmapHeight; if (bitmapSize <= 0) { logger.error("Invalid bitmap size: {}", bitmapSize); return; } ByteBuffer bitmap = ByteBuffer.allocateDirect(bitmapSize); // 烘焙字体位图 int result = stbtt_BakeFontBitmap(fontData, fontHeight, bitmap, bitmapWidth, bitmapHeight, firstChar, charData); if (result <= 0) { logger.error("stbtt_BakeFontBitmap failed with result: {}", result); charData.free(); return; } // 创建纹理 fontTextureId = createTextureFromBitmap(bitmapWidth, bitmapHeight, bitmap); if (fontTextureId == 0) { logger.error("Failed to create font texture"); charData.free(); return; } initialized = true; logger.debug("TextRenderer initialized successfully with texture ID: {}", fontTextureId); } catch (Exception e) { logger.error("Exception during TextRenderer initialization: {}", e.getMessage()); if (charData != null) { charData.free(); charData = null; } } shader.stop(); } private int createTextureFromBitmap(int width, int height, ByteBuffer pixels) { RenderSystem.assertOnRenderThread(); int textureId = RenderSystem.genTextures(); RenderSystem.bindTexture(textureId); // 使用更兼容的纹理格式 RenderSystem.texImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_ALPHA, width, height, 0, GL11.GL_ALPHA, GL11.GL_UNSIGNED_BYTE, pixels); RenderSystem.setTextureMinFilter(GL11.GL_LINEAR); RenderSystem.setTextureMagFilter(GL11.GL_LINEAR); RenderSystem.setTextureWrapS(GL12.GL_CLAMP_TO_EDGE); RenderSystem.setTextureWrapT(GL12.GL_CLAMP_TO_EDGE); RenderSystem.bindTexture(0); return textureId; } /** * 渲染文字(使用 RenderSystem 封装,不使用 glBegin/glEnd) * * @param text 要显示的文字 * @param x 世界坐标 X * @param y 世界坐标 Y * @param color 文字颜色 */ public void renderText(String text, float x, float y, Vector4f color) { if (!initialized || text == null || text.isEmpty()) return; RenderSystem.assertOnRenderThread(); // 保存当前状态 RenderSystem.pushState(); try { // 检查文本着色器是否存在,如果不存在则创建默认的 ShaderProgram shader = ShaderManagement.getShaderProgram("TextShader"); shader.use(); ShaderManagement.setUniformVec4(shader, "uColor", color); ShaderManagement.setUniformInt(shader, "uTexture", 0); RenderSystem.enableBlend(); RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); RenderSystem.disableDepthTest(); RenderSystem.activeTexture(GL13.GL_TEXTURE0); RenderSystem.bindTexture(fontTextureId); Vector2f offset = ModelRender.getCameraOffset(); float px = x + offset.x; float py = y + offset.y; try (MemoryStack stack = MemoryStack.stackPush()) { STBTTAlignedQuad q = STBTTAlignedQuad.mallocStack(stack); float[] xpos = {px}; float[] ypos = {py}; Tesselator tesselator = Tesselator.getInstance(); BufferBuilder builder = tesselator.getBuilder(); // 计算估计的顶点数量:每个字符6个顶点(2个三角形) int estimatedVertexCount = text.length() * 6; // 修复:begin方法需要2个参数 builder.begin(RenderSystem.DRAW_TRIANGLES, estimatedVertexCount); for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); if (c < firstChar || c >= firstChar + charCount) continue; stbtt_GetBakedQuad(charData, bitmapWidth, bitmapHeight, c - firstChar, xpos, ypos, q, true); // 使用两个三角形组成一个四边形 // 第一个三角形 builder.vertex(q.x0(), q.y0(), q.s0(), q.t0()); builder.vertex(q.x1(), q.y0(), q.s1(), q.t0()); builder.vertex(q.x0(), q.y1(), q.s0(), q.t1()); // 第二个三角形 builder.vertex(q.x1(), q.y0(), q.s1(), q.t0()); builder.vertex(q.x1(), q.y1(), q.s1(), q.t1()); builder.vertex(q.x0(), q.y1(), q.s0(), q.t1()); } tesselator.end(); } RenderSystem.checkGLError("renderText"); } finally { // 恢复之前的状态 RenderSystem.popState(); } } /** * 清理字体资源 */ public void cleanup() { if (fontTextureId != 0) { RenderSystem.deleteTextures(fontTextureId); fontTextureId = 0; } if (charData != null) { charData.free(); charData = null; } initialized = false; } public boolean isInitialized() { return initialized; } public int getFontTextureId() { return fontTextureId; } }