- 添加 Camera 类,支持位置、缩放、Z轴控制- 在 ModelRender 中集成摄像机投影矩阵计算 - 实现屏幕坐标到世界坐标的转换方法 - 添加默认文字渲染器和字体加载逻辑 - 在渲染面板中添加摄像机控制的鼠标手势支持 - 支持通过鼠标滚轮进行摄像机缩放操作 - 添加摄像机状态显示和调试信息渲染 - 实现多选框渲染逻辑的重构和优化 -修复坐标系变换相关的边界框计算问题 - 增加摄像机启用/禁用快捷键支持
237 lines
8.0 KiB
Java
237 lines
8.0 KiB
Java
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;
|
||
}
|
||
} |