mirror of
https://github.com/JannisX11/blockbench.git
synced 2025-04-12 17:41:57 +08:00
Pre-compute vertex weights for proportional editing
Improve mesh selection update performance
This commit is contained in:
parent
1c941145bb
commit
65934f71be
@ -2,83 +2,99 @@ function sameMeshEdge(edge_a, edge_b) {
|
||||
return edge_a.equals(edge_b) || (edge_a[0] == edge_b[1] && edge_a[1] == edge_b[0])
|
||||
}
|
||||
|
||||
function proportionallyEditMeshVertices(mesh, callback) {
|
||||
if (!BarItems.proportional_editing.value) return;
|
||||
|
||||
let selected_vertices = mesh.getSelectedVertices();
|
||||
let {range, falloff, selection} = StateMemory.proportional_editing_options;
|
||||
let linear_distance = selection == 'linear';
|
||||
const ProportionalEdit = {
|
||||
vertex_weights: {},
|
||||
calculateWeights(mesh) {
|
||||
if (!BarItems.proportional_editing.value) return;
|
||||
|
||||
let all_mesh_connections;
|
||||
if (!linear_distance) {
|
||||
all_mesh_connections = {};
|
||||
for (let fkey in mesh.faces) {
|
||||
let face = mesh.faces[fkey];
|
||||
face.getEdges().forEach(edge => {
|
||||
if (!all_mesh_connections[edge[0]]) {
|
||||
all_mesh_connections[edge[0]] = [edge[1]];
|
||||
} else {
|
||||
all_mesh_connections[edge[0]].safePush(edge[1]);
|
||||
}
|
||||
if (!all_mesh_connections[edge[1]]) {
|
||||
all_mesh_connections[edge[1]] = [edge[0]];
|
||||
} else {
|
||||
all_mesh_connections[edge[1]].safePush(edge[0]);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
for (let vkey in mesh.vertices) {
|
||||
if (selected_vertices.includes(vkey)) continue;
|
||||
|
||||
let distance = Infinity;
|
||||
if (linear_distance) {
|
||||
// Linear Distance
|
||||
selected_vertices.forEach(vkey2 => {
|
||||
let pos1 = mesh.vertices[vkey];
|
||||
let pos2 = mesh.vertices[vkey2];
|
||||
let distance_square = Math.pow(pos1[0] - pos2[0], 2) + Math.pow(pos1[1] - pos2[1], 2) + Math.pow(pos1[2] - pos2[2], 2);
|
||||
if (distance_square < distance) {
|
||||
distance = distance_square;
|
||||
}
|
||||
})
|
||||
distance = Math.sqrt(distance);
|
||||
} else {
|
||||
// Connection Distance
|
||||
let found_match_depth = 0;
|
||||
let scanned = [];
|
||||
let frontier = [vkey];
|
||||
|
||||
depth_crawler:
|
||||
for (let depth = 1; depth <= range; depth++) {
|
||||
let new_frontier = [];
|
||||
for (let vkey1 of frontier) {
|
||||
let connections = all_mesh_connections[vkey1]?.filter(vkey2 => !scanned.includes(vkey2));
|
||||
if (!connections || connections.length == 0) continue;
|
||||
scanned.push(...connections);
|
||||
new_frontier.push(...connections);
|
||||
}
|
||||
for (let vkey2 of new_frontier) {
|
||||
if (selected_vertices.includes(vkey2)) {
|
||||
found_match_depth = depth;
|
||||
break depth_crawler;
|
||||
let selected_vertices = mesh.getSelectedVertices();
|
||||
let {range, falloff, selection} = StateMemory.proportional_editing_options;
|
||||
let linear_distance = selection == 'linear';
|
||||
|
||||
let all_mesh_connections;
|
||||
if (!linear_distance) {
|
||||
all_mesh_connections = {};
|
||||
for (let fkey in mesh.faces) {
|
||||
let face = mesh.faces[fkey];
|
||||
face.getEdges().forEach(edge => {
|
||||
if (!all_mesh_connections[edge[0]]) {
|
||||
all_mesh_connections[edge[0]] = [edge[1]];
|
||||
} else {
|
||||
all_mesh_connections[edge[0]].safePush(edge[1]);
|
||||
}
|
||||
}
|
||||
frontier = new_frontier;
|
||||
}
|
||||
if (found_match_depth) {
|
||||
distance = found_match_depth;
|
||||
if (!all_mesh_connections[edge[1]]) {
|
||||
all_mesh_connections[edge[1]] = [edge[0]];
|
||||
} else {
|
||||
all_mesh_connections[edge[1]].safePush(edge[0]);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if (distance > range) continue;
|
||||
|
||||
let blend = 1 - (distance / (linear_distance ? range : range+1));
|
||||
switch (falloff) {
|
||||
case 'hermite_spline': blend = Math.hermiteBlend(blend); break;
|
||||
case 'constant': blend = 1; break;
|
||||
ProportionalEdit.vertex_weights[mesh.uuid] = {};
|
||||
|
||||
for (let vkey in mesh.vertices) {
|
||||
if (selected_vertices.includes(vkey)) continue;
|
||||
|
||||
let distance = Infinity;
|
||||
if (linear_distance) {
|
||||
// Linear Distance
|
||||
selected_vertices.forEach(vkey2 => {
|
||||
let pos1 = mesh.vertices[vkey];
|
||||
let pos2 = mesh.vertices[vkey2];
|
||||
let distance_square = Math.pow(pos1[0] - pos2[0], 2) + Math.pow(pos1[1] - pos2[1], 2) + Math.pow(pos1[2] - pos2[2], 2);
|
||||
if (distance_square < distance) {
|
||||
distance = distance_square;
|
||||
}
|
||||
})
|
||||
distance = Math.sqrt(distance);
|
||||
} else {
|
||||
// Connection Distance
|
||||
let found_match_depth = 0;
|
||||
let scanned = [];
|
||||
let frontier = [vkey];
|
||||
|
||||
depth_crawler:
|
||||
for (let depth = 1; depth <= range; depth++) {
|
||||
let new_frontier = [];
|
||||
for (let vkey1 of frontier) {
|
||||
let connections = all_mesh_connections[vkey1]?.filter(vkey2 => !scanned.includes(vkey2));
|
||||
if (!connections || connections.length == 0) continue;
|
||||
scanned.push(...connections);
|
||||
new_frontier.push(...connections);
|
||||
}
|
||||
for (let vkey2 of new_frontier) {
|
||||
if (selected_vertices.includes(vkey2)) {
|
||||
found_match_depth = depth;
|
||||
break depth_crawler;
|
||||
}
|
||||
}
|
||||
frontier = new_frontier;
|
||||
}
|
||||
if (found_match_depth) {
|
||||
distance = found_match_depth;
|
||||
}
|
||||
}
|
||||
if (distance > range) continue;
|
||||
|
||||
let blend = 1 - (distance / (linear_distance ? range : range+1));
|
||||
switch (falloff) {
|
||||
case 'hermite_spline': blend = Math.hermiteBlend(blend); break;
|
||||
case 'constant': blend = 1; break;
|
||||
}
|
||||
ProportionalEdit.vertex_weights[mesh.uuid][vkey] = blend;
|
||||
}
|
||||
},
|
||||
editVertices(mesh, per_vertex) {
|
||||
if (!BarItems.proportional_editing.value) return;
|
||||
|
||||
let selected_vertices = mesh.getSelectedVertices();
|
||||
for (let vkey in mesh.vertices) {
|
||||
if (selected_vertices.includes(vkey)) continue;
|
||||
|
||||
let blend = ProportionalEdit.vertex_weights[mesh.uuid][vkey];
|
||||
per_vertex(vkey, blend);
|
||||
}
|
||||
callback(vkey, blend);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +113,12 @@ function moveElementsRelative(difference, index, event) { //Multiple
|
||||
difference *= canvasGridSize(event.shiftKey || Pressing.overrides.shift, event.ctrlOrCmd || Pressing.overrides.ctrl);
|
||||
}
|
||||
|
||||
if (BarItems.proportional_editing.value) {
|
||||
Mesh.selected.forEach(mesh => {
|
||||
ProportionalEdit.calculateWeights(mesh);
|
||||
})
|
||||
}
|
||||
|
||||
moveElementsInSpace(difference, axes[index]);
|
||||
updateSelection();
|
||||
|
||||
@ -582,8 +588,7 @@ function moveElementsInSpace(difference, axis) {
|
||||
selected_vertices.forEach(key => {
|
||||
el.vertices[key].V3_add(difference_vec);
|
||||
})
|
||||
proportionallyEditMeshVertices(el, (vkey, blend) => {
|
||||
console.log(vkey, blend)
|
||||
ProportionalEdit.editVertices(el, (vkey, blend) => {
|
||||
el.vertices[vkey].V3_add(difference_vec[0] * blend, difference_vec[1] * blend, difference_vec[2] * blend);
|
||||
})
|
||||
|
||||
|
@ -1137,6 +1137,11 @@
|
||||
}
|
||||
})
|
||||
}
|
||||
if (BarItems.proportional_editing.value) {
|
||||
Mesh.selected.forEach(mesh => {
|
||||
ProportionalEdit.calculateWeights(mesh);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (rotate_group) {
|
||||
|
@ -1039,6 +1039,18 @@ new NodePreviewController(Mesh, {
|
||||
mesh.outline.geometry.needsUpdate = true;
|
||||
}
|
||||
|
||||
let face_outlines = {};
|
||||
if (BarItems.selection_mode.value == 'face' || BarItems.selection_mode.value == 'cluster') {
|
||||
selected_faces.forEach(fkey => {
|
||||
let face = element.faces[fkey];
|
||||
face.vertices.forEach(vkey => {
|
||||
if (!face_outlines[vkey]) face_outlines[vkey] = new Set();
|
||||
face.vertices.forEach(vkey2 => {
|
||||
if (vkey2 != vkey) face_outlines[vkey].add(vkey2);
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
let line_colors = [];
|
||||
mesh.outline.vertex_order.forEach((key, i) => {
|
||||
let key_b = Modes.edit && mesh.outline.vertex_order[i + ((i%2) ? -1 : 1) ];
|
||||
@ -1049,7 +1061,7 @@ new NodePreviewController(Mesh, {
|
||||
} else if (BarItems.selection_mode.value == 'edge' && selected_edges.find(edge => sameMeshEdge([key, key_b], edge))) {
|
||||
color = white;
|
||||
selected = true;
|
||||
} else if ((BarItems.selection_mode.value == 'face' || BarItems.selection_mode.value == 'cluster') && selected_faces.find(fkey => element.faces[fkey].vertices.includes(key) && element.faces[fkey].vertices.includes(key_b))) {
|
||||
} else if ((BarItems.selection_mode.value == 'face' || BarItems.selection_mode.value == 'cluster') && face_outlines[key] && face_outlines[key].has(key_b)) {
|
||||
color = white;
|
||||
selected = true;
|
||||
} else {
|
||||
@ -1085,13 +1097,14 @@ new NodePreviewController(Mesh, {
|
||||
|
||||
let array = new Array(mesh.geometry.attributes.highlight.count).fill(highlighted);
|
||||
let selection_mode = BarItems.selection_mode.value;
|
||||
let selected_faces = element.getSelectedFaces();
|
||||
|
||||
if (!force_off && element.selected && Modes.edit) {
|
||||
let i = 0;
|
||||
for (let fkey in element.faces) {
|
||||
let face = element.faces[fkey];
|
||||
if (face.vertices.length < 3) continue;
|
||||
if (face.isSelected() && (selection_mode == 'face' || selection_mode == 'cluster')) {
|
||||
if (selected_faces.indexOf(fkey) != -1 && (selection_mode == 'face' || selection_mode == 'cluster')) {
|
||||
for (let j = 0; j < face.vertices.length; j++) {
|
||||
array[i] = 2;
|
||||
i++;
|
||||
|
Loading…
x
Reference in New Issue
Block a user