2019-12-16 03:04:31 +08:00
|
|
|
const Painter = {
|
|
|
|
currentPixel: [-1, -1],
|
|
|
|
brushChanges: false,
|
|
|
|
current: {/*texture, image*/},
|
2020-03-05 03:56:17 +08:00
|
|
|
selection: {},
|
|
|
|
mirror_painting: false,
|
|
|
|
lock_alpha: false,
|
2021-08-07 19:51:00 +08:00
|
|
|
erase_mode: false,
|
2019-12-16 03:04:31 +08:00
|
|
|
edit(texture, cb, options) {
|
|
|
|
if (!options.no_undo) {
|
|
|
|
Undo.initEdit({textures: [texture], bitmap: true})
|
|
|
|
}
|
2020-01-24 01:53:36 +08:00
|
|
|
if (texture.mode === 'link') {
|
|
|
|
texture.source = 'data:image/png;base64,' + texture.getBase64()
|
|
|
|
texture.mode = 'bitmap'
|
|
|
|
texture.saved = false
|
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
var instance = Painter.current[options.method === 'jimp' ? 'image' : 'canvas']
|
|
|
|
Painter.current[options.method === 'jimp' ? 'canvas' : 'image'] = undefined
|
|
|
|
|
2022-02-05 22:39:11 +08:00
|
|
|
var edit_name = options.no_undo ? null : (options.edit_name || 'Edit texture');
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
if (options.use_cache &&
|
|
|
|
texture === Painter.current.texture &&
|
|
|
|
typeof instance === 'object'
|
|
|
|
) {
|
|
|
|
//IS CACHED
|
2020-03-05 03:56:17 +08:00
|
|
|
if (options.method === 'jimp') {
|
2019-12-16 03:04:31 +08:00
|
|
|
instance = cb(instance) || instance
|
|
|
|
} else {
|
2020-03-05 03:56:17 +08:00
|
|
|
instance = cb(instance) || instance
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
if (options.no_update === true) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-05 03:56:17 +08:00
|
|
|
if (options.method === 'jimp') {
|
2019-12-16 03:04:31 +08:00
|
|
|
Painter.current.image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
|
|
|
|
texture.updateSource(dataUrl)
|
|
|
|
if (!options.no_undo) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Undo.finishEdit(edit_name)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
2022-05-15 19:12:29 +08:00
|
|
|
if (options.no_undo) {
|
|
|
|
let map = texture.getMaterial().map
|
|
|
|
map.image = Painter.current.canvas;
|
|
|
|
map.needsUpdate = true;
|
|
|
|
texture.display_canvas = true;
|
|
|
|
UVEditor.vue.updateTextureCanvas();
|
|
|
|
} else {
|
|
|
|
texture.updateSource(instance.toDataURL())
|
2020-03-05 03:56:17 +08:00
|
|
|
Undo.finishEdit(edit_name)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2020-03-05 03:56:17 +08:00
|
|
|
if (options.method === 'jimp') {
|
2019-12-16 03:04:31 +08:00
|
|
|
Painter.current.texture = texture
|
|
|
|
Jimp.read(Buffer.from(texture.source.replace('data:image/png;base64,', ''), 'base64')).then(function(image) {
|
|
|
|
image = cb(image) || image
|
|
|
|
Painter.current.image = image
|
|
|
|
image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
|
|
|
|
texture.updateSource(dataUrl)
|
|
|
|
if (!options.no_undo) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Undo.finishEdit(edit_name)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Painter.current.texture = texture
|
|
|
|
var c = Painter.current.canvas = Painter.getCanvas(texture)
|
2021-12-14 07:14:23 +08:00
|
|
|
Painter.current.ctx = c.getContext('2d');
|
2020-03-05 03:56:17 +08:00
|
|
|
c = cb(c) || c;
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
texture.updateSource(c.toDataURL())
|
|
|
|
if (!options.no_undo) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Undo.finishEdit(edit_name)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
|
|
|
|
//alpha
|
|
|
|
setAlphaMatrix(texture, x, y, val) {
|
2019-12-16 03:04:31 +08:00
|
|
|
if (!Painter.current.alpha_matrix) Painter.current.alpha_matrix = {}
|
|
|
|
var mx = Painter.current.alpha_matrix;
|
2020-03-05 03:56:17 +08:00
|
|
|
if (!mx[texture.uuid]) mx[texture.uuid] = {};
|
|
|
|
if (!mx[texture.uuid][x]) mx[texture.uuid][x] = {};
|
|
|
|
if (mx[texture.uuid][x][y]) {
|
|
|
|
val = Math.max(val, mx[texture.uuid][x][y])
|
|
|
|
}
|
|
|
|
mx[texture.uuid][x][y] = val
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
getAlphaMatrix(texture, x, y) {
|
2019-12-16 03:04:31 +08:00
|
|
|
return Painter.current.alpha_matrix
|
2020-03-05 03:56:17 +08:00
|
|
|
&& Painter.current.alpha_matrix[texture.uuid]
|
|
|
|
&& Painter.current.alpha_matrix[texture.uuid][x]
|
|
|
|
&& Painter.current.alpha_matrix[texture.uuid][x][y];
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
// Preview Brush
|
|
|
|
startPaintToolCanvas(data, e) {
|
2021-03-09 03:00:54 +08:00
|
|
|
if (!data.intersects && Toolbox.selected.id == 'color_picker') {
|
2020-10-30 22:54:33 +08:00
|
|
|
var preview = Preview.selected;
|
2019-12-16 03:04:31 +08:00
|
|
|
if (preview && preview.background && preview.background.imgtag) {
|
|
|
|
|
2021-03-09 03:00:54 +08:00
|
|
|
let bg_pos = preview.canvas.style.backgroundPosition.split(' ').map(v => parseFloat(v));
|
|
|
|
let bg_size = parseFloat(preview.canvas.style.backgroundSize);
|
2019-12-16 03:04:31 +08:00
|
|
|
var ctx = Painter.getCanvas(preview.background.imgtag).getContext('2d')
|
2021-03-09 03:00:54 +08:00
|
|
|
var pixel_ratio = preview.background.imgtag.width / bg_size;
|
|
|
|
var x = (e.offsetX - bg_pos[0]) * pixel_ratio
|
|
|
|
var y = (e.offsetY - bg_pos[1]) * pixel_ratio
|
|
|
|
if (x >= 0 && y >= 0 && x < preview.background.imgtag.width && y < preview.background.imgtag.height) {
|
2021-05-29 20:25:09 +08:00
|
|
|
let color = Painter.getPixelColor(ctx, x, y);
|
2022-07-31 17:46:22 +08:00
|
|
|
if (settings.pick_color_opacity.value) {
|
|
|
|
let opacity = Math.floor(color.getAlpha()*256);
|
|
|
|
for (let id in BarItems) {
|
|
|
|
let tool = BarItems[id];
|
|
|
|
if (tool.tool_settings && tool.tool_settings.brush_opacity) {
|
|
|
|
tool.tool_settings.brush_opacity = opacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-29 20:25:09 +08:00
|
|
|
ColorPanel.set(color);
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-07-30 00:17:26 +08:00
|
|
|
if (!data.intersects || (data.element && data.element.locked)) return;
|
|
|
|
var texture = data.element.faces[data.face].getTexture()
|
2019-12-16 03:04:31 +08:00
|
|
|
if (!texture || (texture.error && texture.error !== 2)) {
|
|
|
|
Blockbench.showQuickMessage('message.untextured')
|
|
|
|
return;
|
|
|
|
}
|
2022-07-31 02:22:50 +08:00
|
|
|
let offset = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brush?.offset_even_radius ? 0.5 : 0;
|
|
|
|
var x = data.intersects[0].uv.x * texture.img.naturalWidth + offset;
|
|
|
|
var y = (1-data.intersects[0].uv.y) * texture.img.naturalHeight + offset;
|
|
|
|
if (!Toolbox.selected.brush || Toolbox.selected.brush.floor_coordinates !== false) {
|
|
|
|
x = Math.floor(x); y = Math.floor(y);
|
|
|
|
}
|
2021-07-30 00:17:26 +08:00
|
|
|
Painter.startPaintTool(texture, x, y, data.element.faces[data.face].uv, e, data)
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
if (Toolbox.selected.id !== 'color_picker') {
|
2020-03-05 03:56:17 +08:00
|
|
|
addEventListeners(document, 'mousemove touchmove', Painter.movePaintToolCanvas, false );
|
|
|
|
addEventListeners(document, 'mouseup touchend', Painter.stopPaintToolCanvas, false );
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
movePaintToolCanvas(event) {
|
2019-12-16 03:04:31 +08:00
|
|
|
convertTouchEvent(event);
|
|
|
|
var data = Canvas.raycast(event)
|
2021-07-30 00:17:26 +08:00
|
|
|
if (data && data.element && !data.element.locked) {
|
|
|
|
var texture = data.element.faces[data.face].getTexture()
|
2019-12-16 03:04:31 +08:00
|
|
|
if (texture) {
|
|
|
|
var x, y, new_face;
|
2022-07-31 02:22:50 +08:00
|
|
|
let offset = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brush?.offset_even_radius ? 0.5 : 0;
|
2020-03-05 03:56:17 +08:00
|
|
|
x = Math.floor( data.intersects[0].uv.x * texture.img.naturalWidth + offset );
|
|
|
|
y = Math.floor( (1-data.intersects[0].uv.y) * texture.img.naturalHeight + offset );
|
2019-12-16 03:04:31 +08:00
|
|
|
if (texture.img.naturalWidth + texture.img.naturalHeight == 0) return;
|
|
|
|
|
|
|
|
if (x === Painter.current.x && y === Painter.current.y) {
|
|
|
|
return
|
|
|
|
}
|
2021-08-28 18:33:59 +08:00
|
|
|
if (Painter.current.face !== data.face || Painter.current.element !== data.element) {
|
2021-05-30 20:38:09 +08:00
|
|
|
if (Toolbox.selected.id === 'draw_shape_tool' || Toolbox.selected.id === 'gradient_tool') {
|
2020-03-05 03:56:17 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
Painter.current.x = x
|
|
|
|
Painter.current.y = y
|
|
|
|
Painter.current.face = data.face
|
2021-08-28 18:33:59 +08:00
|
|
|
Painter.current.element = data.element
|
2019-12-16 03:04:31 +08:00
|
|
|
new_face = true
|
|
|
|
if (texture !== Painter.current.texture) {
|
|
|
|
Undo.current_save.addTexture(texture)
|
|
|
|
}
|
|
|
|
}
|
2021-07-30 00:17:26 +08:00
|
|
|
Painter.movePaintTool(texture, x, y, event, new_face, data.element.faces[data.face].uv)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
stopPaintToolCanvas() {
|
|
|
|
removeEventListeners(document, 'mousemove touchmove', Painter.movePaintToolCanvas, false );
|
|
|
|
removeEventListeners(document, 'mouseup touchend', Painter.stopPaintToolCanvas, false );
|
|
|
|
Painter.stopPaintTool();
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
// Paint Tool Main
|
|
|
|
startPaintTool(texture, x, y, uvTag, event, data) {
|
|
|
|
//Called directly by startPaintToolCanvas and startBrushUV
|
2022-07-31 02:22:50 +08:00
|
|
|
|
|
|
|
if (settings.paint_with_stylus_only.value && !(event.touches && event.touches[0] && event.touches[0].touchType == 'stylus')) return;
|
|
|
|
|
2022-08-06 05:52:53 +08:00
|
|
|
if (Toolbox.selected.brush && Toolbox.selected.brush.onStrokeStart) {
|
|
|
|
let result = Toolbox.selected.brush.onStrokeStart({texture, x, y, uv: uvTag, event, raycast_data: data});
|
|
|
|
if (result == false) return;
|
|
|
|
}
|
|
|
|
|
2019-12-16 03:04:31 +08:00
|
|
|
if (Toolbox.selected.id === 'color_picker') {
|
|
|
|
Painter.colorPicker(texture, x, y)
|
2022-07-31 02:22:50 +08:00
|
|
|
|
2021-05-30 20:38:09 +08:00
|
|
|
} else if (Toolbox.selected.id === 'draw_shape_tool' || Toolbox.selected.id === 'gradient_tool') {
|
2020-03-05 03:56:17 +08:00
|
|
|
|
2020-07-18 19:31:46 +08:00
|
|
|
Undo.initEdit({textures: [texture], selected_texture: true, bitmap: true});
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.brushChanges = false;
|
|
|
|
Painter.painting = true;
|
|
|
|
Painter.current = {
|
2021-08-28 18:33:59 +08:00
|
|
|
element: data && data.element,
|
2020-03-05 03:56:17 +08:00
|
|
|
face: data && data.face,
|
|
|
|
x, y,
|
|
|
|
clear: document.createElement('canvas'),
|
2022-04-02 21:04:43 +08:00
|
|
|
face_matrices: {}
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
Painter.startPixel = [x, y];
|
|
|
|
Painter.current.clear.width = texture.width;
|
|
|
|
Painter.current.clear.height = texture.height;
|
|
|
|
Painter.current.clear.getContext('2d').drawImage(texture.img, 0, 0);
|
|
|
|
|
2019-12-16 03:04:31 +08:00
|
|
|
} else {
|
2020-07-18 19:31:46 +08:00
|
|
|
Undo.initEdit({textures: [texture], selected_texture: true, bitmap: true});
|
2019-12-16 03:04:31 +08:00
|
|
|
Painter.brushChanges = false;
|
|
|
|
Painter.painting = true;
|
2022-04-02 21:04:43 +08:00
|
|
|
Painter.current.face_matrices = {};
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
if (data) {
|
2021-08-28 18:33:59 +08:00
|
|
|
var is_line = (event.shiftKey || Pressing.overrides.shift) && Painter.current.element == data.element && Painter.current.face == data.face
|
|
|
|
Painter.current.element = data.element;
|
2019-12-16 03:04:31 +08:00
|
|
|
Painter.current.face = data.face;
|
|
|
|
} else {
|
|
|
|
//uv editor
|
2021-08-03 17:04:40 +08:00
|
|
|
var is_line = (event.shiftKey || Pressing.overrides.shift);
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
2022-08-06 05:52:53 +08:00
|
|
|
if (Toolbox.selected.brush.line == false) is_line = false;
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2021-12-14 07:14:23 +08:00
|
|
|
texture.edit(canvas => {
|
|
|
|
if (is_line) {
|
|
|
|
Painter.drawBrushLine(texture, x, y, event, false, uvTag);
|
|
|
|
} else {
|
|
|
|
Painter.current.x = Painter.current.y = 0
|
|
|
|
Painter.useBrushlike(texture, x, y, event, uvTag)
|
|
|
|
}
|
|
|
|
Painter.current.x = x;
|
|
|
|
Painter.current.y = y;
|
|
|
|
}, {no_undo: true, use_cache: true});
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
movePaintTool(texture, x, y, event, new_face, uv) {
|
|
|
|
// Called directly from movePaintToolCanvas and moveBrushUV
|
2022-08-06 05:52:53 +08:00
|
|
|
|
|
|
|
if (Toolbox.selected.brush && Toolbox.selected.brush.onStrokeMove) {
|
|
|
|
let result = Toolbox.selected.brush.onStrokeMove({texture, x, y, uv, event, raycast_data: data});
|
|
|
|
if (result == false) return;
|
|
|
|
}
|
|
|
|
|
2020-03-05 03:56:17 +08:00
|
|
|
if (Toolbox.selected.id === 'draw_shape_tool') {
|
|
|
|
|
|
|
|
Painter.useShapeTool(texture, x, y, event, uv)
|
|
|
|
|
2021-05-30 20:38:09 +08:00
|
|
|
} else if (Toolbox.selected.id === 'gradient_tool') {
|
|
|
|
|
|
|
|
Painter.useGradientTool(texture, x, y, event, uv)
|
|
|
|
|
2020-03-05 03:56:17 +08:00
|
|
|
} else {
|
2021-12-14 07:14:23 +08:00
|
|
|
texture.edit(canvas => {
|
|
|
|
Painter.drawBrushLine(texture, x, y, event, new_face, uv)
|
|
|
|
}, {no_undo: true, use_cache: true});
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
Painter.current.x = x;
|
|
|
|
Painter.current.y = y;
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
stopPaintTool() {
|
|
|
|
//Called directly by stopPaintToolCanvas and stopBrushUV
|
2022-08-06 05:52:53 +08:00
|
|
|
if (Toolbox.selected.brush && Toolbox.selected.brush.onStrokeEnd) {
|
|
|
|
let result = Toolbox.selected.brush.onStrokeEnd({texture, x, y, uv, event, raycast_data: data});
|
|
|
|
if (result == false) return;
|
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
if (Painter.brushChanges) {
|
2021-06-06 15:28:22 +08:00
|
|
|
Undo.finishEdit('Paint texture');
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.brushChanges = false;
|
|
|
|
}
|
2021-05-30 20:38:09 +08:00
|
|
|
if (Toolbox.selected.id == 'gradient_tool') {
|
|
|
|
Blockbench.setStatusBarText();
|
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
delete Painter.current.alpha_matrix;
|
|
|
|
Painter.painting = false;
|
|
|
|
Painter.currentPixel = [-1, -1];
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
// Tools
|
2021-09-23 21:59:26 +08:00
|
|
|
setupRectFromFace(uvTag, texture) {
|
|
|
|
let rect;
|
|
|
|
let uvFactorX = texture.width / Project.texture_width;
|
|
|
|
let uvFactorY = texture.display_height / Project.texture_height;
|
|
|
|
if (uvTag) {
|
|
|
|
let anim_offset = texture.display_height * texture.currentFrame;
|
|
|
|
if (uvTag instanceof Array) {
|
|
|
|
rect = Painter.editing_area = [
|
|
|
|
uvTag[0] * uvFactorX,
|
|
|
|
uvTag[1] * uvFactorY + anim_offset,
|
|
|
|
uvTag[2] * uvFactorX,
|
|
|
|
uvTag[3] * uvFactorY + anim_offset
|
|
|
|
]
|
|
|
|
for (var t = 0; t < 2; t++) {
|
|
|
|
if (rect[t] > rect[t+2]) {
|
|
|
|
[rect[t], rect[t+2]] = [rect[t+2], rect[t]]
|
|
|
|
}
|
2021-11-10 03:40:01 +08:00
|
|
|
rect[t] = Math.floor(Math.roundTo(rect[t], 2))
|
|
|
|
rect[t+2] = Math.ceil(Math.roundTo(rect[t+2], 2))
|
2021-09-23 21:59:26 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let min_x = Project.texture_width, min_y = Project.texture_height, max_x = 0, max_y = 0;
|
2022-08-04 06:11:19 +08:00
|
|
|
|
2021-09-23 21:59:26 +08:00
|
|
|
for (let vkey in uvTag) {
|
|
|
|
min_x = Math.min(min_x, uvTag[vkey][0]); max_x = Math.max(max_x, uvTag[vkey][0]);
|
|
|
|
min_y = Math.min(min_y, uvTag[vkey][1]); max_y = Math.max(max_y, uvTag[vkey][1]);
|
|
|
|
}
|
2022-08-04 06:11:19 +08:00
|
|
|
|
|
|
|
let current_face = Mesh.selected[0] && Mesh.selected[0].faces[Painter.current.face];
|
|
|
|
if (current_face) {
|
|
|
|
let island = current_face.getUVIsland();
|
|
|
|
island.forEach(fkey => {
|
|
|
|
let face = Mesh.selected[0].faces[fkey];
|
|
|
|
for (let vkey in face.uv) {
|
|
|
|
min_x = Math.min(min_x, face.uv[vkey][0]); max_x = Math.max(max_x, face.uv[vkey][0]);
|
|
|
|
min_y = Math.min(min_y, face.uv[vkey][1]); max_y = Math.max(max_y, face.uv[vkey][1]);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-09-23 21:59:26 +08:00
|
|
|
rect = Painter.editing_area = [
|
|
|
|
Math.floor(min_x * uvFactorX),
|
|
|
|
Math.floor(min_y * uvFactorY) + anim_offset,
|
|
|
|
Math.ceil(max_x * uvFactorX),
|
|
|
|
Math.ceil(max_y * uvFactorY) + anim_offset
|
|
|
|
]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rect = Painter.editing_area = [0, 0, texture.img.naturalWidth, texture.img.naturalHeight]
|
|
|
|
}
|
|
|
|
return rect;
|
|
|
|
},
|
2020-04-26 02:25:07 +08:00
|
|
|
useBrushlike(texture, x, y, event, uvTag, no_update, is_opposite) {
|
2020-03-05 03:56:17 +08:00
|
|
|
if (Painter.currentPixel[0] === x && Painter.currentPixel[1] === y) return;
|
|
|
|
Painter.currentPixel = [x, y]
|
|
|
|
Painter.brushChanges = true;
|
2021-09-23 21:59:26 +08:00
|
|
|
let uvFactorX = texture.width / Project.texture_width;
|
|
|
|
let uvFactorY = texture.display_height / Project.texture_height;
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2020-03-05 03:56:17 +08:00
|
|
|
if (Painter.mirror_painting && !is_opposite) {
|
2022-02-01 23:48:18 +08:00
|
|
|
let target = Painter.getMirrorPaintTarget(texture, x, y, uvTag)
|
|
|
|
if (target) {
|
|
|
|
let old_element = Painter.current.element;
|
2022-03-25 03:09:12 +08:00
|
|
|
let old_face = Painter.current.face;
|
2022-02-01 23:48:18 +08:00
|
|
|
Painter.current.element = target.element;
|
2022-03-25 03:09:12 +08:00
|
|
|
Painter.current.face = target.face;
|
2022-02-01 23:48:18 +08:00
|
|
|
Painter.useBrushlike(texture, target.x, target.y, event, target.uv_tag, true, true);
|
|
|
|
Painter.current.element = old_element;
|
2022-03-25 03:09:12 +08:00
|
|
|
Painter.current.face = old_face;
|
|
|
|
}
|
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2021-12-14 07:14:23 +08:00
|
|
|
let ctx = Painter.current.ctx;
|
|
|
|
ctx.save()
|
2020-03-05 03:56:17 +08:00
|
|
|
|
2021-12-14 07:14:23 +08:00
|
|
|
ctx.beginPath();
|
|
|
|
let rect = Painter.setupRectFromFace(uvTag, texture);
|
|
|
|
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]]
|
|
|
|
ctx.rect(rect[0], rect[1], w, h)
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2021-12-14 07:14:23 +08:00
|
|
|
if (Toolbox.selected.id === 'fill_tool') {
|
|
|
|
Painter.useFilltool(texture, ctx, x, y, { rect, uvFactorX, uvFactorY, w, h })
|
|
|
|
} else {
|
|
|
|
Painter.useBrush(texture, ctx, x, y, event)
|
|
|
|
}
|
|
|
|
Painter.editing_area = undefined;
|
2020-04-26 02:25:07 +08:00
|
|
|
},
|
|
|
|
useBrush(texture, ctx, x, y, event) {
|
|
|
|
|
|
|
|
var color = tinycolor(ColorPanel.get()).toRgb();
|
|
|
|
var size = BarItems.slider_brush_size.get();
|
|
|
|
let softness = BarItems.slider_brush_softness.get()/100;
|
2021-10-14 22:39:02 +08:00
|
|
|
let b_opacity = BarItems.slider_brush_opacity.get()/255;
|
2022-07-31 02:22:50 +08:00
|
|
|
let tool = Toolbox.selected;
|
2020-04-26 02:25:07 +08:00
|
|
|
|
|
|
|
ctx.clip()
|
2022-03-25 03:09:12 +08:00
|
|
|
if (Painter.current.element instanceof Mesh) {
|
|
|
|
let face = Painter.current.element.faces[Painter.current.face];
|
2022-04-02 21:04:43 +08:00
|
|
|
if (face && face.vertices.length > 2 && !Painter.current.face_matrices[Painter.current.face]) {
|
|
|
|
Painter.current.face_matrices[Painter.current.face] = face.getOccupationMatrix(true, [0, 0]);
|
2022-08-04 06:11:19 +08:00
|
|
|
let island = face.getUVIsland();
|
|
|
|
for (let fkey of island) {
|
|
|
|
let face = Painter.current.element.faces[fkey];
|
|
|
|
face.getOccupationMatrix(true, [0, 0], Painter.current.face_matrices[Painter.current.face]);
|
|
|
|
}
|
2022-03-25 03:09:12 +08:00
|
|
|
}
|
|
|
|
}
|
2020-07-16 15:32:59 +08:00
|
|
|
if (event.touches && event.touches[0] && event.touches[0].touchType == 'stylus' && event.touches[0].force) {
|
2020-04-26 02:25:07 +08:00
|
|
|
|
|
|
|
// Stylus
|
|
|
|
var touch = event.touches[0];
|
|
|
|
if (touch.force == 1) touch.force == Painter.current.force || 0;
|
|
|
|
Painter.current.force = touch.force;
|
|
|
|
|
|
|
|
if (settings.brush_opacity_modifier.value == 'pressure' && touch.force) {
|
2021-04-12 01:59:06 +08:00
|
|
|
b_opacity = Math.clamp(b_opacity * Math.clamp(touch.force*1.25, 0, 1), 0, 100);
|
2020-04-26 02:25:07 +08:00
|
|
|
|
|
|
|
} else if (settings.brush_opacity_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
|
2021-04-12 01:59:06 +08:00
|
|
|
var modifier = Math.clamp(0.5 / (touch.altitudeAngle + 0.3), 0, 1);
|
2020-04-26 02:25:07 +08:00
|
|
|
b_opacity = Math.clamp(b_opacity * modifier, 0, 100);
|
|
|
|
}
|
|
|
|
if (settings.brush_size_modifier.value == 'pressure' && touch.force) {
|
|
|
|
size = Math.clamp(touch.force * size * 2, 1, 20);
|
|
|
|
|
|
|
|
} else if (settings.brush_size_modifier.value == 'tilt' && touch.altitudeAngle !== undefined) {
|
|
|
|
size *= Math.clamp(1.5 / (touch.altitudeAngle + 0.3), 1, 4);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-31 02:22:50 +08:00
|
|
|
if (tool.brush.draw) {
|
2020-04-26 02:25:07 +08:00
|
|
|
|
2022-07-31 02:22:50 +08:00
|
|
|
tool.brush.draw({ctx, x, y, size, softness, texture, event});
|
2020-04-26 02:25:07 +08:00
|
|
|
|
2022-07-31 02:22:50 +08:00
|
|
|
} else {
|
|
|
|
let shape = BarItems.brush_shape.value;
|
|
|
|
if (false) {
|
|
|
|
// Custom area
|
|
|
|
} else if (shape == 'square') {
|
|
|
|
Painter.editSquare(ctx, x, y, size, softness, function(pxcolor, local_opacity, px, py) {
|
2022-07-31 17:46:22 +08:00
|
|
|
if (Painter.current.face_matrices[Painter.current.face] && settings.paint_side_restrict.value) {
|
|
|
|
let matrix = Painter.current.face_matrices[Painter.current.face];
|
|
|
|
if (!matrix[Math.floor(px)] || !matrix[Math.floor(px)][Math.floor(py)]) {
|
|
|
|
return pxcolor;
|
|
|
|
}
|
|
|
|
}
|
2022-07-31 02:22:50 +08:00
|
|
|
return tool.brush.changePixel(px, py, pxcolor, local_opacity, {color, opacity: b_opacity, ctx, x, y, size, softness, texture, event});
|
|
|
|
})
|
|
|
|
} else if (shape == 'circle') {
|
|
|
|
Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, local_opacity, px, py) {
|
2022-07-31 17:46:22 +08:00
|
|
|
if (Painter.current.face_matrices[Painter.current.face] && settings.paint_side_restrict.value) {
|
|
|
|
let matrix = Painter.current.face_matrices[Painter.current.face];
|
|
|
|
if (!matrix[Math.floor(px)] || !matrix[Math.floor(px)][Math.floor(py)]) {
|
|
|
|
return pxcolor;
|
|
|
|
}
|
|
|
|
}
|
2022-07-31 02:22:50 +08:00
|
|
|
return tool.brush.changePixel(px, py, pxcolor, local_opacity, {color, opacity: b_opacity, ctx, x, y, size, softness, texture, event});
|
|
|
|
})
|
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2020-04-26 02:25:07 +08:00
|
|
|
}
|
|
|
|
ctx.restore();
|
|
|
|
},
|
|
|
|
useFilltool(texture, ctx, x, y, area) {
|
|
|
|
var color = tinycolor(ColorPanel.get()).toRgb();
|
2021-10-14 22:39:02 +08:00
|
|
|
let b_opacity = BarItems.slider_brush_opacity.get()/255;
|
2021-09-14 02:36:51 +08:00
|
|
|
var fill_mode = BarItems.fill_mode.get()
|
|
|
|
var element = Painter.current.element;
|
2020-04-26 02:25:07 +08:00
|
|
|
let {rect, uvFactorX, uvFactorY, w, h} = area;
|
|
|
|
|
2021-09-22 19:47:46 +08:00
|
|
|
if (Painter.erase_mode && (fill_mode === 'element' || fill_mode === 'face')) {
|
2021-08-07 19:51:00 +08:00
|
|
|
ctx.globalAlpha = b_opacity;
|
2021-09-14 02:36:51 +08:00
|
|
|
ctx.fillStyle = 'white';
|
|
|
|
ctx.globalCompositeOperation = 'destination-out';
|
|
|
|
} else {
|
|
|
|
ctx.fillStyle = tinycolor(ColorPanel.get()).setAlpha(b_opacity).toRgbString();
|
2022-04-29 04:34:11 +08:00
|
|
|
if (Painter.lock_alpha) {
|
|
|
|
ctx.globalCompositeOperation = 'source-atop';
|
|
|
|
}
|
2021-08-07 19:51:00 +08:00
|
|
|
}
|
|
|
|
|
2021-09-22 19:47:46 +08:00
|
|
|
if (element instanceof Cube && fill_mode === 'element') {
|
2022-02-18 01:01:21 +08:00
|
|
|
ctx.beginPath();
|
2021-08-28 18:33:59 +08:00
|
|
|
for (var face in element.faces) {
|
|
|
|
var tag = element.faces[face]
|
2020-07-16 15:32:59 +08:00
|
|
|
if (tag.getTexture() === texture) {
|
2020-04-26 02:25:07 +08:00
|
|
|
var face_rect = getRectangle(
|
2021-12-22 01:23:48 +08:00
|
|
|
tag.uv[0] * uvFactorX,
|
|
|
|
tag.uv[1] * uvFactorY,
|
|
|
|
tag.uv[2] * uvFactorX,
|
|
|
|
tag.uv[3] * uvFactorY
|
2020-04-26 02:25:07 +08:00
|
|
|
)
|
2021-09-25 20:47:41 +08:00
|
|
|
let animation_offset = texture.currentFrame * texture.display_height;
|
2021-12-22 01:23:48 +08:00
|
|
|
ctx.rect(
|
|
|
|
Math.floor(face_rect.ax),
|
|
|
|
Math.floor(face_rect.ay) + animation_offset,
|
|
|
|
Math.ceil(face_rect.bx) - Math.floor(face_rect.ax),
|
|
|
|
Math.ceil(face_rect.by) - Math.floor(face_rect.ay)
|
|
|
|
)
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
}
|
2022-02-18 01:01:21 +08:00
|
|
|
ctx.fill()
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2022-02-18 01:01:21 +08:00
|
|
|
} else if (element instanceof Mesh && (fill_mode === 'element' || fill_mode === 'face')) {
|
|
|
|
ctx.beginPath();
|
2021-09-22 19:47:46 +08:00
|
|
|
for (var fkey in element.faces) {
|
|
|
|
var face = element.faces[fkey];
|
2022-02-18 01:01:21 +08:00
|
|
|
if (fill_mode === 'face' && fkey !== Painter.current.face) continue;
|
2021-09-22 19:47:46 +08:00
|
|
|
if (face.vertices.length <= 2 || face.getTexture() !== texture) continue;
|
|
|
|
|
2022-04-02 21:04:43 +08:00
|
|
|
let matrix = Painter.current.face_matrices[fkey] || face.getOccupationMatrix(true, [0, 0]);
|
|
|
|
Painter.current.face_matrices[fkey] = matrix;
|
2022-02-18 01:01:21 +08:00
|
|
|
for (let x in matrix) {
|
|
|
|
for (let y in matrix[x]) {
|
|
|
|
if (!matrix[x][y]) continue;
|
|
|
|
x = parseInt(x); y = parseInt(y);
|
|
|
|
ctx.rect(x, y, 1, 1);
|
|
|
|
}
|
|
|
|
}
|
2021-09-22 19:47:46 +08:00
|
|
|
}
|
2022-02-18 01:01:21 +08:00
|
|
|
ctx.fill()
|
2021-09-22 19:47:46 +08:00
|
|
|
|
2020-04-26 02:25:07 +08:00
|
|
|
} else if (fill_mode === 'face') {
|
|
|
|
ctx.fill()
|
|
|
|
} else {
|
2022-07-09 02:47:06 +08:00
|
|
|
let image_data = ctx.getImageData(x, y, 1, 1)
|
|
|
|
let pxcol = [...image_data.data];
|
|
|
|
let map = {}
|
2020-04-26 02:25:07 +08:00
|
|
|
Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
|
|
|
|
if (pxcol.equals(px)) {
|
|
|
|
if (!map[x]) map[x] = {}
|
|
|
|
map[x][y] = true
|
|
|
|
}
|
|
|
|
})
|
|
|
|
var scan_value = true;
|
|
|
|
if (fill_mode === 'color_connected') {
|
|
|
|
function checkPx(x, y) {
|
|
|
|
if (map[x] && map[x][y]) {
|
|
|
|
map[x][y] = false;
|
|
|
|
|
|
|
|
checkPx(x+1, y)
|
|
|
|
checkPx(x-1, y)
|
|
|
|
checkPx(x, y+1)
|
|
|
|
checkPx(x, y-1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
checkPx(x, y)
|
|
|
|
scan_value = false;
|
|
|
|
}
|
|
|
|
Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
|
|
|
|
if (map[x] && map[x][y] === scan_value) {
|
|
|
|
var pxcolor = {
|
|
|
|
r: px[0],
|
|
|
|
g: px[1],
|
|
|
|
b: px[2],
|
|
|
|
a: px[3]/255
|
|
|
|
}
|
2021-08-07 19:51:00 +08:00
|
|
|
var result_color = pxcolor;
|
|
|
|
if (!Painter.erase_mode) {
|
|
|
|
result_color = Painter.combineColors(pxcolor, color, b_opacity);
|
|
|
|
} else if (!Painter.lock_alpha) {
|
|
|
|
if (b_opacity == 1) {
|
|
|
|
result_color.r = result_color.g = result_color.b = result_color.a = 0;
|
|
|
|
} else {
|
|
|
|
result_color.a = Math.clamp(result_color.a * (1-b_opacity), 0, 1);
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 02:25:07 +08:00
|
|
|
px[0] = result_color.r
|
|
|
|
px[1] = result_color.g
|
|
|
|
px[2] = result_color.b
|
|
|
|
if (!Painter.lock_alpha) px[3] = result_color.a*255
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-08-07 19:51:00 +08:00
|
|
|
ctx.globalAlpha = 1.0;
|
|
|
|
ctx.globalCompositeOperation = 'source-over'
|
2020-03-05 03:56:17 +08:00
|
|
|
},
|
2022-02-01 23:48:18 +08:00
|
|
|
getMirrorPaintTarget(texture, x, y, uvTag) {
|
|
|
|
if (!uvTag || !Painter.current.element) return;
|
|
|
|
let mirror_element = Painter.getMirrorElement(Painter.current.element);
|
2022-07-31 02:22:50 +08:00
|
|
|
let even_brush_size = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brush?.offset_even_radius;
|
2022-02-01 23:48:18 +08:00
|
|
|
if (mirror_element instanceof Cube) {
|
|
|
|
|
|
|
|
let uvFactorX = 1 / Project.texture_width * texture.img.naturalWidth;
|
|
|
|
let uvFactorY = 1 / Project.texture_height * texture.img.naturalHeight;
|
|
|
|
|
2022-03-25 03:09:12 +08:00
|
|
|
let fkey = Painter.current.face;
|
|
|
|
let side_face = (fkey === 'west' || fkey === 'east')
|
|
|
|
if (side_face) fkey = CubeFace.opposite[fkey];
|
|
|
|
let face = mirror_element.faces[fkey];
|
2022-02-01 23:48:18 +08:00
|
|
|
|
|
|
|
if (side_face &&
|
|
|
|
uvTag[1] === face.uv[1] && uvTag[3] === face.uv[3] &&
|
|
|
|
Math.min(uvTag[0], uvTag[2]) === Math.min(face.uv[0], face.uv[2])
|
|
|
|
//same face
|
|
|
|
) return;
|
|
|
|
|
|
|
|
//calculate original point
|
|
|
|
var point_on_uv = [
|
|
|
|
x - Math.min(uvTag[0], uvTag[2]) * uvFactorX,
|
|
|
|
y - Math.min(uvTag[1], uvTag[3]) * uvFactorY,
|
|
|
|
]
|
|
|
|
//calculate new point
|
|
|
|
if (face.uv[0] > face.uv[0+2] == uvTag[0] > uvTag[0+2]) {
|
|
|
|
point_on_uv[0] = Math.max(face.uv[0], face.uv[0+2]) * uvFactorX - point_on_uv[0] - 1;
|
|
|
|
if (even_brush_size) point_on_uv[0] += 1
|
|
|
|
} else {
|
|
|
|
point_on_uv[0] = Math.min(face.uv[0], face.uv[0+2]) * uvFactorX + point_on_uv[0];
|
|
|
|
}
|
|
|
|
if (face.uv[1] > face.uv[1+2] == uvTag[1] > uvTag[1+2]) {
|
|
|
|
point_on_uv[1] = Math.min(face.uv[1], face.uv[1+2]) * uvFactorY + point_on_uv[1];
|
|
|
|
} else {
|
|
|
|
point_on_uv[1] = Math.max(face.uv[1], face.uv[1+2]) * uvFactorY - point_on_uv[1] - 1;
|
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
return {
|
|
|
|
element: mirror_element,
|
|
|
|
x: point_on_uv[0],
|
|
|
|
y: point_on_uv[1],
|
2022-03-25 03:09:12 +08:00
|
|
|
uv_tag: face.uv,
|
|
|
|
face: fkey
|
2022-02-01 23:48:18 +08:00
|
|
|
}
|
2021-09-22 03:13:24 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
} else if (mirror_element instanceof Mesh) {
|
|
|
|
|
|
|
|
let mesh = mirror_element;
|
|
|
|
|
|
|
|
let clicked_face = mesh.faces[Painter.current.face];
|
|
|
|
let normal = clicked_face.getNormal(true);
|
|
|
|
let center = clicked_face.getCenter();
|
|
|
|
let e = 0.01;
|
|
|
|
let face;
|
2022-03-25 03:09:12 +08:00
|
|
|
let match_fkey;
|
2022-02-01 23:48:18 +08:00
|
|
|
for (let fkey in mesh.faces) {
|
|
|
|
let normal2 = mesh.faces[fkey].getNormal(true);
|
|
|
|
let center2 = mesh.faces[fkey].getCenter();
|
|
|
|
if (
|
|
|
|
Math.epsilon(normal[0], -normal2[0], e) && Math.epsilon(normal[1], normal2[1], e) && Math.epsilon(normal[2], normal2[2], e) &&
|
|
|
|
Math.epsilon(center[0], -center2[0], e) && Math.epsilon(center[1], center2[1], e) && Math.epsilon(center[2], center2[2], e)
|
|
|
|
) {
|
|
|
|
face = mesh.faces[fkey];
|
2022-03-25 03:09:12 +08:00
|
|
|
match_fkey = fkey;
|
2021-09-22 03:13:24 +08:00
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
}
|
|
|
|
if (!face) return;
|
|
|
|
|
|
|
|
if (!even_brush_size) {
|
|
|
|
x += 0.5; y += 0.5;
|
|
|
|
}
|
|
|
|
let world_coord = mesh.mesh.localToWorld(clicked_face.UVToLocal([x, y]));
|
|
|
|
world_coord.x *= -1;
|
|
|
|
mesh.mesh.worldToLocal(world_coord);
|
|
|
|
let point_on_uv = face.localToUV(world_coord);
|
|
|
|
|
|
|
|
if (even_brush_size) {
|
|
|
|
point_on_uv = point_on_uv.map(v => Math.round(v))
|
|
|
|
} else {
|
|
|
|
point_on_uv = point_on_uv.map(v => Math.floor(v))
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
element: mesh,
|
|
|
|
x: point_on_uv[0],
|
|
|
|
y: point_on_uv[1],
|
2022-03-25 03:09:12 +08:00
|
|
|
uv_tag: face.uv,
|
|
|
|
face: match_fkey
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
drawBrushLine(texture, end_x, end_y, event, new_face, uv) {
|
|
|
|
var start_x = (Painter.current.x == undefined ? end_x : Painter.current.x);
|
|
|
|
var start_y = (Painter.current.y == undefined ? end_y : Painter.current.y);
|
|
|
|
|
|
|
|
var diff_x = end_x - start_x;
|
|
|
|
var diff_y = end_y - start_y;
|
|
|
|
|
2021-12-14 07:14:23 +08:00
|
|
|
var length = Math.round(Math.sqrt(diff_x*diff_x + diff_y*diff_y))
|
2020-03-05 03:56:17 +08:00
|
|
|
if (new_face && !length) {
|
|
|
|
length = 1
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
2022-08-06 05:52:53 +08:00
|
|
|
var interval = Toolbox.selected.brush.line_interval || 1;
|
|
|
|
var i = Math.min(interval, length);
|
2020-03-05 03:56:17 +08:00
|
|
|
var x, y;
|
|
|
|
while (i <= length) {
|
|
|
|
x = Math.round(start_x + diff_x / length * i)
|
|
|
|
y = Math.round(start_y + diff_y / length * i)
|
2020-04-26 02:25:07 +08:00
|
|
|
Painter.useBrushlike(texture, x, y, event, uv, i < length-1);
|
2022-08-06 05:52:53 +08:00
|
|
|
i += interval;
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
useShapeTool(texture, x, y, event, uvTag) {
|
|
|
|
Painter.brushChanges = true;
|
|
|
|
|
|
|
|
texture.edit(function(canvas) {
|
|
|
|
var ctx = canvas.getContext('2d')
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.drawImage(Painter.current.clear, 0, 0)
|
|
|
|
|
|
|
|
let color = tinycolor(ColorPanel.get()).toRgb();
|
2021-10-14 22:39:02 +08:00
|
|
|
let b_opacity = BarItems.slider_brush_opacity.get()/255;
|
2020-03-05 03:56:17 +08:00
|
|
|
var width = BarItems.slider_brush_size.get();
|
|
|
|
let shape = BarItems.draw_shape_type.get();
|
|
|
|
let hollow = shape.substr(-1) == 'h';
|
|
|
|
shape = shape.replace(/_h$/, '');
|
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
function drawShape(start_x, start_y, x, y, uvTag) {
|
2020-03-05 03:56:17 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
var rect = Painter.setupRectFromFace(uvTag, texture);
|
|
|
|
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]]
|
2020-03-05 03:56:17 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
let diff_x = x - start_x;
|
|
|
|
let diff_y = y - start_y;
|
2020-03-05 03:56:17 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
if (event.shiftKey || Pressing.overrides.shift) {
|
|
|
|
let clamp = Math.floor((Math.abs(diff_x) + Math.abs(diff_y))/2);
|
|
|
|
diff_x = diff_x>0 ? clamp : -clamp;
|
|
|
|
diff_y = diff_y>0 ? clamp : -clamp;
|
|
|
|
}
|
2021-08-07 19:51:00 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
if (Painter.erase_mode) {
|
|
|
|
ctx.globalAlpha = b_opacity;
|
|
|
|
ctx.globalCompositeOperation = 'destination-out'
|
2022-04-29 04:34:11 +08:00
|
|
|
} else if (Painter.lock_alpha) {
|
|
|
|
ctx.globalCompositeOperation = 'source-atop';
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
|
|
|
|
if (shape === 'rectangle') {
|
|
|
|
ctx.strokeStyle = ctx.fillStyle = tinycolor(ColorPanel.get()).setAlpha(b_opacity).toRgbString();
|
|
|
|
ctx.lineWidth = width;
|
|
|
|
ctx.beginPath();
|
|
|
|
var rect = getRectangle(start_x, start_y, start_x+diff_x, start_y+diff_y);
|
|
|
|
|
|
|
|
if (hollow) {
|
|
|
|
ctx.rect(rect.ax+(width%2 ? 0.5 : 1), rect.ay+(width%2 ? 0.5 : 1), rect.x, rect.y);
|
|
|
|
ctx.stroke();
|
|
|
|
} else {
|
|
|
|
ctx.rect(rect.ax, rect.ay, rect.x+1, rect.y+1);
|
|
|
|
ctx.fill();
|
|
|
|
}
|
|
|
|
} else if (shape === 'ellipse') {
|
|
|
|
Painter.modifyCanvasSection(ctx, rect[0], rect[1], w, h, (changePixel) => {
|
|
|
|
//changePixel(0, 0, editPx)
|
|
|
|
function editPx(pxcolor) {
|
|
|
|
if (!Painter.erase_mode) {
|
2022-04-29 04:34:11 +08:00
|
|
|
let result_color = Painter.combineColors(pxcolor, color, b_opacity);
|
|
|
|
if (Painter.lock_alpha) {
|
|
|
|
result_color = {
|
|
|
|
r: result_color.r,
|
|
|
|
g: result_color.g,
|
|
|
|
b: result_color.b,
|
|
|
|
a: pxcolor.a
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result_color;
|
2021-08-07 19:51:00 +08:00
|
|
|
} else {
|
2022-02-01 23:48:18 +08:00
|
|
|
if (b_opacity == 1) {
|
|
|
|
pxcolor.r = pxcolor.g = pxcolor.b = pxcolor.a = 0;
|
|
|
|
} else {
|
|
|
|
pxcolor.a = Math.clamp(pxcolor.a * (1-b_opacity), 0, 1);
|
|
|
|
}
|
|
|
|
return pxcolor;
|
2021-08-07 19:51:00 +08:00
|
|
|
}
|
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
if (hollow) {
|
|
|
|
let r_min = Math.trunc(-width/2);
|
|
|
|
let r_max = Math.ceil(width/2);
|
|
|
|
for (var diff_x_m = diff_x+r_min; diff_x_m < diff_x+r_max; diff_x_m++) {
|
|
|
|
for (var diff_y_m = diff_y+r_min; diff_y_m < diff_y+r_max; diff_y_m++) {
|
|
|
|
for (var i = 0; i < Math.abs(diff_x_m); i++) {
|
|
|
|
for (var j = 0; j < 4; j++) {
|
|
|
|
changePixel(
|
|
|
|
start_x + (j<2?1:-1) * i,
|
|
|
|
start_y + (j%2?1:-1) * Math.round(Math.cos(Math.asin(i / Math.abs(diff_x_m))) * diff_y_m),
|
|
|
|
editPx
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (var i = 0; i < Math.abs(diff_y_m); i++) {
|
|
|
|
for (var j = 0; j < 4; j++) {
|
|
|
|
changePixel(
|
|
|
|
start_x + (j<2?1:-1) * Math.round(Math.sin(Math.acos(i / Math.abs(diff_y_m))) * diff_x_m),
|
|
|
|
start_y + (j%2?1:-1) * i,
|
|
|
|
editPx
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
diff_x = Math.clamp(diff_x, -64, 64);
|
|
|
|
diff_y = Math.clamp(diff_y, -64, 64);
|
|
|
|
for (var i = 0; i <= Math.abs(diff_x); i++) {
|
|
|
|
let radius = Math.round(Math.cos(Math.asin(i / Math.abs(diff_x))) * Math.abs(diff_y))
|
|
|
|
for (var k = 0; k <= radius; k++) {
|
2020-03-05 03:56:17 +08:00
|
|
|
for (var j = 0; j < 4; j++) {
|
|
|
|
changePixel(
|
2022-02-01 23:48:18 +08:00
|
|
|
start_x + (j<2?1:-1) * i,
|
|
|
|
start_y + (j%2?1:-1) * k,
|
2020-03-05 03:56:17 +08:00
|
|
|
editPx
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
}
|
|
|
|
for (var i = 0; i <= Math.abs(diff_y); i++) {
|
|
|
|
let radius = Math.round(Math.sin(Math.acos(i / Math.abs(diff_y))) * Math.abs(diff_x))
|
|
|
|
for (var k = 0; k <= radius; k++) {
|
2020-03-05 03:56:17 +08:00
|
|
|
for (var j = 0; j < 4; j++) {
|
|
|
|
changePixel(
|
2022-02-01 23:48:18 +08:00
|
|
|
start_x + (j<2?1:-1) * k,
|
|
|
|
start_y + (j%2?1:-1) * i,
|
2020-03-05 03:56:17 +08:00
|
|
|
editPx
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
drawShape(Painter.startPixel[0], Painter.startPixel[1], x, y, uvTag);
|
|
|
|
|
|
|
|
if (Painter.mirror_painting) {
|
|
|
|
let target = Painter.getMirrorPaintTarget(texture, x, y, uvTag);
|
|
|
|
if (target) {
|
|
|
|
let start_target = Painter.getMirrorPaintTarget(texture, Painter.startPixel[0], Painter.startPixel[1], uvTag);
|
|
|
|
let old_element = Painter.current.element;
|
2022-03-25 03:09:12 +08:00
|
|
|
let old_face = Painter.current.face;
|
2022-02-01 23:48:18 +08:00
|
|
|
Painter.current.element = target.element;
|
2022-03-25 03:09:12 +08:00
|
|
|
Painter.current.face = target.face;
|
2022-02-01 23:48:18 +08:00
|
|
|
drawShape(start_target.x, start_target.y, target.x, target.y, target.uv_tag)
|
|
|
|
Painter.current.element = old_element;
|
2022-03-25 03:09:12 +08:00
|
|
|
Painter.current.face = old_face;
|
2022-02-01 23:48:18 +08:00
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
2022-02-01 23:48:18 +08:00
|
|
|
|
2020-03-05 03:56:17 +08:00
|
|
|
//Painter.editing_area = undefined;
|
2021-08-07 19:51:00 +08:00
|
|
|
ctx.globalAlpha = 1.0;
|
|
|
|
ctx.globalCompositeOperation = 'source-over';
|
2020-03-05 03:56:17 +08:00
|
|
|
|
|
|
|
}, {no_undo: true, use_cache: true});
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2021-05-30 20:38:09 +08:00
|
|
|
useGradientTool(texture, x, y, event, uvTag) {
|
|
|
|
Painter.brushChanges = true;
|
|
|
|
|
|
|
|
texture.edit(function(canvas) {
|
2022-02-01 23:48:18 +08:00
|
|
|
let b_opacity = BarItems.slider_brush_opacity.get()/255;
|
2021-05-30 20:38:09 +08:00
|
|
|
var ctx = canvas.getContext('2d')
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
ctx.drawImage(Painter.current.clear, 0, 0)
|
2022-04-29 04:34:11 +08:00
|
|
|
if (Painter.lock_alpha) {
|
|
|
|
ctx.globalCompositeOperation = 'source-atop';
|
|
|
|
}
|
2021-05-30 20:38:09 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
function drawGradient(start_x, start_y, x, y, uvTag) {
|
|
|
|
let rect = Painter.setupRectFromFace(uvTag, texture);
|
|
|
|
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]];
|
|
|
|
let diff_x = x - start_x;
|
|
|
|
let diff_y = y - start_y;
|
|
|
|
|
|
|
|
if (event.shiftKey || Pressing.overrides.shift) {
|
|
|
|
let length = Math.sqrt(Math.pow(diff_x, 2) + Math.pow(diff_y, 2));
|
|
|
|
|
|
|
|
let ratio = Math.abs(diff_x) / Math.abs(diff_y);
|
|
|
|
if (ratio < 0.25) {
|
|
|
|
ratio = 0;
|
|
|
|
diff_x = 0;
|
|
|
|
diff_y = length;
|
|
|
|
} else if (ratio < 0.75) {
|
|
|
|
ratio = 0.5;
|
|
|
|
diff_x = Math.round(length / 2.2361);
|
|
|
|
diff_y = diff_x * 2;
|
|
|
|
} else if (ratio < 1.5) {
|
|
|
|
ratio = 1;
|
|
|
|
diff_x = Math.round(Math.sqrt(Math.pow(length, 2) / 2));
|
|
|
|
diff_y = diff_x;
|
|
|
|
} else if (ratio < 3) {
|
|
|
|
ratio = 2;
|
|
|
|
diff_y = Math.round(length / 2.2361);
|
|
|
|
diff_x = diff_y * 2;
|
|
|
|
} else {
|
|
|
|
ratio = Infinity;
|
|
|
|
diff_x = length;
|
|
|
|
diff_y = 0;
|
|
|
|
}
|
|
|
|
x = start_x + diff_x * Math.sign(x - start_x);
|
|
|
|
y = start_y + diff_y * Math.sign(y - start_y);
|
2021-05-30 20:38:09 +08:00
|
|
|
}
|
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
let gradient = ctx.createLinearGradient(start_x, start_y, x, y);
|
|
|
|
gradient.addColorStop(0, tinycolor(ColorPanel.get()).setAlpha(b_opacity).toRgbString());
|
|
|
|
gradient.addColorStop(1, tinycolor(ColorPanel.get()).setAlpha(0).toRgbString());
|
2021-05-30 20:38:09 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
ctx.beginPath();
|
|
|
|
ctx.fillStyle = gradient;
|
|
|
|
ctx.rect(rect[0], rect[1], w, h);
|
|
|
|
ctx.fill();
|
2021-05-30 20:38:09 +08:00
|
|
|
|
2022-02-01 23:48:18 +08:00
|
|
|
return [diff_x, diff_y];
|
|
|
|
}
|
|
|
|
let [diff_x, diff_y] = drawGradient(Painter.startPixel[0], Painter.startPixel[1], x, y, uvTag);
|
|
|
|
|
|
|
|
if (Painter.mirror_painting) {
|
|
|
|
let target = Painter.getMirrorPaintTarget(texture, x, y, uvTag);
|
|
|
|
if (target) {
|
|
|
|
let start_target = Painter.getMirrorPaintTarget(texture, Painter.startPixel[0], Painter.startPixel[1], uvTag);
|
|
|
|
let old_element = Painter.current.element;
|
|
|
|
Painter.current.element = target.element;
|
|
|
|
drawGradient(start_target.x, start_target.y, target.x, target.y, target.uv_tag)
|
|
|
|
Painter.current.element = old_element;
|
|
|
|
}
|
|
|
|
}
|
2022-04-29 04:34:11 +08:00
|
|
|
ctx.globalCompositeOperation = 'source-over';
|
|
|
|
|
2021-05-30 20:38:09 +08:00
|
|
|
let degrees = Math.round(Math.radToDeg(Math.atan2(diff_x, diff_y)) * 4) / 4;
|
|
|
|
Blockbench.setStatusBarText(`${Math.round(diff_x)} x ${Math.round(diff_y)}, ${degrees}°`);
|
|
|
|
|
|
|
|
}, {no_undo: true, use_cache: true});
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
colorPicker(texture, x, y) {
|
|
|
|
var ctx = Painter.getCanvas(texture).getContext('2d')
|
2021-05-29 20:25:09 +08:00
|
|
|
let color = Painter.getPixelColor(ctx, x, y);
|
2022-07-31 17:46:22 +08:00
|
|
|
if (settings.pick_color_opacity.value) {
|
|
|
|
let opacity = Math.floor(color.getAlpha()*256);
|
|
|
|
for (let id in BarItems) {
|
|
|
|
let tool = BarItems[id];
|
|
|
|
if (tool.tool_settings && tool.tool_settings.brush_opacity) {
|
|
|
|
tool.tool_settings.brush_opacity = opacity;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-05-29 20:25:09 +08:00
|
|
|
ColorPanel.set(color);
|
2020-03-05 03:56:17 +08:00
|
|
|
},
|
|
|
|
// Util
|
2019-12-16 03:04:31 +08:00
|
|
|
combineColors(base, added, opacity) {
|
2020-03-05 03:56:17 +08:00
|
|
|
if (Math.isNumber(base)) base = Jimp.intToRGBA(base)
|
|
|
|
if (Math.isNumber(added)) added = Jimp.intToRGBA(added)
|
|
|
|
|
2022-06-27 04:43:00 +08:00
|
|
|
if (added.a*opacity == 1) return {r: added.r, g: added.g, b: added.b, a: added.a};
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
var original_a = added.a
|
2020-03-05 03:56:17 +08:00
|
|
|
added.a = added.a*opacity
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
var mix = {};
|
2022-07-31 17:46:22 +08:00
|
|
|
mix.a = Math.clamp(1 - (1 - added.a) * (1 - base.a), 0, 1); // alpha
|
2019-12-16 03:04:31 +08:00
|
|
|
mix.r = Math.round((added.r * added.a / mix.a) + (base.r * base.a * (1 - added.a) / mix.a)); // red
|
|
|
|
mix.g = Math.round((added.g * added.a / mix.a) + (base.g * base.a * (1 - added.a) / mix.a)); // green
|
|
|
|
mix.b = Math.round((added.b * added.a / mix.a) + (base.b * base.a * (1 - added.a) / mix.a)); // blue
|
|
|
|
|
|
|
|
added.a = original_a
|
|
|
|
return mix;
|
|
|
|
},
|
2022-07-31 17:46:22 +08:00
|
|
|
blendColors(base, added, opacity, blend_mode) {
|
|
|
|
if (Math.isNumber(base)) base = Jimp.intToRGBA(base)
|
|
|
|
if (Math.isNumber(added)) added = Jimp.intToRGBA(added)
|
|
|
|
|
|
|
|
var original_a = added.a
|
|
|
|
added.a = added.a*opacity
|
|
|
|
|
|
|
|
var mix = {};
|
|
|
|
mix.a = Math.clamp(1 - (1 - added.a) * (1 - base.a), 0, 1); // alpha
|
|
|
|
|
|
|
|
['r', 'g', 'b'].forEach(ch => {
|
|
|
|
let normal_base = base[ch] / 255;
|
|
|
|
let normal_added = added[ch] / 255;
|
|
|
|
if (base.a == 0) normal_base = normal_added;
|
|
|
|
|
|
|
|
switch (blend_mode) {
|
|
|
|
|
|
|
|
case 'behind':
|
|
|
|
mix[ch] = (normal_base * base.a / mix.a) + (normal_added * added.a * (1 - base.a) / mix.a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'color':
|
|
|
|
mix[ch] = ((normal_base / normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'multiply':
|
|
|
|
mix[ch] = ((normal_base * normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'divide':
|
|
|
|
mix[ch] = ((normal_base / normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'add':
|
|
|
|
mix[ch] = ((normal_base + normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'subtract':
|
|
|
|
mix[ch] = ((normal_base - normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Todo: Equations for remaining blend modes
|
|
|
|
case 'screen':
|
|
|
|
mix[ch] = ((normal_base / normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'hard_light':
|
|
|
|
mix[ch] = ((normal_base / normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'difference':
|
|
|
|
mix[ch] = ((normal_base - normal_added) * added.a) + (normal_base * (1-added.a));
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
mix[ch] = Math.clamp(Math.round(255 * mix[ch]), 0, 255);
|
|
|
|
})
|
|
|
|
|
|
|
|
added.a = original_a
|
|
|
|
return mix;
|
|
|
|
},
|
2022-02-01 23:48:18 +08:00
|
|
|
getMirrorElement(element) {
|
2020-03-05 03:56:17 +08:00
|
|
|
let center = Format.centered_grid ? 0 : 8;
|
2021-12-21 17:07:14 +08:00
|
|
|
let e = 0.01
|
2021-09-22 03:13:24 +08:00
|
|
|
if (element instanceof Cube) {
|
2021-12-21 17:07:14 +08:00
|
|
|
if (Math.epsilon(element.from[0]-center, center-element.to[0], e) && !element.rotation[1] && !element.rotation[2]) {
|
2021-09-22 03:13:24 +08:00
|
|
|
return element;
|
|
|
|
} else {
|
|
|
|
for (var element2 of Cube.all) {
|
|
|
|
if (
|
2022-05-20 23:01:33 +08:00
|
|
|
Math.epsilon(element.inflate, element2.inflate, e) &&
|
2021-09-22 03:13:24 +08:00
|
|
|
Math.epsilon(element.from[2], element2.from[2], e) && Math.epsilon(element.to[2], element2.to[2], e) &&
|
|
|
|
Math.epsilon(element.from[1], element2.from[1], e) && Math.epsilon(element.to[1], element2.to[1], e) &&
|
|
|
|
Math.epsilon(element.size(0), element2.size(0), e) && Math.epsilon(element.to[0]-center, center-element2.from[0], e)
|
|
|
|
) {
|
|
|
|
return element2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} else if (element instanceof Mesh) {
|
2021-12-21 17:07:14 +08:00
|
|
|
if (element instanceof Mesh && Math.epsilon(element.origin[0], center, e) && !element.rotation[1] && !element.rotation[2]) {
|
2021-09-22 03:13:24 +08:00
|
|
|
return element;
|
|
|
|
} else {
|
|
|
|
for (var element2 of Mesh.all) {
|
|
|
|
if (Object.keys(element.vertices).length !== Object.keys(element2.vertices).length) continue;
|
|
|
|
return element2;
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
}
|
2021-09-22 03:13:24 +08:00
|
|
|
return element;
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
},
|
2019-12-16 03:04:31 +08:00
|
|
|
updateNslideValues() {
|
|
|
|
BarItems.slider_brush_size.update()
|
|
|
|
BarItems.slider_brush_softness.update()
|
|
|
|
BarItems.slider_brush_opacity.update()
|
2020-03-05 03:56:17 +08:00
|
|
|
},
|
|
|
|
getCanvas(texture) {
|
2022-05-15 19:12:29 +08:00
|
|
|
let canvas = texture instanceof Texture ? texture.canvas : document.createElement('canvas');
|
|
|
|
let ctx = canvas.getContext('2d');
|
|
|
|
canvas.width = texture.width;
|
|
|
|
canvas.height = texture.height;
|
2020-03-05 03:56:17 +08:00
|
|
|
ctx.drawImage(texture instanceof Texture ? texture.img : texture, 0, 0)
|
2022-05-15 19:12:29 +08:00
|
|
|
return canvas;
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
|
|
|
scanCanvas(ctx, x, y, w, h, cb) {
|
2022-07-09 02:47:06 +08:00
|
|
|
let arr = ctx.getImageData(x, y, w, h)
|
|
|
|
for (let i = 0; i < arr.data.length; i += 4) {
|
|
|
|
let pixel = [arr.data[i], arr.data[i+1], arr.data[i+2], arr.data[i+3]]
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2022-07-09 02:47:06 +08:00
|
|
|
let px = x + (i/4) % w
|
|
|
|
let py = y + Math.floor((i/4) / w)
|
2020-03-05 03:56:17 +08:00
|
|
|
if (px >= ctx.canvas.width || px < 0 || py >= ctx.canvas.height || py < 0) continue;
|
2022-07-09 02:47:06 +08:00
|
|
|
let result = cb(px, py, pixel) || pixel
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2022-07-09 02:47:06 +08:00
|
|
|
result.forEach((p, pi) => {
|
|
|
|
if (p != arr.data[i+pi]) arr.data[i+pi] = p
|
2019-12-16 03:04:31 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
ctx.putImageData(arr, x, y)
|
|
|
|
},
|
2021-05-29 20:25:09 +08:00
|
|
|
getPixelColor(ctx, x, y) {
|
2022-07-09 02:47:06 +08:00
|
|
|
let {data} = ctx.getImageData(x, y, 1, 1)
|
2021-05-29 20:25:09 +08:00
|
|
|
return new tinycolor({
|
|
|
|
r: data[0],
|
|
|
|
g: data[1],
|
|
|
|
b: data[2],
|
|
|
|
a: data[3]/256
|
|
|
|
})
|
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
modifyCanvasSection(ctx, x, y, w, h, cb) {
|
|
|
|
var arr = ctx.getImageData(x, y, w, h)
|
|
|
|
var processed = [];
|
|
|
|
|
|
|
|
cb((px, py, editPx) => {
|
|
|
|
//changePixel
|
|
|
|
px = Math.floor(px)-x;
|
|
|
|
py = Math.floor(py)-y;
|
|
|
|
if (px < 0 || px >= w) return;
|
|
|
|
if (py < 0 || py >= h) return;
|
|
|
|
let start = (px + py*w) * 4;
|
|
|
|
if (processed.includes(start)) return;
|
|
|
|
processed.push(start);
|
|
|
|
var result_color = editPx({
|
|
|
|
r: arr.data[start+0],
|
|
|
|
g: arr.data[start+1],
|
|
|
|
b: arr.data[start+2],
|
|
|
|
a: arr.data[start+3]/255
|
|
|
|
})
|
|
|
|
arr.data[start+0] = result_color.r
|
|
|
|
arr.data[start+1] = result_color.g
|
|
|
|
arr.data[start+2] = result_color.b
|
|
|
|
arr.data[start+3] = result_color.a*255
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx.putImageData(arr, x, y)
|
|
|
|
},
|
2019-12-16 03:04:31 +08:00
|
|
|
drawRectangle(image, color, rect) {
|
|
|
|
var color = Jimp.intToRGBA(color)
|
|
|
|
image.scan(rect.x, rect.y, rect.w, rect.h, function (x, y, idx) {
|
|
|
|
this.bitmap.data[idx + 0] = color.r
|
|
|
|
this.bitmap.data[idx + 1] = color.g
|
|
|
|
this.bitmap.data[idx + 2] = color.b
|
|
|
|
this.bitmap.data[idx + 3] = color.a
|
|
|
|
});
|
|
|
|
},
|
|
|
|
editCircle(ctx, x, y, r, s, editPx) {
|
2020-03-05 03:56:17 +08:00
|
|
|
r = Math.round(r+1)/2
|
2020-03-11 05:19:17 +08:00
|
|
|
Painter.scanCanvas(ctx, x-Math.ceil(r)-2, y-Math.ceil(r)-2, 2*r+3, 2*r+3, function (px, py, pixel) {
|
2019-12-16 03:04:31 +08:00
|
|
|
if (
|
|
|
|
settings.paint_side_restrict.value &&
|
|
|
|
Painter.editing_area &&
|
|
|
|
typeof Painter.editing_area === 'object' &&
|
|
|
|
(
|
|
|
|
px+0.02 < Math.floor(Painter.editing_area[0]) ||
|
|
|
|
py+0.02 < Math.floor(Painter.editing_area[1]) ||
|
|
|
|
px+0.02 >= Painter.editing_area[2] ||
|
|
|
|
py+0.02 >= Painter.editing_area[3]
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-11 05:19:17 +08:00
|
|
|
px -= x - r%1;
|
|
|
|
py -= y - r%1;
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
var distance = Math.sqrt(px*px + py*py)
|
|
|
|
if (s*r != 0) {
|
|
|
|
var pos_on_gradient = (distance-(1-s)*r) / (s*r)
|
|
|
|
} else {
|
2022-07-31 02:22:50 +08:00
|
|
|
if (r < 8) {
|
|
|
|
distance *= 1.2;
|
|
|
|
}
|
|
|
|
var pos_on_gradient = Math.floor(distance/r);
|
|
|
|
}
|
|
|
|
|
|
|
|
var opacity = limitNumber(1-pos_on_gradient, 0, 1)
|
|
|
|
|
|
|
|
if (opacity > 0) {
|
|
|
|
var result_color = editPx({
|
|
|
|
r: pixel[0],
|
|
|
|
g: pixel[1],
|
|
|
|
b: pixel[2],
|
|
|
|
a: pixel[3]/255
|
|
|
|
}, opacity, px+x, py+y)
|
|
|
|
pixel[0] = result_color.r
|
|
|
|
pixel[1] = result_color.g
|
|
|
|
pixel[2] = result_color.b
|
|
|
|
pixel[3] = result_color.a*255
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
editSquare(ctx, x, y, r, s, editPx) {
|
|
|
|
r = Math.round(r+1)/2
|
|
|
|
Painter.scanCanvas(ctx, x-Math.ceil(r)-2, y-Math.ceil(r)-2, 2*r+3, 2*r+3, function (px, py, pixel) {
|
|
|
|
if (
|
|
|
|
settings.paint_side_restrict.value &&
|
|
|
|
Painter.editing_area &&
|
|
|
|
typeof Painter.editing_area === 'object' &&
|
|
|
|
(
|
|
|
|
px+0.02 < Math.floor(Painter.editing_area[0]) ||
|
|
|
|
py+0.02 < Math.floor(Painter.editing_area[1]) ||
|
|
|
|
px+0.02 >= Painter.editing_area[2] ||
|
|
|
|
py+0.02 >= Painter.editing_area[3]
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
px -= x - r%1;
|
|
|
|
py -= y - r%1;
|
|
|
|
|
|
|
|
var distance = Math.max(Math.abs(px), Math.abs(py));
|
|
|
|
if (s*r != 0) {
|
|
|
|
var pos_on_gradient = (distance-(1-s)*r) / (s*r)
|
|
|
|
} else {
|
|
|
|
var pos_on_gradient = Math.floor((distance)/r)
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var opacity = limitNumber(1-pos_on_gradient, 0, 1)
|
|
|
|
|
|
|
|
if (opacity > 0) {
|
|
|
|
var result_color = editPx({
|
|
|
|
r: pixel[0],
|
|
|
|
g: pixel[1],
|
|
|
|
b: pixel[2],
|
|
|
|
a: pixel[3]/255
|
|
|
|
}, opacity, px+x, py+y)
|
|
|
|
pixel[0] = result_color.r
|
|
|
|
pixel[1] = result_color.g
|
|
|
|
pixel[2] = result_color.b
|
|
|
|
pixel[3] = result_color.a*255
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
drawRotatedRectangle(image, color, rect, cx, cy, angle) {
|
|
|
|
var color = Jimp.intToRGBA(color)
|
|
|
|
var sin = Math.sin(-Math.degToRad(angle))
|
|
|
|
var cos = Math.cos(-Math.degToRad(angle))
|
|
|
|
function rotatePoint(px, py) {
|
|
|
|
px -= cx
|
|
|
|
py -= cy
|
|
|
|
return {
|
|
|
|
x: (px * cos - py * sin) + cx,
|
|
|
|
y: (px * sin + py * cos) + cy
|
|
|
|
}
|
|
|
|
}
|
|
|
|
image.scan(0, 0, 48, 48, function (px, py, idx) {
|
|
|
|
var rotated = rotatePoint(px, py)
|
|
|
|
if (
|
|
|
|
rotated.x > rect.x-1 && rotated.x < rect.x + rect.w+2 &&
|
|
|
|
rotated.y > rect.y-1 && rotated.y < rect.y + rect.h+2
|
|
|
|
) {
|
|
|
|
var opacity = limitNumber(rect.x - rotated.x, 0, 1) +
|
|
|
|
limitNumber(rotated.x - (rect.x + rect.w), 0, 1) +
|
|
|
|
limitNumber(rect.y - rotated.y, 0, 1) +
|
|
|
|
limitNumber(rotated.y - (rect.y + rect.h), 0, 1)
|
|
|
|
|
|
|
|
opacity = 1-limitNumber(opacity*1.61, 0, 1)
|
|
|
|
if (this.bitmap.data[idx + 3]) {
|
|
|
|
opacity = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
this.bitmap.data[idx + 0] = color.r
|
|
|
|
this.bitmap.data[idx + 1] = color.g
|
|
|
|
this.bitmap.data[idx + 2] = color.b
|
|
|
|
this.bitmap.data[idx + 3] = color.a*opacity
|
|
|
|
}
|
|
|
|
})
|
2020-01-24 01:53:36 +08:00
|
|
|
}
|
|
|
|
}
|
2019-12-16 03:04:31 +08:00
|
|
|
|
|
|
|
|
|
|
|
BARS.defineActions(function() {
|
|
|
|
|
2022-03-25 03:09:12 +08:00
|
|
|
new Tool('pan_tool', {
|
|
|
|
icon: 'pan_tool',
|
|
|
|
category: 'tools',
|
|
|
|
cursor: 'grab',
|
|
|
|
selectFace: false,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
allowed_view_modes: ['textured'],
|
|
|
|
modes: ['paint'],
|
|
|
|
condition: Blockbench.isMobile && {modes: ['paint']}
|
|
|
|
})
|
2019-12-16 03:04:31 +08:00
|
|
|
new Tool('brush_tool', {
|
|
|
|
icon: 'fa-paint-brush',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
2020-01-24 01:53:36 +08:00
|
|
|
cursor: 'crosshair',
|
2019-12-16 03:04:31 +08:00
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
2022-07-31 02:22:50 +08:00
|
|
|
brush: {
|
|
|
|
blend_modes: true,
|
|
|
|
shapes: true,
|
|
|
|
size: true,
|
|
|
|
softness: true,
|
|
|
|
opacity: true,
|
|
|
|
offset_even_radius: true,
|
|
|
|
changePixel(px, py, pxcolor, local_opacity, {color, opacity, ctx, x, y, size, softness, texture, event}) {
|
2022-07-31 17:46:22 +08:00
|
|
|
let blend_mode = BarItems.blend_mode.value;
|
2022-07-31 02:22:50 +08:00
|
|
|
var a = opacity * local_opacity;
|
2022-07-31 17:46:22 +08:00
|
|
|
|
|
|
|
if (blend_mode == 'set_opacity') {
|
|
|
|
if (Painter.lock_alpha && pxcolor.a == 0) return pxcolor;
|
|
|
|
return {r: color.r, g: color.g, b: color.b, a}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
var before = Painter.getAlphaMatrix(texture, px, py)
|
|
|
|
Painter.setAlphaMatrix(texture, px, py, a);
|
|
|
|
if (a > before) {
|
|
|
|
a = (a - before) / (1 - before);
|
|
|
|
} else if (before) {
|
|
|
|
a = 0;
|
|
|
|
}
|
|
|
|
let result_color;
|
|
|
|
if (blend_mode == 'default') {
|
|
|
|
result_color = Painter.combineColors(pxcolor, color, a);
|
|
|
|
} else {
|
|
|
|
result_color = Painter.blendColors(pxcolor, color, a, blend_mode);
|
|
|
|
}
|
|
|
|
if (Painter.lock_alpha) result_color.a = pxcolor.a
|
|
|
|
return result_color;
|
2022-07-31 02:22:50 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2021-02-20 18:58:53 +08:00
|
|
|
keybind: new Keybind({key: 'b'}),
|
2019-12-16 03:04:31 +08:00
|
|
|
modes: ['paint'],
|
2021-11-30 06:50:51 +08:00
|
|
|
onCanvasClick(data) {
|
|
|
|
Painter.startPaintToolCanvas(data, data.event);
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2021-11-30 06:50:51 +08:00
|
|
|
onSelect() {
|
|
|
|
Painter.updateNslideValues();
|
|
|
|
Interface.addSuggestedModifierKey('alt', 'action.color_picker');
|
|
|
|
Interface.addSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('alt', 'action.color_picker');
|
|
|
|
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
2022-08-06 05:52:53 +08:00
|
|
|
let copy_source;
|
|
|
|
let stroke_start_pos;
|
|
|
|
new Tool('copy_brush', {
|
|
|
|
icon: 'fa-stamp',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
cursor: 'crosshair',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
brush: {
|
|
|
|
shapes: true,
|
|
|
|
size: true,
|
|
|
|
softness: true,
|
|
|
|
opacity: true,
|
|
|
|
offset_even_radius: true,
|
|
|
|
onStrokeStart({texture, event, x, y}) {
|
|
|
|
if (event.ctrlOrCmd || Pressing.overrides.ctrl) {
|
|
|
|
if (!Painter.current.canvas) {
|
|
|
|
Painter.current.canvas = Painter.getCanvas(texture);
|
|
|
|
}
|
|
|
|
let size = BarItems.slider_brush_size.get();
|
|
|
|
copy_source = {
|
|
|
|
data: texture.canvas.getContext('2d').getImageData(0, 0, texture.width, texture.height).data,
|
|
|
|
width: texture.width,
|
|
|
|
height: texture.height,
|
|
|
|
size,
|
|
|
|
x,
|
|
|
|
y,
|
|
|
|
}
|
|
|
|
UVEditor.vue.copy_brush_source = {
|
|
|
|
x, y,
|
|
|
|
size,
|
|
|
|
texture: texture.uuid
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (!copy_source) return false;
|
|
|
|
stroke_start_pos = [x, y]
|
|
|
|
}
|
|
|
|
},
|
|
|
|
changePixel(px, py, pxcolor, local_opacity, {opacity,x, y, texture}) {
|
|
|
|
let a = opacity * local_opacity;
|
|
|
|
let mode = BarItems.copy_brush_mode.value
|
|
|
|
|
|
|
|
let source_pos;
|
|
|
|
if (mode == 'copy') {
|
|
|
|
source_pos = [
|
|
|
|
Math.round(copy_source.x + (px - stroke_start_pos[0])),
|
|
|
|
Math.round(copy_source.y + (py - stroke_start_pos[1])),
|
|
|
|
]
|
|
|
|
} else if (mode == 'pattern') {
|
|
|
|
let size = copy_source.size;
|
|
|
|
let grid_start = [
|
|
|
|
copy_source.x - size/2,
|
|
|
|
copy_source.y - size/2,
|
|
|
|
]
|
|
|
|
source_pos = [
|
|
|
|
Math.floor(grid_start[0] + ((px + size*200 - (grid_start[0] % size)) % size)),
|
|
|
|
Math.floor(grid_start[1] + ((py + size*200 - (grid_start[1] % size)) % size)),
|
|
|
|
]
|
|
|
|
} else {
|
|
|
|
source_pos = [
|
|
|
|
Math.round(copy_source.x + (px - x)),
|
|
|
|
Math.round(copy_source.y + (py - y)),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
if (source_pos[0] < 0 || source_pos[0] >= copy_source.width || source_pos[1] < 0 || source_pos[1] >= copy_source.height) {
|
|
|
|
return pxcolor;
|
|
|
|
}
|
2022-08-06 19:37:20 +08:00
|
|
|
|
2022-08-06 05:52:53 +08:00
|
|
|
let source_index = (source_pos[0] + source_pos[1] * copy_source.width) * 4;
|
|
|
|
let color = {
|
|
|
|
r: copy_source.data[source_index + 0],
|
|
|
|
g: copy_source.data[source_index + 1],
|
|
|
|
b: copy_source.data[source_index + 2],
|
|
|
|
a: copy_source.data[source_index + 3] / 255
|
|
|
|
}
|
|
|
|
|
2022-08-06 19:37:20 +08:00
|
|
|
let before = Painter.getAlphaMatrix(texture, px, py)
|
|
|
|
Painter.setAlphaMatrix(texture, px, py, a * color.a);
|
|
|
|
if (a > before) {
|
|
|
|
a = (a - before) / (1 - before);
|
|
|
|
} else if (before) {
|
|
|
|
a = 0;
|
|
|
|
}
|
|
|
|
|
2022-08-06 05:52:53 +08:00
|
|
|
let result_color = Painter.combineColors(pxcolor, color, a);
|
|
|
|
if (Painter.lock_alpha) result_color.a = pxcolor.a
|
|
|
|
return result_color;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
allowed_view_modes: ['textured'],
|
|
|
|
modes: ['paint'],
|
|
|
|
onCanvasClick(data) {
|
|
|
|
Painter.startPaintToolCanvas(data, data.event);
|
|
|
|
},
|
|
|
|
onSelect() {
|
|
|
|
Painter.updateNslideValues();
|
|
|
|
Interface.addSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
|
|
|
Interface.addSuggestedModifierKey('ctrl', 'modifier_actions.set_copy_source');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
|
|
|
Interface.removeSuggestedModifierKey('ctrl', 'modifier_actions.set_copy_source');
|
|
|
|
UVEditor.vue.copy_brush_source = null;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
BarItems.copy_brush.tool_settings.brush_size = 16;
|
2019-12-16 03:04:31 +08:00
|
|
|
new Tool('fill_tool', {
|
|
|
|
icon: 'format_color_fill',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
2020-01-24 01:53:36 +08:00
|
|
|
cursor: 'crosshair',
|
2019-12-16 03:04:31 +08:00
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2019-12-16 03:04:31 +08:00
|
|
|
modes: ['paint'],
|
|
|
|
onCanvasClick: function(data) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.startPaintToolCanvas(data, data.event)
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
|
|
|
onSelect: function() {
|
2021-11-30 06:50:51 +08:00
|
|
|
Painter.updateNslideValues();
|
|
|
|
Interface.addSuggestedModifierKey('alt', 'action.color_picker');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('alt', 'action.color_picker');
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
new Tool('eraser', {
|
|
|
|
icon: 'fa-eraser',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
2021-02-28 23:16:37 +08:00
|
|
|
alt_tool: 'color_picker',
|
2019-12-16 03:04:31 +08:00
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
2020-01-24 01:53:36 +08:00
|
|
|
cursor: 'crosshair',
|
2019-12-16 03:04:31 +08:00
|
|
|
paintTool: true,
|
2022-07-31 02:22:50 +08:00
|
|
|
brush: {
|
|
|
|
shapes: true,
|
|
|
|
size: true,
|
|
|
|
softness: true,
|
|
|
|
opacity: true,
|
|
|
|
offset_even_radius: true,
|
|
|
|
changePixel(px, py, pxcolor, local_opacity, {opacity, ctx, x, y, size, softness, texture, event}) {
|
|
|
|
if (Painter.lock_alpha) return pxcolor;
|
|
|
|
|
|
|
|
var a = opacity * local_opacity;
|
|
|
|
|
|
|
|
var before = Painter.getAlphaMatrix(texture, px, py)
|
|
|
|
Painter.setAlphaMatrix(texture, px, py, a);
|
|
|
|
|
|
|
|
if (a > before) {
|
|
|
|
a = (a - before) / (1 - before);
|
|
|
|
} else if (before) {
|
|
|
|
a = 0;
|
|
|
|
}
|
|
|
|
pxcolor.a = Math.clamp(pxcolor.a * (1-a), 0, 1);
|
|
|
|
return pxcolor;
|
|
|
|
}
|
|
|
|
},
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2019-12-16 03:04:31 +08:00
|
|
|
modes: ['paint'],
|
2021-02-20 18:58:53 +08:00
|
|
|
keybind: new Keybind({key: 'e'}),
|
2019-12-16 03:04:31 +08:00
|
|
|
onCanvasClick: function(data) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.startPaintToolCanvas(data, data.event)
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
|
|
|
onSelect: function() {
|
|
|
|
Painter.updateNslideValues()
|
2021-11-30 06:50:51 +08:00
|
|
|
Interface.addSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.draw_line');
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
new Tool('color_picker', {
|
|
|
|
icon: 'colorize',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
2020-01-24 01:53:36 +08:00
|
|
|
cursor: 'crosshair',
|
2019-12-16 03:04:31 +08:00
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2019-12-16 03:04:31 +08:00
|
|
|
modes: ['paint'],
|
|
|
|
onCanvasClick: function(data) {
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.startPaintToolCanvas(data, data.event)
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
|
|
|
onSelect: function() {
|
|
|
|
Painter.updateNslideValues()
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
new Tool('draw_shape_tool', {
|
|
|
|
icon: 'fas.fa-shapes',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
|
|
|
cursor: 'crosshair',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2020-03-05 03:56:17 +08:00
|
|
|
modes: ['paint'],
|
|
|
|
condition: {modes: ['paint']},
|
2021-02-20 18:58:53 +08:00
|
|
|
keybind: new Keybind({key: 'u'}),
|
2020-03-05 03:56:17 +08:00
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startPaintToolCanvas(data, data.event)
|
2019-12-16 03:04:31 +08:00
|
|
|
},
|
2020-03-05 03:56:17 +08:00
|
|
|
onSelect: function() {
|
|
|
|
Painter.updateNslideValues()
|
2021-12-12 20:26:23 +08:00
|
|
|
Interface.addSuggestedModifierKey('shift', 'modifier_actions.uniform_scaling');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.uniform_scaling');
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
2021-05-30 20:38:09 +08:00
|
|
|
new Tool('gradient_tool', {
|
|
|
|
icon: 'gradient',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
|
|
|
cursor: 'crosshair',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
allowed_view_modes: ['textured'],
|
|
|
|
modes: ['paint'],
|
|
|
|
condition: {modes: ['paint']},
|
|
|
|
//keybind: new Keybind({key: 'u'}),
|
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startPaintToolCanvas(data, data.event)
|
|
|
|
},
|
|
|
|
onSelect: function() {
|
|
|
|
Painter.updateNslideValues()
|
2021-12-12 20:26:23 +08:00
|
|
|
Interface.addSuggestedModifierKey('shift', 'modifier_actions.snap_direction');
|
|
|
|
},
|
|
|
|
onUnselect() {
|
|
|
|
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.snap_direction');
|
2021-05-30 20:38:09 +08:00
|
|
|
}
|
|
|
|
})
|
2020-03-05 03:56:17 +08:00
|
|
|
new Tool('copy_paste_tool', {
|
|
|
|
icon: 'fa-vector-square',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
|
|
|
cursor: 'crosshair',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
2021-05-07 21:57:24 +08:00
|
|
|
allowed_view_modes: ['textured'],
|
2020-03-05 03:56:17 +08:00
|
|
|
modes: ['paint'],
|
|
|
|
condition: {modes: ['paint']},
|
2021-08-08 00:33:50 +08:00
|
|
|
keybind: new Keybind({key: 'm'}),
|
2022-07-31 17:46:22 +08:00
|
|
|
onCanvasClick(data) {
|
|
|
|
if (data && data.element) {
|
|
|
|
Blockbench.showQuickMessage('message.copy_paste_tool_viewport')
|
|
|
|
}
|
2021-08-08 00:33:50 +08:00
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
})
|
2019-12-16 03:04:31 +08:00
|
|
|
|
2022-07-31 02:22:50 +08:00
|
|
|
new BarSelect('brush_shape', {
|
|
|
|
category: 'paint',
|
|
|
|
condition: () => Toolbox && Toolbox.selected.brush && Toolbox.selected.brush.shapes,
|
|
|
|
onChange() {
|
|
|
|
BARS.updateConditions();
|
|
|
|
UVEditor.vue.brush_type = this.value;
|
|
|
|
},
|
|
|
|
icon_mode: true,
|
|
|
|
options: {
|
|
|
|
square: {name: true, icon: 'fas.fa-square'},
|
|
|
|
circle: {name: true, icon: 'circle'}
|
|
|
|
}
|
|
|
|
})
|
2020-03-05 03:56:17 +08:00
|
|
|
new BarSelect('draw_shape_type', {
|
|
|
|
category: 'paint',
|
|
|
|
condition: () => Toolbox && Toolbox.selected.id === 'draw_shape_tool',
|
2019-12-16 03:04:31 +08:00
|
|
|
onChange() {
|
|
|
|
BARS.updateConditions();
|
|
|
|
Painter.updateNslideValues()
|
|
|
|
},
|
2021-09-16 06:49:57 +08:00
|
|
|
icon_mode: true,
|
2019-12-16 03:04:31 +08:00
|
|
|
options: {
|
2021-09-16 06:49:57 +08:00
|
|
|
rectangle: {name: true, icon: 'fas.fa-square'},
|
|
|
|
rectangle_h: {name: true, icon: 'far.fa-square'},
|
|
|
|
ellipse: {name: true, icon: 'circle'},
|
|
|
|
ellipse_h: {name: true, icon: 'radio_button_unchecked'},
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
2022-07-31 17:46:22 +08:00
|
|
|
new BarSelect('blend_mode', {
|
|
|
|
category: 'paint',
|
|
|
|
condition: () => (Toolbox && ((Toolbox.selected.brush?.blend_modes) || ['draw_shape_tool'].includes(Toolbox.selected.id))),
|
|
|
|
options: {
|
|
|
|
default: true,
|
|
|
|
set_opacity: true,
|
|
|
|
color: true,
|
|
|
|
behind: true,
|
|
|
|
multiply: true,
|
|
|
|
divide: true,
|
|
|
|
add: true,
|
|
|
|
subtract: true,
|
|
|
|
overlay: true,
|
|
|
|
hard_light: true,
|
|
|
|
difference: true,
|
|
|
|
}
|
|
|
|
})
|
2019-12-16 03:04:31 +08:00
|
|
|
new BarSelect('fill_mode', {
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
2019-12-16 03:04:31 +08:00
|
|
|
condition: () => Toolbox && Toolbox.selected.id === 'fill_tool',
|
|
|
|
options: {
|
|
|
|
face: true,
|
2021-09-22 19:47:46 +08:00
|
|
|
element: true,
|
2020-03-05 03:56:17 +08:00
|
|
|
color_connected: true,
|
2019-12-16 03:04:31 +08:00
|
|
|
color: true,
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
})
|
2022-08-06 05:52:53 +08:00
|
|
|
new BarSelect('copy_brush_mode', {
|
|
|
|
category: 'paint',
|
|
|
|
condition: () => Toolbox && ['copy_brush'].includes(Toolbox.selected.id),
|
|
|
|
options: {
|
|
|
|
copy: true,
|
|
|
|
pattern: true,
|
|
|
|
sample: true
|
|
|
|
}
|
|
|
|
})
|
2021-01-23 19:22:05 +08:00
|
|
|
new Toggle('mirror_painting', {
|
|
|
|
icon: 'flip',
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
|
|
|
condition: () => Modes.paint,
|
2021-01-23 19:22:05 +08:00
|
|
|
onChange: function (value) {
|
|
|
|
Painter.mirror_painting = value;
|
2021-01-25 04:10:43 +08:00
|
|
|
if (value) {
|
|
|
|
let size = 16*16;
|
2022-04-04 01:22:03 +08:00
|
|
|
var grid = new THREE.GridHelper(size, 16*2, new THREE.LineBasicMaterial({color: gizmo_colors.outline}));
|
2021-01-25 04:10:43 +08:00
|
|
|
grid.rotation.z = Math.PI/2;
|
|
|
|
grid.position.y = size/2;
|
2022-04-04 01:22:03 +08:00
|
|
|
grid.position.x = Format.centered_grid ? 0 : 8;
|
2021-01-25 04:10:43 +08:00
|
|
|
scene.add(grid);
|
|
|
|
setTimeout(() => {
|
|
|
|
scene.remove(grid);
|
|
|
|
grid.geometry.dispose();
|
|
|
|
}, 1000)
|
|
|
|
}
|
2020-03-05 03:56:17 +08:00
|
|
|
}
|
|
|
|
})
|
2021-08-07 19:51:00 +08:00
|
|
|
new Toggle('color_erase_mode', {
|
|
|
|
icon: 'remove_circle',
|
|
|
|
category: 'paint',
|
|
|
|
condition: {
|
|
|
|
tools: ['fill_tool', 'draw_shape_tool']
|
|
|
|
},
|
|
|
|
onChange: function (value) {
|
|
|
|
Painter.erase_mode = value;
|
|
|
|
}
|
|
|
|
})
|
2021-01-23 19:22:05 +08:00
|
|
|
new Toggle('lock_alpha', {
|
|
|
|
icon: 'fas.fa-chess-board',
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
|
|
|
condition: () => Modes.paint,
|
2021-01-23 19:22:05 +08:00
|
|
|
onChange: function () {
|
2020-03-05 03:56:17 +08:00
|
|
|
Painter.lock_alpha = !Painter.lock_alpha;
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-01-23 19:22:05 +08:00
|
|
|
new Toggle('painting_grid', {
|
|
|
|
icon: 'grid_on',
|
2019-12-16 03:04:31 +08:00
|
|
|
category: 'view',
|
|
|
|
condition: () => Modes.paint,
|
2021-02-20 18:58:53 +08:00
|
|
|
keybind: new Keybind({key: 'g'}),
|
2021-01-23 19:22:05 +08:00
|
|
|
linked_setting: 'painting_grid'
|
2019-12-16 03:04:31 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
new NumSlider('slider_brush_size', {
|
2022-07-31 02:22:50 +08:00
|
|
|
condition: () => (Toolbox && ((Toolbox.selected.brush?.size) || ['draw_shape_tool'].includes(Toolbox.selected.id))),
|
2020-09-28 00:05:20 +08:00
|
|
|
tool_setting: 'brush_size',
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
2019-12-16 03:04:31 +08:00
|
|
|
settings: {
|
2020-04-26 02:25:07 +08:00
|
|
|
min: 1, max: 50, interval: 1, default: 1,
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
})
|
|
|
|
new NumSlider('slider_brush_softness', {
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
2022-07-31 02:22:50 +08:00
|
|
|
condition: () => (Toolbox && (Toolbox.selected.brush?.softness)),
|
2020-09-28 00:05:20 +08:00
|
|
|
tool_setting: 'brush_softness',
|
2019-12-16 03:04:31 +08:00
|
|
|
settings: {
|
|
|
|
min: 0, max: 100, default: 0,
|
2021-08-22 23:12:31 +08:00
|
|
|
show_bar: true,
|
2019-12-16 03:04:31 +08:00
|
|
|
interval: function(event) {
|
|
|
|
if (event.shiftKey && event.ctrlOrCmd) {
|
|
|
|
return 0.25;
|
|
|
|
} else if (event.shiftKey) {
|
|
|
|
return 5;
|
|
|
|
} else if (event.ctrlOrCmd) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
new NumSlider('slider_brush_opacity', {
|
2020-03-05 03:56:17 +08:00
|
|
|
category: 'paint',
|
2022-07-31 02:22:50 +08:00
|
|
|
condition: () => (Toolbox && ((Toolbox.selected.brush?.opacity) || ['fill_tool', 'draw_shape_tool', 'gradient_tool'].includes(Toolbox.selected.id))),
|
2020-09-28 00:05:20 +08:00
|
|
|
tool_setting: 'brush_opacity',
|
2019-12-16 03:04:31 +08:00
|
|
|
settings: {
|
2021-10-14 22:39:02 +08:00
|
|
|
min: 0, max: 255, default: 255,
|
2021-08-22 23:12:31 +08:00
|
|
|
show_bar: true,
|
2019-12-16 03:04:31 +08:00
|
|
|
interval: function(event) {
|
|
|
|
if (event.shiftKey && event.ctrlOrCmd) {
|
2021-10-14 22:39:02 +08:00
|
|
|
return 1;
|
2019-12-16 03:04:31 +08:00
|
|
|
} else if (event.shiftKey) {
|
2021-10-14 22:39:02 +08:00
|
|
|
return 4;
|
2019-12-16 03:04:31 +08:00
|
|
|
} else if (event.ctrlOrCmd) {
|
|
|
|
return 1;
|
|
|
|
} else {
|
2021-10-14 22:39:02 +08:00
|
|
|
return 8;
|
2019-12-16 03:04:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|