blockbench/js/transform.js
JannisX11 2c98c43754 Add mesh inset action
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
2021-09-16 16:52:21 +02:00

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');
}
}
})
})