2c98c43754
Implement normal transform space Turn name of locked elements gray in outliner Fix face selection issue Improve performance by hiding unselected faces on large meshes Fix issue where imported bbmodels would switch to generic model
1720 lines
52 KiB
JavaScript
1720 lines
52 KiB
JavaScript
//Actions
|
|
function origin2geometry() {
|
|
|
|
if (Format.bone_rig && Group.selected) {
|
|
Undo.initEdit({group: Group.selected})
|
|
|
|
if (!Group.selected || Group.selected.children.length === 0) return;
|
|
var position = new THREE.Vector3();
|
|
let amount = 0;
|
|
Group.selected.children.forEach(function(obj) {
|
|
if (obj.getWorldCenter) {
|
|
position.add(obj.getWorldCenter());
|
|
amount++;
|
|
}
|
|
})
|
|
position.divideScalar(amount);
|
|
Group.selected.mesh.parent.worldToLocal(position);
|
|
if (Group.selected.parent instanceof Group) {
|
|
position.x += Group.selected.parent.origin[0];
|
|
position.y += Group.selected.parent.origin[1];
|
|
position.z += Group.selected.parent.origin[2];
|
|
}
|
|
Group.selected.transferOrigin(position.toArray());
|
|
|
|
} else if (Outliner.selected[0]) {
|
|
Undo.initEdit({elements: Outliner.selected})
|
|
|
|
var center = getSelectionCenter();
|
|
var original_center = center.slice();
|
|
|
|
Outliner.selected.forEach(element => {
|
|
if (!element.transferOrigin) return;
|
|
if (Format.bone_rig && element.parent instanceof Group) {
|
|
var v = new THREE.Vector3().fromArray(original_center);
|
|
element.parent.mesh.worldToLocal(v);
|
|
v.x += element.parent.origin[0];
|
|
v.y += element.parent.origin[1];
|
|
v.z += element.parent.origin[2];
|
|
center = v.toArray();
|
|
element.transferOrigin(center)
|
|
} else {
|
|
element.transferOrigin(original_center)
|
|
}
|
|
})
|
|
}
|
|
Canvas.updateView({
|
|
elements: Outliner.selected,
|
|
element_aspects: {transform: true, geometry: true},
|
|
groups: Group.selected && [Group.selected],
|
|
selection: true
|
|
});
|
|
Undo.finishEdit('Center pivot')
|
|
}
|
|
function getSelectionCenter(all = false) {
|
|
if (Group.selected && selected.length == 0 && !all) {
|
|
let vec = THREE.fastWorldPosition(Group.selected.mesh, new THREE.Vector3());
|
|
return vec.toArray();
|
|
}
|
|
var center = [0, 0, 0]
|
|
var i = 0;
|
|
let items = (selected.length == 0 || all) ? elements : selected;
|
|
items.forEach(obj => {
|
|
if (obj.getWorldCenter) {
|
|
var pos = obj.getWorldCenter();
|
|
center[0] += pos.x
|
|
center[1] += pos.y
|
|
center[2] += pos.z
|
|
}
|
|
})
|
|
if (items.length) {
|
|
for (var i = 0; i < 3; i++) {
|
|
center[i] = center[i] / items.length
|
|
}
|
|
}
|
|
if (!Format.centered_grid) {
|
|
center.V3_add(8, 8, 8)
|
|
}
|
|
return center;
|
|
}
|
|
function limitToBox(val, inflate) {
|
|
if (typeof inflate != 'number') inflate = 0;
|
|
if (!(Format.canvas_limit && !settings.deactivate_size_limit.value)) {
|
|
return val;
|
|
} else if (val + inflate > 32) {
|
|
return 32 - inflate;
|
|
} else if (val - inflate < -16) {
|
|
return -16 + inflate;
|
|
} else {
|
|
return val;
|
|
}
|
|
}
|
|
//Movement
|
|
function moveCubesRelative(difference, index, event) { //Multiple
|
|
if (!quad_previews.current || !Cube.selected.length) {
|
|
return;
|
|
}
|
|
var _has_groups = Format.bone_rig && Group.selected && Group.selected.matchesSelection() && Toolbox.selected.transformerMode == 'translate';
|
|
|
|
Undo.initEdit({elements: Cube.selected, outliner: _has_groups})
|
|
var axes = []
|
|
// < >
|
|
// PageUpDown
|
|
// ^ v
|
|
var facing = quad_previews.current.getFacingDirection()
|
|
var height = quad_previews.current.getFacingHeight()
|
|
switch (facing) {
|
|
case 'north': axes = [0, 2, 1]; break;
|
|
case 'south': axes = [0, 2, 1]; break;
|
|
case 'west': axes = [2, 0, 1]; break;
|
|
case 'east': axes = [2, 0, 1]; break;
|
|
}
|
|
|
|
if (height !== 'middle') {
|
|
if (index === 1) {
|
|
index = 2
|
|
} else if (index === 2) {
|
|
index = 1
|
|
}
|
|
}
|
|
if (facing === 'south' && (index === 0 || index === 1)) difference *= -1
|
|
if (facing === 'west' && index === 0) difference *= -1
|
|
if (facing === 'east' && index === 1) difference *= -1
|
|
if (index === 2 && height !== 'down') difference *= -1
|
|
if (index === 1 && height === 'up') difference *= -1
|
|
|
|
if (event) {
|
|
difference *= canvasGridSize(event.shiftKey || Pressing.overrides.shift, event.ctrlOrCmd || Pressing.overrides.ctrl);
|
|
}
|
|
|
|
moveElementsInSpace(difference, axes[index]);
|
|
updateSelection();
|
|
|
|
Undo.finishEdit('Move elements')
|
|
}
|
|
//Rotate
|
|
function rotateSelected(axis, steps) {
|
|
let affected = [...Cube.selected, ...Mesh.selected];
|
|
if (!affected.length) return;
|
|
Undo.initEdit({elements: affected});
|
|
if (!steps) steps = 1
|
|
var origin = [8, 8, 8]
|
|
if (Group.selected && Format.bone_rig) {
|
|
origin = Group.selected.origin.slice()
|
|
} else if (Format.centered_grid) {
|
|
origin = [0, 0, 0]
|
|
} else {
|
|
origin = affected[0].origin.slice()
|
|
}
|
|
affected.forEach(function(obj) {
|
|
obj.roll(axis, steps, origin)
|
|
})
|
|
updateSelection();
|
|
Undo.finishEdit('Rotate elements')
|
|
}
|
|
//Mirror
|
|
function mirrorSelected(axis) {
|
|
if (Modes.animate && Timeline.selected.length) {
|
|
|
|
Undo.initEdit({keyframes: Timeline.selected})
|
|
for (var kf of Timeline.selected) {
|
|
kf.flip(axis)
|
|
}
|
|
Undo.finishEdit('Flipped keyframes');
|
|
updateKeyframeSelection();
|
|
Animator.preview();
|
|
|
|
} else if (Modes.edit && selected.length) {
|
|
Undo.initEdit({elements: selected, outliner: Format.bone_rig})
|
|
var center = Format.centered_grid ? 0 : 8;
|
|
if (Format.bone_rig) {
|
|
if (Group.selected && Group.selected.matchesSelection()) {
|
|
function flipGroup(group) {
|
|
if (group.type === 'group') {
|
|
for (var i = 0; i < 3; i++) {
|
|
if (i === axis) {
|
|
group.origin[i] *= -1
|
|
} else {
|
|
group.rotation[i] *= -1
|
|
}
|
|
}
|
|
if (axis == 0 && group.name.includes('right')) {
|
|
let name = group.name.replace(/right/g, 'left').replace(/2/, '');
|
|
if (!Group.all.find(g => g.name == name)) group.name = name;
|
|
|
|
} else if (axis == 0 && group.name.includes('left')) {
|
|
let name = group.name.replace(/left/g, 'right').replace(/2/, '');
|
|
if (!Group.all.find(g => g.name == name)) group.name = name;
|
|
}
|
|
}
|
|
Canvas.updateAllBones([group]);
|
|
}
|
|
flipGroup(Group.selected)
|
|
Group.selected.forEachChild(flipGroup)
|
|
}
|
|
}
|
|
selected.forEach(function(obj) {
|
|
obj.flip(axis, center, false)
|
|
if (Project.box_uv && obj instanceof Cube && axis === 0) {
|
|
obj.shade = !obj.shade
|
|
Canvas.updateUV(obj)
|
|
}
|
|
})
|
|
updateSelection()
|
|
Undo.finishEdit('Flip selection')
|
|
}
|
|
}
|
|
|
|
const Vertexsnap = {
|
|
step1: true,
|
|
vertex_gizmos: new THREE.Object3D(),
|
|
line: new THREE.Line(new THREE.BufferGeometry(), Canvas.outlineMaterial),
|
|
elements_with_vertex_gizmos: [],
|
|
hovering: false,
|
|
addVertices: function(element) {
|
|
if (Vertexsnap.elements_with_vertex_gizmos.includes(element)) return;
|
|
if (element.visibility === false) return;
|
|
let {mesh} = element;
|
|
|
|
$('#preview').get(0).removeEventListener("mousemove", Vertexsnap.hoverCanvas)
|
|
$('#preview').get(0).addEventListener("mousemove", Vertexsnap.hoverCanvas)
|
|
|
|
if (!mesh.vertex_points) {
|
|
mesh.updateMatrixWorld()
|
|
let vectors = [];
|
|
let positions = mesh.geometry.attributes.position.array;
|
|
for (let i = 0; i < positions.length; i += 3) {
|
|
let vec = [positions[i], positions[i+1], positions[i+2]];
|
|
if (!vectors.find(vec2 => vec.equals(vec2))) {
|
|
vectors.push(vec);
|
|
}
|
|
}
|
|
vectors.push([0, 0, 0]);
|
|
|
|
let points = new THREE.Points(new THREE.BufferGeometry(), new THREE.PointsMaterial().copy(Canvas.meshVertexMaterial));
|
|
points.vertices = vectors;
|
|
let vector_positions = [];
|
|
vectors.forEach(vector => vector_positions.push(...vector));
|
|
let vector_colors = [];
|
|
vectors.forEach(vector => vector_colors.push(gizmo_colors.grid.r, gizmo_colors.grid.g, gizmo_colors.grid.b));
|
|
points.geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vector_positions), 3));
|
|
points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(new Float32Array(vector_colors), 3));
|
|
points.material.transparent = true;
|
|
mesh.vertex_points = points;
|
|
mesh.outline.add(points);
|
|
}
|
|
mesh.vertex_points.visible = true;
|
|
mesh.vertex_points.renderOrder = 900;
|
|
|
|
Vertexsnap.elements_with_vertex_gizmos.push(element)
|
|
},
|
|
clearVertexGizmos: function() {
|
|
Project.model_3d.remove(Vertexsnap.line);
|
|
Vertexsnap.elements_with_vertex_gizmos.forEach(element => {
|
|
if (element.mesh.vertex_points) {
|
|
element.mesh.vertex_points.visible = false;
|
|
if (element instanceof Mesh == false) {
|
|
element.mesh.vertex_points.parent.remove(element.mesh.vertex_points);
|
|
delete element.mesh.vertex_points;
|
|
}
|
|
}
|
|
|
|
})
|
|
Vertexsnap.elements_with_vertex_gizmos.empty();
|
|
$('#preview').get(0).removeEventListener("mousemove", Vertexsnap.hoverCanvas)
|
|
},
|
|
hoverCanvas: function(event) {
|
|
let data = Canvas.raycast(event)
|
|
|
|
if (Vertexsnap.hovering) {
|
|
Project.model_3d.remove(Vertexsnap.line);
|
|
Vertexsnap.elements_with_vertex_gizmos.forEach(el => {
|
|
let points = el.mesh.vertex_points;
|
|
let colors = [];
|
|
for (let i = 0; i < points.geometry.attributes.position.count; i++) {
|
|
let color;
|
|
if (data && data.element == el && data.type == 'vertex' && data.vertex_index == i) {
|
|
color = gizmo_colors.outline;
|
|
} else {
|
|
color = gizmo_colors.grid;
|
|
}
|
|
colors.push(color.r, color.g, color.b);
|
|
}
|
|
points.material.depthTest = !(data.element == el);
|
|
points.geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
|
|
})
|
|
}
|
|
if (!data || data.type !== 'vertex') {
|
|
Blockbench.setStatusBarText()
|
|
return;
|
|
}
|
|
Vertexsnap.hovering = true
|
|
|
|
if (Vertexsnap.step1 === false) {
|
|
let {line} = Vertexsnap;
|
|
let {geometry} = line;
|
|
|
|
let vertex_pos = Vertexsnap.getGlobalVertexPos(data.element, data.vertex);
|
|
geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array([...Vertexsnap.vertex_pos.toArray(), ...vertex_pos.toArray()]), 3));
|
|
|
|
line.renderOrder = 900
|
|
Project.model_3d.add(Vertexsnap.line);
|
|
//Measure
|
|
var diff = new THREE.Vector3().copy(Vertexsnap.vertex_pos);
|
|
diff.sub(vertex_pos);
|
|
Blockbench.setStatusBarText(tl('status_bar.vertex_distance', [trimFloatNumber(diff.length())] ));
|
|
}
|
|
},
|
|
select: function() {
|
|
Vertexsnap.clearVertexGizmos()
|
|
Outliner.selected.forEach(function(element) {
|
|
Vertexsnap.addVertices(element)
|
|
})
|
|
if (Outliner.selected.length) {
|
|
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
|
|
}
|
|
},
|
|
canvasClick: function(data) {
|
|
if (!data || data.type !== 'vertex') return;
|
|
|
|
if (Vertexsnap.step1) {
|
|
Vertexsnap.step1 = false
|
|
Vertexsnap.vertex_pos = Vertexsnap.getGlobalVertexPos(data.element, data.vertex);
|
|
Vertexsnap.vertex_index = data.vertex_index;
|
|
Vertexsnap.move_origin = typeof data.vertex !== 'string' && data.vertex.allEqual(0);
|
|
Vertexsnap.elements = Outliner.selected.slice();
|
|
Vertexsnap.clearVertexGizmos()
|
|
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
|
|
|
|
} else {
|
|
Vertexsnap.snap(data)
|
|
$('#preview').css('cursor', (Vertexsnap.step1 ? 'copy' : 'alias'))
|
|
}
|
|
Blockbench.setStatusBarText()
|
|
},
|
|
getGlobalVertexPos(element, vertex) {
|
|
let vector = new THREE.Vector3();
|
|
vector.fromArray(vertex instanceof Array ? vertex : element.vertices[vertex]);
|
|
element.mesh.localToWorld(vector);
|
|
return vector;
|
|
},
|
|
snap: function(data) {
|
|
Undo.initEdit({elements: Vertexsnap.elements})
|
|
|
|
let mode = BarItems.vertex_snap_mode.get()
|
|
|
|
if (Vertexsnap.move_origin) {
|
|
|
|
Vertexsnap.elements.forEach(function(element) {
|
|
let vec = Vertexsnap.getGlobalVertexPos(data.element, data.vertex);
|
|
|
|
if (Format.bone_rig && element.parent instanceof Group && element.mesh.parent) {
|
|
element.mesh.parent.worldToLocal(vec);
|
|
}
|
|
let vec_array = vec.toArray()
|
|
vec_array.V3_add(element.parent.origin);
|
|
element.transferOrigin(vec_array)
|
|
})
|
|
} else {
|
|
|
|
var global_delta = Vertexsnap.getGlobalVertexPos(data.element, data.vertex);
|
|
global_delta.sub(Vertexsnap.vertex_pos)
|
|
|
|
if (mode === 'scale' && !Format.integer_size && Vertexsnap.elements[0] instanceof Cube) {
|
|
//Scale
|
|
|
|
var m;
|
|
switch (Vertexsnap.vertex_index) {
|
|
case 0: m=[ 1,1,1 ]; break;
|
|
case 1: m=[ 1,1,0 ]; break;
|
|
case 2: m=[ 1,0,1 ]; break;
|
|
case 3: m=[ 1,0,0 ]; break;
|
|
case 4: m=[ 0,1,0 ]; break;
|
|
case 5: m=[ 0,1,1 ]; break;
|
|
case 6: m=[ 0,0,0 ]; break;
|
|
case 7: m=[ 0,0,1 ]; break;
|
|
}
|
|
|
|
Vertexsnap.elements.forEach(function(obj) {
|
|
if (obj instanceof Cube == false) return;
|
|
var q = obj.mesh.getWorldQuaternion(new THREE.Quaternion()).invert()
|
|
var cube_pos = new THREE.Vector3().copy(global_delta).applyQuaternion(q)
|
|
|
|
for (i=0; i<3; i++) {
|
|
if (m[i] === 1) {
|
|
obj.to[i] = limitToBox(obj.to[i] + cube_pos.getComponent(i), obj.inflate)
|
|
} else {
|
|
obj.from[i] = limitToBox(obj.from[i] + cube_pos.getComponent(i), -obj.inflate)
|
|
}
|
|
}
|
|
if (Project.box_uv && obj.visibility) {
|
|
Canvas.updateUV(obj)
|
|
}
|
|
})
|
|
} else if (mode === 'move') {
|
|
Vertexsnap.elements.forEach(function(obj) {
|
|
var cube_pos = new THREE.Vector3().copy(global_delta)
|
|
|
|
if (Format.bone_rig && obj.parent instanceof Group && obj.mesh.parent) {
|
|
var q = obj.mesh.parent.getWorldQuaternion(new THREE.Quaternion()).invert();
|
|
cube_pos.applyQuaternion(q);
|
|
}
|
|
if (obj instanceof Cube && Format.rotate_cubes) {
|
|
obj.origin.V3_add(cube_pos);
|
|
}
|
|
var in_box = obj.moveVector(cube_pos.toArray());
|
|
if (!in_box && Format.canvas_limit && !settings.deactivate_size_limit.value) {
|
|
Blockbench.showMessageBox({translateKey: 'canvas_limit_error'})
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
Vertexsnap.clearVertexGizmos()
|
|
Canvas.updateAllPositions()
|
|
Undo.finishEdit('Use vertex snap')
|
|
Vertexsnap.step1 = true
|
|
}
|
|
}
|
|
//Scale
|
|
function scaleAll(save, size) {
|
|
if (save === true) {
|
|
hideDialog()
|
|
}
|
|
if (size === undefined) {
|
|
size = $('#model_scale_label').val()
|
|
}
|
|
var origin = [
|
|
parseFloat($('#scaling_origin_x').val())||0,
|
|
parseFloat($('#scaling_origin_y').val())||0,
|
|
parseFloat($('#scaling_origin_z').val())||0,
|
|
]
|
|
var overflow = [];
|
|
Outliner.selected.forEach(function(obj) {
|
|
obj.autouv = 0;
|
|
origin.forEach(function(ogn, i) {
|
|
if ($('#model_scale_'+getAxisLetter(i)+'_axis').is(':checked')) {
|
|
|
|
if (obj.from) {
|
|
obj.from[i] = (obj.before.from[i] - obj.inflate - ogn) * size;
|
|
if (obj.from[i] + ogn > 32 || obj.from[i] + ogn < -16) overflow.push(obj);
|
|
obj.from[i] = limitToBox(obj.from[i] + obj.inflate + ogn, -obj.inflate);
|
|
}
|
|
|
|
if (obj.to) {
|
|
obj.to[i] = (obj.before.to[i] + obj.inflate - ogn) * size;
|
|
if (obj.to[i] + ogn > 32 || obj.to[i] + ogn < -16) overflow.push(obj);
|
|
obj.to[i] = limitToBox(obj.to[i] - obj.inflate + ogn, obj.inflate);
|
|
if (Format.integer_size) {
|
|
obj.to[i] = obj.from[i] + Math.round(obj.to[i] - obj.from[i])
|
|
}
|
|
}
|
|
|
|
if (obj.origin) {
|
|
obj.origin[i] = (obj.before.origin[i] - ogn) * size;
|
|
obj.origin[i] = obj.origin[i] + ogn;
|
|
}
|
|
|
|
if (obj instanceof Mesh) {
|
|
for (let key in obj.vertices) {
|
|
obj.vertices[key][i] = (obj.before.vertices[key][i] - ogn) * size + ogn;
|
|
}
|
|
}
|
|
} else {
|
|
|
|
if (obj.from) obj.from[i] = obj.before.from[i];
|
|
if (obj.to) obj.to[i] = obj.before.to[i];
|
|
|
|
if (obj.origin) obj.origin[i] = obj.before.origin[i];
|
|
|
|
if (obj instanceof Mesh) {
|
|
for (let key in obj.vertices) {
|
|
obj.vertices[key][i] = obj.before.vertices[key][i];
|
|
}
|
|
}
|
|
}
|
|
})
|
|
if (save === true) {
|
|
delete obj.before
|
|
}
|
|
if (Project.box_uv) {
|
|
Canvas.updateUV(obj)
|
|
}
|
|
})
|
|
if (Format.bone_rig && Group.selected) {
|
|
Group.selected.forEachChild((g) => {
|
|
g.origin[0] = g.old_origin[0] * size
|
|
g.origin[1] = g.old_origin[1] * size
|
|
g.origin[2] = g.old_origin[2] * size
|
|
if (save === true) {
|
|
delete g.old_origin
|
|
}
|
|
}, Group)
|
|
}
|
|
if (overflow.length && Format.canvas_limit && !settings.deactivate_size_limit.value) {
|
|
scaleAll.overflow = overflow;
|
|
$('#scaling_clipping_warning').text('Model clipping: Your model is too large for the canvas')
|
|
$('#scale_overflow_btn').css('display', 'inline-block')
|
|
} else {
|
|
$('#scaling_clipping_warning').text('')
|
|
$('#scale_overflow_btn').hide()
|
|
}
|
|
Canvas.updatePositions()
|
|
if (save === true) {
|
|
Undo.finishEdit('Scale model')
|
|
}
|
|
}
|
|
function modelScaleSync(label) {
|
|
if (label) {
|
|
var size = $('#model_scale_label').val()
|
|
$('#model_scale_range').val(size)
|
|
} else {
|
|
var size = $('#model_scale_range').val()
|
|
$('#model_scale_label').val(size)
|
|
}
|
|
scaleAll(false, size)
|
|
}
|
|
function cancelScaleAll() {
|
|
selected.forEach(function(obj) {
|
|
if (obj === undefined) return;
|
|
if (obj.from) obj.from.V3_set(obj.before.from);
|
|
if (obj.to) obj.to.V3_set(obj.before.to);
|
|
if (obj.origin) obj.origin.V3_set(obj.before.origin);
|
|
if (obj instanceof Mesh) {
|
|
for (let key in obj.vertices) {
|
|
obj.vertices[key].V3_set(obj.before.vertices[key]);
|
|
}
|
|
}
|
|
delete obj.before
|
|
if (Project.box_uv) {
|
|
Canvas.updateUV(obj)
|
|
}
|
|
})
|
|
if (Format.bone_rig && Group.selected) {
|
|
Group.selected.forEachChild((g) => {
|
|
g.origin[0] = g.old_origin[0]
|
|
g.origin[1] = g.old_origin[1]
|
|
g.origin[2] = g.old_origin[2]
|
|
delete g.old_origin
|
|
}, Group)
|
|
}
|
|
Canvas.updatePositions()
|
|
hideDialog()
|
|
}
|
|
function setScaleAllPivot(mode) {
|
|
if (mode === 'selection') {
|
|
var center = getSelectionCenter()
|
|
} else {
|
|
var center = Cube.selected[0] && Cube.selected[0].origin;
|
|
}
|
|
if (center) {
|
|
$('input#scaling_origin_x').val(center[0]);
|
|
$('input#scaling_origin_y').val(center[1]);
|
|
$('input#scaling_origin_z').val(center[2]);
|
|
}
|
|
}
|
|
function scaleAllSelectOverflow() {
|
|
cancelScaleAll()
|
|
selected.empty();
|
|
scaleAll.overflow.forEach(obj => {
|
|
obj.selectLow()
|
|
})
|
|
updateSelection();
|
|
}
|
|
//Center
|
|
function centerElementsAll(axis) {
|
|
centerElements(0, false)
|
|
centerElements(1, false)
|
|
centerElements(2, false)
|
|
Canvas.updatePositions()
|
|
}
|
|
function centerElements(axis, update) {
|
|
if (!selected.length) return;
|
|
let center = getSelectionCenter()[axis];
|
|
var difference = (Format.centered_grid ? 0 : 8) - center
|
|
|
|
selected.forEach(function(obj) {
|
|
if (obj.movable) obj.origin[axis] += difference;
|
|
if (obj.to) obj.to[axis] = limitToBox(obj.to[axis] + difference, obj.inflate);
|
|
if (obj instanceof Cube) obj.from[axis] = limitToBox(obj.from[axis] + difference, obj.inflate);
|
|
})
|
|
|
|
if (update !== false) {
|
|
Canvas.updatePositions()
|
|
}
|
|
}
|
|
|
|
//Move
|
|
function moveElementsInSpace(difference, axis) {
|
|
let space = Transformer.getTransformSpace()
|
|
let group = Format.bone_rig && Group.selected && Group.selected.matchesSelection() && Group.selected;
|
|
var group_m;
|
|
let quaternion = new THREE.Quaternion();
|
|
let vector = new THREE.Vector3();
|
|
|
|
if (group) {
|
|
if (space === 0) {
|
|
group_m = vector.set(0, 0, 0);
|
|
group_m[getAxisLetter(axis)] = difference;
|
|
|
|
var rotation = new THREE.Quaternion();
|
|
group.mesh.parent.getWorldQuaternion(rotation);
|
|
group_m.applyQuaternion(rotation.invert());
|
|
|
|
group.forEachChild(g => {
|
|
g.origin.V3_add(group_m.x, group_m.y, group_m.z);
|
|
}, Group, true)
|
|
|
|
} else if (space === 2) {
|
|
group_m = new THREE.Vector3();
|
|
group_m[getAxisLetter(axis)] = difference;
|
|
|
|
group_m.applyQuaternion(group.mesh.quaternion);
|
|
|
|
group.forEachChild(g => {
|
|
g.origin.V3_add(group_m.x, group_m.y, group_m.z);
|
|
}, Group, true)
|
|
|
|
} else {
|
|
group.forEachChild(g => {
|
|
g.origin[axis] += difference
|
|
}, Group, true)
|
|
}
|
|
Canvas.updateAllBones([Group.selected]);
|
|
}
|
|
|
|
selected.forEach(el => {
|
|
|
|
if (!group_m && el instanceof Mesh && Project.selected_vertices[el.uuid] && Project.selected_vertices[el.uuid].length > 0) {
|
|
|
|
let selection_rotation = space == 3 && el.getSelectionRotation();
|
|
Project.selected_vertices[el.uuid].forEach(key => {
|
|
|
|
if (space == 2) {
|
|
el.vertices[key][axis] += difference;
|
|
|
|
} else if (space == 3) {
|
|
let m = vector.set(0, 0, 0);
|
|
m[getAxisLetter(axis)] = difference;
|
|
m.applyEuler(selection_rotation);
|
|
el.vertices[key].V3_add(m.x, m.y, m.z);
|
|
|
|
} else {
|
|
let m = vector.set(0, 0, 0);
|
|
m[getAxisLetter(axis)] = difference;
|
|
m.applyQuaternion(el.mesh.getWorldQuaternion(quaternion).invert());
|
|
el.vertices[key].V3_add(m.x, m.y, m.z);
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
if (space == 2 && !group_m) {
|
|
if (el instanceof Locator) {
|
|
let m = vector.set(0, 0, 0);
|
|
m[getAxisLetter(axis)] = difference;
|
|
m.applyQuaternion(el.mesh.quaternion);
|
|
el.from.V3_add(m.x, m.y, m.z);
|
|
|
|
} else if (el instanceof TextureMesh) {
|
|
el.local_pivot[axis] += difference;
|
|
|
|
} else {
|
|
if (el.movable) el.from[axis] += difference;
|
|
if (el.resizable && el.to) el.to[axis] += difference;
|
|
}
|
|
|
|
} else if (space instanceof Group) {
|
|
if (el.movable && el instanceof Mesh == false) el.from[axis] += difference;
|
|
if (el.resizable && el.to) el.to[axis] += difference;
|
|
if (el.rotatable && el instanceof Locator == false) el.origin[axis] += difference;
|
|
} else {
|
|
let move_origin = !!group;
|
|
if (group_m) {
|
|
var m = group_m
|
|
} else {
|
|
var m = vector.set(0, 0, 0);
|
|
m[getAxisLetter(axis)] = difference;
|
|
|
|
let parent = el.parent;
|
|
while (parent instanceof Group) {
|
|
if (!parent.rotation.allEqual(0)) break;
|
|
parent = parent.parent;
|
|
}
|
|
|
|
if (parent == 'root') {
|
|
// If none of the parent groups are rotated, move origin.
|
|
move_origin = true;
|
|
} else {
|
|
var rotation = new THREE.Quaternion();
|
|
if (el.mesh && el instanceof Locator == false) {
|
|
el.mesh.getWorldQuaternion(rotation);
|
|
} else if (el.parent instanceof Group) {
|
|
el.parent.mesh.getWorldQuaternion(rotation);
|
|
}
|
|
m.applyQuaternion(rotation.invert());
|
|
}
|
|
}
|
|
|
|
if (el.movable && el instanceof Mesh == false) el.from.V3_add(m.x, m.y, m.z);
|
|
if (el.resizable && el.to) el.to.V3_add(m.x, m.y, m.z);
|
|
if (move_origin) {
|
|
if (el.rotatable && el instanceof Locator == false && el instanceof TextureMesh == false) el.origin.V3_add(m.x, m.y, m.z);
|
|
}
|
|
}
|
|
}
|
|
if (el instanceof Cube) {
|
|
el.mapAutoUV()
|
|
}
|
|
})
|
|
Canvas.updateView({elements: selected, element_aspects: {transform: true, geometry: true}})
|
|
}
|
|
|
|
//Rotate
|
|
function getRotationInterval(event) {
|
|
if (Format.rotation_limit) {
|
|
return 22.5;
|
|
} else if ((event.shiftKey || Pressing.overrides.shift) && (event.ctrlOrCmd || Pressing.overrides.ctrl)) {
|
|
return 0.25;
|
|
} else if (event.shiftKey || Pressing.overrides.shift) {
|
|
return 22.5;
|
|
} else if (event.ctrlOrCmd || Pressing.overrides.ctrl) {
|
|
return 1;
|
|
} else {
|
|
return 2.5;
|
|
}
|
|
}
|
|
function getRotationObject() {
|
|
if (Format.bone_rig && Group.selected) return Group.selected;
|
|
let elements = Outliner.selected.filter(element => {
|
|
return element.rotatable && (element instanceof Cube == false || Format.rotate_cubes);
|
|
})
|
|
if (elements.length) return elements;
|
|
}
|
|
function rotateOnAxis(modify, axis, slider) {
|
|
var things = getRotationObject();
|
|
if (!things) return;
|
|
if (things instanceof Array == false) things = [things];
|
|
/*
|
|
if (Format.bone_rig && Group.selected) {
|
|
if (!Group.selected) return;
|
|
let obj = Group.selected.mesh
|
|
|
|
if (typeof space == 'object') {
|
|
let normal = axis == 0 ? THREE.NormalX : (axis == 1 ? THREE.NormalY : THREE.NormalZ)
|
|
let rotWorldMatrix = new THREE.Matrix4();
|
|
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(modify(0)))
|
|
rotWorldMatrix.multiply(obj.matrix)
|
|
obj.matrix.copy(rotWorldMatrix)
|
|
obj.setRotationFromMatrix(rotWorldMatrix)
|
|
let e = obj.rotation;
|
|
Group.selected.rotation[0] = Math.radToDeg(e.x);
|
|
Group.selected.rotation[1] = Math.radToDeg(e.y);
|
|
Group.selected.rotation[2] = Math.radToDeg(e.z);
|
|
Canvas.updateAllBones()
|
|
|
|
} else if (space == 0) {
|
|
let normal = axis == 0 ? THREE.NormalX : (axis == 1 ? THREE.NormalY : THREE.NormalZ)
|
|
let rotWorldMatrix = new THREE.Matrix4();
|
|
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(modify(0)))
|
|
rotWorldMatrix.multiply(obj.matrixWorld)
|
|
|
|
let inverse = new THREE.Matrix4().copy(obj.parent.matrixWorld).invert()
|
|
rotWorldMatrix.premultiply(inverse)
|
|
|
|
obj.matrix.copy(rotWorldMatrix)
|
|
obj.setRotationFromMatrix(rotWorldMatrix)
|
|
let e = obj.rotation;
|
|
Group.selected.rotation[0] = Math.radToDeg(e.x);
|
|
Group.selected.rotation[1] = Math.radToDeg(e.y);
|
|
Group.selected.rotation[2] = Math.radToDeg(e.z);
|
|
Canvas.updateAllBones()
|
|
|
|
} else {
|
|
var value = modify(Group.selected.rotation[axis]);
|
|
Group.selected.rotation[axis] = Math.trimDeg(value)
|
|
Canvas.updateAllBones()
|
|
}
|
|
return;
|
|
}
|
|
*/
|
|
//Warning
|
|
if (Format.rotation_limit && settings.dialog_rotation_limit.value) {
|
|
var i = 0;
|
|
while (i < Cube.selected.length) {
|
|
if (Cube.selected[i].rotation[(axis+1)%3] ||
|
|
Cube.selected[i].rotation[(axis+2)%3]
|
|
) {
|
|
i = Infinity
|
|
|
|
Blockbench.showMessageBox({
|
|
title: tl('message.rotation_limit.title'),
|
|
icon: 'rotate_right',
|
|
message: tl('message.rotation_limit.message'),
|
|
buttons: [tl('dialog.ok'), tl('dialog.dontshowagain')]
|
|
}, function(r) {
|
|
if (r === 1) {
|
|
settings.dialog_rotation_limit.value = false
|
|
Settings.save()
|
|
}
|
|
})
|
|
return;
|
|
//Gotta stop the numslider here
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
var axis_letter = getAxisLetter(axis)
|
|
var origin = things[0].origin
|
|
things.forEach(function(obj, i) {
|
|
if (!obj.rotation.allEqual(0)) {
|
|
origin = obj.origin
|
|
}
|
|
})
|
|
|
|
let space = Transformer.getTransformSpace()
|
|
if (axis instanceof THREE.Vector3) space = 0;
|
|
things.forEach(obj => {
|
|
let mesh = obj.mesh;
|
|
if (obj instanceof Cube && !Format.bone_rig) {
|
|
if (obj.origin.allEqual(0)) {
|
|
obj.origin.V3_set(origin)
|
|
}
|
|
}
|
|
|
|
if (!Group.selected && obj instanceof Mesh && Project.selected_vertices[obj.uuid] && Project.selected_vertices[obj.uuid].length > 0) {
|
|
|
|
let normal = axis == 0 ? THREE.NormalX : (axis == 1 ? THREE.NormalY : THREE.NormalZ)
|
|
let rotWorldMatrix = new THREE.Matrix4();
|
|
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(modify(0)))
|
|
if (space instanceof Group || space == 'root') {
|
|
rotWorldMatrix.multiply(mesh.matrix);
|
|
} else if (space == 0) {
|
|
rotWorldMatrix.multiply(mesh.matrixWorld);
|
|
}
|
|
let q = new THREE.Quaternion().setFromRotationMatrix(rotWorldMatrix);
|
|
if (space instanceof Group || space == 'root') {
|
|
q.premultiply(mesh.quaternion.invert());
|
|
mesh.quaternion.invert();
|
|
} else if (space == 0) {
|
|
let quat = mesh.getWorldQuaternion(new THREE.Quaternion()).invert();
|
|
q.premultiply(quat);
|
|
}
|
|
|
|
let vector = new THREE.Vector3();
|
|
let local_pivot = obj.mesh.worldToLocal(new THREE.Vector3().copy(Transformer.position))
|
|
|
|
Project.selected_vertices[obj.uuid].forEach(key => {
|
|
vector.fromArray(obj.vertices[key]);
|
|
vector.sub(local_pivot);
|
|
vector.applyQuaternion(q);
|
|
vector.add(local_pivot);
|
|
obj.vertices[key].V3_set(vector.x, vector.y, vector.z);
|
|
})
|
|
|
|
} else if (slider || (space == 2 && Format.rotation_limit)) {
|
|
var obj_val = modify(obj.rotation[axis]);
|
|
obj_val = Math.trimDeg(obj_val)
|
|
if (Format.rotation_limit) {
|
|
//Limit To 1 Axis
|
|
obj.rotation[(axis+1)%3] = 0
|
|
obj.rotation[(axis+2)%3] = 0
|
|
//Limit Angle
|
|
obj_val = Math.round(obj_val/22.5)*22.5
|
|
if (obj_val > 45 || obj_val < -45) {
|
|
|
|
let f = obj_val > 45
|
|
let can_roll = obj.roll(axis, f!=(axis==1) ? 1 : 3);
|
|
if (can_roll) {
|
|
obj_val = f ? -22.5 : 22.5;
|
|
} else {
|
|
obj_val = Math.clamp(obj_val, -45, 45);
|
|
}
|
|
}
|
|
}
|
|
obj.rotation[axis] = obj_val
|
|
if (obj instanceof Cube) {
|
|
obj.rotation_axis = axis_letter
|
|
}
|
|
} else if (space == 2) {
|
|
|
|
let old_order = mesh.rotation.order;
|
|
mesh.rotation.reorder(axis == 0 ? 'ZYX' : (axis == 1 ? 'ZXY' : 'XYZ'))
|
|
var obj_val = modify(Math.radToDeg(mesh.rotation[axis_letter]));
|
|
obj_val = Math.trimDeg(obj_val)
|
|
mesh.rotation[axis_letter] = Math.degToRad(obj_val);
|
|
mesh.rotation.reorder(old_order);
|
|
|
|
obj.rotation[0] = Math.radToDeg(mesh.rotation.x);
|
|
obj.rotation[1] = Math.radToDeg(mesh.rotation.y);
|
|
obj.rotation[2] = Math.radToDeg(mesh.rotation.z);
|
|
|
|
} else if (space instanceof Group) {
|
|
let normal = axis == 0 ? THREE.NormalX : (axis == 1 ? THREE.NormalY : THREE.NormalZ)
|
|
let rotWorldMatrix = new THREE.Matrix4();
|
|
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(modify(0)))
|
|
rotWorldMatrix.multiply(mesh.matrix)
|
|
mesh.matrix.copy(rotWorldMatrix)
|
|
mesh.setRotationFromMatrix(rotWorldMatrix)
|
|
let e = mesh.rotation;
|
|
obj.rotation[0] = Math.radToDeg(e.x);
|
|
obj.rotation[1] = Math.radToDeg(e.y);
|
|
obj.rotation[2] = Math.radToDeg(e.z);
|
|
|
|
} else if (space == 0) {
|
|
let normal = axis instanceof THREE.Vector3
|
|
? axis
|
|
: axis == 0 ? THREE.NormalX : (axis == 1 ? THREE.NormalY : THREE.NormalZ)
|
|
let rotWorldMatrix = new THREE.Matrix4();
|
|
rotWorldMatrix.makeRotationAxis(normal, Math.degToRad(modify(0)))
|
|
rotWorldMatrix.multiply(mesh.matrixWorld)
|
|
|
|
let inverse = new THREE.Matrix4().copy(mesh.parent.matrixWorld).invert()
|
|
rotWorldMatrix.premultiply(inverse)
|
|
|
|
mesh.matrix.copy(rotWorldMatrix)
|
|
mesh.setRotationFromMatrix(rotWorldMatrix)
|
|
let e = mesh.rotation;
|
|
obj.rotation[0] = Math.radToDeg(e.x);
|
|
obj.rotation[1] = Math.radToDeg(e.y);
|
|
obj.rotation[2] = Math.radToDeg(e.z);
|
|
|
|
}
|
|
if (obj instanceof Group) {
|
|
Canvas.updateView({groups: [obj]});
|
|
}
|
|
})
|
|
}
|
|
|
|
BARS.defineActions(function() {
|
|
|
|
|
|
new BarSelect('transform_space', {
|
|
condition: {modes: ['edit', 'animate'], tools: ['move_tool', 'pivot_tool']},
|
|
category: 'transform',
|
|
value: 'local',
|
|
options: {
|
|
global: true,
|
|
bone: {condition: () => Format.bone_rig, name: true},
|
|
local: true,
|
|
normal: {condition: () => Mesh.selected.length, name: true}
|
|
},
|
|
onChange() {
|
|
updateSelection();
|
|
}
|
|
})
|
|
new BarSelect('rotation_space', {
|
|
condition: {modes: ['edit', 'animate'], tools: ['rotate_tool']},
|
|
category: 'transform',
|
|
value: 'local',
|
|
options: {
|
|
global: 'action.transform_space.global',
|
|
bone: {condition: () => Format.bone_rig, name: true, name: 'action.transform_space.bone'},
|
|
local: 'action.transform_space.local'
|
|
},
|
|
onChange() {
|
|
updateSelection();
|
|
}
|
|
})
|
|
let grid_locked_interval = function(event) {
|
|
event = event||0;
|
|
return canvasGridSize(event.shiftKey || Pressing.overrides.shift, event.ctrlOrCmd || Pressing.overrides.ctrl);
|
|
}
|
|
|
|
function moveOnAxis(modify, axis) {
|
|
selected.forEach(function(obj, i) {
|
|
if (obj instanceof Mesh && obj.getSelectedVertices().length) {
|
|
|
|
let vertices = obj.getSelectedVertices();
|
|
vertices.forEach(vkey => {
|
|
obj.vertices[vkey][axis] = modify(obj.vertices[vkey][axis]);
|
|
})
|
|
obj.preview_controller.updateGeometry(obj);
|
|
|
|
} else if (obj.movable) {
|
|
var val = modify(obj.from[axis]);
|
|
|
|
if (Format.canvas_limit && !settings.deactivate_size_limit.value) {
|
|
var size = obj.to ? obj.size(axis) : 0;
|
|
val = limitToBox(limitToBox(val, -obj.inflate) + size, obj.inflate) - size
|
|
}
|
|
|
|
var before = obj.from[axis];
|
|
obj.from[axis] = val;
|
|
if (obj.to) {
|
|
obj.to[axis] += (val - before);
|
|
}
|
|
if (obj instanceof Cube) {
|
|
obj.mapAutoUV()
|
|
}
|
|
obj.preview_controller.updateTransform(obj);
|
|
obj.preview_controller.updateGeometry(obj);
|
|
}
|
|
})
|
|
TickUpdates.selection = true;
|
|
}
|
|
function getPos(axis) {
|
|
let element = Outliner.selected[0];
|
|
if (element instanceof Mesh && element.getSelectedVertices().length) {
|
|
let vertices = element.getSelectedVertices();
|
|
let sum = 0;
|
|
vertices.forEach(vkey => sum += element.vertices[vkey][axis]);
|
|
return sum / vertices.length;
|
|
|
|
} else if (element instanceof Cube) {
|
|
return element.from[axis];
|
|
} else {
|
|
return element.origin[axis]
|
|
}
|
|
}
|
|
new NumSlider('slider_pos_x', {
|
|
name: tl('action.slider_pos', ['X']),
|
|
description: tl('action.slider_pos.desc', ['X']),
|
|
color: 'x',
|
|
category: 'transform',
|
|
condition: () => (selected.length && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
return getPos(0);
|
|
},
|
|
change: function(modify) {
|
|
moveOnAxis(modify, 0)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element position')
|
|
}
|
|
})
|
|
new NumSlider('slider_pos_y', {
|
|
name: tl('action.slider_pos', ['Y']),
|
|
description: tl('action.slider_pos.desc', ['Y']),
|
|
color: 'y',
|
|
category: 'transform',
|
|
condition: () => (selected.length && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
return getPos(1);
|
|
},
|
|
change: function(modify) {
|
|
moveOnAxis(modify, 1)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element position')
|
|
}
|
|
})
|
|
new NumSlider('slider_pos_z', {
|
|
name: tl('action.slider_pos', ['Z']),
|
|
description: tl('action.slider_pos.desc', ['Z']),
|
|
color: 'z',
|
|
category: 'transform',
|
|
condition: () => (selected.length && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
return getPos(2);
|
|
},
|
|
change: function(modify) {
|
|
moveOnAxis(modify, 2)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element position')
|
|
}
|
|
})
|
|
|
|
|
|
function resizeOnAxis(modify, axis) {
|
|
selected.forEach(function(obj, i) {
|
|
if (obj.resizable) {
|
|
obj.resize(modify, axis, false, true)
|
|
} else if (obj.scalable) {
|
|
obj.scale[axis] = modify(obj.scale[axis]);
|
|
obj.preview_controller.updateTransform(obj);
|
|
obj.preview_controller.updateGeometry(obj);
|
|
}
|
|
})
|
|
}
|
|
new NumSlider('slider_size_x', {
|
|
name: tl('action.slider_size', ['X']),
|
|
description: tl('action.slider_size.desc', ['X']),
|
|
color: 'x',
|
|
category: 'transform',
|
|
condition: () => (Outliner.selected[0] && (Outliner.selected[0].resizable || Outliner.selected[0].scalable) && Outliner.selected[0] instanceof Mesh == false && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Outliner.selected[0].scalable) {
|
|
return Outliner.selected[0].scale[0]
|
|
} else if (Outliner.selected[0].resizable) {
|
|
return Outliner.selected[0].to[0] - Outliner.selected[0].from[0]
|
|
}
|
|
},
|
|
change: function(modify) {
|
|
resizeOnAxis(modify, 0)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element size')
|
|
}
|
|
})
|
|
new NumSlider('slider_size_y', {
|
|
name: tl('action.slider_size', ['Y']),
|
|
description: tl('action.slider_size.desc', ['Y']),
|
|
color: 'y',
|
|
category: 'transform',
|
|
condition: () => (Outliner.selected[0] && (Outliner.selected[0].resizable || Outliner.selected[0].scalable) && Outliner.selected[0] instanceof Mesh == false && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Outliner.selected[0].scalable) {
|
|
return Outliner.selected[0].scale[1]
|
|
} else if (Outliner.selected[0].resizable) {
|
|
return Outliner.selected[0].to[1] - Outliner.selected[0].from[1]
|
|
}
|
|
},
|
|
change: function(modify) {
|
|
resizeOnAxis(modify, 1)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element size')
|
|
}
|
|
})
|
|
new NumSlider('slider_size_z', {
|
|
name: tl('action.slider_size', ['Z']),
|
|
description: tl('action.slider_size.desc', ['Z']),
|
|
color: 'z',
|
|
category: 'transform',
|
|
condition: () => (Outliner.selected[0] && (Outliner.selected[0].resizable || Outliner.selected[0].scalable)&& Outliner.selected[0] instanceof Mesh == false && Modes.edit),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Outliner.selected[0].scalable) {
|
|
return Outliner.selected[0].scale[2]
|
|
} else if (Outliner.selected[0].resizable) {
|
|
return Outliner.selected[0].to[2] - Outliner.selected[0].from[2]
|
|
}
|
|
},
|
|
change: function(modify) {
|
|
resizeOnAxis(modify, 2)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change element size')
|
|
}
|
|
})
|
|
//Inflate
|
|
new NumSlider('slider_inflate', {
|
|
category: 'transform',
|
|
condition: function() {return Cube.selected.length && Modes.edit},
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
return Cube.selected[0].inflate
|
|
},
|
|
change: function(modify) {
|
|
Cube.selected.forEach(function(obj, i) {
|
|
var v = modify(obj.inflate)
|
|
if (Format.canvas_limit && !settings.deactivate_size_limit.value) {
|
|
v = obj.from[0] - Math.clamp(obj.from[0]-v, -16, 32);
|
|
v = obj.from[1] - Math.clamp(obj.from[1]-v, -16, 32);
|
|
v = obj.from[2] - Math.clamp(obj.from[2]-v, -16, 32);
|
|
v = Math.clamp(obj.to[0]+v, -16, 32) - obj.to[0];
|
|
v = Math.clamp(obj.to[1]+v, -16, 32) - obj.to[1];
|
|
v = Math.clamp(obj.to[2]+v, -16, 32) - obj.to[2];
|
|
}
|
|
obj.inflate = v
|
|
})
|
|
Canvas.updatePositions()
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Inflate elements')
|
|
}
|
|
})
|
|
|
|
//Rotation
|
|
new NumSlider('slider_rotation_x', {
|
|
name: tl('action.slider_rotation', ['X']),
|
|
description: tl('action.slider_rotation.desc', ['X']),
|
|
color: 'x',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit && getRotationObject()),
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.rotation[0];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.rotation[0];
|
|
},
|
|
change: function(modify) {
|
|
rotateOnAxis(modify, 0, true)
|
|
Canvas.updatePositions()
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: Cube.selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit(getRotationObject() instanceof Group ? 'Rotate group' : 'Rotate elements');
|
|
},
|
|
getInterval: getRotationInterval
|
|
})
|
|
new NumSlider('slider_rotation_y', {
|
|
name: tl('action.slider_rotation', ['Y']),
|
|
description: tl('action.slider_rotation.desc', ['Y']),
|
|
color: 'y',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit && getRotationObject()),
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.rotation[1];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.rotation[1];
|
|
},
|
|
change: function(modify) {
|
|
rotateOnAxis(modify, 1, true)
|
|
Canvas.updatePositions()
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit(getRotationObject() instanceof Group ? 'Rotate group' : 'Rotate elements');
|
|
},
|
|
getInterval: getRotationInterval
|
|
})
|
|
new NumSlider('slider_rotation_z', {
|
|
name: tl('action.slider_rotation', ['Z']),
|
|
description: tl('action.slider_rotation.desc', ['Z']),
|
|
color: 'z',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit && getRotationObject()),
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.rotation[2];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.rotation[2];
|
|
},
|
|
change: function(modify) {
|
|
rotateOnAxis(modify, 2, true)
|
|
Canvas.updatePositions()
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit(getRotationObject() instanceof Group ? 'Rotate group' : 'Rotate elements');
|
|
},
|
|
getInterval: getRotationInterval
|
|
})
|
|
function rotateCondition() {
|
|
return (Modes.edit && (
|
|
(Format.bone_rig && Group.selected) ||
|
|
(Format.rotate_cubes && Cube.selected.length)
|
|
))
|
|
}
|
|
|
|
//Origin
|
|
function moveOriginOnAxis(modify, axis) {
|
|
var rotation_object = getRotationObject()
|
|
|
|
if (rotation_object instanceof Group) {
|
|
var val = modify(rotation_object.origin[axis]);
|
|
rotation_object.origin[axis] = val;
|
|
Canvas.updateView({groups: [rotation_object], group_aspects: {transform: true}, selection: true})
|
|
if (Format.bone_rig) {
|
|
Canvas.updateAllBones()
|
|
}
|
|
} else {
|
|
rotation_object.forEach(function(obj, i) {
|
|
var val = modify(obj.origin[axis]);
|
|
obj.origin[axis] = val;
|
|
})
|
|
Canvas.updateView({elements: rotation_object, element_aspects: {transform: true, geometry: true}, selection: true})
|
|
}
|
|
if (Modes.animate) {
|
|
Animator.preview();
|
|
}
|
|
}
|
|
new NumSlider('slider_origin_x', {
|
|
name: tl('action.slider_origin', ['X']),
|
|
description: tl('action.slider_origin.desc', ['X']),
|
|
color: 'x',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit || Modes.animate) && getRotationObject() && (Group.selected || Outliner.selected.length > Locator.selected.length),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.origin[0];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && el.origin && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.origin[0];
|
|
},
|
|
change: function(modify) {
|
|
moveOriginOnAxis(modify, 0)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change pivot point')
|
|
}
|
|
})
|
|
new NumSlider('slider_origin_y', {
|
|
name: tl('action.slider_origin', ['Y']),
|
|
description: tl('action.slider_origin.desc', ['Y']),
|
|
color: 'y',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit || Modes.animate) && getRotationObject() && (Group.selected || Outliner.selected.length > Locator.selected.length),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.origin[1];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && el.origin && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.origin[1];
|
|
},
|
|
change: function(modify) {
|
|
moveOriginOnAxis(modify, 1)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change pivot point')
|
|
}
|
|
})
|
|
new NumSlider('slider_origin_z', {
|
|
name: tl('action.slider_origin', ['Z']),
|
|
description: tl('action.slider_origin.desc', ['Z']),
|
|
color: 'z',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit || Modes.animate) && getRotationObject() && (Group.selected || Outliner.selected.length > Locator.selected.length),
|
|
getInterval: grid_locked_interval,
|
|
get: function() {
|
|
if (Format.bone_rig && Group.selected) {
|
|
return Group.selected.origin[2];
|
|
}
|
|
let ref = Outliner.selected.find(el => {
|
|
return el.rotatable && el.origin && (Format.rotate_cubes || el instanceof Cube == false)
|
|
})
|
|
if (ref) return ref.origin[2];
|
|
},
|
|
change: function(modify) {
|
|
moveOriginOnAxis(modify, 2)
|
|
},
|
|
onBefore: function() {
|
|
Undo.initEdit({elements: selected, group: Group.selected})
|
|
},
|
|
onAfter: function() {
|
|
Undo.finishEdit('Change pivot point')
|
|
}
|
|
})
|
|
|
|
new Action('scale', {
|
|
icon: 'settings_overscan',
|
|
category: 'transform',
|
|
condition: () => (Modes.edit && selected.length),
|
|
click: function () {
|
|
$('#model_scale_range, #model_scale_label').val(1)
|
|
$('#scaling_clipping_warning').text('')
|
|
|
|
Undo.initEdit({elements: Outliner.selected, outliner: Format.bone_rig})
|
|
|
|
Outliner.selected.forEach(function(obj) {
|
|
obj.before = {
|
|
from: obj.from ? obj.from.slice() : undefined,
|
|
to: obj.to ? obj.to.slice() : undefined,
|
|
origin: obj.origin ? obj.origin.slice() : undefined
|
|
}
|
|
if (obj instanceof Mesh) {
|
|
obj.before.vertices = {};
|
|
for (let key in obj.vertices) {
|
|
obj.before.vertices[key] = obj.vertices[key].slice();
|
|
}
|
|
}
|
|
})
|
|
if (Format.bone_rig && Group.selected) {
|
|
Group.selected.forEachChild((g) => {
|
|
g.old_origin = g.origin.slice();
|
|
}, Group, true)
|
|
}
|
|
showDialog('scaling')
|
|
var v = Format.centered_grid ? 0 : 8;
|
|
var origin = Group.selected ? Group.selected.origin : [v, 0, v];
|
|
$('#scaling_origin_x').val(origin[0])
|
|
$('#scaling_origin_y').val(origin[1])
|
|
$('#scaling_origin_z').val(origin[2])
|
|
scaleAll(false, 1)
|
|
}
|
|
})
|
|
new Action('rotate_x_cw', {
|
|
name: tl('action.rotate_cw', 'X'),
|
|
icon: 'rotate_right',
|
|
color: 'x',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(0, 1);
|
|
}
|
|
})
|
|
new Action('rotate_x_ccw', {
|
|
name: tl('action.rotate_ccw', 'X'),
|
|
icon: 'rotate_left',
|
|
color: 'x',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(0, 3);
|
|
}
|
|
})
|
|
new Action('rotate_y_cw', {
|
|
name: tl('action.rotate_cw', 'Y'),
|
|
icon: 'rotate_right',
|
|
color: 'y',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(1, 1);
|
|
}
|
|
})
|
|
new Action('rotate_y_ccw', {
|
|
name: tl('action.rotate_ccw', 'Y'),
|
|
icon: 'rotate_left',
|
|
color: 'y',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(1, 3);
|
|
}
|
|
})
|
|
new Action('rotate_z_cw', {
|
|
name: tl('action.rotate_cw', 'Z'),
|
|
icon: 'rotate_right',
|
|
color: 'z',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(2, 1);
|
|
}
|
|
})
|
|
new Action('rotate_z_ccw', {
|
|
name: tl('action.rotate_ccw', 'Z'),
|
|
icon: 'rotate_left',
|
|
color: 'z',
|
|
category: 'transform',
|
|
click: function () {
|
|
rotateSelected(2, 3);
|
|
}
|
|
})
|
|
|
|
new Action('flip_x', {
|
|
name: tl('action.flip', 'X'),
|
|
icon: 'icon-mirror_x',
|
|
color: 'x',
|
|
category: 'transform',
|
|
click: function () {
|
|
mirrorSelected(0);
|
|
}
|
|
})
|
|
new Action('flip_y', {
|
|
name: tl('action.flip', 'Y'),
|
|
icon: 'icon-mirror_y',
|
|
color: 'y',
|
|
category: 'transform',
|
|
click: function () {
|
|
mirrorSelected(1);
|
|
}
|
|
})
|
|
new Action('flip_z', {
|
|
name: tl('action.flip', 'Z'),
|
|
icon: 'icon-mirror_z',
|
|
color: 'z',
|
|
category: 'transform',
|
|
click: function () {
|
|
mirrorSelected(2);
|
|
}
|
|
})
|
|
|
|
new Action('center_x', {
|
|
name: tl('action.center', 'X'),
|
|
icon: 'vertical_align_center',
|
|
color: 'x',
|
|
category: 'transform',
|
|
click: function () {
|
|
Undo.initEdit({elements: selected});
|
|
centerElements(0);
|
|
Undo.finishEdit('Center selection on X axis')
|
|
}
|
|
})
|
|
new Action('center_y', {
|
|
name: tl('action.center', 'Y'),
|
|
icon: 'vertical_align_center',
|
|
color: 'y',
|
|
category: 'transform',
|
|
click: function () {
|
|
Undo.initEdit({elements: selected});
|
|
centerElements(1);
|
|
Undo.finishEdit('Center selection on Y axis')
|
|
}
|
|
})
|
|
new Action('center_z', {
|
|
name: tl('action.center', 'Z'),
|
|
icon: 'vertical_align_center',
|
|
color: 'z',
|
|
category: 'transform',
|
|
click: function () {
|
|
Undo.initEdit({elements: selected});
|
|
centerElements(2);
|
|
Undo.finishEdit('Center selection on Z axis')
|
|
}
|
|
})
|
|
new Action('center_all', {
|
|
icon: 'filter_center_focus',
|
|
category: 'transform',
|
|
click: function () {
|
|
Undo.initEdit({elements: selected});
|
|
centerElementsAll();
|
|
Undo.finishEdit('Center selection')
|
|
}
|
|
})
|
|
|
|
//Move Cube Keys
|
|
new Action('move_up', {
|
|
icon: 'arrow_upward',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 38, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(-1, 2, e)}
|
|
})
|
|
new Action('move_down', {
|
|
icon: 'arrow_downward',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 40, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(1, 2, e)}
|
|
})
|
|
new Action('move_left', {
|
|
icon: 'arrow_back',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 37, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(-1, 0, e)}
|
|
})
|
|
new Action('move_right', {
|
|
icon: 'arrow_forward',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 39, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(1, 0, e)}
|
|
})
|
|
new Action('move_forth', {
|
|
icon: 'keyboard_arrow_up',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 33, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(-1, 1, e)}
|
|
})
|
|
new Action('move_back', {
|
|
icon: 'keyboard_arrow_down',
|
|
category: 'transform',
|
|
condition: {modes: ['edit'], method: () => (!open_menu && selected.length)},
|
|
keybind: new Keybind({key: 34, ctrl: null, shift: null}),
|
|
click: function (e) {moveCubesRelative(1, 1, e)}
|
|
})
|
|
|
|
new Action('toggle_visibility', {
|
|
icon: 'visibility',
|
|
category: 'transform',
|
|
click: function () {toggleCubeProperty('visibility')}
|
|
})
|
|
new Action('toggle_locked', {
|
|
icon: 'fas.fa-lock',
|
|
category: 'transform',
|
|
click: function () {toggleCubeProperty('locked')}
|
|
})
|
|
new Action('toggle_export', {
|
|
icon: 'save',
|
|
category: 'transform',
|
|
click: function () {toggleCubeProperty('export')}
|
|
})
|
|
new Action('toggle_autouv', {
|
|
icon: 'fullscreen_exit',
|
|
category: 'transform',
|
|
condition: {modes: ['edit']},
|
|
click: function () {toggleCubeProperty('autouv')}
|
|
})
|
|
new Action('toggle_shade', {
|
|
icon: 'wb_sunny',
|
|
category: 'transform',
|
|
condition: () => !Project.box_uv && Modes.edit,
|
|
click: function () {toggleCubeProperty('shade')}
|
|
})
|
|
new Action('toggle_mirror_uv', {
|
|
icon: 'icon-mirror_x',
|
|
category: 'transform',
|
|
condition: () => Project.box_uv && (Modes.edit || Modes.paint),
|
|
click: function () {toggleCubeProperty('shade')}
|
|
})
|
|
new Action('update_autouv', {
|
|
icon: 'brightness_auto',
|
|
category: 'transform',
|
|
condition: () => !Project.box_uv && Modes.edit,
|
|
click: function () {
|
|
if (Cube.selected.length) {
|
|
Undo.initEdit({elements: Cube.selected[0].forSelected(), selection: true})
|
|
Cube.selected[0].forSelected(function(cube) {
|
|
cube.mapAutoUV()
|
|
})
|
|
Undo.finishEdit('Update auto UV')
|
|
}
|
|
}
|
|
})
|
|
new Action('origin_to_geometry', {
|
|
icon: 'filter_center_focus',
|
|
category: 'transform',
|
|
condition: {modes: ['edit', 'animate']},
|
|
click: function () {origin2geometry()}
|
|
})
|
|
new Action('rescale_toggle', {
|
|
icon: 'check_box_outline_blank',
|
|
category: 'transform',
|
|
condition: function() {return Format.rotation_limit && Cube.selected.length;},
|
|
click: function () {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
var value = !Cube.selected[0].rescale
|
|
Cube.selected.forEach(function(cube) {
|
|
cube.rescale = value
|
|
})
|
|
Canvas.updatePositions()
|
|
updateNslideValues()
|
|
Undo.finishEdit('Toggle cube rescale')
|
|
}
|
|
})
|
|
new Action('bone_reset_toggle', {
|
|
icon: 'check_box_outline_blank',
|
|
category: 'transform',
|
|
condition: function() {return Format.bone_rig && Group.selected;},
|
|
click: function () {
|
|
Undo.initEdit({group: Group.selected})
|
|
Group.selected.reset = !Group.selected.reset
|
|
updateNslideValues()
|
|
Undo.finishEdit('Toggle bone reset')
|
|
}
|
|
})
|
|
|
|
new Action('remove_blank_faces', {
|
|
icon: 'cancel_presentation',
|
|
category: 'filter',
|
|
condition: () => !Format.box_uv,
|
|
click: function () {
|
|
Undo.initEdit({elements: Cube.selected})
|
|
var arr = Cube.selected.slice()
|
|
var empty_cubes = [];
|
|
var cleared_total = 0;
|
|
unselectAll()
|
|
arr.forEach(cube => {
|
|
var clear_count = 0;
|
|
for (var face in cube.faces) {
|
|
var face_tag = cube.faces[face];
|
|
if (face_tag.texture == false) {
|
|
face_tag.texture = null
|
|
clear_count++;
|
|
cleared_total++;
|
|
}
|
|
}
|
|
if (clear_count == 6) {
|
|
empty_cubes.push(cube);
|
|
}
|
|
})
|
|
updateSelection();
|
|
Blockbench.showQuickMessage(tl('message.removed_faces', [cleared_total]))
|
|
if (empty_cubes.length) {
|
|
Blockbench.showMessageBox({
|
|
title: tl('message.cleared_blank_faces.title'),
|
|
icon: 'rotate_right',
|
|
message: tl('message.cleared_blank_faces.message', [empty_cubes.length]),
|
|
buttons: ['generic.remove', 'dialog.cancel'],
|
|
confirm: 0,
|
|
cancel: 1,
|
|
}, function(r) {
|
|
empty_cubes.forEach(cube => {
|
|
if (r == 0) {
|
|
cube.remove();
|
|
} else {
|
|
for (var face in cube.faces) {
|
|
cube.faces[face].texture = false;
|
|
}
|
|
}
|
|
})
|
|
updateSelection();
|
|
Canvas.updateAllFaces();
|
|
Undo.finishEdit('Remove blank faces');
|
|
})
|
|
} else {
|
|
Canvas.updateAllFaces();
|
|
Undo.finishEdit('Remove blank faces');
|
|
}
|
|
}
|
|
})
|
|
})
|