feat(render): 实现模型部件变换控制面板

- 新增 TransformPanel 类,提供图形界面控制模型部件的位移、旋转、缩放和中心点
- 在 ModelLayerPanelTest 中集成变换面板,支持自动更新选中部件
- 为 ModelPart 添加事件系统,支持变换属性变更通知
- 实现 Mesh2D 的 pivot 和 originalPivot 分离,支持更精确的变换控制- 添加 ModelEvent 接口,用于模型部件事件触发机制
- 优化 ModelRenderPanel 的选中部件获取逻辑
- 完善模型点击监听器,支持自动切换到变换控制选项卡
-修复拖拽移动中心点时的边界检查问题
- 增强各变换操作的边界验证和错误处理
- 改进中心点绘制逻辑,增加边界检查和回退机制

重要更新
- 修复上个版本的所有问题,并且增加新的面板观测图层的各种信息
This commit is contained in:
tzdwindows 7
2025-10-18 15:27:04 +08:00
parent b3c50ca794
commit 6a3eb89aaf
7 changed files with 815 additions and 85 deletions

View File

@@ -3,6 +3,7 @@ package com.chuangzhou.vivid2D.test;
import com.chuangzhou.vivid2D.render.awt.ModelClickListener;
import com.chuangzhou.vivid2D.render.awt.ModelLayerPanel;
import com.chuangzhou.vivid2D.render.awt.ModelRenderPanel;
import com.chuangzhou.vivid2D.render.awt.TransformPanel;
import com.chuangzhou.vivid2D.render.model.Model2D;
import com.chuangzhou.vivid2D.render.model.ModelPart;
import com.chuangzhou.vivid2D.render.model.util.Mesh2D;
@@ -30,20 +31,13 @@ public class ModelLayerPanelTest {
}
// 创建 UI
JFrame frame = new JFrame("ModelLayerPanel 测试(含渲染面板)");
JFrame frame = new JFrame("ModelLayerPanel 测试(含渲染面板和变换面板");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
// 左侧:图层面板(传入 renderPanel 后可在面板中绑定贴图到 GL 上下文)
// 先创建一个占位 renderPanel再把它传给 layerPanelModelRenderPanel 构造需要尺寸)
ModelRenderPanel renderPanel = new ModelRenderPanel(model, 640, 480);
//renderPanel.addModelClickListener(new ModelClickListener() {
// @Override
// public void onModelClicked(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
// if (mesh == null) return;
// System.out.println("点击了模型:" + mesh.getName() + ",模型坐标:" + modelX + ", " + modelY + ",屏幕坐标:" + screenX + ", " + screenY);
// }
//});
ModelLayerPanel layerPanel = new ModelLayerPanel(model, renderPanel);
layerPanel.setPreferredSize(new Dimension(260, 600));
frame.add(layerPanel, BorderLayout.WEST);
@@ -52,11 +46,25 @@ public class ModelLayerPanelTest {
renderPanel.setPreferredSize(new Dimension(640, 480));
frame.add(renderPanel, BorderLayout.CENTER);
// 右侧:显示模型树(用于观察当前模型部件结构)
// 创建变换面板
TransformPanel transformPanel = new TransformPanel(renderPanel);
// 右侧:创建选项卡面板,包含模型树和变换面板
JTabbedPane rightTabbedPane = new JTabbedPane();
// 模型树选项卡
JTree tree = new JTree(model.toTreeNode());
JScrollPane treeScroll = new JScrollPane(tree);
treeScroll.setPreferredSize(new Dimension(240, 600));
frame.add(treeScroll, BorderLayout.EAST);
treeScroll.setPreferredSize(new Dimension(280, 600));
rightTabbedPane.addTab("模型结构", treeScroll);
// 变换面板选项卡
JScrollPane transformScroll = new JScrollPane(transformPanel);
transformScroll.setPreferredSize(new Dimension(280, 600));
rightTabbedPane.addTab("变换控制", transformScroll);
rightTabbedPane.setPreferredSize(new Dimension(300, 600));
frame.add(rightTabbedPane, BorderLayout.EAST);
// 底部:演示按钮(刷新树以反映面板中对模型的更改)
JPanel bottom = new JPanel(new FlowLayout(FlowLayout.LEFT));
@@ -80,8 +88,39 @@ public class ModelLayerPanelTest {
});
bottom.add(printOrderBtn);
// 添加选中部件更新按钮
JButton updateSelectionBtn = new JButton("更新选中部件");
updateSelectionBtn.addActionListener(e -> {
renderPanel.executeInGLContext(() -> {
ModelPart selectedPart = renderPanel.getSelectedPart();
transformPanel.setSelectedPart(selectedPart);
if (selectedPart != null) {
System.out.println("已选中部件: " + selectedPart.getName());
} else {
System.out.println("未选中任何部件");
}
});
});
bottom.add(updateSelectionBtn);
frame.add(bottom, BorderLayout.SOUTH);
// 添加模型点击监听器,自动更新变换面板的选中部件
renderPanel.addModelClickListener(new ModelClickListener() {
@Override
public void onModelClicked(Mesh2D mesh, float modelX, float modelY, int screenX, int screenY) {
if (mesh == null) return;
System.out.println("点击了模型:" + mesh.getName() + ",模型坐标:" + modelX + ", " + modelY + ",屏幕坐标:" + screenX + ", " + screenY);
// 自动更新变换面板的选中部件
ModelPart selectedPart = renderPanel.getSelectedPart();
transformPanel.setSelectedPart(selectedPart);
// 切换到变换控制选项卡
rightTabbedPane.setSelectedIndex(1);
}
});
// 监听窗口关闭,确保释放 GL 资源
frame.addWindowListener(new java.awt.event.WindowAdapter() {
@Override
@@ -104,9 +143,9 @@ public class ModelLayerPanelTest {
}
});
frame.setSize(1200, 700);
frame.setSize(1300, 700);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
}