feat(render): 实现中文文本渲染与悬停提示功能- 在 Mesh2D 中增加悬停状态支持,允许显示红色边框和名称标签

- 添加 splitLines 方法支持文本自动换行显示
- 重构 TextRenderer 以支持 ASCII 和中文字符混合渲染
- 增加 getTextWidth 方法用于计算文本实际渲染宽度
- 修复 RenderSystem 中字体加载方法命名一致性问题- 调整 ModelRenderPanel 中坐标转换逻辑,确保拾取准确性
- 移除冗余的 Matrix3fUtils 引用,优化包导入结构- 完善 Mesh2D 绘制流程中的程序状态管理和纹理绑定操作- 为 Mesh2D 和 ModelPart 建立双向关联,便于获取模型部件名称
- 修改摄像机偏移计算方式,提高渲染坐标一致性
This commit is contained in:
tzdwindows 7
2025-10-25 10:08:09 +08:00
parent d2bb534d26
commit 331d836d62
10 changed files with 473 additions and 229 deletions

View File

@@ -195,6 +195,7 @@ public final class ModelRender {
private static final int FONT_BITMAP_HEIGHT = 512;
private static final int FONT_FIRST_CHAR = 32;
private static final int FONT_CHAR_COUNT = 96;
// ================== 摄像机API方法 ==================
/**
@@ -329,15 +330,15 @@ public final class ModelRender {
// 初始化默认字体(可替换为你自己的 TTF 数据)
ByteBuffer fontData = null;
try {
fontData = RenderSystem.loadWindowsFont("Arial.ttf");
fontData = RenderSystem.loadFont("FZYTK.TTF");
} catch (Exception e) {
logger.warn("Failed to load Arial.ttf, trying fallback fonts", e);
// 尝试其他字体
try {
fontData = RenderSystem.loadWindowsFont("arial.ttf");
fontData = RenderSystem.loadFont("arial.ttf");
} catch (Exception e2) {
try {
fontData = RenderSystem.loadWindowsFont("times.ttf");
fontData = RenderSystem.loadFont("times.ttf");
} catch (Exception e3) {
logger.error("All font loading attempts failed");
}
@@ -348,7 +349,7 @@ public final class ModelRender {
defaultTextRenderer = new TextRenderer(FONT_BITMAP_WIDTH, FONT_BITMAP_HEIGHT, FONT_FIRST_CHAR, FONT_CHAR_COUNT);
RenderSystem.checkGLError("TextRenderer constructor");
defaultTextRenderer.initialize(fontData, 32.0f); // 字体像素高度 32
defaultTextRenderer.initialize(fontData, 20.0f);
RenderSystem.checkGLError("defaultTextRenderer initialization");
if (!defaultTextRenderer.isInitialized()) {
@@ -635,17 +636,17 @@ public final class ModelRender {
RenderSystem.checkGLError("after_render_colliders");
}
if (defaultTextRenderer != null) {
String camInfo = String.format("Camera X: %.2f Y: %.2f Zoom: %.2f",
camera.getPosition().x,
camera.getPosition().y,
camera.getZoom());
float x = 10.0f;
float y = viewportHeight - 30.0f;
Vector4f color = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f);
renderText(camInfo, x, y, color);
RenderSystem.checkGLError("renderText");
}
//if (defaultTextRenderer != null) {
// String camInfo = String.format("Camera X: %.2f Y: %.2f Zoom: %.2f",
// camera.getPosition().x,
// camera.getPosition().y,
// camera.getZoom());
// float x = 10.0f;
// float y = viewportHeight - 30.0f;
// Vector4f color = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f);
// renderText(camInfo, x, y, color);
// RenderSystem.checkGLError("renderText");
//}
RenderSystem.checkGLError("render_end");
}
@@ -801,8 +802,12 @@ public final class ModelRender {
if (!mesh.isVisible()) return;
// 如果 mesh 已经被烘焙到世界坐标,则传 identity 矩阵给 shader防止重复变换
Matrix3f matToUse = mesh.isBakedToWorld() ? new Matrix3f().identity() : modelMatrix;
Matrix3f matToUse = mesh.isBakedToWorld() ? new Matrix3f().identity() : new Matrix3f(modelMatrix);
// 手动应用摄像机偏移
Vector2f offset = getCameraOffset();
matToUse.m20(matToUse.m20() - offset.x);
matToUse.m21(matToUse.m21() - offset.y);
// 设置纹理相关的uniform
if (mesh.getTexture() != null) {
mesh.getTexture().bind(0); // 绑定到纹理单元0
@@ -822,6 +827,7 @@ public final class ModelRender {
RenderSystem.checkGLError("renderMesh");
}
// ================== 渲染碰撞箱相关实现 ==================
private static void renderPhysicsColliders(PhysicsSystem physics) {
@@ -984,9 +990,14 @@ public final class ModelRender {
setUniformVec4Internal(sp, "uColor", new Vector4f(1,1,1,1));
}
public static TextRenderer getTextRenderer() {
return defaultTextRenderer;
}
// ================== 工具 ==================
private static Matrix3f buildOrthoProjection(int width, int height) {
Matrix3f m = new Matrix3f();
// 这个投影把屏幕像素坐标x in [0,width], y in [0,height])映射到 NDC [-1,1]x[1,-1]
m.set(
2.0f / width, 0.0f, -1.0f,
0.0f, -2.0f / height, 1.0f,
@@ -995,19 +1006,20 @@ public final class ModelRender {
return m;
}
/**
* 渲染文字
* @param text 文字内容
* @param x 世界坐标 X
* @param y 世界坐标 Y
* @param y 世界坐标 Y ,反转的
* @param color RGBA 颜色
*/
public static void renderText(String text, float x, float y, Vector4f color) {
if (!initialized || defaultTextRenderer == null) return;
RenderSystem.assertOnRenderThread();
Vector2f offset = getCameraOffset();
float px = x + offset.x;
float py = y + offset.y;
float px = x - offset.x;
float py = y - offset.y;
defaultTextRenderer.renderText(text, px, py, color);
}