feat(render): 实现图层管理和渲染优化功能- 新增 LayerCellRenderer 类,用于渲染模型图层列表,支持可见性切换和缩略图显示- 添加 LayerOperationManager 类,提供图层的增删改查和视觉顺序调整功能
- 实现 LayerReorderTransferHandler 类,支持通过拖拽方式重新排列图层顺序- 优化 Mesh2D 类,引入 renderVertices 渲染缓存机制,提升渲染性能 - 完善二级顶点系统,增强网格变形算法,修复顶点移动和平移相关问题 - 改进三角分配变形算法,增加 pinned 控制点支持和整体位移校正 - 更新 GLContextManager任务队列处理逻辑,增加超时和中断处理机制- 修正模型包装器文档注释格式,提高代码可读性
This commit is contained in:
@@ -14,6 +14,7 @@ import com.chuangzhou.vivid2D.render.systems.sources.ShaderManagement;
|
||||
import com.chuangzhou.vivid2D.render.systems.sources.ShaderProgram;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Vector2f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
import org.lwjgl.opengl.GL15;
|
||||
@@ -674,6 +675,309 @@ public final class ModelRender {
|
||||
RenderSystem.checkGLError("render_end");
|
||||
}
|
||||
|
||||
// ================== 缩略图渲染方法 ==================
|
||||
|
||||
/**
|
||||
* 渲染模型缩略图(图层式渲染,不受摄像机控制)
|
||||
*
|
||||
* <p>该方法提供类似PS图层预览的缩略图渲染功能:</p>
|
||||
* <ul>
|
||||
* <li>固定位置和大小,不受摄像机影响</li>
|
||||
* <li>自动缩放确保模型完全可见</li>
|
||||
* <li>禁用复杂效果以提高性能</li>
|
||||
* <li>独立的渲染状态管理</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param model 要渲染的模型
|
||||
* @param x 缩略图左上角X坐标(屏幕坐标)
|
||||
* @param y 缩略图左上角Y坐标(屏幕坐标)
|
||||
* @param width 缩略图宽度
|
||||
* @param height 缩略图高度
|
||||
*/
|
||||
public static void renderThumbnail(Model2D model, float x, float y, float width, float height) {
|
||||
if (!initialized) throw new IllegalStateException("ModelRender not initialized");
|
||||
if (model == null) return;
|
||||
|
||||
RenderSystem.assertOnRenderThread();
|
||||
RenderSystem.checkGLError("renderThumbnail_start");
|
||||
|
||||
// 保存原始状态以便恢复
|
||||
boolean originalRenderColliders = renderColliders;
|
||||
boolean originalRenderLightPositions = renderLightPositions;
|
||||
int originalViewportWidth = viewportWidth;
|
||||
int originalViewportHeight = viewportHeight;
|
||||
|
||||
try {
|
||||
// 设置缩略图专用状态
|
||||
renderColliders = false;
|
||||
renderLightPositions = false;
|
||||
|
||||
// 设置缩略图视口(屏幕坐标)
|
||||
RenderSystem.viewport((int)x, (int)y, (int)width, (int)height);
|
||||
|
||||
// 清除缩略图区域
|
||||
RenderSystem.clear(GL11.GL_COLOR_BUFFER_BIT | (enableDepthTest ? GL11.GL_DEPTH_BUFFER_BIT : 0));
|
||||
RenderSystem.checkGLError("thumbnail_after_clear");
|
||||
|
||||
// 简化版的模型更新(跳过物理系统)
|
||||
model.update(0.016f); // 使用固定时间步长
|
||||
|
||||
// 计算模型边界和缩放比例
|
||||
ThumbnailBounds bounds = calculateThumbnailBounds(model, width, height);
|
||||
|
||||
// 设置缩略图专用的正交投影(固定位置,不受摄像机影响)
|
||||
Matrix3f proj = buildThumbnailProjection(width, height);
|
||||
Matrix3f view = new Matrix3f().identity();
|
||||
|
||||
// 使用默认着色器
|
||||
defaultProgram.use();
|
||||
RenderSystem.checkGLError("thumbnail_after_use_program");
|
||||
|
||||
// 设置基础变换矩阵
|
||||
setUniformMatrix3(defaultProgram, "uProjectionMatrix", proj);
|
||||
setUniformMatrix3(defaultProgram, "uViewMatrix", view);
|
||||
setUniformFloatInternal(defaultProgram, "uCameraZ", 0f); // 固定Z位置
|
||||
RenderSystem.checkGLError("thumbnail_after_set_matrices");
|
||||
|
||||
// 简化光源:只使用环境光
|
||||
setupThumbnailLighting(defaultProgram, model);
|
||||
RenderSystem.checkGLError("thumbnail_after_setup_lighting");
|
||||
|
||||
// 应用缩放和平移确保模型完全可见
|
||||
Matrix3f thumbnailTransform = new Matrix3f(
|
||||
bounds.scale, 0, bounds.offsetX,
|
||||
0, bounds.scale, bounds.offsetY,
|
||||
0, 0, 1
|
||||
);
|
||||
|
||||
// 递归渲染所有根部件(应用缩略图专用变换)
|
||||
for (ModelPart p : model.getParts()) {
|
||||
if (p.getParent() != null) continue;
|
||||
renderPartForThumbnail(p, thumbnailTransform);
|
||||
}
|
||||
RenderSystem.checkGLError("thumbnail_after_render_parts");
|
||||
|
||||
} finally {
|
||||
// 恢复原始状态
|
||||
renderColliders = originalRenderColliders;
|
||||
renderLightPositions = originalRenderLightPositions;
|
||||
RenderSystem.viewport(0, 0, originalViewportWidth, originalViewportHeight);
|
||||
}
|
||||
|
||||
RenderSystem.checkGLError("renderThumbnail_end");
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩略图边界计算结果
|
||||
*/
|
||||
private static class ThumbnailBounds {
|
||||
public float minX, maxX, minY, maxY;
|
||||
public float scale;
|
||||
public float offsetX, offsetY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算模型的边界和合适的缩放比例
|
||||
*/
|
||||
private static ThumbnailBounds calculateThumbnailBounds(Model2D model, float thumbWidth, float thumbHeight) {
|
||||
ThumbnailBounds bounds = new ThumbnailBounds();
|
||||
|
||||
// 初始化为极值
|
||||
bounds.minX = Float.MAX_VALUE;
|
||||
bounds.maxX = Float.MIN_VALUE;
|
||||
bounds.minY = Float.MAX_VALUE;
|
||||
bounds.maxY = Float.MIN_VALUE;
|
||||
|
||||
// 计算模型的世界坐标边界(递归遍历所有部件)
|
||||
calculateModelBounds(model, bounds, new Matrix3f().identity());
|
||||
|
||||
// 如果模型没有有效边界,使用默认值
|
||||
if (bounds.minX > bounds.maxX) {
|
||||
bounds.minX = -50f;
|
||||
bounds.maxX = 50f;
|
||||
bounds.minY = -50f;
|
||||
bounds.maxY = 50f;
|
||||
}
|
||||
|
||||
// 计算模型宽度和高度
|
||||
float modelWidth = bounds.maxX - bounds.minX;
|
||||
float modelHeight = bounds.maxY - bounds.minY;
|
||||
|
||||
// 计算中心点
|
||||
float centerX = (bounds.minX + bounds.maxX) * 0.5f;
|
||||
float centerY = (bounds.minY + bounds.maxY) * 0.5f;
|
||||
|
||||
// 计算缩放比例(考虑边距)
|
||||
float margin = 0.1f; // 10%边距
|
||||
float scaleX = (thumbWidth * (1 - margin)) / modelWidth;
|
||||
float scaleY = (thumbHeight * (1 - margin)) / modelHeight;
|
||||
bounds.scale = Math.min(scaleX, scaleY);
|
||||
|
||||
// 计算偏移量(将模型中心对齐到缩略图中心)
|
||||
bounds.offsetX = -centerX;
|
||||
bounds.offsetY = -centerY;
|
||||
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归计算模型的边界
|
||||
*/
|
||||
private static void calculateModelBounds(Model2D model, ThumbnailBounds bounds, Matrix3f parentTransform) {
|
||||
for (ModelPart part : model.getParts()) {
|
||||
if (part.getParent() != null) continue; // 只处理根部件
|
||||
|
||||
// 计算部件的世界变换
|
||||
part.updateWorldTransform(parentTransform, false);
|
||||
Matrix3f worldTransform = part.getWorldTransform();
|
||||
|
||||
// 计算部件的边界
|
||||
calculatePartBounds(part, bounds, worldTransform);
|
||||
|
||||
// 递归处理子部件
|
||||
for (ModelPart child : part.getChildren()) {
|
||||
calculateModelBoundsForPart(child, bounds, worldTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归计算部件及其子部件的边界
|
||||
*/
|
||||
private static void calculateModelBoundsForPart(ModelPart part, ThumbnailBounds bounds, Matrix3f parentTransform) {
|
||||
part.updateWorldTransform(parentTransform, false);
|
||||
Matrix3f worldTransform = part.getWorldTransform();
|
||||
|
||||
calculatePartBounds(part, bounds, worldTransform);
|
||||
|
||||
for (ModelPart child : part.getChildren()) {
|
||||
calculateModelBoundsForPart(child, bounds, worldTransform);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算单个部件的边界
|
||||
*/
|
||||
private static void calculatePartBounds(ModelPart part, ThumbnailBounds bounds, Matrix3f worldTransform) {
|
||||
for (Mesh2D mesh : part.getMeshes()) {
|
||||
if (!mesh.isVisible()) continue;
|
||||
|
||||
// 获取网格的顶点数据
|
||||
float[] vertices = mesh.getVertices(); // 假设有这个方法获取原始顶点
|
||||
if (vertices == null) continue;
|
||||
|
||||
// 变换顶点并更新边界
|
||||
for (int i = 0; i < vertices.length; i += 3) { // 假设顶点格式:x, y, z
|
||||
float x = vertices[i];
|
||||
float y = vertices[i + 1];
|
||||
|
||||
// 应用世界变换
|
||||
Vector3f transformed = new Vector3f(x, y, 1.0f);
|
||||
worldTransform.transform(transformed);
|
||||
|
||||
// 更新边界
|
||||
bounds.minX = Math.min(bounds.minX, transformed.x);
|
||||
bounds.maxX = Math.max(bounds.maxX, transformed.x);
|
||||
bounds.minY = Math.min(bounds.minY, transformed.y);
|
||||
bounds.maxY = Math.max(bounds.maxY, transformed.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建缩略图专用的正交投影矩阵
|
||||
*/
|
||||
private static Matrix3f buildThumbnailProjection(float width, float height) {
|
||||
Matrix3f m = new Matrix3f();
|
||||
// 标准正交投影,不受摄像机影响
|
||||
m.set(
|
||||
2.0f / width, 0.0f, -1.0f,
|
||||
0.0f, -2.0f / height, 1.0f,
|
||||
0.0f, 0.0f, 1.0f
|
||||
);
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩略图专用的部件渲染
|
||||
*/
|
||||
public static void renderPartForThumbnail(ModelPart part, Matrix3f parentTransform) {
|
||||
part.updateWorldTransform(parentTransform, false);
|
||||
Matrix3f world = part.getWorldTransform();
|
||||
|
||||
setPartUniforms(defaultProgram, part);
|
||||
setUniformMatrix3(defaultProgram, "uModelMatrix", world);
|
||||
|
||||
for (Mesh2D mesh : part.getMeshes()) {
|
||||
renderMeshForThumbnail(mesh, world);
|
||||
}
|
||||
|
||||
for (ModelPart child : part.getChildren()) {
|
||||
renderPartForThumbnail(child, world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩略图专用的网格渲染
|
||||
*/
|
||||
private static void renderMeshForThumbnail(Mesh2D mesh, Matrix3f modelMatrix) {
|
||||
if (!mesh.isVisible()) return;
|
||||
|
||||
Matrix3f matToUse = mesh.isBakedToWorld() ? new Matrix3f().identity() : new Matrix3f(modelMatrix);
|
||||
|
||||
if (mesh.getTexture() != null) {
|
||||
mesh.getTexture().bind(0);
|
||||
setUniformIntInternal(defaultProgram, "uTexture", 0);
|
||||
} else {
|
||||
RenderSystem.bindTexture(defaultTextureId);
|
||||
setUniformIntInternal(defaultProgram, "uTexture", 0);
|
||||
}
|
||||
|
||||
setUniformMatrix3(defaultProgram, "uModelMatrix", matToUse);
|
||||
mesh.draw(defaultProgram.programId, matToUse);
|
||||
|
||||
RenderSystem.checkGLError("renderMeshForThumbnail");
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置缩略图专用的简化光照
|
||||
*/
|
||||
private static void setupThumbnailLighting(ShaderProgram sp, Model2D model) {
|
||||
List<LightSource> lights = model.getLights();
|
||||
int ambientLightCount = 0;
|
||||
|
||||
// 查找环境光
|
||||
for (int i = 0; i < lights.size() && ambientLightCount < 1; i++) {
|
||||
LightSource light = lights.get(i);
|
||||
if (light.isEnabled() && light.isAmbient()) {
|
||||
setUniformVec2Internal(sp, "uLightsPos[0]", new Vector2f(0f, 0f));
|
||||
setUniformVec3Internal(sp, "uLightsColor[0]", light.getColor());
|
||||
setUniformFloatInternal(sp, "uLightsIntensity[0]", light.getIntensity());
|
||||
setUniformIntInternal(sp, "uLightsIsAmbient[0]", 1);
|
||||
setUniformIntInternal(sp, "uLightsIsGlow[0]", 0);
|
||||
ambientLightCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有环境光,创建一个默认的环境光
|
||||
if (ambientLightCount == 0) {
|
||||
setUniformVec2Internal(sp, "uLightsPos[0]", new Vector2f(0f, 0f));
|
||||
setUniformVec3Internal(sp, "uLightsColor[0]", new Vector3f(0.8f, 0.8f, 0.8f));
|
||||
setUniformFloatInternal(sp, "uLightsIntensity[0]", 1.0f);
|
||||
setUniformIntInternal(sp, "uLightsIsAmbient[0]", 1);
|
||||
setUniformIntInternal(sp, "uLightsIsGlow[0]", 0);
|
||||
ambientLightCount = 1;
|
||||
}
|
||||
|
||||
setUniformIntInternal(sp, "uLightCount", ambientLightCount);
|
||||
|
||||
// 禁用所有其他光源槽位
|
||||
for (int i = ambientLightCount; i < MAX_LIGHTS; i++) {
|
||||
setUniformFloatInternal(sp, "uLightsIntensity[" + i + "]", 0f);
|
||||
setUniformIntInternal(sp, "uLightsIsAmbient[" + i + "]", 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置所有非默认着色器的顶点坐标相关uniform
|
||||
|
||||
Reference in New Issue
Block a user