feat(render): 实现模型图层管理与选中高亮功能

- 添加 ModelLayerPanel 图层管理面板,支持图层增删、重排、重命名- 实现 Mesh2D 选中状态管理与可视化高亮边框绘制
- 添加模型点击与悬停事件监听接口 ModelClickListener
- 引入完整着色器接口 CompleteShader 及默认片段着色器实现
- 改进 BufferUploader 支持颜色 uniform 传递- 完善 Mesh2D 复制逻辑与边界框计算方法
- 重构部分工具类包路径并增强矩阵工具功能
- 移除 LightSourceData 中冗余的构造逻辑

重要更新
- 更新了一个可视化界面可以控制图层顺序(ModelLayerPanel),并且给ModelRenderPanel增加了很多新功能,比如设置模型图层位置、大小
- 重写了逻辑着色器(Shader)、BufferUploader逻辑,让着色器能够规范的注册和使用
This commit is contained in:
tzdwindows 7
2025-10-17 18:16:24 +08:00
parent 27744d4b5c
commit 879069a9f4
25 changed files with 3700 additions and 838 deletions

View File

@@ -8,7 +8,9 @@ import com.chuangzhou.vivid2D.render.model.util.LightSource;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem;
import com.chuangzhou.vivid2D.render.systems.RenderSystem;
import com.chuangzhou.vivid2D.render.systems.ShaderSources;
import com.chuangzhou.vivid2D.render.systems.sources.CompleteShader;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
import org.joml.Matrix3f;
import org.joml.Vector2f;
import org.joml.Vector4f;
@@ -19,8 +21,6 @@ import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;
/**
* vivid2D 模型完整渲染系统
*
@@ -106,24 +106,20 @@ public final class ModelRender {
*/
private static final boolean enableBlending = true;
private static final int SHADER_MAX_LIGHTS = 8;
/**
* 最大光源数量,用于限制同时启用的光源数量
* 默认值80
*/
private static final int MAX_LIGHTS = 80;
// ================== 着色器与资源管理 ==================
/**
* 着色器程序缓存映射,按名称存储已编译的着色器程序
* 键:着色器名称(如 "default"
* 值:对应的着色器程序对象
* @see ShaderSources.ShaderProgram
*/
private static final Map<String, ShaderSources.ShaderProgram> shaderMap = new HashMap<>();
/**
* 默认着色器程序,用于大多数模型的渲染
* 包含基础的光照、纹理和变换功能
* @see #compileDefaultShader()
*/
private static ShaderSources.ShaderProgram defaultProgram = null;
private static ShaderProgram defaultProgram = null;
/**
* 网格GPU资源缓存管理已上传到GPU的网格数据
@@ -183,7 +179,7 @@ public final class ModelRender {
*/
public static boolean renderLightPositions = true;
// ================== 内部类:ShaderSources.ShaderProgram ==================
// ================== 内部类ShaderProgram ==================
// ================== 内部类MeshGLResources ==================
@@ -216,6 +212,10 @@ public final class ModelRender {
try {
compileDefaultShader();
// 初始化所有非默认着色器的基础信息
initNonDefaultShaders();
} catch (RuntimeException ex) {
logger.error("Failed to compile default shader: {}", ex.getMessage());
throw ex;
@@ -229,6 +229,76 @@ public final class ModelRender {
logger.info("ModelRender initialized successfully");
}
/**
* 初始化所有非默认着色器的基础信息(顶点坐标等)
*/
private static void initNonDefaultShaders() {
List<CompleteShader> shaderList = ShaderManagement.getShaderList();
if (shaderList == null || shaderList.isEmpty()) {
logger.info("No shaders found to initialize");
return;
}
int nonDefaultCount = 0;
for (CompleteShader shader : shaderList) {
// 跳过默认着色器,只初始化非默认的
if (shader.isDefaultShader()) {
continue;
}
try {
// 获取着色器程序
ShaderProgram program = ShaderManagement.getShaderProgram(shader.getShaderName());
if (program == null) {
logger.warn("Shader program not found for: {}", shader.getShaderName());
continue;
}
// 设置着色器的基础uniforms主要是顶点坐标相关的
initShaderBasicUniforms(program, shader);
nonDefaultCount++;
logger.debug("Initialized non-default shader: {}", shader.getShaderName());
} catch (Exception e) {
logger.error("Failed to initialize non-default shader: {}", shader.getShaderName(), e);
}
}
logger.info("Initialized {} non-default shaders", nonDefaultCount);
}
/**
* 初始化着色器的基础uniforms顶点坐标相关
*/
private static void initShaderBasicUniforms(ShaderProgram program, CompleteShader shader) {
program.use();
try {
// 设置基础的变换矩阵为单位矩阵
setUniformMatrix3(program, "uModelMatrix", new Matrix3f().identity());
setUniformMatrix3(program, "uViewMatrix", new Matrix3f().identity());
// 设置投影矩阵(使用当前视口尺寸)
Matrix3f projection = buildOrthoProjection(viewportWidth, viewportHeight);
setUniformMatrix3(program, "uProjectionMatrix", projection);
// 设置基础颜色为白色
setUniformVec4Internal(program, "uColor", new Vector4f(1.0f, 1.0f, 1.0f, 1.0f));
// 设置基础不透明度
setUniformFloatInternal(program, "uOpacity", 1.0f);
// 设置纹理单元(如果有纹理的话)
setUniformIntInternal(program, "uTexture", 0);
RenderSystem.checkGLError("initShaderBasicUniforms_" + shader.getShaderName());
} finally {
program.stop();
}
}
private static void logGLInfo() {
logger.info("OpenGL Vendor: {}", RenderSystem.getVendor());
logger.info("OpenGL Renderer: {}", RenderSystem.getRenderer());
@@ -238,12 +308,11 @@ public final class ModelRender {
}
private static void uploadLightsToShader(ShaderSources.ShaderProgram sp, Model2D model) {
private static void uploadLightsToShader(ShaderProgram sp, Model2D model) {
List<com.chuangzhou.vivid2D.render.model.util.LightSource> lights = model.getLights();
int idx = 0;
// 只上传已启用的光源,最多 MAX_LIGHTS8
for (int i = 0; i < lights.size() && idx < 8; i++) {
for (int i = 0; i < lights.size() && idx < MAX_LIGHTS; i++) {
com.chuangzhou.vivid2D.render.model.util.LightSource l = lights.get(i);
if (!l.isEnabled()) continue;
@@ -267,7 +336,7 @@ public final class ModelRender {
setUniformIntInternal(sp, "uLightCount", idx);
// 禁用剩余槽位(确保 shader 中不会读取到垃圾值)
for (int i = idx; i < 8; i++) {
for (int i = idx; i < MAX_LIGHTS; i++) {
setUniformFloatInternal(sp, "uLightsIntensity[" + i + "]", 0f);
setUniformIntInternal(sp, "uLightsIsAmbient[" + i + "]", 0);
setUniformVec3Internal(sp, "uLightsColor[" + i + "]", new org.joml.Vector3f(0f, 0f, 0f));
@@ -284,53 +353,48 @@ public final class ModelRender {
private static void setupGLState() {
RenderSystem.checkGLError("setupGLState_start");
RenderSystem.clearColor(CLEAR_COLOR.x, CLEAR_COLOR.y, CLEAR_COLOR.z, CLEAR_COLOR.w);
RenderSystem.checkGLError("after_clearColor");
if (enableBlending) {
RenderSystem.enableBlend();
RenderSystem.checkGLError("after_enableBlend");
RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
RenderSystem.checkGLError("after_blendFunc");
} else {
RenderSystem.disableBlend();
RenderSystem.checkGLError("after_disableBlend");
}
if (enableDepthTest) {
RenderSystem.enableDepthTest();
RenderSystem.checkGLError("after_enableDepthTest");
RenderSystem.depthFunc(GL11.GL_LEQUAL);
RenderSystem.checkGLError("after_depthFunc");
RenderSystem.depthMask(true);
RenderSystem.checkGLError("after_depthMask");
RenderSystem.clearDepth(1.0);
RenderSystem.checkGLError("after_clearDepth");
} else {
RenderSystem.disableDepthTest();
RenderSystem.checkGLError("after_disableDepthTest");
}
RenderSystem.checkGLError("setupGLState");
RenderSystem.checkGLError("after_disableCullFace");
}
private static void compileDefaultShader() {
int vs = compileShader(GL20.GL_VERTEX_SHADER, ShaderSources.VERTEX_SHADER_SRC);
int fs = compileShader(GL20.GL_FRAGMENT_SHADER, ShaderSources.FRAGMENT_SHADER_SRC);
int prog = linkProgram(vs, fs);
ShaderSources.ShaderProgram sp = new ShaderSources.ShaderProgram(prog);
shaderMap.put("default", sp);
defaultProgram = sp;
sp.use();
setUniformIntInternal(sp, "uTexture", 0);
setUniformFloatInternal(sp, "uOpacity", 1.0f);
setUniformVec4Internal(sp, "uColor", new Vector4f(1,1,1,1));
setUniformIntInternal(sp, "uBlendMode", 0);
setUniformIntInternal(sp, "uDebugMode", 0);
setUniformIntInternal(sp, "uLightCount", 0); // 默认没有光源
sp.stop();
}
private static int compileShader(int type, String src) {
RenderSystem.assertOnRenderThread();
return RenderSystem.compileShader(type, src);
}
private static int linkProgram(int vs, int fs) {
RenderSystem.assertOnRenderThread();
return RenderSystem.linkProgram(vs, fs);
ShaderManagement.compileAllShaders();
defaultProgram = ShaderManagement.getDefaultProgram();
if (defaultProgram == null) {
throw new RuntimeException("Failed to compile default shader: no default shader found");
}
}
private static void createDefaultTexture() {
@@ -348,9 +412,8 @@ public final class ModelRender {
for (MeshGLResources r : meshResources.values()) r.dispose();
meshResources.clear();
// shaders
for (ShaderSources.ShaderProgram sp : shaderMap.values()) sp.delete();
shaderMap.clear();
// 使用新的着色器管理系统清理着色器
ShaderManagement.cleanup();
defaultProgram = null;
// textures
@@ -393,25 +456,33 @@ public final class ModelRender {
return;
}
defaultProgram.use();
RenderSystem.checkGLError("after_use_program");
// 设置投影与视图
// 设置投影与视图矩阵(所有着色器都需要)
Matrix3f proj = buildOrthoProjection(viewportWidth, viewportHeight);
setUniformMatrix3(defaultProgram, "uProjectionMatrix", proj);
setUniformMatrix3(defaultProgram, "uViewMatrix", new Matrix3f().identity());
RenderSystem.checkGLError("after_set_matrices");
Matrix3f view = new Matrix3f().identity();
// 添加光源数据上传
// 1. 首先设置默认着色器
defaultProgram.use();
RenderSystem.checkGLError("after_use_default_program");
// 设置默认着色器的投影与视图
setUniformMatrix3(defaultProgram, "uProjectionMatrix", proj);
setUniformMatrix3(defaultProgram, "uViewMatrix", view);
RenderSystem.checkGLError("after_set_default_matrices");
// 添加光源数据上传到默认着色器
uploadLightsToShader(defaultProgram, model);
RenderSystem.checkGLError("after_upload_lights");
// 2. 设置非默认着色器的顶点坐标相关uniform
setupNonDefaultShaders(proj, view);
RenderSystem.checkGLError("after_setup_non_default_shaders");
// 在渲染光源位置前检查
RenderSystem.checkGLError("before_render_light_positions");
renderLightPositions(model);
RenderSystem.checkGLError("after_render_light_positions");
// 递归渲染所有根部件
// 递归渲染所有根部件(使用默认着色器)
Matrix3f identity = new Matrix3f().identity();
for (ModelPart p : model.getParts()) {
if (p.getParent() != null) continue;
@@ -428,6 +499,55 @@ public final class ModelRender {
RenderSystem.checkGLError("render_end");
}
/**
* 设置所有非默认着色器的顶点坐标相关uniform
*/
private static void setupNonDefaultShaders(Matrix3f projection, Matrix3f view) {
List<CompleteShader> shaderList = ShaderManagement.getShaderList();
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());
RenderSystem.checkGLError("setupNonDefaultShaders_" + shader.getShaderName());
} catch (Exception e) {
logger.warn("Failed to setup non-default shader: {}", shader.getShaderName(), e);
}
}
} finally {
// 恢复之前绑定的着色器程序
if (currentProgram != 0) {
GL20.glUseProgram(currentProgram);
}
}
}
private static void renderLightPositions(Model2D model) {
if (!renderLightPositions) return;
// 设置灯泡颜色为光源的颜色
@@ -658,39 +778,39 @@ public final class ModelRender {
}
// ================== uniform 设置辅助(内部使用,确保 program 已绑定) ==================
private static void setUniformIntInternal(ShaderSources.ShaderProgram sp, String name, int value) {
private static void setUniformIntInternal(ShaderProgram sp, String name, int value) {
int loc = sp.getUniformLocation(name);
if (loc != -1) RenderSystem.uniform1i(loc, value);
}
private static void setUniformVec3Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector3f vec) {
private static void setUniformVec3Internal(ShaderProgram sp, String name, org.joml.Vector3f vec) {
int loc = sp.getUniformLocation(name);
if (loc != -1) RenderSystem.uniform3f(loc, vec);
}
private static void setUniformVec2Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector2f vec) {
private static void setUniformVec2Internal(ShaderProgram sp, String name, org.joml.Vector2f vec) {
int loc = sp.getUniformLocation(name);
if (loc != -1) RenderSystem.uniform2f(loc, vec);
}
private static void setUniformFloatInternal(ShaderSources.ShaderProgram sp, String name, float value) {
private static void setUniformFloatInternal(ShaderProgram sp, String name, float value) {
int loc = sp.getUniformLocation(name);
if (loc != -1) RenderSystem.uniform1f(loc, value);
}
private static void setUniformVec4Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector4f vec) {
private static void setUniformVec4Internal(ShaderProgram sp, String name, org.joml.Vector4f vec) {
int loc = sp.getUniformLocation(name);
if (loc != -1) RenderSystem.uniform4f(loc, vec);
}
private static void setUniformMatrix3(ShaderSources.ShaderProgram sp, String name, org.joml.Matrix3f m) {
private static void setUniformMatrix3(ShaderProgram sp, String name, org.joml.Matrix3f m) {
int loc = sp.getUniformLocation(name);
if (loc == -1) return;
RenderSystem.uniformMatrix3(loc, m);
}
// ================== 部件属性 ==================
private static void setPartUniforms(ShaderSources.ShaderProgram sp, ModelPart part) {
private static void setPartUniforms(ShaderProgram sp, ModelPart part) {
setUniformFloatInternal(sp, "uOpacity", part.getOpacity());
int blend = 0;
ModelPart.BlendMode bm = part.getBlendMode();