feat(render): 实现中文文本渲染与悬停提示功能- 在 Mesh2D 中增加悬停状态支持,允许显示红色边框和名称标签
- 添加 splitLines 方法支持文本自动换行显示 - 重构 TextRenderer 以支持 ASCII 和中文字符混合渲染 - 增加 getTextWidth 方法用于计算文本实际渲染宽度 - 修复 RenderSystem 中字体加载方法命名一致性问题- 调整 ModelRenderPanel 中坐标转换逻辑,确保拾取准确性 - 移除冗余的 Matrix3fUtils 引用,优化包导入结构- 完善 Mesh2D 绘制流程中的程序状态管理和纹理绑定操作- 为 Mesh2D 和 ModelPart 建立双向关联,便于获取模型部件名称 - 修改摄像机偏移计算方式,提高渲染坐标一致性
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user