From fe4142902cfaac133cf3b77c5242fc618d4d3de9 Mon Sep 17 00:00:00 2001 From: tzdwindows 7 <3076584115@qq.com> Date: Fri, 21 Nov 2025 16:46:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(render):=20=E5=AE=9E=E7=8E=B0=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E7=BC=A9=E6=94=BE=E6=94=AF=E6=8C=81=E4=B8=8E=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E6=B8=B2=E6=9F=93=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 BoundingBox 类添加获取中心点坐标的便捷方法 - 重构 Mesh2D 悬停提示框绘制逻辑,支持基于摄像机缩放的动态尺寸计算 - 在 ModelRender 中新增带缩放参数的文本渲染方法 - 重写 MultiSelectionBoxRenderer 以适配动态缩放,统一使用像素单位配置 - 优化 ParametersManagement 日志记录方式 - 修复 TextRenderer 字体颜色传递问题 - 更新 TextShader 着色器代码以兼容新的渲染管线和透明度处理 --- .../vivid2D/render/ModelRender.java | 26 +- .../render/MultiSelectionBoxRenderer.java | 348 ++++++++---------- .../vivid2D/render/TextRenderer.java | 1 + .../awt/manager/ParametersManagement.java | 6 +- .../vivid2D/render/model/Mesh2D.java | 72 ++-- .../render/model/util/BoundingBox.java | 16 + .../systems/sources/def/TextShader.java | 26 +- 7 files changed, 234 insertions(+), 261 deletions(-) diff --git a/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java b/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java index df28d05..c91c71b 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java @@ -52,7 +52,7 @@ import java.util.concurrent.atomic.AtomicInteger; *
  • {@link com.chuangzhou.vivid2D.test.ModelTest2} - 进阶模型测试
  • * * - * @author tzdwindows + * @author tzdwindows 7 * @version 1.2 * @since 2025-10-13 */ @@ -982,38 +982,23 @@ public final class ModelRender { if (shaderList == null || shaderList.isEmpty()) { return; } - - // 保存当前绑定的着色器程序 int currentProgram = GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM); - try { for (CompleteShader shader : shaderList) { - // 跳过默认着色器 if (shader.isDefaultShader()) { continue; } - try { - // 获取着色器程序 ShaderProgram program = ShaderManagement.getShaderProgram(shader.getShaderName()); if (program == null || program.programId == 0) { continue; } - program.use(); - - // 只设置顶点坐标相关的uniform setUniformMatrix3(program, "uProjectionMatrix", projection); setUniformMatrix3(program, "uViewMatrix", view); - - // 设置基础模型矩阵为单位矩阵 setUniformMatrix3(program, "uModelMatrix", new Matrix3f().identity()); - - // 设置摄像机Z轴位置 setUniformFloatInternal(program, "uCameraZ", camera.getZPosition()); - RenderSystem.checkGLError("setupNonDefaultShaders_" + shader.getShaderName()); - } catch (Exception e) { logger.warn("Failed to setup non-default shader: {}", shader.getShaderName(), e); } @@ -1353,6 +1338,15 @@ public final class ModelRender { defaultTextRenderer.renderText(text, px, py, color); } + public static void renderText(String text, float x, float y, Vector4f color, float scale) { + if (!initialized || defaultTextRenderer == null) return; + RenderSystem.assertOnRenderThread(); + Vector2f offset = getCameraOffset(); + float px = x - offset.x; + float py = y - offset.y; + defaultTextRenderer.renderText(text, px, py, color, scale); + } + /** * 获取默认摄像机与当前摄像机之间的偏移量 * diff --git a/src/main/java/com/chuangzhou/vivid2D/render/MultiSelectionBoxRenderer.java b/src/main/java/com/chuangzhou/vivid2D/render/MultiSelectionBoxRenderer.java index f367d55..ba417e2 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/MultiSelectionBoxRenderer.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/MultiSelectionBoxRenderer.java @@ -9,100 +9,105 @@ import org.joml.Vector4f; import org.lwjgl.opengl.GL11; /** - * MultiSelectionBoxRenderer — 美观版完整实现 + * MultiSelectionBoxRenderer — 美观版完整实现 (已适配动态缩放 - 边框与手柄) * - * 视觉设计目标: - * - 细腻的三层边框(外发光 -> 主边框 -> 内描边) - * - 小巧但可见的手柄(角点与边中点略有区分) - * - 精致的中心点(十字 + 细环) - * - 虚线用于多选边框 - * - 批处理绘制以减少 Draw Call(尽量在一次 TRIANGLES 调用中绘制多数元素) - * - * 注:BufferBuilder 的 vertex(...) 方法签名与项目实现有关,示例中使用 (x,y,z,u) 占位。 + * 特性: + * - 边框、手柄、中心点的大小都会根据视图缩放动态调整,确保在任何缩放级别下都清晰可见。 + * - 所有元素尽可能在一次 GL_TRIANGLES draw call 中完成,以提高效率。 */ public class MultiSelectionBoxRenderer { - // -------------------- 配置常量(可调) -------------------- - // 尺寸 - public static final float DEFAULT_CORNER_SIZE = 8.0f; + // -------------------- 配置常量 (世界单位) -------------------- public static final float DEFAULT_DASH_LENGTH = 10.0f; public static final float DEFAULT_GAP_LENGTH = 6.0f; - private static final float MAIN_BORDER_THICKNESS = 2.5f; - // 手柄与中心点尺寸 - private static final float HANDLE_CORNER_SIZE = DEFAULT_CORNER_SIZE; - private static final float HANDLE_MID_SIZE = 2.8f; - private static final float CENTER_LINE_THICKNESS = 1.2f; - private static final float CENTER_RING_RADIUS = 5.0f; - private static final float CENTER_RING_THICKNESS = 1.2f; + // -------------------- 配置常量 (屏幕像素单位) -------------------- + // 这些值定义了元素在屏幕上看起来应该有多大 + private static final float PIXEL_MAIN_BORDER_THICKNESS = 2.0f; // <-- 新增:主边框的像素厚度 + private static final float PIXEL_HANDLE_CORNER_SIZE = 8.0f; + private static final float PIXEL_HANDLE_MID_SIZE = 6.0f; + private static final float PIXEL_CENTER_LINE_THICKNESS = 1.5f; + private static final float PIXEL_CENTER_CROSS_RADIUS = 7.0f; - // 颜色(更现代、更清爽) - public static final Vector4f DASHED_BORDER_COLOR = new Vector4f(1.0f, 0.85f, 0.0f, 1.0f); // 黄色虚线 - public static final Vector4f SOLID_BORDER_COLOR_OUTER = new Vector4f(0.0f, 0.85f, 0.95f, 0.12f); // 轻微外发光(弱透明) - public static final Vector4f SOLID_BORDER_COLOR_MAIN = new Vector4f(0.0f, 0.92f, 0.94f, 1.0f); // 主边框(青) - public static final Vector4f SOLID_BORDER_COLOR_INNER = new Vector4f(1.0f, 1.0f, 1.0f, 0.9f); // 内描边(接近白) - public static final Vector4f HANDLE_COLOR = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f); // 手柄白 - public static final Vector4f MULTI_SELECTION_HANDLE_COLOR = new Vector4f(1.0f, 0.9f, 0.0f, 1.0f); // 黄色手柄 - public static final Vector4f CENTER_POINT_COLOR = new Vector4f(1.0f, 0.2f, 0.2f, 1.0f); // 中心点红 - - // -------------------- 公共绘制 API -------------------- + // 颜色 + public static final Vector4f DASHED_BORDER_COLOR = new Vector4f(1.0f, 0.85f, 0.0f, 1.0f); + public static final Vector4f SOLID_BORDER_COLOR_MAIN = new Vector4f(0.0f, 0.92f, 0.94f, 1.0f); + public static final Vector4f HANDLE_COLOR = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f); + public static final Vector4f MULTI_SELECTION_HANDLE_COLOR = new Vector4f(1.0f, 0.9f, 0.0f, 1.0f); + public static final Vector4f CENTER_POINT_COLOR = new Vector4f(1.0f, 0.2f, 0.2f, 1.0f); /** * 绘制单选的选择框(主入口) * * @param bounds 包围盒(世界坐标) * @param pivot 旋转中心 / 中心点(世界坐标) + * @param zoom 当前摄像机的缩放值 (e.g., from ModelRender.getCamera().getZoom()) */ - public static void drawSelectBox(BoundingBox bounds, Vector2f pivot) { - if (bounds == null || !bounds.isValid()) return; + public static void drawSelectBox(BoundingBox bounds, Vector2f pivot, float zoom) { + if (bounds == null || !bounds.isValid() || zoom <= 1e-6f) return; + + // 根据 zoom 计算所有元素在世界坐标下的实际尺寸 + float worldBorderThickness = PIXEL_MAIN_BORDER_THICKNESS / zoom; + float worldCornerSize = PIXEL_HANDLE_CORNER_SIZE / zoom; + float worldMidSize = PIXEL_HANDLE_MID_SIZE / zoom; + float worldCenterLineThickness = PIXEL_CENTER_LINE_THICKNESS / zoom; + float worldCenterCrossRadius = PIXEL_CENTER_CROSS_RADIUS / zoom; + float minX = bounds.getMinX(); float minY = bounds.getMinY(); float maxX = bounds.getMaxX(); float maxY = bounds.getMaxY(); + Tesselator tesselator = Tesselator.getInstance(); BufferBuilder bb = tesselator.getBuilder(); RenderSystem.enableBlend(); RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - float scaledCornerSize = Math.max(4.0f, DEFAULT_CORNER_SIZE); - float scaledMidSize = Math.max(2.0f, HANDLE_MID_SIZE); - bb.begin(GL11.GL_LINES, 8); // 4条线 * 2个顶点 = 8个顶点 - bb.setColor(SOLID_BORDER_COLOR_MAIN); - addLineVertices(bb, minX, minY, maxX, minY); // 上边 - addLineVertices(bb, maxX, minY, maxX, maxY); // 右边 - addLineVertices(bb, maxX, maxY, minX, maxY); // 下边 - addLineVertices(bb, minX, maxY, minX, minY); // 左边 - tesselator.end(); - bb.begin(RenderSystem.GL_TRIANGLES, (8 + 2) * 6); - bb.setColor(HANDLE_COLOR); - addHandleQuad(bb, minX, minY, scaledCornerSize); - addHandleQuad(bb, maxX, minY, scaledCornerSize); - addHandleQuad(bb, minX, maxY, scaledCornerSize); - addHandleQuad(bb, maxX, maxY, scaledCornerSize); - addHandleQuad(bb, (minX + maxX) * 0.5f, minY, scaledMidSize); - addHandleQuad(bb, (minX + maxX) * 0.5f, maxY, scaledMidSize); - addHandleQuad(bb, minX, (minY + maxY) * 0.5f, scaledMidSize); - addHandleQuad(bb, maxX, (minY + maxY) * 0.5f, scaledMidSize); - bb.setColor(CENTER_POINT_COLOR); - addQuadLine(bb, pivot.x - 6.0f, pivot.y, pivot.x + 6.0f, pivot.y, CENTER_LINE_THICKNESS); - addQuadLine(bb, pivot.x, pivot.y - 6.0f, pivot.x, pivot.y + 6.0f, CENTER_LINE_THICKNESS); - tesselator.end(); - } - /** - * 添加简单的线段顶点(GL_LINES模式) - */ - private static void addLineVertices(BufferBuilder bb, float x0, float y0, float x1, float y1) { - bb.vertex(x0, y0, 0.0f, 0.0f); - bb.vertex(x1, y1, 0.0f, 0.0f); + // 将所有绘制合并到一次 TRIANGLES 调用中 + // 预估顶点数:4条边*6 + 8个手柄*6 + 中心十字*6*2 = 24 + 48 + 12 = 84 + bb.begin(RenderSystem.GL_TRIANGLES, 96); + + // 1. 绘制有厚度的边框 + bb.setColor(SOLID_BORDER_COLOR_MAIN); + addQuadLine(bb, minX, minY, maxX, minY, worldBorderThickness); // 上边 + addQuadLine(bb, maxX, minY, maxX, maxY, worldBorderThickness); // 右边 + addQuadLine(bb, maxX, maxY, minX, maxY, worldBorderThickness); // 下边 + addQuadLine(bb, minX, maxY, minX, minY, worldBorderThickness); // 左边 + + // 2. 绘制手柄 + bb.setColor(HANDLE_COLOR); + addHandleQuad(bb, minX, minY, worldCornerSize); + addHandleQuad(bb, maxX, minY, worldCornerSize); + addHandleQuad(bb, minX, maxY, worldCornerSize); + addHandleQuad(bb, maxX, maxY, worldCornerSize); + addHandleQuad(bb, (minX + maxX) * 0.5f, minY, worldMidSize); + addHandleQuad(bb, (minX + maxX) * 0.5f, maxY, worldMidSize); + addHandleQuad(bb, minX, (minY + maxY) * 0.5f, worldMidSize); + addHandleQuad(bb, maxX, (minY + maxY) * 0.5f, worldMidSize); + + // 3. 绘制中心点 + bb.setColor(CENTER_POINT_COLOR); + addQuadLine(bb, pivot.x - worldCenterCrossRadius, pivot.y, pivot.x + worldCenterCrossRadius, pivot.y, worldCenterLineThickness); + addQuadLine(bb, pivot.x, pivot.y - worldCenterCrossRadius, pivot.x, pivot.y + worldCenterCrossRadius, worldCenterLineThickness); + + tesselator.end(); } /** * 绘制多选框(虚线 + 手柄) * * @param multiBounds 多选包围盒 + * @param zoom 当前摄像机的缩放值 */ - public static void drawMultiSelectionBox(BoundingBox multiBounds) { - if (multiBounds == null || !multiBounds.isValid()) return; + public static void drawMultiSelectionBox(BoundingBox multiBounds, float zoom) { + if (multiBounds == null || !multiBounds.isValid() || zoom <= 1e-6f) return; + + // 根据 zoom 计算所有元素在世界坐标下的实际尺寸 + float worldBorderThickness = PIXEL_MAIN_BORDER_THICKNESS / zoom; + float worldCornerSize = PIXEL_HANDLE_CORNER_SIZE / zoom; + float worldMidSize = PIXEL_HANDLE_MID_SIZE / zoom; + float worldCenterLineThickness = PIXEL_CENTER_LINE_THICKNESS / zoom; + float worldCenterCrossRadius = PIXEL_CENTER_CROSS_RADIUS / zoom; float minX = multiBounds.getMinX(); float minY = multiBounds.getMinY(); @@ -111,157 +116,52 @@ public class MultiSelectionBoxRenderer { Tesselator tesselator = Tesselator.getInstance(); BufferBuilder bb = tesselator.getBuilder(); - RenderSystem.enableBlend(); RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); - // 1) 虚线边框(使用 GL_LINES) + // 合并所有绘制 int estimatedSegments = Math.max(4, (int) Math.ceil((2f * (multiBounds.getWidth() + multiBounds.getHeight())) / (DEFAULT_DASH_LENGTH + DEFAULT_GAP_LENGTH))); - bb.begin(GL11.GL_LINES, estimatedSegments * 2); + bb.begin(RenderSystem.GL_TRIANGLES, estimatedSegments * 6 * 4 + 96); // 넉넉하게 할당 + + // 1. 绘制有厚度的虚线边框 bb.setColor(DASHED_BORDER_COLOR); - addDashedLineVertices(bb, minX, minY, maxX, minY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH); - addDashedLineVertices(bb, maxX, minY, maxX, maxY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH); - addDashedLineVertices(bb, maxX, maxY, minX, maxY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH); - addDashedLineVertices(bb, minX, maxY, minX, minY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH); - tesselator.end(); + addThickDashedLine(bb, minX, minY, maxX, minY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH, worldBorderThickness); + addThickDashedLine(bb, maxX, minY, maxX, maxY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH, worldBorderThickness); + addThickDashedLine(bb, maxX, maxY, minX, maxY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH, worldBorderThickness); + addThickDashedLine(bb, minX, maxY, minX, minY, DEFAULT_DASH_LENGTH, DEFAULT_GAP_LENGTH, worldBorderThickness); - // 2) 手柄与中心(合并为一次三角形绘制) - bb.begin(RenderSystem.GL_TRIANGLES, (8 + 2) * 6); + // 2. 绘制手柄 bb.setColor(MULTI_SELECTION_HANDLE_COLOR); - addHandleQuad(bb, minX, minY, HANDLE_CORNER_SIZE); - addHandleQuad(bb, maxX, minY, HANDLE_CORNER_SIZE); - addHandleQuad(bb, minX, maxY, HANDLE_CORNER_SIZE); - addHandleQuad(bb, maxX, maxY, HANDLE_CORNER_SIZE); - - addHandleQuad(bb, (minX + maxX) * 0.5f, minY, HANDLE_MID_SIZE); - addHandleQuad(bb, (minX + maxX) * 0.5f, maxY, HANDLE_MID_SIZE); - addHandleQuad(bb, minX, (minY + maxY) * 0.5f, HANDLE_MID_SIZE); - addHandleQuad(bb, maxX, (minY + maxY) * 0.5f, HANDLE_MID_SIZE); + addHandleQuad(bb, minX, minY, worldCornerSize); + addHandleQuad(bb, maxX, minY, worldCornerSize); + addHandleQuad(bb, minX, maxY, worldCornerSize); + addHandleQuad(bb, maxX, maxY, worldCornerSize); + addHandleQuad(bb, (minX + maxX) * 0.5f, minY, worldMidSize); + addHandleQuad(bb, (minX + maxX) * 0.5f, maxY, worldMidSize); + addHandleQuad(bb, minX, (minY + maxY) * 0.5f, worldMidSize); + addHandleQuad(bb, maxX, (minY + maxY) * 0.5f, worldMidSize); + // 3. 绘制中心点 Vector2f center = multiBounds.getCenter(); bb.setColor(CENTER_POINT_COLOR); - addQuadLine(bb, center.x - 6.0f, center.y, center.x + 6.0f, center.y, CENTER_LINE_THICKNESS); - addQuadLine(bb, center.x, center.y - 6.0f, center.x, center.y + 6.0f, CENTER_LINE_THICKNESS); + addQuadLine(bb, center.x - worldCenterCrossRadius, center.y, center.x + worldCenterCrossRadius, center.y, worldCenterLineThickness); + addQuadLine(bb, center.x, center.y - worldCenterCrossRadius, center.x, center.y + worldCenterCrossRadius, worldCenterLineThickness); tesselator.end(); } + // -------------------- 辅助绘图方法 -------------------- /** - * 添加一个填充四边形(用两个三角形表示) - */ - private static void addFilledQuad(BufferBuilder bb, float x0, float y0, float x1, float y1) { - // tri 1 - bb.vertex(x0, y0, 0.0f, 0.0f); - bb.vertex(x1, y0, 0.0f, 0.0f); - bb.vertex(x1, y1, 0.0f, 0.0f); - // tri 2 - bb.vertex(x1, y1, 0.0f, 0.0f); - bb.vertex(x0, y1, 0.0f, 0.0f); - bb.vertex(x0, y0, 0.0f, 0.0f); - } - - /** - * 手柄:以中心点绘制正方形手柄(填充) - */ - private static void addHandleQuad(BufferBuilder bb, float cx, float cy, float size) { - float half = size * 0.5f; - addFilledQuad(bb, cx - half, cy - half, cx + half, cy + half); - } - - /** - * 绘制一条由四边形模拟的线段(厚度可控) - */ - private static void addQuadLine(BufferBuilder bb, float x0, float y0, float x1, float y1, float thickness) { - float dx = x1 - x0; - float dy = y1 - y0; - float len = (float) Math.sqrt(dx * dx + dy * dy); - if (len < 1e-6f) return; - - float halfThick = thickness * 0.5f; - float nx = -dy / len * halfThick; - float ny = dx / len * halfThick; - - float v0x = x0 + nx; float v0y = y0 + ny; - float v1x = x1 + nx; float v1y = y1 + ny; - float v2x = x1 - nx; float v2y = y1 - ny; - float v3x = x0 - nx; float v3y = y0 - ny; - - bb.vertex(v0x, v0y, 0.0f, 0.0f); - bb.vertex(v1x, v1y, 0.0f, 0.0f); - bb.vertex(v2x, v2y, 0.0f, 0.0f); - - bb.vertex(v2x, v2y, 0.0f, 0.0f); - bb.vertex(v3x, v3y, 0.0f, 0.0f); - bb.vertex(v0x, v0y, 0.0f, 0.0f); - } - - - /** - * 绘制一个闭合的四边形线环(用于边框三层绘制) + * (新增) 在两点之间生成有厚度的虚线段 (使用 GL_TRIANGLES) * - * @param thickness 厚度(世界坐标) - * @param vertices 顶点序列 x1,y1,x2,y2,... + * @param dashLen 虚线长度(世界坐标) + * @param gapLen 间隙长度(世界坐标) + * @param thickness 虚线的厚度(世界坐标) */ - private static void addQuadLineLoop(BufferBuilder bb, float thickness, float... vertices) { - if (vertices == null || vertices.length < 8) return; - int n = vertices.length / 2; - for (int i = 0; i < n; i++) { - float x0 = vertices[(i * 2)]; - float y0 = vertices[(i * 2) + 1]; - float x1 = vertices[((i + 1) % n) * 2]; - float y1 = vertices[((i + 1) % n) * 2 + 1]; - addQuadLine(bb, x0, y0, x1, y1, thickness); - } - } - - /** - * 绘制圆环(由多个三角形片段组成) - * - * @param cx 中心 x - * @param cy 中心 y - * @param radius 半径 - * @param thickness 环厚度 - * @param segments 分段数(建议 >= 8) - */ - private static void addRing(BufferBuilder bb, float cx, float cy, float radius, float thickness, int segments) { - if (segments < 6) segments = 6; - float halfThick = thickness * 0.5f; - float innerR = Math.max(0.5f, radius - halfThick); - float outerR = radius + halfThick; - - for (int i = 0; i < segments; i++) { - float a0 = (float) (i * 2.0 * Math.PI / segments); - float a1 = (float) ((i + 1) * 2.0 * Math.PI / segments); - - float cos0 = (float) Math.cos(a0), sin0 = (float) Math.sin(a0); - float cos1 = (float) Math.cos(a1), sin1 = (float) Math.sin(a1); - - float x0i = cx + cos0 * innerR, y0i = cy + sin0 * innerR; - float x1i = cx + cos1 * innerR, y1i = cy + sin1 * innerR; - float x0o = cx + cos0 * outerR, y0o = cy + sin0 * outerR; - float x1o = cx + cos1 * outerR, y1o = cy + sin1 * outerR; - - // tri 1 - bb.vertex(x0i, y0i, 0.0f, 0.0f); - bb.vertex(x0o, y0o, 0.0f, 0.0f); - bb.vertex(x1o, y1o, 0.0f, 0.0f); - // tri 2 - bb.vertex(x1o, y1o, 0.0f, 0.0f); - bb.vertex(x1i, y1i, 0.0f, 0.0f); - bb.vertex(x0i, y0i, 0.0f, 0.0f); - } - } - - /** - * 在两点之间生成虚线段顶点(使用 GL_LINES) - * - * @param dashLen 虚线长度(世界坐标) - * @param gapLen 间隙长度(世界坐标) - */ - private static void addDashedLineVertices(BufferBuilder bb, float startX, float startY, float endX, float endY, - float dashLen, float gapLen) { + private static void addThickDashedLine(BufferBuilder bb, float startX, float startY, float endX, float endY, + float dashLen, float gapLen, float thickness) { float dx = endX - startX; float dy = endY - startY; float len = (float) Math.sqrt(dx * dx + dy * dy); @@ -281,8 +181,60 @@ public class MultiSelectionBoxRenderer { float ex = startX + dirX * e; float ey = startY + dirY * e; - bb.vertex(sx, sy, 0.0f, 0.0f); - bb.vertex(ex, ey, 0.0f, 0.0f); + // 为每一小段虚线绘制一个有厚度的四边形 + addQuadLine(bb, sx, sy, ex, ey, thickness); } } -} + + /** + * 手柄:以中心点绘制正方形手柄(填充) + */ + private static void addHandleQuad(BufferBuilder bb, float cx, float cy, float size) { + float half = size * 0.5f; + addFilledQuad(bb, cx - half, cy - half, cx + half, cy + half); + } + + /** + * 绘制一条由四边形模拟的线段(厚度可控) + */ + public static void addQuadLine(BufferBuilder bb, float x0, float y0, float x1, float y1, float thickness) { + float dx = x1 - x0; + float dy = y1 - y0; + float len = (float) Math.sqrt(dx * dx + dy * dy); + if (len < 1e-6f) return; + + float halfThick = thickness * 0.5f; + // 计算线段的法线向量 + float nx = -dy / len * halfThick; + float ny = dx / len * halfThick; + + // 计算四边形的四个顶点 + float v0x = x0 + nx; float v0y = y0 + ny; + float v1x = x1 + nx; float v1y = y1 + ny; + float v2x = x1 - nx; float v2y = y1 - ny; + float v3x = x0 - nx; float v3y = y0 - ny; + + addQuadVertices(bb, v0x, v0y, v1x, v1y, v2x, v2y, v3x, v3y); + } + + /** + * 添加一个填充四边形(用两个三角形表示) + */ + public static void addFilledQuad(BufferBuilder bb, float x0, float y0, float x1, float y1) { + addQuadVertices(bb, x0, y0, x1, y0, x1, y1, x0, y1); + } + + /** + * 辅助方法:添加构成四边形的6个顶点 + */ + private static void addQuadVertices(BufferBuilder bb, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) { + // tri 1 + bb.vertex(x0, y0, 0.0f, 0.0f); + bb.vertex(x1, y1, 0.0f, 0.0f); + bb.vertex(x2, y2, 0.0f, 0.0f); + // tri 2 + bb.vertex(x2, y2, 0.0f, 0.0f); + bb.vertex(x3, y3, 0.0f, 0.0f); + bb.vertex(x0, y0, 0.0f, 0.0f); + } +} \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/render/TextRenderer.java b/src/main/java/com/chuangzhou/vivid2D/render/TextRenderer.java index 21915d2..e2e5590 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/TextRenderer.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/TextRenderer.java @@ -196,6 +196,7 @@ public final class TextRenderer { // 按字符类型分组渲染以减少纹理切换 int currentTexture = -1; boolean batchStarted = false; + builder.setColor(color); for (int i = 0; i < text.length(); i++) { char c = text.charAt(i); diff --git a/src/main/java/com/chuangzhou/vivid2D/render/awt/manager/ParametersManagement.java b/src/main/java/com/chuangzhou/vivid2D/render/awt/manager/ParametersManagement.java index 1c46a73..c46b6f5 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/awt/manager/ParametersManagement.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/awt/manager/ParametersManagement.java @@ -19,19 +19,17 @@ import java.util.*; public class ParametersManagement { private static final Logger logger = LoggerFactory.getLogger(ParametersManagement.class); private final ParametersPanel parametersPanel; - private final ModelRenderPanel renderPanel; public List oldValues = new ArrayList<>(); public ParametersManagement(ParametersPanel parametersPanel) { this.parametersPanel = parametersPanel; - this.renderPanel = parametersPanel.getRenderPanel(); - + ModelRenderPanel renderPanel = parametersPanel.getRenderPanel(); for (int i = 0; i < renderPanel.getModel().getParts().size(); i++) { ModelPart modelPart = renderPanel.getModel().getParts().get(i); modelPart.addEvent((eventName, eventBus) -> { if (eventName.equals("vertex")){ if (!(eventBus instanceof Map)) { - System.err.println("Error: eventBus is not a Map for vertex event."); + logger.error("Error: eventBus is not a Map for vertex event."); return; } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/Mesh2D.java b/src/main/java/com/chuangzhou/vivid2D/render/model/Mesh2D.java index cad7db2..b37df68 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/Mesh2D.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/model/Mesh2D.java @@ -1561,54 +1561,64 @@ public class Mesh2D { if (isSuspension && !selected) { RenderSystem.pushState(); - setSolidShader(modelMatrix); - RenderSystem.enableBlend(); RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); Tesselator t = Tesselator.getInstance(); BufferBuilder bb = t.getBuilder(); - BoundingBox bbox = getBounds(); - if (bbox != null && bbox.isValid()) { - bb.begin(GL11.GL_LINE_LOOP, 4); + float zoom = ModelRender.getCamera().getZoom(); + + if (bbox != null && bbox.isValid() && zoom > 1e-6f) { + + // --- 1. 定义所有元素的期望“像素”尺寸 --- + final float PIXEL_BORDER_THICKNESS = 1.5f; + final float PIXEL_BOX_OFFSET_Y = 8.0f; // 提示框距离物体顶部的垂直像素距离 + final float PIXEL_FONT_SIZE_BASE = 14.0f; + final float PIXEL_LINE_HEIGHT = 18.0f; + final float PIXEL_PADDING = 5.0f; // 文本背景框的内边距 + + // --- 2. 根据 zoom 计算出在“世界坐标”中应有的大小 --- + float worldBorderThickness = PIXEL_BORDER_THICKNESS / zoom; + float worldBoxOffsetY = PIXEL_BOX_OFFSET_Y / zoom; + float worldLineHeight = PIXEL_LINE_HEIGHT / zoom; + float worldPadding = PIXEL_PADDING / zoom; + float textScale = (PIXEL_FONT_SIZE_BASE / 14.0f) / zoom; + + // --- 3. 绘制动态厚度的红色悬停边框 --- + bb.begin(GL11.GL_TRIANGLES, 4 * 6); bb.setColor(new Vector4f(1f, 0f, 0f, 1f)); - bb.vertex(bbox.getMinX(), bbox.getMinY(), 0f, 0f); - bb.vertex(bbox.getMaxX(), bbox.getMinY(), 0f, 0f); - bb.vertex(bbox.getMaxX(), bbox.getMaxY(), 0f, 0f); - bb.vertex(bbox.getMinX(), bbox.getMaxY(), 0f, 0f); + MultiSelectionBoxRenderer.addQuadLine(bb, bbox.getMinX(), bbox.getMinY(), bbox.getMaxX(), bbox.getMinY(), worldBorderThickness); + MultiSelectionBoxRenderer.addQuadLine(bb, bbox.getMaxX(), bbox.getMinY(), bbox.getMaxX(), bbox.getMaxY(), worldBorderThickness); + MultiSelectionBoxRenderer.addQuadLine(bb, bbox.getMaxX(), bbox.getMaxY(), bbox.getMinX(), bbox.getMaxY(), worldBorderThickness); + MultiSelectionBoxRenderer.addQuadLine(bb, bbox.getMinX(), bbox.getMaxY(), bbox.getMinX(), bbox.getMinY(), worldBorderThickness); t.end(); + // --- 4. 计算文本布局 --- String hoverText = getName(); - float textX = bbox.getMaxX() + 5f; - float textY = bbox.getMaxY(); - Vector4f bgColor = new Vector4f(1f, 0f, 0f, 0.8f); - Vector4f fgColor = new Vector4f(1f, 1f, 1f, 1f); - - float lineHeight = 18f; - List lines = splitLines(hoverText, 30); - - float textHeight = lines.size() * lineHeight; - float textWidth = 0f; + float maxTextWidth = 0f; for (String line : lines) { - textWidth = Math.max(textWidth, ModelRender.getTextRenderer().getTextWidth(line)); + maxTextWidth = Math.max(maxTextWidth, ModelRender.getTextRenderer().getTextWidth(line) * textScale); } - + float totalTextHeight = (lines.size() -1) * worldLineHeight; + float boxX = bbox.getCenterX() - (maxTextWidth / 2f); + float boxY = bbox.getMaxY() + worldBoxOffsetY; + Vector4f bgColor = new Vector4f(1f, 0f, 0f, 0.8f); bb.begin(GL11.GL_TRIANGLES, 6); bb.setColor(bgColor); - bb.vertex(textX, textY, 0f, 0f); - bb.vertex(textX + textWidth, textY, 0f, 0f); - bb.vertex(textX + textWidth, textY + textHeight, 0f, 0f); - bb.vertex(textX + textWidth, textY + textHeight, 0f, 0f); - bb.vertex(textX, textY + textHeight, 0f, 0f); - bb.vertex(textX, textY, 0f, 0f); + float bgX0 = boxX - worldPadding; + float bgY0 = boxY - worldPadding; + float bgX1 = boxX + maxTextWidth + worldPadding; + float bgY1 = boxY + totalTextHeight + worldPadding + worldLineHeight; + MultiSelectionBoxRenderer.addFilledQuad(bb, bgX0, bgY0, bgX1, bgY1); t.end(); - + Vector4f fgColor = new Vector4f(1f, 1f, 1f, 1f); for (int i = 0; i < lines.size(); i++) { String line = lines.get(i); - ModelRender.renderText(line, textX, textY + (i + 1) * lineHeight - 5, fgColor); + float lineY = boxY + (lines.size() - 1 - i) * worldLineHeight; + ModelRender.renderText(line, boxX, lineY + 30.0f, fgColor, textScale); } } @@ -1665,7 +1675,7 @@ public class Mesh2D { private void drawSelectBox() { BoundingBox bounds = getBounds(); - MultiSelectionBoxRenderer.drawSelectBox(bounds, pivot); + MultiSelectionBoxRenderer.drawSelectBox(bounds, pivot, ModelRender.getCamera().getZoom()); } /** @@ -1774,7 +1784,7 @@ public class Mesh2D { */ private void drawMultiSelectionBox() { BoundingBox multiBounds = getMultiSelectionBounds(); - MultiSelectionBoxRenderer.drawMultiSelectionBox(multiBounds); + MultiSelectionBoxRenderer.drawMultiSelectionBox(multiBounds,ModelRender.getCamera().getZoom()); } /** diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/util/BoundingBox.java b/src/main/java/com/chuangzhou/vivid2D/render/model/util/BoundingBox.java index dcf2158..501bf03 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/util/BoundingBox.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/model/util/BoundingBox.java @@ -630,4 +630,20 @@ public class BoundingBox { public BoundingBox copy() { return new BoundingBox(this); } + + /** + * 获取包围盒的中心点 X 坐标。 + * @return 中心点的 X 坐标 + */ + public float getCenterX() { + return (this.minX + this.maxX) * 0.5f; + } + + /** + * 获取包围盒的中心点 Y 坐标。 + * @return 中心点的 Y 坐标 + */ + public float getCenterY() { + return (this.minY + this.maxY) * 0.5f; + } } \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/sources/def/TextShader.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/sources/def/TextShader.java index 0007641..6ea07e8 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/systems/sources/def/TextShader.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/sources/def/TextShader.java @@ -6,7 +6,7 @@ import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram; import org.joml.Vector4f; /** - * 文本着色器 + * 文本着色器 (已修正并与渲染引擎兼容) * * @author tzdwindows 7 */ @@ -14,11 +14,6 @@ public class TextShader implements CompleteShader { private final VertexShader vertexShader = new VertexShader(); private final FragmentShader fragmentShader = new FragmentShader(); - private final Vector4f color = new Vector4f(1, 1, 1, 1); - - public void setColor(Vector4f color) { - this.color.set(color); - } @Override public Shader getVertexShader() { @@ -42,12 +37,11 @@ public class TextShader implements CompleteShader { @Override public void setDefaultUniforms(ShaderProgram program) { - // 传递颜色 uniform - program.setUniform4f("uColor", color.x, color.y, color.z, color.w); - // 纹理通常绑定到0号纹理单元 + program.setUniform4f("uColor", 1.0f, 1.0f, 1.0f, 1.0f); program.setUniform1i("uTexture", 0); } + // --- Vertex Shader (已适配 mat3 和 uCameraZ) --- private static class VertexShader implements Shader { @Override public String getShaderCode() { @@ -56,12 +50,18 @@ public class TextShader implements CompleteShader { layout(location = 0) in vec2 aPosition; layout(location = 1) in vec2 aTexCoord; + uniform mat3 uModelMatrix; + uniform mat3 uViewMatrix; uniform mat3 uProjectionMatrix; + + uniform float uCameraZ; out vec2 vTexCoord; void main() { - vec3 p = uProjectionMatrix * vec3(aPosition, 1.0); + vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0); + + // 输出为 gl_Position (vec4) gl_Position = vec4(p.xy, 0.0, 1.0); vTexCoord = aTexCoord; } @@ -86,9 +86,11 @@ public class TextShader implements CompleteShader { uniform vec4 uColor; void main() { - // 使用 .r 通道读取单通道纹理 float alpha = texture(uTexture, vTexCoord).r; FragColor = vec4(uColor.rgb, uColor.a * alpha); + if (FragColor.a < 0.01) { + discard; + } } """; } @@ -98,4 +100,4 @@ public class TextShader implements CompleteShader { return "TextFragmentShader"; } } -} +} \ No newline at end of file