feat(render): 实现模型异步加载与渲染优化
- 在 GLContextManager 中添加模型加载完成的 CompletableFuture 支持- 优化 LiquifyTargetPartRander 使用 renderVertices 替代 vertices- 移除 LiquifyTool 中冗余的 Ctrl 键判断与强制重绘逻辑 - Mesh2D 中移除已废弃的 pinnedController 字段 - MeshData 中新增 renderVertices、isSuspension 等渲染相关字段- ModelLayerPanel 支持模型异步加载完成后的初始化 - ModelRenderPanel 添加模型获取的同步等待机制 - 清理大量冗余注释与无用代码,提升代码可读性
This commit is contained in:
@@ -461,14 +461,9 @@ public final class ModelRender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void logGLInfo() {
|
private static void logGLInfo() {
|
||||||
logger.info("OpenGL Vendor: {}", RenderSystem.getVendor());
|
|
||||||
logger.info("OpenGL Renderer: {}", RenderSystem.getRenderer());
|
|
||||||
logger.info("OpenGL Version: {}", RenderSystem.getOpenGLVersion());
|
|
||||||
logger.info("GLSL Version: {}", RenderSystem.getGLSLVersion());
|
|
||||||
RenderSystem.logDetailedGLInfo();
|
RenderSystem.logDetailedGLInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void uploadLightsToShader(ShaderProgram sp, Model2D model) {
|
private static void uploadLightsToShader(ShaderProgram sp, Model2D model) {
|
||||||
List<com.chuangzhou.vivid2D.render.model.util.LightSource> lights = model.getLights();
|
List<com.chuangzhou.vivid2D.render.model.util.LightSource> lights = model.getLights();
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// ModelLayerPanel.java (现代化重构)
|
|
||||||
package com.chuangzhou.vivid2D.render.awt;
|
package com.chuangzhou.vivid2D.render.awt;
|
||||||
|
|
||||||
import com.chuangzhou.vivid2D.render.awt.manager.LayerOperationManager;
|
import com.chuangzhou.vivid2D.render.awt.manager.LayerOperationManager;
|
||||||
@@ -36,7 +35,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
private DefaultListModel<ModelPart> listModel;
|
private DefaultListModel<ModelPart> listModel;
|
||||||
private JList<ModelPart> layerList;
|
private JList<ModelPart> layerList;
|
||||||
|
|
||||||
// 现代化UI组件
|
|
||||||
private ModernButton addButton;
|
private ModernButton addButton;
|
||||||
private ModernButton removeButton;
|
private ModernButton removeButton;
|
||||||
private ModernButton upButton;
|
private ModernButton upButton;
|
||||||
@@ -51,37 +49,40 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
|
|
||||||
private volatile boolean ignoreSliderEvents = false;
|
private volatile boolean ignoreSliderEvents = false;
|
||||||
|
|
||||||
// 使用重构后的工具类
|
|
||||||
private ThumbnailManager thumbnailManager;
|
private ThumbnailManager thumbnailManager;
|
||||||
private PSDImporter psdImporter;
|
private PSDImporter psdImporter;
|
||||||
private LayerOperationManager operationManager;
|
private LayerOperationManager operationManager;
|
||||||
|
|
||||||
// 现代化颜色方案
|
|
||||||
private static final Color BACKGROUND_COLOR = new Color(45, 45, 48);
|
private static final Color BACKGROUND_COLOR = new Color(45, 45, 48);
|
||||||
private static final Color SURFACE_COLOR = new Color(62, 62, 66);
|
private static final Color SURFACE_COLOR = new Color(62, 62, 66);
|
||||||
private static final Color ACCENT_COLOR = new Color(0, 122, 204);
|
private static final Color ACCENT_COLOR = new Color(0, 122, 204);
|
||||||
private static final Color TEXT_COLOR = new Color(241, 241, 241);
|
private static final Color TEXT_COLOR = new Color(241, 241, 241);
|
||||||
private static final Color BORDER_COLOR = new Color(87, 87, 87);
|
private static final Color BORDER_COLOR = new Color(87, 87, 87);
|
||||||
|
|
||||||
public ModelLayerPanel(Model2D model) {
|
public ModelLayerPanel(ModelRenderPanel renderPanel) {
|
||||||
this(model, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelLayerPanel(Model2D model, ModelRenderPanel renderPanel) {
|
|
||||||
this.model = model;
|
|
||||||
this.renderPanel = renderPanel;
|
this.renderPanel = renderPanel;
|
||||||
|
this.model = renderPanel.getModel();
|
||||||
// 设置现代化外观
|
|
||||||
setupModernLookAndFeel();
|
setupModernLookAndFeel();
|
||||||
|
|
||||||
// 初始化工具类
|
|
||||||
this.thumbnailManager = new ThumbnailManager(renderPanel);
|
this.thumbnailManager = new ThumbnailManager(renderPanel);
|
||||||
this.psdImporter = new PSDImporter(model, renderPanel, this);
|
if (this.model != null) {
|
||||||
this.operationManager = new LayerOperationManager(model);
|
this.psdImporter = new PSDImporter(model, renderPanel, this);
|
||||||
|
this.operationManager = new LayerOperationManager(model);
|
||||||
initComponents();
|
initComponents();
|
||||||
reloadFromModel();
|
reloadFromModel();
|
||||||
generateAllThumbnails();
|
generateAllThumbnails();
|
||||||
|
} else {
|
||||||
|
initComponents();
|
||||||
|
renderPanel.getGlContextManager().waitForModel().thenAccept(m -> {
|
||||||
|
if (m == null) return;
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
this.model = m;
|
||||||
|
this.psdImporter = new PSDImporter(model, renderPanel, ModelLayerPanel.this);
|
||||||
|
this.operationManager = new LayerOperationManager(model);
|
||||||
|
reloadFromModel();
|
||||||
|
generateAllThumbnails();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupModernLookAndFeel() {
|
private void setupModernLookAndFeel() {
|
||||||
@@ -119,13 +120,10 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 现代化组件初始化 ==============
|
|
||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
setLayout(new BorderLayout(10, 10));
|
setLayout(new BorderLayout(10, 10));
|
||||||
listModel = new DefaultListModel<>();
|
listModel = new DefaultListModel<>();
|
||||||
layerList = createModernList();
|
layerList = createModernList();
|
||||||
|
|
||||||
// 创建现代化布局
|
|
||||||
createHeaderPanel();
|
createHeaderPanel();
|
||||||
createCenterPanel();
|
createCenterPanel();
|
||||||
createControlPanel();
|
createControlPanel();
|
||||||
@@ -137,19 +135,13 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
list.setBackground(SURFACE_COLOR);
|
list.setBackground(SURFACE_COLOR);
|
||||||
list.setForeground(TEXT_COLOR);
|
list.setForeground(TEXT_COLOR);
|
||||||
list.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
list.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
list.setFixedCellHeight(70); // 增加行高以显示缩略图
|
list.setFixedCellHeight(70);
|
||||||
|
|
||||||
// 使用独立的渲染器
|
|
||||||
LayerCellRenderer cellRenderer = new LayerCellRenderer(this, thumbnailManager);
|
LayerCellRenderer cellRenderer = new LayerCellRenderer(this, thumbnailManager);
|
||||||
cellRenderer.attachMouseListener(list, listModel);
|
cellRenderer.attachMouseListener(list, listModel);
|
||||||
list.setCellRenderer(cellRenderer);
|
list.setCellRenderer(cellRenderer);
|
||||||
|
|
||||||
// 使用独立的拖拽处理器
|
|
||||||
list.setDragEnabled(true);
|
list.setDragEnabled(true);
|
||||||
list.setTransferHandler(new LayerReorderTransferHandler(this));
|
list.setTransferHandler(new LayerReorderTransferHandler(this));
|
||||||
list.setDropMode(DropMode.INSERT);
|
list.setDropMode(DropMode.INSERT);
|
||||||
|
|
||||||
// 双击重命名
|
|
||||||
list.addMouseListener(new MouseAdapter() {
|
list.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void mouseClicked(MouseEvent e) {
|
public void mouseClicked(MouseEvent e) {
|
||||||
@@ -161,10 +153,7 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 选择变更监听器
|
|
||||||
list.addListSelectionListener(e -> updateUIState());
|
list.addListSelectionListener(e -> updateUIState());
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,23 +174,14 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
JScrollPane scrollPane = new JScrollPane(layerList);
|
JScrollPane scrollPane = new JScrollPane(layerList);
|
||||||
scrollPane.setBorder(createModernBorder("图层列表"));
|
scrollPane.setBorder(createModernBorder("图层列表"));
|
||||||
scrollPane.getViewport().setBackground(SURFACE_COLOR);
|
scrollPane.getViewport().setBackground(SURFACE_COLOR);
|
||||||
|
|
||||||
// 自定义滚动条
|
|
||||||
JScrollBar verticalScrollBar = scrollPane.getVerticalScrollBar();
|
|
||||||
//verticalScrollBar.setUI(new ModernScrollBarUI());
|
|
||||||
|
|
||||||
add(scrollPane, BorderLayout.CENTER);
|
add(scrollPane, BorderLayout.CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createControlPanel() {
|
private void createControlPanel() {
|
||||||
JPanel controlPanel = new JPanel(new BorderLayout(10, 10));
|
JPanel controlPanel = new JPanel(new BorderLayout(10, 10));
|
||||||
controlPanel.setBackground(BACKGROUND_COLOR);
|
controlPanel.setBackground(BACKGROUND_COLOR);
|
||||||
|
|
||||||
// 顶部按钮面板
|
|
||||||
controlPanel.add(createButtonPanel(), BorderLayout.NORTH);
|
controlPanel.add(createButtonPanel(), BorderLayout.NORTH);
|
||||||
// 底部设置面板
|
|
||||||
controlPanel.add(createSettingsPanel(), BorderLayout.SOUTH);
|
controlPanel.add(createSettingsPanel(), BorderLayout.SOUTH);
|
||||||
|
|
||||||
add(controlPanel, BorderLayout.SOUTH);
|
add(controlPanel, BorderLayout.SOUTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,14 +190,12 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
buttonPanel.setBackground(BACKGROUND_COLOR);
|
buttonPanel.setBackground(BACKGROUND_COLOR);
|
||||||
buttonPanel.setBorder(createModernBorder("操作"));
|
buttonPanel.setBorder(createModernBorder("操作"));
|
||||||
|
|
||||||
// 创建现代化按钮
|
|
||||||
addButton = createIconButton("⊕", "添加图层", this::showAddMenu);
|
addButton = createIconButton("⊕", "添加图层", this::showAddMenu);
|
||||||
removeButton = createIconButton("⊖", "删除选中图层", this::onRemoveLayer);
|
removeButton = createIconButton("⊖", "删除选中图层", this::onRemoveLayer);
|
||||||
upButton = createIconButton("↑", "上移图层", this::moveSelectedUp);
|
upButton = createIconButton("↑", "上移图层", this::moveSelectedUp);
|
||||||
downButton = createIconButton("↓", "下移图层", this::moveSelectedDown);
|
downButton = createIconButton("↓", "下移图层", this::moveSelectedDown);
|
||||||
bindTextureButton = createIconButton("📷", "绑定贴图", this::bindTextureToSelectedPart);
|
bindTextureButton = createIconButton("📷", "绑定贴图", this::bindTextureToSelectedPart);
|
||||||
|
|
||||||
// 初始禁用状态
|
|
||||||
removeButton.setEnabled(false);
|
removeButton.setEnabled(false);
|
||||||
upButton.setEnabled(false);
|
upButton.setEnabled(false);
|
||||||
downButton.setEnabled(false);
|
downButton.setEnabled(false);
|
||||||
@@ -237,7 +215,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
settingsPanel.setBackground(BACKGROUND_COLOR);
|
settingsPanel.setBackground(BACKGROUND_COLOR);
|
||||||
settingsPanel.setBorder(createModernBorder("图层设置"));
|
settingsPanel.setBorder(createModernBorder("图层设置"));
|
||||||
|
|
||||||
// 不透明度控制
|
|
||||||
JPanel opacityPanel = new JPanel(new BorderLayout(8, 0));
|
JPanel opacityPanel = new JPanel(new BorderLayout(8, 0));
|
||||||
opacityPanel.setBackground(BACKGROUND_COLOR);
|
opacityPanel.setBackground(BACKGROUND_COLOR);
|
||||||
|
|
||||||
@@ -267,9 +244,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
slider.setBackground(BACKGROUND_COLOR);
|
slider.setBackground(BACKGROUND_COLOR);
|
||||||
slider.setForeground(ACCENT_COLOR);
|
slider.setForeground(ACCENT_COLOR);
|
||||||
|
|
||||||
// 自定义滑块UI
|
|
||||||
//slider.setUI(new ModernSliderUI());
|
|
||||||
|
|
||||||
return slider;
|
return slider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +296,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
operationManager.addLayer(name);
|
operationManager.addLayer(name);
|
||||||
reloadFromModel();
|
reloadFromModel();
|
||||||
|
|
||||||
// 选中新创建的部件
|
|
||||||
ModelPart newPart = findPartByName(name);
|
ModelPart newPart = findPartByName(name);
|
||||||
if (newPart != null) {
|
if (newPart != null) {
|
||||||
selectPart(newPart);
|
selectPart(newPart);
|
||||||
@@ -341,7 +314,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
return model.getPartMap();
|
return model.getPartMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 现代化对话框方法 ==============
|
|
||||||
private void showRenameDialog(ModelPart part) {
|
private void showRenameDialog(ModelPart part) {
|
||||||
String newName = (String) JOptionPane.showInputDialog(
|
String newName = (String) JOptionPane.showInputDialog(
|
||||||
this,
|
this,
|
||||||
@@ -360,7 +332,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============== 原有业务方法(保持不变) ==============
|
|
||||||
public void setModel(Model2D model) {
|
public void setModel(Model2D model) {
|
||||||
this.model = model;
|
this.model = model;
|
||||||
this.psdImporter = new PSDImporter(model, renderPanel, this);
|
this.psdImporter = new PSDImporter(model, renderPanel, this);
|
||||||
@@ -454,7 +425,6 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 现代化边框
|
|
||||||
private TitledBorder createModernBorder(String title) {
|
private TitledBorder createModernBorder(String title) {
|
||||||
TitledBorder border = BorderFactory.createTitledBorder(
|
TitledBorder border = BorderFactory.createTitledBorder(
|
||||||
BorderFactory.createLineBorder(BORDER_COLOR, 1, true),
|
BorderFactory.createLineBorder(BORDER_COLOR, 1, true),
|
||||||
@@ -485,7 +455,7 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 现代化按钮类
|
// 现代化按钮类
|
||||||
private class ModernButton extends JButton {
|
private static class ModernButton extends JButton {
|
||||||
public ModernButton(String text) {
|
public ModernButton(String text) {
|
||||||
super(text);
|
super(text);
|
||||||
setupModernStyle();
|
setupModernStyle();
|
||||||
@@ -520,7 +490,7 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 现代化菜单项类
|
// 现代化菜单项类
|
||||||
private class ModernMenuItem extends JMenuItem {
|
private static class ModernMenuItem extends JMenuItem {
|
||||||
public ModernMenuItem(String text) {
|
public ModernMenuItem(String text) {
|
||||||
super(text);
|
super(text);
|
||||||
setBackground(SURFACE_COLOR);
|
setBackground(SURFACE_COLOR);
|
||||||
@@ -544,16 +514,13 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 现代化弹出菜单
|
// 现代化弹出菜单
|
||||||
private class ModernPopupMenu extends JPopupMenu {
|
private static class ModernPopupMenu extends JPopupMenu {
|
||||||
public ModernPopupMenu() {
|
public ModernPopupMenu() {
|
||||||
setBackground(SURFACE_COLOR);
|
setBackground(SURFACE_COLOR);
|
||||||
setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
|
setBorder(BorderFactory.createLineBorder(BORDER_COLOR));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其余原有方法保持不变...
|
|
||||||
// (reloadFromModel, performVisualReorder, bindTextureToSelectedPart等)
|
|
||||||
|
|
||||||
public void reloadFromModel() {
|
public void reloadFromModel() {
|
||||||
ModelPart selected = layerList.getSelectedValue();
|
ModelPart selected = layerList.getSelectedValue();
|
||||||
|
|
||||||
@@ -745,13 +712,11 @@ public class ModelLayerPanel extends JPanel {
|
|||||||
String name = JOptionPane.showInputDialog(this, "新图层名称:", f.getName());
|
String name = JOptionPane.showInputDialog(this, "新图层名称:", f.getName());
|
||||||
if (name == null || name.trim().isEmpty()) name = f.getName();
|
if (name == null || name.trim().isEmpty()) name = f.getName();
|
||||||
|
|
||||||
// 创建部件与 Mesh
|
|
||||||
ModelPart part = model.createPart(name);
|
ModelPart part = model.createPart(name);
|
||||||
Mesh2D mesh = MeshTextureUtil.createQuadForImage(img, name + "_mesh");
|
Mesh2D mesh = MeshTextureUtil.createQuadForImage(img, name + "_mesh");
|
||||||
mesh.createDefaultSecondaryVertices();
|
mesh.createDefaultSecondaryVertices();
|
||||||
part.addMesh(mesh);
|
part.addMesh(mesh);
|
||||||
|
|
||||||
// 创建纹理
|
|
||||||
if (renderPanel != null) {
|
if (renderPanel != null) {
|
||||||
final String texName = name + "_tex";
|
final String texName = name + "_tex";
|
||||||
final String filePath = f.getAbsolutePath();
|
final String filePath = f.getAbsolutePath();
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import java.awt.event.*;
|
|||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,6 +101,8 @@ public class ModelRenderPanel extends JPanel {
|
|||||||
handleSingleClick();
|
handleSingleClick();
|
||||||
});
|
});
|
||||||
doubleClickTimer.setRepeats(false);
|
doubleClickTimer.setRepeats(false);
|
||||||
|
|
||||||
|
modelsUpdate(getModel());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -541,7 +544,7 @@ public class ModelRenderPanel extends JPanel {
|
|||||||
g2d.setColor(Color.DARK_GRAY);
|
g2d.setColor(Color.DARK_GRAY);
|
||||||
g2d.fillRect(0, 0, panelW, panelH);
|
g2d.fillRect(0, 0, panelW, panelH);
|
||||||
}
|
}
|
||||||
if (modelRef.get() == null) {
|
if (getModel() == null) {
|
||||||
g2d.setColor(new Color(255, 255, 0, 200));
|
g2d.setColor(new Color(255, 255, 0, 200));
|
||||||
g2d.drawString("模型未加载", 10, 20);
|
g2d.drawString("模型未加载", 10, 20);
|
||||||
}
|
}
|
||||||
@@ -550,20 +553,23 @@ public class ModelRenderPanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public void modelsUpdate(Model2D model){
|
||||||
* 设置模型
|
for (int i = 0; i < model.getParts().size(); i++) {
|
||||||
*/
|
model.getParts().get(i).setPosition(model.getParts().get(i).getPosition().x, model.getParts().get(i).getPosition().y);
|
||||||
public void setModel(Model2D model) {
|
}
|
||||||
glContextManager.executeInGLContext(() -> {
|
|
||||||
modelRef.set(model);
|
|
||||||
logger.info("模型已更新");
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前渲染的模型
|
* 获取当前渲染的模型
|
||||||
*/
|
*/
|
||||||
public Model2D getModel() {
|
public Model2D getModel() {
|
||||||
|
if (modelRef.get() == null) {
|
||||||
|
try {
|
||||||
|
return glContextManager.waitForModel().get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
return modelRef.get();
|
return modelRef.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -621,7 +627,7 @@ public class ModelRenderPanel extends JPanel {
|
|||||||
* 通过网格查找对应的 ModelPart
|
* 通过网格查找对应的 ModelPart
|
||||||
*/
|
*/
|
||||||
public ModelPart findPartByMesh(Mesh2D mesh) {
|
public ModelPart findPartByMesh(Mesh2D mesh) {
|
||||||
Model2D model = modelRef.get();
|
Model2D model = getModel();
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
for (ModelPart part : model.getParts()) {
|
for (ModelPart part : model.getParts()) {
|
||||||
ModelPart found = findPartByMeshRecursive(part, mesh);
|
ModelPart found = findPartByMeshRecursive(part, mesh);
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ public class GLContextManager {
|
|||||||
private static final float ZOOM_SMOOTHING = 0.18f; // 0..1, 越大收敛越快(建议 0.12-0.25)
|
private static final float ZOOM_SMOOTHING = 0.18f; // 0..1, 越大收敛越快(建议 0.12-0.25)
|
||||||
private RepaintCallback repaintCallback;
|
private RepaintCallback repaintCallback;
|
||||||
|
|
||||||
|
private final CompletableFuture<Model2D> modelReady = new CompletableFuture<>();
|
||||||
|
|
||||||
public GLContextManager(String modelPath, int width, int height) {
|
public GLContextManager(String modelPath, int width, int height) {
|
||||||
this.modelPath = modelPath;
|
this.modelPath = modelPath;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
@@ -60,6 +62,9 @@ public class GLContextManager {
|
|||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.modelRef.set(model);
|
this.modelRef.set(model);
|
||||||
|
if (model != null && !modelReady.isDone()) {
|
||||||
|
modelReady.complete(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHeight() {
|
public int getHeight() {
|
||||||
@@ -134,9 +139,17 @@ public class GLContextManager {
|
|||||||
private void loadModelInContext() {
|
private void loadModelInContext() {
|
||||||
try {
|
try {
|
||||||
if (modelPath != null) {
|
if (modelPath != null) {
|
||||||
Model2D model = Model2D.loadFromFile(modelPath);
|
Model2D model;
|
||||||
|
try {
|
||||||
|
model = Model2D.loadFromFile(modelPath);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
model = new Model2D("新的项目");
|
||||||
|
}
|
||||||
modelRef.set(model);
|
modelRef.set(model);
|
||||||
logger.info("模型加载成功: {}", modelPath);
|
logger.info("模型加载成功: {}", modelPath);
|
||||||
|
if (model != null && !modelReady.isDone()) {
|
||||||
|
modelReady.complete(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("模型加载失败: {}", e.getMessage(), e);
|
logger.error("模型加载失败: {}", e.getMessage(), e);
|
||||||
@@ -154,6 +167,10 @@ public class GLContextManager {
|
|||||||
}
|
}
|
||||||
renderThread = new Thread(() -> {
|
renderThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
|
if (modelRef.get() != null && !modelReady.isDone()) {
|
||||||
|
modelReady.complete(modelRef.get());
|
||||||
|
}
|
||||||
|
|
||||||
createOffscreenContext();
|
createOffscreenContext();
|
||||||
|
|
||||||
// 等待上下文就绪后再开始渲染循环(contextReady 由 createOffscreenContext 完成)
|
// 等待上下文就绪后再开始渲染循环(contextReady 由 createOffscreenContext 完成)
|
||||||
@@ -590,4 +607,18 @@ public class GLContextManager {
|
|||||||
public void setCameraDragging(boolean cameraDragging) {
|
public void setCameraDragging(boolean cameraDragging) {
|
||||||
this.cameraDragging = cameraDragging;
|
this.cameraDragging = cameraDragging;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 GLContextManager 获取当前模型引用
|
||||||
|
*/
|
||||||
|
public Model2D getModel() {
|
||||||
|
return modelRef.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待模型加载完成(若已经完成会立即返回已完成的 CompletableFuture)
|
||||||
|
*/
|
||||||
|
public CompletableFuture<Model2D> waitForModel() {
|
||||||
|
return modelReady;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -227,16 +227,10 @@ public class LiquifyTool extends Tool {
|
|||||||
*/
|
*/
|
||||||
private void applyLiquifyEffect(float modelX, float modelY) {
|
private void applyLiquifyEffect(float modelX, float modelY) {
|
||||||
if (liquifyTargetPart == null) return;
|
if (liquifyTargetPart == null) return;
|
||||||
|
|
||||||
Vector2f brushCenter = new Vector2f(modelX, modelY);
|
Vector2f brushCenter = new Vector2f(modelX, modelY);
|
||||||
|
|
||||||
// 判断是否按住Ctrl键,决定是否创建顶点
|
|
||||||
boolean createVertices = renderPanel.getKeyboardManager().getIsCtrlPressed();
|
boolean createVertices = renderPanel.getKeyboardManager().getIsCtrlPressed();
|
||||||
|
|
||||||
liquifyTargetPart.applyLiquify(brushCenter, liquifyBrushSize,
|
liquifyTargetPart.applyLiquify(brushCenter, liquifyBrushSize,
|
||||||
liquifyBrushStrength, currentLiquifyMode, 1, createVertices);
|
liquifyBrushStrength, currentLiquifyMode, 1, createVertices);
|
||||||
|
|
||||||
// 强制重绘
|
|
||||||
renderPanel.repaint();
|
renderPanel.repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +240,6 @@ public class LiquifyTool extends Tool {
|
|||||||
private boolean isOverTargetMesh(float modelX, float modelY) {
|
private boolean isOverTargetMesh(float modelX, float modelY) {
|
||||||
if (liquifyTargetMesh == null) return false;
|
if (liquifyTargetMesh == null) return false;
|
||||||
|
|
||||||
// 更新边界框
|
|
||||||
liquifyTargetMesh.updateBounds();
|
liquifyTargetMesh.updateBounds();
|
||||||
return liquifyTargetMesh.containsPoint(modelX, modelY);
|
return liquifyTargetMesh.containsPoint(modelX, modelY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2055,40 +2055,24 @@ public class ModelPart {
|
|||||||
// 1. 保存局部顶点到 originalVertices
|
// 1. 保存局部顶点到 originalVertices
|
||||||
float[] localVertices = mesh.getVertices().clone();
|
float[] localVertices = mesh.getVertices().clone();
|
||||||
mesh.setOriginalVertices(localVertices);
|
mesh.setOriginalVertices(localVertices);
|
||||||
|
// 2. 计算世界坐标并写入 *renderVertices*,而不是
|
||||||
// 2. 确保 renderVertices 数组已初始化
|
|
||||||
// (您需要 Mesh2D.java 中有这个方法)
|
|
||||||
// mesh.ensureRenderVerticesInitialized();
|
|
||||||
|
|
||||||
// 3. 计算世界坐标并写入 *renderVertices*,而不是
|
|
||||||
int vc = mesh.getVertexCount();
|
int vc = mesh.getVertexCount();
|
||||||
for (int i = 0; i < vc; i++) {
|
for (int i = 0; i < vc; i++) {
|
||||||
Vector2f local = new Vector2f(localVertices[i * 2], localVertices[i * 2 + 1]);
|
Vector2f local = new Vector2f(localVertices[i * 2], localVertices[i * 2 + 1]);
|
||||||
Vector2f worldPt = Matrix3fUtils.transformPoint(this.worldTransform, local);
|
Vector2f worldPt = Matrix3fUtils.transformPoint(this.worldTransform, local);
|
||||||
|
// mesh.setVertex(i, worldPt.x, worldPt.y);
|
||||||
// 错误:mesh.setVertex(i, worldPt.x, worldPt.y);
|
mesh.setRenderVertex(i, worldPt.x, worldPt.y);
|
||||||
// 正确:
|
|
||||||
mesh.setRenderVertex(i, worldPt.x, worldPt.y); // 假设 setRenderVertex 存在
|
|
||||||
}
|
}
|
||||||
|
// 3. 同步 pivot
|
||||||
// 4. 同步 pivot
|
|
||||||
try {
|
try {
|
||||||
Vector2f origPivot = mesh.getOriginalPivot();
|
Vector2f origPivot = mesh.getOriginalPivot();
|
||||||
Vector2f worldPivot = Matrix3fUtils.transformPoint(this.worldTransform, origPivot);
|
Vector2f worldPivot = Matrix3fUtils.transformPoint(this.worldTransform, origPivot);
|
||||||
mesh.setPivot(worldPivot.x, worldPivot.y); // 现在这个会成功(因为步骤1的修复)
|
mesh.setPivot(worldPivot.x, worldPivot.y); // 现在这个会成功(因为步骤1的修复)
|
||||||
} catch (Exception ignored) {
|
} catch (Exception ignored) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 新增:初始化木偶控制点的位置 ====================
|
|
||||||
initializePuppetPinsPosition(mesh);
|
initializePuppetPinsPosition(mesh);
|
||||||
|
|
||||||
// 标记为已烘焙到世界坐标(语义上明确),并确保 bounds/dirty 状态被正确刷新
|
|
||||||
mesh.setBakedToWorld(true);
|
mesh.setBakedToWorld(true);
|
||||||
|
|
||||||
// 确保 GPU 数据在下一次绘制时会被上传(如果当前在渲染线程,也可以直接 uploadToGPU)
|
|
||||||
mesh.markDirty();
|
mesh.markDirty();
|
||||||
|
|
||||||
// 将拷贝加入到本部件
|
|
||||||
meshes.add(mesh);
|
meshes.add(mesh);
|
||||||
boundsDirty = true;
|
boundsDirty = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public class MeshData implements Serializable {
|
|||||||
|
|
||||||
// 原始顶点数据(用于变形恢复)
|
// 原始顶点数据(用于变形恢复)
|
||||||
public float[] originalVertices;
|
public float[] originalVertices;
|
||||||
|
public float[] renderVertices = null;
|
||||||
|
public String modelPartName = null; // 关联的ModelPart名称
|
||||||
|
public boolean isSuspension = false;
|
||||||
|
public Vector2f previewPoint = null;
|
||||||
|
|
||||||
// 变换相关
|
// 变换相关
|
||||||
public Vector2f pivot;
|
public Vector2f pivot;
|
||||||
@@ -83,6 +87,12 @@ public class MeshData implements Serializable {
|
|||||||
|
|
||||||
// 保存原始顶点数据
|
// 保存原始顶点数据
|
||||||
this.originalVertices = mesh.getOriginalVertices();
|
this.originalVertices = mesh.getOriginalVertices();
|
||||||
|
this.renderVertices = mesh.getRenderVertices(); // 获取渲染顶点副本
|
||||||
|
this.isSuspension = mesh.isSuspension();
|
||||||
|
this.previewPoint = mesh.getPreviewPoint() != null ? new Vector2f(mesh.getPreviewPoint()) : null;
|
||||||
|
if (mesh.getModelPart() != null) {
|
||||||
|
this.modelPartName = mesh.getModelPart().getName();
|
||||||
|
}
|
||||||
|
|
||||||
// 保存变换数据
|
// 保存变换数据
|
||||||
this.pivot = new Vector2f(mesh.getPivot());
|
this.pivot = new Vector2f(mesh.getPivot());
|
||||||
@@ -169,6 +179,11 @@ public class MeshData implements Serializable {
|
|||||||
// 恢复二级顶点
|
// 恢复二级顶点
|
||||||
restoreSecondaryVertices(mesh);
|
restoreSecondaryVertices(mesh);
|
||||||
|
|
||||||
|
mesh.setSuspension(this.isSuspension);
|
||||||
|
if (this.previewPoint != null) {
|
||||||
|
mesh.setPreviewPoint(new Vector2f(this.previewPoint));
|
||||||
|
}
|
||||||
|
|
||||||
// 恢复木偶控制点
|
// 恢复木偶控制点
|
||||||
restorePuppetPins(mesh);
|
restorePuppetPins(mesh);
|
||||||
|
|
||||||
@@ -237,6 +252,11 @@ public class MeshData implements Serializable {
|
|||||||
copy.bakedToWorld = this.bakedToWorld;
|
copy.bakedToWorld = this.bakedToWorld;
|
||||||
copy.isRenderVertices = this.isRenderVertices;
|
copy.isRenderVertices = this.isRenderVertices;
|
||||||
|
|
||||||
|
copy.renderVertices = this.renderVertices != null ? this.renderVertices.clone() : null;
|
||||||
|
copy.modelPartName = this.modelPartName;
|
||||||
|
copy.isSuspension = this.isSuspension;
|
||||||
|
copy.previewPoint = this.previewPoint != null ? new Vector2f(this.previewPoint) : null;
|
||||||
|
|
||||||
// 复制原始顶点数据
|
// 复制原始顶点数据
|
||||||
copy.originalVertices = this.originalVertices != null ? this.originalVertices.clone() : null;
|
copy.originalVertices = this.originalVertices != null ? this.originalVertices.clone() : null;
|
||||||
|
|
||||||
|
|||||||
@@ -102,7 +102,6 @@ public class Mesh2D {
|
|||||||
// ==================== 构造器 ====================
|
// ==================== 构造器 ====================
|
||||||
|
|
||||||
private static final float SNAP_THRESHOLD = 0.01f; // 靠近判定阈值(按需要调大,比如 0.1f)
|
private static final float SNAP_THRESHOLD = 0.01f; // 靠近判定阈值(按需要调大,比如 0.1f)
|
||||||
private SecondaryVertex pinnedController = null; // 当前作为“钉子”的控制点(若有)
|
|
||||||
public Mesh2D() {
|
public Mesh2D() {
|
||||||
this.name = "unnamed";
|
this.name = "unnamed";
|
||||||
this.vertices = new float[0];
|
this.vertices = new float[0];
|
||||||
@@ -1011,7 +1010,6 @@ public class Mesh2D {
|
|||||||
v.setPosition(otherPos);
|
v.setPosition(otherPos);
|
||||||
// 把被靠近的那个当作钉子
|
// 把被靠近的那个当作钉子
|
||||||
other.setPinned(true);
|
other.setPinned(true);
|
||||||
pinnedController = other;
|
|
||||||
// 把移动到其上的顶点锁定
|
// 把移动到其上的顶点锁定
|
||||||
v.setLocked(true);
|
v.setLocked(true);
|
||||||
logger.info("SecondaryVertex {} snapped to {}. {} pinned, {} locked.", v.getId(), other.getId(), other.getId(), v.getId());
|
logger.info("SecondaryVertex {} snapped to {}. {} pinned, {} locked.", v.getId(), other.getId(), other.getId(), v.getId());
|
||||||
@@ -1048,7 +1046,6 @@ public class Mesh2D {
|
|||||||
sv.setPinned(false);
|
sv.setPinned(false);
|
||||||
sv.setLocked(false);
|
sv.setLocked(false);
|
||||||
}
|
}
|
||||||
pinnedController = null;
|
|
||||||
logger.info("All secondary vertices unpinned/unlocked");
|
logger.info("All secondary vertices unpinned/unlocked");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2291,6 +2288,10 @@ public class Mesh2D {
|
|||||||
return puppetPins.size();
|
return puppetPins.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSuspension() {
|
||||||
|
return isSuspension;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 顶点变换器接口
|
* 顶点变换器接口
|
||||||
*/
|
*/
|
||||||
@@ -2684,15 +2685,10 @@ public class Mesh2D {
|
|||||||
* 标记数据已修改
|
* 标记数据已修改
|
||||||
*/
|
*/
|
||||||
public void markDirty() {
|
public void markDirty() {
|
||||||
// 删除旧 GPU 对象(若有),并标记脏
|
|
||||||
deleteGPU();
|
deleteGPU();
|
||||||
this.dirty = true;
|
this.dirty = true;
|
||||||
this.boundsDirty = true;
|
this.boundsDirty = true;
|
||||||
this.multiSelectionDirty = true;
|
this.multiSelectionDirty = true;
|
||||||
|
|
||||||
// 渲染缓存(renderVertices)不再可信,清除烘焙标志
|
|
||||||
this.bakedToWorld = false;
|
|
||||||
// 注意:不立即清除 renderVertices 数组(保留以供 debug),但 bakedToWorld=false 可确保上传使用局部 vertices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class LiquifyTargetPartRander extends RanderTools {
|
|||||||
drawRoundedBoundingOutline(bb, bounds, outline, 1.0f, 0f);
|
drawRoundedBoundingOutline(bb, bounds, outline, 1.0f, 0f);
|
||||||
|
|
||||||
// 优化点:根据顶点数量选择低/高质量绘制
|
// 优化点:根据顶点数量选择低/高质量绘制
|
||||||
int vertexCount = mesh2D.getVertices() == null ? 0 : mesh2D.getVertices().length / 2;
|
int vertexCount = mesh2D.getRenderVertices() == null ? 0 : mesh2D.getRenderVertices().length / 2;
|
||||||
boolean large = vertexCount > LARGE_VERTEX_THRESHOLD;
|
boolean large = vertexCount > LARGE_VERTEX_THRESHOLD;
|
||||||
|
|
||||||
// 边框:始终绘制(合并一次 begin/end)
|
// 边框:始终绘制(合并一次 begin/end)
|
||||||
@@ -172,13 +172,13 @@ public class LiquifyTargetPartRander extends RanderTools {
|
|||||||
|
|
||||||
// 合并绘制外轮廓(单次 begin/end)
|
// 合并绘制外轮廓(单次 begin/end)
|
||||||
private void drawOutlineOnce(BufferBuilder bb, Mesh2D mesh2D) {
|
private void drawOutlineOnce(BufferBuilder bb, Mesh2D mesh2D) {
|
||||||
if (mesh2D.getVertices() == null || mesh2D.getVertices().length < 4) return;
|
if (mesh2D.getRenderVertices() == null || mesh2D.getRenderVertices().length < 4) return;
|
||||||
Vector4f OUTER_LINE = new Vector4f(1f, 0.85f, 0.35f, 0.12f);
|
Vector4f OUTER_LINE = new Vector4f(1f, 0.85f, 0.35f, 0.12f);
|
||||||
int count = mesh2D.getVertices().length / 2;
|
int count = mesh2D.getRenderVertices().length / 2;
|
||||||
GL11.glLineWidth(1.0f);
|
GL11.glLineWidth(1.0f);
|
||||||
bb.begin(GL11.GL_LINE_LOOP, count);
|
bb.begin(GL11.GL_LINE_LOOP, count);
|
||||||
bb.setColor(OUTER_LINE);
|
bb.setColor(OUTER_LINE);
|
||||||
float[] verts = mesh2D.getVertices();
|
float[] verts = mesh2D.getRenderVertices();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int base = i * 2;
|
int base = i * 2;
|
||||||
bb.vertex(verts[base], verts[base + 1], 0f, 0f);
|
bb.vertex(verts[base], verts[base + 1], 0f, 0f);
|
||||||
@@ -195,7 +195,7 @@ public class LiquifyTargetPartRander extends RanderTools {
|
|||||||
int lines = (idx.length / 3) * 6; // 每三角 6 顶点(3 条线)
|
int lines = (idx.length / 3) * 6; // 每三角 6 顶点(3 条线)
|
||||||
bb.begin(GL11.GL_LINES, lines);
|
bb.begin(GL11.GL_LINES, lines);
|
||||||
bb.setColor(innerLine);
|
bb.setColor(innerLine);
|
||||||
float[] verts = mesh2D.getVertices();
|
float[] verts = mesh2D.getRenderVertices();
|
||||||
for (int i = 0; i < idx.length; i += 3) {
|
for (int i = 0; i < idx.length; i += 3) {
|
||||||
int i1 = idx[i];
|
int i1 = idx[i];
|
||||||
int i2 = idx[i + 1];
|
int i2 = idx[i + 1];
|
||||||
@@ -215,13 +215,13 @@ public class LiquifyTargetPartRander extends RanderTools {
|
|||||||
|
|
||||||
// 顶点点标记 - 优化版:大网格时采样绘制、减少 segment、合并操作
|
// 顶点点标记 - 优化版:大网格时采样绘制、减少 segment、合并操作
|
||||||
private void drawLiquifyVertexPointsOptimized(Mesh2D mesh2D, BufferBuilder bb, List<TextItem> pendingTexts, boolean large, int vertexCount) {
|
private void drawLiquifyVertexPointsOptimized(Mesh2D mesh2D, BufferBuilder bb, List<TextItem> pendingTexts, boolean large, int vertexCount) {
|
||||||
if (mesh2D.getVertices() == null) return;
|
if (mesh2D.getRenderVertices() == null) return;
|
||||||
|
|
||||||
final float BASE_SIZE = Math.max(1.5f, mesh2D.secondaryVertexSize * (large ? 0.35f : 0.6f));
|
final float BASE_SIZE = Math.max(1.5f, mesh2D.secondaryVertexSize * (large ? 0.35f : 0.6f));
|
||||||
final Vector4f FILL = new Vector4f(1f, 0.6f, 0.15f, large ? 0.10f : 0.18f);
|
final Vector4f FILL = new Vector4f(1f, 0.6f, 0.15f, large ? 0.10f : 0.18f);
|
||||||
final Vector4f STROKE = new Vector4f(1f, 1f, 1f, large ? 0.75f : 0.9f);
|
final Vector4f STROKE = new Vector4f(1f, 1f, 1f, large ? 0.75f : 0.9f);
|
||||||
|
|
||||||
float[] verts = mesh2D.getVertices();
|
float[] verts = mesh2D.getRenderVertices();
|
||||||
|
|
||||||
// 采样步长,保证绘制点数量不会超过 TARGET_VERTEX_DRAW(越大网格步长越大)
|
// 采样步长,保证绘制点数量不会超过 TARGET_VERTEX_DRAW(越大网格步长越大)
|
||||||
int step = 1;
|
int step = 1;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.chuangzhou.vivid2D.render.model.Model2D;
|
|||||||
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
import com.chuangzhou.vivid2D.render.model.ModelPart;
|
||||||
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||||
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -22,7 +23,6 @@ import java.util.List;
|
|||||||
public class ModelLayerPanelTest {
|
public class ModelLayerPanelTest {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
//LookAndFeel defaultLaf = isDarkMode ? : new FlatMacLightLaf();
|
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel(new FlatMacDarkLaf());
|
UIManager.setLookAndFeel(new FlatMacDarkLaf());
|
||||||
} catch (UnsupportedLookAndFeelException e) {
|
} catch (UnsupportedLookAndFeelException e) {
|
||||||
@@ -30,129 +30,79 @@ public class ModelLayerPanelTest {
|
|||||||
}
|
}
|
||||||
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
|
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
|
||||||
System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));
|
System.setErr(new PrintStream(System.err, true, StandardCharsets.UTF_8));
|
||||||
// 创建示例模型并添加图层
|
|
||||||
Model2D model = new Model2D("示例模型");
|
|
||||||
|
|
||||||
// 调整一些初始属性(可选)
|
|
||||||
ModelPart person = model.getPart("人物");
|
|
||||||
if (person != null) {
|
|
||||||
try {
|
|
||||||
person.setOpacity(0.85f);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建 UI
|
|
||||||
JFrame frame = new JFrame("ModelLayerPanel 测试(含渲染面板和变换面板)");
|
JFrame frame = new JFrame("ModelLayerPanel 测试(含渲染面板和变换面板)");
|
||||||
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||||
frame.setLayout(new BorderLayout());
|
frame.setLayout(new BorderLayout());
|
||||||
|
ModelRenderPanel renderPanel = new ModelRenderPanel("C:\\Users\\Administrator\\Desktop\\testing.model", 640, 480);
|
||||||
// 左侧:图层面板(传入 renderPanel 后可在面板中绑定贴图到 GL 上下文)
|
ModelLayerPanel layerPanel = new ModelLayerPanel(renderPanel);
|
||||||
// 先创建一个占位 renderPanel,再把它传给 layerPanel(ModelRenderPanel 构造需要尺寸)
|
|
||||||
ModelRenderPanel renderPanel = new ModelRenderPanel(model, 640, 480);
|
|
||||||
ModelLayerPanel layerPanel = new ModelLayerPanel(model, renderPanel);
|
|
||||||
layerPanel.setPreferredSize(new Dimension(260, 600));
|
layerPanel.setPreferredSize(new Dimension(260, 600));
|
||||||
frame.add(layerPanel, BorderLayout.WEST);
|
frame.add(layerPanel, BorderLayout.WEST);
|
||||||
|
|
||||||
// 中间:渲染面板
|
|
||||||
renderPanel.setPreferredSize(new Dimension(640, 480));
|
renderPanel.setPreferredSize(new Dimension(640, 480));
|
||||||
frame.add(renderPanel, BorderLayout.CENTER);
|
frame.add(renderPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
// 创建变换面板
|
|
||||||
TransformPanel transformPanel = new TransformPanel(renderPanel);
|
TransformPanel transformPanel = new TransformPanel(renderPanel);
|
||||||
|
|
||||||
// 右侧:创建选项卡面板,包含模型树和变换面板
|
|
||||||
JTabbedPane rightTabbedPane = new JTabbedPane();
|
JTabbedPane rightTabbedPane = new JTabbedPane();
|
||||||
|
renderPanel.getGlContextManager().waitForModel().thenAccept(model -> {
|
||||||
// 模型树选项卡
|
if (model == null) return;
|
||||||
JTree tree = new JTree(model.toTreeNode());
|
JTree tree = new JTree(model.toTreeNode());
|
||||||
JScrollPane treeScroll = new JScrollPane(tree);
|
JScrollPane treeScroll = new JScrollPane(tree);
|
||||||
treeScroll.setPreferredSize(new Dimension(280, 600));
|
treeScroll.setPreferredSize(new Dimension(280, 600));
|
||||||
rightTabbedPane.addTab("模型结构", treeScroll);
|
rightTabbedPane.addTab("模型结构", treeScroll);
|
||||||
|
JScrollPane transformScroll = new JScrollPane(transformPanel);
|
||||||
// 变换面板选项卡
|
transformScroll.setPreferredSize(new Dimension(280, 600));
|
||||||
JScrollPane transformScroll = new JScrollPane(transformPanel);
|
rightTabbedPane.addTab("变换控制", transformScroll);
|
||||||
transformScroll.setPreferredSize(new Dimension(280, 600));
|
rightTabbedPane.setPreferredSize(new Dimension(300, 600));
|
||||||
rightTabbedPane.addTab("变换控制", transformScroll);
|
frame.add(rightTabbedPane, BorderLayout.EAST);
|
||||||
|
JPanel bottom = getBottom(renderPanel, transformPanel);
|
||||||
rightTabbedPane.setPreferredSize(new Dimension(300, 600));
|
frame.add(bottom, BorderLayout.SOUTH);
|
||||||
frame.add(rightTabbedPane, BorderLayout.EAST);
|
renderPanel.addModelClickListener((mesh, modelX, modelY, screenX, screenY) -> {
|
||||||
|
if (mesh == null) return;
|
||||||
// 底部:演示按钮(刷新树以反映面板中对模型的更改)
|
System.out.println("点击了模型:" + mesh.getName() + ",模型坐标:" + modelX + ", " + modelY + ",屏幕坐标:" + screenX + ", " + screenY);
|
||||||
JPanel bottom = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
||||||
JButton refreshBtn = new JButton("刷新模型树");
|
transformPanel.setSelectedParts(selectedPart);
|
||||||
refreshBtn.addActionListener(e -> {
|
rightTabbedPane.setSelectedIndex(1);
|
||||||
tree.setModel(new javax.swing.tree.DefaultTreeModel(model.toTreeNode()));
|
});
|
||||||
for (int i = 0; i < tree.getRowCount(); i++) tree.expandRow(i);
|
frame.addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
// 同步通知渲染面板(如果需要)去刷新模型
|
@Override
|
||||||
try {
|
public void windowClosed(java.awt.event.WindowEvent e) {
|
||||||
renderPanel.setModel(model);
|
try {
|
||||||
} catch (Exception ignored) {
|
renderPanel.getGlContextManager().dispose();
|
||||||
}
|
} catch (Throwable ignored) {
|
||||||
|
}
|
||||||
|
model.saveToFile("C:\\Users\\Administrator\\Desktop\\testing.model");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frame.setSize(1300, 700);
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.setVisible(true);
|
||||||
});
|
});
|
||||||
bottom.add(refreshBtn);
|
});
|
||||||
|
}
|
||||||
|
|
||||||
JButton printOrderBtn = new JButton("打印部件顺序(控制台)");
|
private static @NotNull JPanel getBottom(ModelRenderPanel renderPanel, TransformPanel transformPanel) {
|
||||||
printOrderBtn.addActionListener(e -> {
|
JPanel bottom = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||||
System.out.println("当前模型部件顺序:");
|
|
||||||
|
JButton printOrderBtn = new JButton("打印部件顺序(控制台)");
|
||||||
|
printOrderBtn.addActionListener(e -> {
|
||||||
|
System.out.println("当前模型部件顺序:");
|
||||||
|
renderPanel.getGlContextManager().waitForModel().thenAccept(model -> {
|
||||||
|
if (model == null) return;
|
||||||
for (ModelPart p : model.getParts()) {
|
for (ModelPart p : model.getParts()) {
|
||||||
System.out.println(" - " + p.getName() + " (可见=" + p.isVisible() + ", 不透明度=" + p.getOpacity() + ")");
|
System.out.println(" - " + p.getName() + " (可见=" + p.isVisible() + ", 不透明度=" + p.getOpacity() + ")");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
bottom.add(printOrderBtn);
|
});
|
||||||
|
bottom.add(printOrderBtn);
|
||||||
|
|
||||||
// 添加选中部件更新按钮
|
// 添加选中部件更新按钮
|
||||||
JButton updateSelectionBtn = new JButton("更新选中部件");
|
JButton updateSelectionBtn = new JButton("更新选中部件");
|
||||||
updateSelectionBtn.addActionListener(e -> {
|
updateSelectionBtn.addActionListener(e -> {
|
||||||
renderPanel.getGlContextManager().executeInGLContext(() -> {
|
renderPanel.getGlContextManager().executeInGLContext(() -> {
|
||||||
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
|
||||||
transformPanel.setSelectedParts(selectedPart);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
bottom.add(updateSelectionBtn);
|
|
||||||
|
|
||||||
frame.add(bottom, BorderLayout.SOUTH);
|
|
||||||
|
|
||||||
// 添加模型点击监听器,自动更新变换面板的选中部件
|
|
||||||
renderPanel.addModelClickListener((mesh, modelX, modelY, screenX, screenY) -> {
|
|
||||||
if (mesh == null) return;
|
|
||||||
System.out.println("点击了模型:" + mesh.getName() + ",模型坐标:" + modelX + ", " + modelY + ",屏幕坐标:" + screenX + ", " + screenY);
|
|
||||||
|
|
||||||
// 自动更新变换面板的选中部件
|
|
||||||
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
List<ModelPart> selectedPart = renderPanel.getSelectedParts();
|
||||||
transformPanel.setSelectedParts(selectedPart);
|
transformPanel.setSelectedParts(selectedPart);
|
||||||
|
|
||||||
// 切换到变换控制选项卡
|
|
||||||
rightTabbedPane.setSelectedIndex(1);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听窗口关闭,确保释放 GL 资源
|
|
||||||
frame.addWindowListener(new java.awt.event.WindowAdapter() {
|
|
||||||
@Override
|
|
||||||
public void windowClosing(java.awt.event.WindowEvent e) {
|
|
||||||
// 先释放渲染面板相关 GL 资源与线程
|
|
||||||
//try {
|
|
||||||
// renderPanel.dispose();
|
|
||||||
//} catch (Throwable t) {
|
|
||||||
// t.printStackTrace();
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void windowClosed(java.awt.event.WindowEvent e) {
|
|
||||||
// 进程退出(确保彻底关闭)
|
|
||||||
try {
|
|
||||||
renderPanel.getGlContextManager().dispose();
|
|
||||||
} catch (Throwable ignored) {
|
|
||||||
}
|
|
||||||
model.saveToFile("C:\\Users\\Administrator\\Desktop\\testing.model");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
frame.setSize(1300, 700);
|
|
||||||
frame.setLocationRelativeTo(null);
|
|
||||||
frame.setVisible(true);
|
|
||||||
});
|
});
|
||||||
|
bottom.add(updateSelectionBtn);
|
||||||
|
return bottom;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user