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:
@@ -324,9 +324,6 @@ public class SelectionTool extends Tool {
|
||||
for (ModelPart part : selectedParts) {
|
||||
Vector2f pos = part.getPosition();
|
||||
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) {
|
||||
if (lastSelectedMesh == null) return;
|
||||
|
||||
ModelPart selectedPart = findPartByMesh(lastSelectedMesh);
|
||||
if (selectedPart == null) return;
|
||||
|
||||
float deltaX = modelX - dragStartX;
|
||||
float deltaY = modelY - dragStartY;
|
||||
|
||||
float relScaleX = 1.0f;
|
||||
float relScaleY = 1.0f;
|
||||
|
||||
// 根据拖拽模式计算相对缩放比例
|
||||
switch (currentDragMode) {
|
||||
case RESIZE_LEFT:
|
||||
relScaleX = (resizeStartWidth - deltaX) / Math.max(1e-6f, resizeStartWidth);
|
||||
@@ -436,92 +429,47 @@ public class SelectionTool extends Tool {
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果按住Shift键,等比例缩放
|
||||
// Shift 键等比例缩放
|
||||
if (renderPanel.getKeyboardManager().getIsShiftPressed() || shiftDuringDrag) {
|
||||
float uniform = (relScaleX + relScaleY) * 0.5f;
|
||||
relScaleX = uniform;
|
||||
relScaleY = uniform;
|
||||
}
|
||||
|
||||
// 应用缩放到所有选中的部件
|
||||
List<ModelPart> selectedParts = getSelectedParts();
|
||||
Vector2f center = getMultiSelectionCenter(); // 整个多选的中心点
|
||||
|
||||
for (ModelPart part : selectedParts) {
|
||||
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;
|
||||
dragStartY = modelY;
|
||||
resizeStartWidth *= relScaleX;
|
||||
resizeStartHeight *= relScaleY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步部件下所有网格的二级顶点位置
|
||||
*/
|
||||
private void syncSecondaryVerticesForPart(ModelPart part, float deltaX, float deltaY) {
|
||||
if (part == null) return;
|
||||
private Vector2f getMultiSelectionCenter() {
|
||||
List<ModelPart> selectedParts = getSelectedParts();
|
||||
if (selectedParts.isEmpty()) return new Vector2f(0, 0);
|
||||
|
||||
List<Mesh2D> meshes = part.getMeshes();
|
||||
if (meshes == null) return;
|
||||
float sumX = 0f;
|
||||
float sumY = 0f;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步部件下所有网格的二级顶点缩放
|
||||
*/
|
||||
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();
|
||||
}
|
||||
for (ModelPart part : selectedParts) {
|
||||
Vector2f pos = part.getPosition();
|
||||
sumX += pos.x;
|
||||
sumY += pos.y;
|
||||
}
|
||||
|
||||
// 递归处理子部件
|
||||
for (ModelPart child : part.getChildren()) {
|
||||
syncSecondaryVerticesScaleForPart(child, scaleX, scaleY);
|
||||
}
|
||||
return new Vector2f(sumX / selectedParts.size(), sumY / selectedParts.size());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1549,7 +1549,9 @@ public class ModelPart {
|
||||
public void setPosition(float x, float y) {
|
||||
// 防止递归调用
|
||||
if (inMultiSelectionOperation) {
|
||||
// 直接执行单选择辑,避免递归
|
||||
float deltaX = x - position.x;
|
||||
float deltaY = y - position.y;
|
||||
|
||||
position.set(x, y);
|
||||
markTransformDirty();
|
||||
updateLocalTransform();
|
||||
@@ -1560,28 +1562,42 @@ public class ModelPart {
|
||||
mesh.setPivot(worldPivot.x, worldPivot.y);
|
||||
}
|
||||
|
||||
updateMeshVertices();
|
||||
syncSecondaryVerticesForPart(this, deltaX, deltaY);
|
||||
triggerEvent("position");
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果是多选状态下的移动,使用多选移动方法
|
||||
if (isInMultiSelection() && !getSelectedMeshes().isEmpty()) {
|
||||
Vector2f currentPos = getPosition();
|
||||
float dx = x - currentPos.x;
|
||||
float dy = y - currentPos.y;
|
||||
float oldX = position.x;
|
||||
float oldY = position.y;
|
||||
|
||||
// 多选状态下移动
|
||||
if (isInMultiSelection() && !getSelectedMeshes().isEmpty()) {
|
||||
float dx = x - oldX;
|
||||
float dy = y - oldY;
|
||||
|
||||
// 设置标志防止递归
|
||||
inMultiSelectionOperation = true;
|
||||
try {
|
||||
moveSelectedMeshes(dx, dy);
|
||||
moveSelectedMeshes(dx, dy); // 这里会调用 setPosition,但被 inMultiSelectionOperation 拦截
|
||||
} finally {
|
||||
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;
|
||||
}
|
||||
|
||||
// 原有单选择辑
|
||||
// 单选逻辑
|
||||
position.set(x, y);
|
||||
markTransformDirty();
|
||||
updateLocalTransform();
|
||||
@@ -1592,12 +1608,57 @@ public class ModelPart {
|
||||
mesh.setPivot(worldPivot.x, worldPivot.y);
|
||||
}
|
||||
|
||||
float deltaX = x - oldX;
|
||||
float deltaY = y - oldY;
|
||||
|
||||
updateMeshVertices();
|
||||
syncSecondaryVerticesForPart(this, deltaX, deltaY);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新所有网格的顶点位置以反映当前变换
|
||||
*/
|
||||
|
||||
@@ -657,6 +657,10 @@ public class Mesh2D {
|
||||
markDirty();
|
||||
}
|
||||
|
||||
public boolean isShowLiquifyOverlay() {
|
||||
return showLiquifyOverlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中心点
|
||||
*/
|
||||
|
||||
@@ -53,7 +53,7 @@ public class LiquifyTargetPartRander extends RanderTools {
|
||||
* 主渲染入口。收集文本命令到 pendingTexts,绘制完成后 popState,再把文本绘制出来(外部渲染)
|
||||
*/
|
||||
private void drawLiquifyOverlay(Mesh2D mesh2D, Matrix3f modelMatrix) {
|
||||
if (!isAlgorithmEnabled("showLiquifyOverlay")) return;
|
||||
if (!isAlgorithmEnabled("showLiquifyOverlay") || !mesh2D.isShowLiquifyOverlay()) return;
|
||||
|
||||
List<TextItem> pendingTexts = new ArrayList<>();
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class VertexDeformationRander extends RanderTools {
|
||||
* 绘制二级顶点(现代化样式)
|
||||
*/
|
||||
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();
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user