feat(render): 实现 liquify overlay 显示控制与顶点同步优化

- 在 Mesh2D 中新增 isShowLiquifyOverlay 方法,用于控制 liquify overlay 的显示状态
- 修改 drawLiquifyOverlay 方法,增加对 mesh2D.isShowLiquifyOverlay() 的判断
- 重构 ModelPart 的 setPosition 方法,优化多选和单选状态下的顶点同步逻辑- 新增 syncSecondaryVerticesForPart 方法,实现部件及其子部件的二级顶点同步移动
- 移除 SelectionTool 中冗余的 syncSecondaryVerticesForPart 方法- 优化 SelectionTool 的 resize 操作逻辑,提高代码可读性和性能
- 在 VertexDeformationRander 中增加对 showSecondaryVertices 状态的检查- 完善多选操作时的中心点计算逻辑,提升用户体验
This commit is contained in:
tzdwindows 7
2025-11-01 19:17:03 +08:00
parent 5c66838b3e
commit c5097f91be
5 changed files with 97 additions and 84 deletions

View File

@@ -324,9 +324,6 @@ public class SelectionTool extends Tool {
for (ModelPart part : selectedParts) { for (ModelPart part : selectedParts) {
Vector2f pos = part.getPosition(); Vector2f pos = part.getPosition();
part.setPosition(pos.x + deltaX, pos.y + deltaY); part.setPosition(pos.x + deltaX, pos.y + deltaY);
// 同步移动该部件下的所有网格的二级顶点
syncSecondaryVerticesForPart(part, deltaX, deltaY);
} }
// 更新拖拽起始位置 // 更新拖拽起始位置
@@ -395,16 +392,12 @@ public class SelectionTool extends Tool {
private void handleResizeDrag(float modelX, float modelY) { private void handleResizeDrag(float modelX, float modelY) {
if (lastSelectedMesh == null) return; if (lastSelectedMesh == null) return;
ModelPart selectedPart = findPartByMesh(lastSelectedMesh);
if (selectedPart == null) return;
float deltaX = modelX - dragStartX; float deltaX = modelX - dragStartX;
float deltaY = modelY - dragStartY; float deltaY = modelY - dragStartY;
float relScaleX = 1.0f; float relScaleX = 1.0f;
float relScaleY = 1.0f; float relScaleY = 1.0f;
// 根据拖拽模式计算相对缩放比例
switch (currentDragMode) { switch (currentDragMode) {
case RESIZE_LEFT: case RESIZE_LEFT:
relScaleX = (resizeStartWidth - deltaX) / Math.max(1e-6f, resizeStartWidth); relScaleX = (resizeStartWidth - deltaX) / Math.max(1e-6f, resizeStartWidth);
@@ -436,92 +429,47 @@ public class SelectionTool extends Tool {
break; break;
} }
// 如果按住Shift键等比例缩放 // Shift 键等比例缩放
if (renderPanel.getKeyboardManager().getIsShiftPressed() || shiftDuringDrag) { if (renderPanel.getKeyboardManager().getIsShiftPressed() || shiftDuringDrag) {
float uniform = (relScaleX + relScaleY) * 0.5f; float uniform = (relScaleX + relScaleY) * 0.5f;
relScaleX = uniform; relScaleX = uniform;
relScaleY = uniform; relScaleY = uniform;
} }
// 应用缩放到所有选中的部件
List<ModelPart> selectedParts = getSelectedParts(); List<ModelPart> selectedParts = getSelectedParts();
Vector2f center = getMultiSelectionCenter(); // 整个多选的中心点
for (ModelPart part : selectedParts) { for (ModelPart part : selectedParts) {
Vector2f currentScale = part.getScale(); Vector2f currentScale = part.getScale();
part.setScale(currentScale.x * relScaleX, currentScale.y * relScaleY); float newScaleX = currentScale.x * relScaleX;
float newScaleY = currentScale.y * relScaleY;
// 同步缩放该部件下的所有网格的二级顶点 // 更新部件自身缩放
//syncSecondaryVerticesScaleForPart(part, relScaleX, relScaleY); part.setScale(newScaleX, newScaleY);
} }
// 更新拖拽起始位置和初始尺寸
// 更新拖拽起始点和尺寸
dragStartX = modelX; dragStartX = modelX;
dragStartY = modelY; dragStartY = modelY;
resizeStartWidth *= relScaleX; resizeStartWidth *= relScaleX;
resizeStartHeight *= relScaleY; resizeStartHeight *= relScaleY;
} }
/** private Vector2f getMultiSelectionCenter() {
* 同步部件下所有网格的二级顶点位置 List<ModelPart> selectedParts = getSelectedParts();
*/ if (selectedParts.isEmpty()) return new Vector2f(0, 0);
private void syncSecondaryVerticesForPart(ModelPart part, float deltaX, float deltaY) {
if (part == null) return;
List<Mesh2D> meshes = part.getMeshes(); float sumX = 0f;
if (meshes == null) return; float sumY = 0f;
for (Mesh2D mesh : meshes) { for (ModelPart part : selectedParts) {
if (mesh != null && mesh.isVisible() && mesh.getSecondaryVertexCount() > 0) { Vector2f pos = part.getPosition();
sumX += pos.x;
List<SecondaryVertex> secondaryVertices = mesh.getSecondaryVertices(); sumY += pos.y;
if (secondaryVertices != null) {
// 遍历所有顶点,逐个调用 moveSecondaryVertex
for (SecondaryVertex vertex : secondaryVertices) {
// 【修正 1避免双重平移和状态冲突】
// 仅对未锁定/未固定的顶点执行局部坐标平移。
// 锁定的顶点不应被工具的同步逻辑移动,它们应该随 ModelPart 的世界变换移动。
if (!vertex.isLocked() && !vertex.isPinned()) {
// 计算顶点的新局部坐标 (position + delta)
float newX = vertex.getPosition().x + deltaX;
float newY = vertex.getPosition().y + deltaY;
// 使用 moveSecondaryVertex 方法
mesh.moveSecondaryVertex(vertex, newX, newY);
// 注意mesh.moveSecondaryVertex 内部会触发形变计算和 markDirty
}
}
}
}
}
part.setPosition(part.getPosition());
// 递归处理子部件
for (ModelPart child : part.getChildren()) {
syncSecondaryVerticesForPart(child, deltaX, deltaY);
}
}
/**
* 同步部件下所有网格的二级顶点缩放
*/
private void syncSecondaryVerticesScaleForPart(ModelPart part, float scaleX, float scaleY) {
if (part == null) return;
List<Mesh2D> meshes = part.getMeshes();
if (meshes == null) return;
for (Mesh2D mesh : meshes) {
if (mesh != null && mesh.getSecondaryVertexCount() > 0) {
mesh.syncSecondaryVerticesToBounds();
}
} }
// 递归处理子部件 return new Vector2f(sumX / selectedParts.size(), sumY / selectedParts.size());
for (ModelPart child : part.getChildren()) {
syncSecondaryVerticesScaleForPart(child, scaleX, scaleY);
}
} }
/** /**

View File

@@ -1549,7 +1549,9 @@ public class ModelPart {
public void setPosition(float x, float y) { public void setPosition(float x, float y) {
// 防止递归调用 // 防止递归调用
if (inMultiSelectionOperation) { if (inMultiSelectionOperation) {
// 直接执行单选择辑,避免递归 float deltaX = x - position.x;
float deltaY = y - position.y;
position.set(x, y); position.set(x, y);
markTransformDirty(); markTransformDirty();
updateLocalTransform(); updateLocalTransform();
@@ -1560,28 +1562,42 @@ public class ModelPart {
mesh.setPivot(worldPivot.x, worldPivot.y); mesh.setPivot(worldPivot.x, worldPivot.y);
} }
updateMeshVertices(); syncSecondaryVerticesForPart(this, deltaX, deltaY);
triggerEvent("position"); triggerEvent("position");
return; return;
} }
// 如果是多选状态下的移动,使用多选移动方法 float oldX = position.x;
if (isInMultiSelection() && !getSelectedMeshes().isEmpty()) { float oldY = position.y;
Vector2f currentPos = getPosition();
float dx = x - currentPos.x; // 多选状态下移动
float dy = y - currentPos.y; if (isInMultiSelection() && !getSelectedMeshes().isEmpty()) {
float dx = x - oldX;
float dy = y - oldY;
// 设置标志防止递归
inMultiSelectionOperation = true; inMultiSelectionOperation = true;
try { try {
moveSelectedMeshes(dx, dy); moveSelectedMeshes(dx, dy); // 这里会调用 setPosition但被 inMultiSelectionOperation 拦截
} finally { } finally {
inMultiSelectionOperation = false; inMultiSelectionOperation = false;
} }
// 更新自身位置
position.set(x, y);
markTransformDirty();
updateLocalTransform();
recomputeWorldTransformRecursive();
for (Mesh2D mesh : meshes) {
Vector2f worldPivot = Matrix3fUtils.transformPoint(worldTransform, mesh.getOriginalPivot());
mesh.setPivot(worldPivot.x, worldPivot.y);
}
triggerEvent("position");
return; return;
} }
// 原有单选 // 单选
position.set(x, y); position.set(x, y);
markTransformDirty(); markTransformDirty();
updateLocalTransform(); updateLocalTransform();
@@ -1592,12 +1608,57 @@ public class ModelPart {
mesh.setPivot(worldPivot.x, worldPivot.y); mesh.setPivot(worldPivot.x, worldPivot.y);
} }
float deltaX = x - oldX;
float deltaY = y - oldY;
updateMeshVertices(); syncSecondaryVerticesForPart(this, deltaX, deltaY);
triggerEvent("position"); triggerEvent("position");
} }
/**
* 同步部件下所有网格的二级顶点位置
*/
private void syncSecondaryVerticesForPart(ModelPart part, float deltaX, float deltaY) {
if (part == null) return;
List<Mesh2D> meshes = part.getMeshes();
if (meshes == null) return;
for (Mesh2D mesh : meshes) {
if (mesh != null && mesh.isVisible() && mesh.getSecondaryVertexCount() > 0) {
List<SecondaryVertex> secondaryVertices = mesh.getSecondaryVertices();
if (secondaryVertices != null) {
// 遍历所有顶点,逐个调用 moveSecondaryVertex
for (SecondaryVertex vertex : secondaryVertices) {
// 【修正 1避免双重平移和状态冲突】
// 仅对未锁定/未固定的顶点执行局部坐标平移。
// 锁定的顶点不应被工具的同步逻辑移动,它们应该随 ModelPart 的世界变换移动。
if (!vertex.isLocked() && !vertex.isPinned()) {
// 计算顶点的新局部坐标 (position + delta)
float newX = vertex.getPosition().x + deltaX;
float newY = vertex.getPosition().y + deltaY;
// 使用 moveSecondaryVertex 方法
mesh.moveSecondaryVertex(vertex, newX, newY);
// 注意mesh.moveSecondaryVertex 内部会触发形变计算和 markDirty
}
}
}
}
}
part.setPosition(part.getPosition());
// 递归处理子部件
for (ModelPart child : part.getChildren()) {
syncSecondaryVerticesForPart(child, deltaX, deltaY);
}
}
/** /**
* 更新所有网格的顶点位置以反映当前变换 * 更新所有网格的顶点位置以反映当前变换
*/ */

View File

@@ -657,6 +657,10 @@ public class Mesh2D {
markDirty(); markDirty();
} }
public boolean isShowLiquifyOverlay() {
return showLiquifyOverlay;
}
/** /**
* 设置中心点 * 设置中心点
*/ */

View File

@@ -53,7 +53,7 @@ public class LiquifyTargetPartRander extends RanderTools {
* 主渲染入口。收集文本命令到 pendingTexts绘制完成后 popState再把文本绘制出来外部渲染 * 主渲染入口。收集文本命令到 pendingTexts绘制完成后 popState再把文本绘制出来外部渲染
*/ */
private void drawLiquifyOverlay(Mesh2D mesh2D, Matrix3f modelMatrix) { private void drawLiquifyOverlay(Mesh2D mesh2D, Matrix3f modelMatrix) {
if (!isAlgorithmEnabled("showLiquifyOverlay")) return; if (!isAlgorithmEnabled("showLiquifyOverlay") || !mesh2D.isShowLiquifyOverlay()) return;
List<TextItem> pendingTexts = new ArrayList<>(); List<TextItem> pendingTexts = new ArrayList<>();

View File

@@ -41,7 +41,7 @@ public class VertexDeformationRander extends RanderTools {
* 绘制二级顶点(现代化样式) * 绘制二级顶点(现代化样式)
*/ */
private void drawSecondaryVertices(Mesh2D mesh2D, Matrix3f modelMatrix) { private void drawSecondaryVertices(Mesh2D mesh2D, Matrix3f modelMatrix) {
if (!isAlgorithmEnabled("showSecondaryVertices") || mesh2D.getSecondaryVertices().isEmpty()) return; if (!isAlgorithmEnabled("showSecondaryVertices") || mesh2D.getSecondaryVertices().isEmpty() || !mesh2D.isShowSecondaryVertices()) return;
RenderSystem.pushState(); RenderSystem.pushState();
try { try {