2019-07-18 00:02:07 +08:00
|
|
|
const Painter = {
|
|
|
|
color: 0x0000ffff,
|
|
|
|
currentPixel: [-1, -1],
|
|
|
|
brushChanges: false,
|
|
|
|
current: {/*texture, image*/},
|
|
|
|
background_color: new ColorPicker({
|
|
|
|
id: 'background_color',
|
|
|
|
label: true,
|
|
|
|
private: true,
|
|
|
|
}),
|
2018-03-29 02:48:11 +08:00
|
|
|
edit(texture, cb, options) {
|
2018-12-16 23:18:20 +08:00
|
|
|
if (!options.no_undo) {
|
2018-10-18 01:50:25 +08:00
|
|
|
Undo.initEdit({textures: [texture], bitmap: true})
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
var instance = Painter.current[options.method === 'canvas' ? 'canvas' : 'image']
|
|
|
|
Painter.current[options.method === 'canvas' ? 'image' : 'canvas'] = undefined
|
|
|
|
|
2018-03-29 02:48:11 +08:00
|
|
|
if (options.use_cache &&
|
|
|
|
texture === Painter.current.texture &&
|
2018-12-27 21:03:04 +08:00
|
|
|
typeof instance === 'object'
|
2018-03-29 02:48:11 +08:00
|
|
|
) {
|
2018-12-27 21:03:04 +08:00
|
|
|
//IS CACHED
|
|
|
|
if (options.method !== 'canvas') {
|
|
|
|
instance = cb(instance) || instance
|
|
|
|
} else {
|
|
|
|
cb(instance)
|
|
|
|
}
|
2018-12-16 23:18:20 +08:00
|
|
|
if (options.no_update === true) {
|
|
|
|
return;
|
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
|
|
|
|
if (options.method !== 'canvas') {
|
|
|
|
Painter.current.image.getBase64(Jimp.MIME_PNG, function(a, dataUrl){
|
2018-12-03 02:37:06 +08:00
|
|
|
texture.updateSource(dataUrl)
|
2018-12-16 23:18:20 +08:00
|
|
|
if (!options.no_undo) {
|
2018-10-18 01:50:25 +08:00
|
|
|
Undo.finishEdit('edit_texture')
|
|
|
|
}
|
|
|
|
})
|
2018-12-27 21:03:04 +08:00
|
|
|
} else {
|
|
|
|
texture.updateSource(instance.toDataURL())
|
|
|
|
if (!options.no_undo) {
|
|
|
|
Undo.finishEdit('edit_texture')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (options.method !== 'canvas') {
|
|
|
|
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) {
|
|
|
|
Undo.finishEdit('edit_texture')
|
|
|
|
}
|
|
|
|
})
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Painter.current.texture = texture
|
2019-02-04 04:09:35 +08:00
|
|
|
var c = Painter.current.canvas = Painter.getCanvas(texture)
|
2018-12-27 21:03:04 +08:00
|
|
|
cb(c)
|
|
|
|
|
|
|
|
texture.updateSource(c.toDataURL())
|
|
|
|
if (!options.no_undo) {
|
|
|
|
Undo.finishEdit('edit_texture')
|
|
|
|
}
|
|
|
|
}
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
setAlphaMatrix(tex, x, y, val) {
|
|
|
|
if (!Painter.current.alpha_matrix) Painter.current.alpha_matrix = {}
|
|
|
|
var mx = Painter.current.alpha_matrix;
|
|
|
|
if (!mx[tex.uuid]) mx[tex.uuid] = {};
|
|
|
|
if (!mx[tex.uuid][x]) mx[tex.uuid][x] = {};
|
|
|
|
mx[tex.uuid][x][y] = val
|
|
|
|
},
|
|
|
|
getAlphaMatrix(tex, x, y) {
|
|
|
|
return Painter.current.alpha_matrix
|
|
|
|
&& Painter.current.alpha_matrix[tex.uuid]
|
|
|
|
&& Painter.current.alpha_matrix[tex.uuid][x]
|
|
|
|
&& Painter.current.alpha_matrix[tex.uuid][x][y];
|
|
|
|
},
|
|
|
|
|
2018-04-05 23:11:29 +08:00
|
|
|
startBrushCanvas(data, event) {
|
2019-03-10 05:06:35 +08:00
|
|
|
Painter.current.face = data.face;
|
|
|
|
Painter.current.cube = data.cube;
|
2019-02-04 04:09:35 +08:00
|
|
|
var texture = data.cube.faces[data.face].getTexture()
|
2019-09-06 06:16:54 +08:00
|
|
|
if (!texture || (texture.error && texture.error !== 2)) {
|
2018-10-18 01:50:25 +08:00
|
|
|
Blockbench.showQuickMessage('message.untextured')
|
2019-09-06 06:16:54 +08:00
|
|
|
return;
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-09-06 06:16:54 +08:00
|
|
|
var x = Math.floor( data.intersects[0].uv.x * texture.img.naturalWidth )
|
|
|
|
var y = Math.floor( (1-data.intersects[0].uv.y) * texture.img.naturalHeight )
|
|
|
|
Painter.startBrush(texture, x, y, data.cube.faces[data.face].uv, event)
|
|
|
|
|
|
|
|
if (Toolbox.selected.id !== 'color_picker') {
|
2018-04-05 23:11:29 +08:00
|
|
|
document.addEventListener('mousemove', Painter.moveBrushCanvas, false );
|
|
|
|
document.addEventListener('mouseup', Painter.stopBrushCanvas, false );
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-04-05 23:11:29 +08:00
|
|
|
moveBrushCanvas(force) {
|
2018-03-29 02:48:11 +08:00
|
|
|
var data = Canvas.raycast()
|
|
|
|
if (data) {
|
2019-02-04 04:09:35 +08:00
|
|
|
var texture = data.cube.faces[data.face].getTexture()
|
2018-10-18 01:50:25 +08:00
|
|
|
if (texture) {
|
2018-10-24 03:49:04 +08:00
|
|
|
var x, y, new_face;
|
2019-07-18 00:02:07 +08:00
|
|
|
x = Math.floor( data.intersects[0].uv.x * texture.img.naturalWidth )
|
|
|
|
y = Math.floor( (1-data.intersects[0].uv.y) * texture.img.naturalHeight )
|
2018-10-24 03:49:04 +08:00
|
|
|
if (x === Painter.current.x && y === Painter.current.y) {
|
2018-12-27 21:03:04 +08:00
|
|
|
return
|
2018-10-24 03:49:04 +08:00
|
|
|
}
|
2018-11-12 04:19:08 +08:00
|
|
|
if (Painter.current.face !== data.face || Painter.current.cube !== data.cube) {
|
2018-10-24 03:49:04 +08:00
|
|
|
Painter.current.x = x
|
|
|
|
Painter.current.y = y
|
|
|
|
Painter.current.face = data.face
|
2018-11-12 04:19:08 +08:00
|
|
|
Painter.current.cube = data.cube
|
2018-10-24 03:49:04 +08:00
|
|
|
new_face = true
|
2018-12-27 21:03:04 +08:00
|
|
|
if (texture !== Painter.current.texture) {
|
|
|
|
Undo.current_save.addTexture(texture)
|
|
|
|
}
|
2018-10-24 03:49:04 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.drawBrushLine(texture, x, y, new_face, data.cube.faces[data.face].uv)
|
2018-04-05 23:11:29 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
|
|
|
drawBrushLine(texture, end_x, end_y, new_face, uv) {
|
|
|
|
var x = end_x;
|
|
|
|
var y = end_y;
|
|
|
|
var diff = {
|
2019-08-18 00:26:14 +08:00
|
|
|
x: x - (Painter.current.x == undefined ? x : Painter.current.x),
|
|
|
|
y: y - (Painter.current.y == undefined ? y : Painter.current.y),
|
2019-07-18 00:02:07 +08:00
|
|
|
}
|
|
|
|
var length = Math.sqrt(diff.x*diff.x + diff.y*diff.y)
|
|
|
|
if (new_face && !length) {
|
|
|
|
length = 1
|
|
|
|
}
|
|
|
|
var i = 0;
|
|
|
|
while (i < length) {
|
|
|
|
x = Math.round(end_x - diff.x / length * i)
|
|
|
|
y = Math.round(end_y - diff.y / length * i)
|
|
|
|
Painter.useBrush(texture, x, y, uv, i < length-1)
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
Painter.current.x = end_x;
|
|
|
|
Painter.current.y = end_y;
|
|
|
|
},
|
2018-04-05 23:11:29 +08:00
|
|
|
stopBrushCanvas() {
|
|
|
|
document.removeEventListener( 'mousemove', Painter.moveBrushCanvas, false );
|
|
|
|
document.removeEventListener( 'mouseup', Painter.stopBrushCanvas, false );
|
|
|
|
Painter.stopBrush()
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-04-05 23:11:29 +08:00
|
|
|
startBrush(texture, x, y, uvTag, event) {
|
2019-07-18 00:02:07 +08:00
|
|
|
if (Toolbox.selected.id === 'color_picker') {
|
2018-04-05 23:11:29 +08:00
|
|
|
Painter.colorPicker(texture, x, y)
|
2019-07-18 00:02:07 +08:00
|
|
|
} else {
|
|
|
|
Undo.initEdit({textures: [texture], bitmap: true});
|
|
|
|
Painter.brushChanges = false;
|
|
|
|
if (event.shiftKey) {
|
|
|
|
Painter.drawBrushLine(texture, x, y);
|
|
|
|
} else {
|
|
|
|
Painter.current.x = Painter.current.y = 0
|
|
|
|
Painter.useBrush(texture, x, y, uvTag)
|
|
|
|
Painter.current.x = x;
|
|
|
|
Painter.current.y = y;
|
|
|
|
}
|
2018-04-05 23:11:29 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2019-02-04 04:09:35 +08:00
|
|
|
getCanvas(texture) {
|
|
|
|
var c = document.createElement('canvas')
|
|
|
|
var ctx = c.getContext('2d');
|
2019-08-01 06:01:47 +08:00
|
|
|
c.width = texture.width;
|
|
|
|
c.height = texture.height;
|
2019-02-04 04:09:35 +08:00
|
|
|
ctx.drawImage(texture.img, 0, 0)
|
|
|
|
return c;
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-04-05 23:11:29 +08:00
|
|
|
colorPicker(texture, x, y) {
|
2019-02-04 04:09:35 +08:00
|
|
|
var ctx = Painter.getCanvas(texture).getContext('2d')
|
|
|
|
Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => {
|
|
|
|
var t = tinycolor({
|
|
|
|
r: px[0],
|
|
|
|
g: px[1],
|
|
|
|
b: px[2],
|
|
|
|
a: px[3]/256
|
|
|
|
})
|
2019-04-08 00:53:33 +08:00
|
|
|
ColorPanel.set(t)
|
2019-02-04 04:09:35 +08:00
|
|
|
})
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-12-16 23:18:20 +08:00
|
|
|
useBrush(texture, x, y, uvTag, no_update) {
|
2018-04-05 23:11:29 +08:00
|
|
|
if ((Painter.currentPixel[0] !== x || Painter.currentPixel[1] !== y)) {
|
|
|
|
Painter.currentPixel = [x, y]
|
|
|
|
Painter.brushChanges = true
|
2018-03-29 02:48:11 +08:00
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
texture.edit(function(canvas) {
|
|
|
|
var ctx = canvas.getContext('2d')
|
|
|
|
ctx.save()
|
|
|
|
|
2019-04-08 00:53:33 +08:00
|
|
|
var color = ColorPanel.get().toRgb();//.toRgbString()
|
2018-10-18 01:50:25 +08:00
|
|
|
var size = BarItems.slider_brush_size.get();
|
2019-07-18 00:02:07 +08:00
|
|
|
let softness = BarItems.slider_brush_softness.get()/100;
|
|
|
|
let b_opacity = BarItems.slider_brush_opacity.get()/100;
|
|
|
|
let m_opacity = BarItems.slider_brush_min_opacity.value/100;
|
|
|
|
let tool = Toolbox.selected.id;
|
|
|
|
let noise = BarItems.brush_mode.get() == 'noise';
|
2018-03-29 02:48:11 +08:00
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
ctx.beginPath();
|
2018-04-05 23:11:29 +08:00
|
|
|
if (uvTag) {
|
2018-12-27 21:03:04 +08:00
|
|
|
var rect = Painter.editing_area = [
|
2018-04-05 23:11:29 +08:00
|
|
|
uvTag[0] / 16 * texture.img.naturalWidth,
|
|
|
|
uvTag[1] / 16 * texture.img.naturalHeight,
|
|
|
|
uvTag[2] / 16 * texture.img.naturalWidth,
|
|
|
|
uvTag[3] / 16 * texture.img.naturalHeight
|
|
|
|
]
|
|
|
|
} else {
|
2019-02-04 04:09:35 +08:00
|
|
|
var rect = Painter.editing_area = [0, 0, texture.img.naturalWidth, texture.img.naturalHeight]
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
for (var t = 0; t < 2; t++) {
|
|
|
|
if (rect[t] > rect[t+2]) {
|
|
|
|
[rect[t], rect[t+2]] = [rect[t+2], rect[t]]
|
|
|
|
}
|
2019-04-08 00:53:33 +08:00
|
|
|
rect[t] = Math.round(rect[t])
|
|
|
|
rect[t+2] = Math.round(rect[t+2])
|
2018-04-05 23:11:29 +08:00
|
|
|
}
|
2019-02-04 04:09:35 +08:00
|
|
|
var [w, h] = [rect[2] - rect[0], rect[3] - rect[1]]
|
|
|
|
ctx.rect(rect[0], rect[1], w, h)
|
2018-04-05 23:11:29 +08:00
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
if (tool === 'fill_tool') {
|
|
|
|
|
2019-04-08 00:53:33 +08:00
|
|
|
ctx.fillStyle = ColorPanel.get().toRgbString()
|
2018-12-27 21:03:04 +08:00
|
|
|
|
2019-02-04 04:09:35 +08:00
|
|
|
var fill_mode = BarItems.fill_mode.get()
|
2019-03-10 05:06:35 +08:00
|
|
|
var cube = Painter.current.cube;
|
2019-02-04 04:09:35 +08:00
|
|
|
if (cube && fill_mode === 'cube') {
|
|
|
|
for (var face in cube.faces) {
|
|
|
|
var tag = cube.faces[face]
|
|
|
|
if (tag.texture === Painter.current.texture.uuid) {
|
|
|
|
var rect = getRectangle(
|
|
|
|
Math.floor(tag.uv[0] / 16 * texture.img.naturalWidth),
|
|
|
|
Math.floor(tag.uv[1] / 16 * texture.img.naturalHeight),
|
|
|
|
Math.ceil(tag.uv[2] / 16 * texture.img.naturalWidth),
|
|
|
|
Math.ceil(tag.uv[3] / 16 * texture.img.naturalHeight)
|
|
|
|
)
|
|
|
|
ctx.rect(rect.ax, rect.ay, rect.x, rect.y)
|
|
|
|
ctx.fill()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (fill_mode === 'face') {
|
|
|
|
ctx.fill()
|
2018-12-27 21:03:04 +08:00
|
|
|
} else {
|
2019-02-04 04:09:35 +08:00
|
|
|
|
|
|
|
var pxcol = [];
|
|
|
|
var map = {}
|
|
|
|
Painter.scanCanvas(ctx, x, y, 1, 1, (x, y, px) => {
|
|
|
|
px.forEach((val, i) => {
|
|
|
|
pxcol[i] = val
|
|
|
|
})
|
|
|
|
})
|
|
|
|
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
|
|
|
|
}
|
|
|
|
})
|
|
|
|
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)
|
|
|
|
Painter.scanCanvas(ctx, rect[0], rect[1], w, h, (x, y, px) => {
|
|
|
|
if (map[x] && map[x][y] === false) {
|
|
|
|
var pxcolor = {
|
|
|
|
r: px[0],
|
|
|
|
g: px[1],
|
|
|
|
b: px[2],
|
|
|
|
a: px[3]/255
|
|
|
|
}
|
|
|
|
var result_color = Painter.combineColors(pxcolor, color, 1);
|
|
|
|
px[0] = result_color.r
|
|
|
|
px[1] = result_color.g
|
|
|
|
px[2] = result_color.b
|
|
|
|
px[3] = result_color.a*255
|
|
|
|
}
|
|
|
|
})
|
2018-12-27 21:03:04 +08:00
|
|
|
}
|
2019-02-04 04:09:35 +08:00
|
|
|
} else {
|
|
|
|
ctx.clip()
|
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
if (tool === 'brush_tool') {
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity, px, py) {
|
|
|
|
var a = noise
|
|
|
|
? Math.randomab(m_opacity, b_opacity) * opacity
|
|
|
|
: b_opacity * opacity;
|
|
|
|
var before = Painter.getAlphaMatrix(texture, px, py)
|
|
|
|
Painter.setAlphaMatrix(texture, px, py, a);
|
|
|
|
if (before) a = Math.clamp(a-before, 0, 1);
|
|
|
|
var result_color = Painter.combineColors(pxcolor, color, a);
|
2018-12-27 21:03:04 +08:00
|
|
|
return result_color;
|
|
|
|
})
|
|
|
|
} else if (tool === 'eraser') {
|
|
|
|
Painter.editCircle(ctx, x, y, size, softness, function(pxcolor, opacity) {
|
2019-07-18 00:02:07 +08:00
|
|
|
var a = noise
|
|
|
|
? Math.randomab(m_opacity, b_opacity)
|
|
|
|
: b_opacity;
|
|
|
|
return {r: pxcolor.r, g: pxcolor.g, b: pxcolor.b, a: pxcolor.a*(1-a)};
|
2018-12-27 21:03:04 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
ctx.restore();
|
2018-04-05 23:11:29 +08:00
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
Painter.editing_area = undefined;
|
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
}, {method: 'canvas', no_undo: true, use_cache: true, no_update});
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
stopBrush() {
|
|
|
|
if (Painter.brushChanges) {
|
2018-12-27 21:03:04 +08:00
|
|
|
Undo.finishEdit('paint');
|
|
|
|
Painter.brushChanges = false;
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
delete Painter.current.alpha_matrix;
|
2018-12-27 21:03:04 +08:00
|
|
|
Painter.currentPixel = [-1, -1];
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
combineColors(base, added, opacity) {
|
|
|
|
if (typeof base === 'number') base = Jimp.intToRGBA(base)
|
|
|
|
if (typeof added === 'number') added = Jimp.intToRGBA(added)
|
|
|
|
|
|
|
|
var original_a = added.a
|
|
|
|
added.a = (added.a)*opacity
|
|
|
|
|
|
|
|
var mix = {};
|
|
|
|
mix.a = limitNumber(1 - (1 - added.a) * (1 - base.a), 0, 1); // alpha
|
|
|
|
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;
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
|
|
|
updateNslideValues() {
|
|
|
|
BarItems.slider_brush_size.update()
|
|
|
|
BarItems.slider_brush_softness.update()
|
|
|
|
BarItems.slider_brush_opacity.update()
|
|
|
|
BarItems.slider_brush_min_opacity.update()
|
|
|
|
},
|
2018-12-27 21:03:04 +08:00
|
|
|
scanCanvas(ctx, x, y, w, h, cb) {
|
|
|
|
var arr = ctx.getImageData(x, y, w, h)
|
|
|
|
for (var i = 0; i < arr.data.length; i += 4) {
|
|
|
|
var pixel = arr.data.slice(i, i+4)
|
|
|
|
|
|
|
|
var px = (i/4) % w
|
|
|
|
var py = Math.floor((i/4) / w)
|
|
|
|
pixel = cb(x+px, y+py, pixel)||pixel
|
|
|
|
|
|
|
|
pixel.forEach((p, pi) => {
|
|
|
|
arr.data[i+pi] = p
|
|
|
|
})
|
|
|
|
}
|
|
|
|
ctx.putImageData(arr, x, y)
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +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) {
|
2018-10-18 01:50:25 +08:00
|
|
|
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
|
2018-03-29 02:48:11 +08:00
|
|
|
});
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
editFace(image, x, y, editPx) {
|
|
|
|
var x = Math.floor(Painter.editing_area[0]-0.5)
|
|
|
|
var y = Math.floor(Painter.editing_area[1]-0.5)
|
|
|
|
var width = Math.floor(Painter.editing_area[2]+1.5) - Math.floor(Painter.editing_area[0])
|
|
|
|
var height = Math.floor(Painter.editing_area[3]+1.5) - Math.floor(Painter.editing_area[1])
|
|
|
|
image.scan(x, y, width, height, function (px, py, idx) {
|
|
|
|
|
|
|
|
if (px >= this.bitmap.width ||
|
|
|
|
px < 0 ||
|
|
|
|
py >= this.bitmap.height ||
|
|
|
|
py < 0
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
typeof Painter.editing_area === 'object' &&
|
|
|
|
(
|
|
|
|
px+0.2 < Painter.editing_area[0] ||
|
|
|
|
py+0.2 < Painter.editing_area[1] ||
|
|
|
|
px+0.2 >= Painter.editing_area[2] ||
|
|
|
|
py+0.2 >= Painter.editing_area[3]
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var result_color = editPx({
|
|
|
|
r:this.bitmap.data[idx+0],
|
|
|
|
g:this.bitmap.data[idx+1],
|
|
|
|
b:this.bitmap.data[idx+2],
|
|
|
|
a:this.bitmap.data[idx+3]/255
|
|
|
|
})
|
2018-10-18 01:50:25 +08:00
|
|
|
this.bitmap.data[idx+0] = result_color.r
|
|
|
|
this.bitmap.data[idx+1] = result_color.g
|
|
|
|
this.bitmap.data[idx+2] = result_color.b
|
|
|
|
this.bitmap.data[idx+3] = result_color.a*255
|
2018-03-29 02:48:11 +08:00
|
|
|
|
|
|
|
});
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-12-27 21:03:04 +08:00
|
|
|
editCircle(ctx, x, y, r, s, editPx) {
|
2018-03-29 02:48:11 +08:00
|
|
|
r = Math.round(r)
|
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
Painter.scanCanvas(ctx, x-r-1, y-r-1, 2*r+3, 2*r+3, function (px, py, pixel) {
|
2018-12-03 02:37:06 +08:00
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
|
|
|
|
if (px >= ctx.canvas.width ||
|
2018-03-29 02:48:11 +08:00
|
|
|
px < 0 ||
|
2018-12-27 21:03:04 +08:00
|
|
|
py >= ctx.canvas.height ||
|
2018-03-29 02:48:11 +08:00
|
|
|
py < 0
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
settings.paint_side_restrict.value &&
|
|
|
|
Painter.editing_area &&
|
|
|
|
typeof Painter.editing_area === 'object' &&
|
|
|
|
(
|
2018-12-03 02:37:06 +08:00
|
|
|
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] ||
|
2018-12-27 21:03:04 +08:00
|
|
|
py+0.02 >= Painter.editing_area[3]
|
2018-03-29 02:48:11 +08:00
|
|
|
)
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
px -= x;
|
|
|
|
py -= y;
|
|
|
|
|
|
|
|
var distance = Math.sqrt(px*px + py*py)
|
|
|
|
if (s*r != 0) {
|
|
|
|
var pos_on_gradient = (distance-(1-s)*r) / (s*r)
|
|
|
|
} else {
|
|
|
|
var pos_on_gradient = Math.floor(distance/r)
|
|
|
|
}
|
|
|
|
|
|
|
|
var opacity = limitNumber(1-pos_on_gradient, 0, 1)
|
|
|
|
|
|
|
|
if (opacity > 0) {
|
|
|
|
var result_color = editPx({
|
2018-12-27 21:03:04 +08:00
|
|
|
r: pixel[0],
|
|
|
|
g: pixel[1],
|
|
|
|
b: pixel[2],
|
|
|
|
a: pixel[3]/255
|
2019-07-18 00:02:07 +08:00
|
|
|
}, opacity, px+x, py+y)
|
2018-12-27 21:03:04 +08:00
|
|
|
pixel[0] = result_color.r
|
|
|
|
pixel[1] = result_color.g
|
|
|
|
pixel[2] = result_color.b
|
|
|
|
pixel[3] = result_color.a*255
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
|
|
|
});
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-10-18 01:50:25 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
})
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
addBitmapDialog() {
|
|
|
|
var dialog = new Dialog({
|
|
|
|
id: 'add_bitmap',
|
2018-10-18 01:50:25 +08:00
|
|
|
title: tl('dialog.create_texture.title'),
|
2019-04-08 00:53:33 +08:00
|
|
|
form: {
|
2019-08-18 00:26:14 +08:00
|
|
|
name: {label: 'generic.name', value: 'texture'},
|
|
|
|
folder: {label: 'dialog.create_texture.folder'},
|
|
|
|
template: {label: 'dialog.create_texture.template', type: 'checkbox', condition: Cube.all.length},
|
|
|
|
color: {type: 'color', colorpicker: Painter.background_color},
|
|
|
|
resolution: {label: 'dialog.create_texture.resolution', type: 'number', value: 16, min: 16, max: 2048},
|
2019-04-08 00:53:33 +08:00
|
|
|
},
|
|
|
|
onConfirm: function(results) {
|
2019-07-18 00:02:07 +08:00
|
|
|
results.particle = 'auto';
|
2018-03-29 02:48:11 +08:00
|
|
|
dialog.hide()
|
2019-08-18 00:26:14 +08:00
|
|
|
if (results.template) {
|
|
|
|
var dialog2 = new Dialog({
|
|
|
|
id: 'texture_template',
|
|
|
|
title: tl('dialog.create_texture.template'),
|
|
|
|
form: {
|
|
|
|
compress: {label: 'dialog.create_texture.compress', type: 'checkbox', value: true},
|
|
|
|
power: {label: 'dialog.create_texture.power', type: 'checkbox', value: true},
|
|
|
|
double_use: {label: 'dialog.create_texture.double_use', type: 'checkbox', value: true, condition: Project.box_uv},
|
|
|
|
color: {type: 'color', colorpicker: Painter.background_color},
|
|
|
|
resolution: {label: 'dialog.create_texture.resolution', type: 'select', value: 16, options: {
|
|
|
|
16: '16',
|
|
|
|
32: '32',
|
|
|
|
64: '64',
|
|
|
|
128: '128',
|
|
|
|
256: '256',
|
|
|
|
512: '512',
|
|
|
|
}},
|
|
|
|
},
|
|
|
|
onConfirm: function(results2) {
|
|
|
|
$.extend(results, results2)
|
|
|
|
Painter.addBitmap(results)
|
|
|
|
dialog2.hide()
|
|
|
|
}
|
|
|
|
}).show()
|
|
|
|
if (Painter.background_color.get().toHex8() === 'ffffffff') {
|
|
|
|
Painter.background_color.set('#00000000')
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Painter.addBitmap(results)
|
|
|
|
}
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-04-08 00:53:33 +08:00
|
|
|
}).show()
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
addBitmap(options, after) {
|
|
|
|
if (typeof options !== 'object') {
|
|
|
|
options = {}
|
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
if (isNaN(options.resolution) || !options.resolution) {
|
|
|
|
options.resolution = 16
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
|
|
|
if (options.color === undefined) {
|
2018-10-18 09:32:42 +08:00
|
|
|
options.color = new tinycolor().toRgb()
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
if (Format.single_texture) {
|
2018-12-27 21:03:04 +08:00
|
|
|
options.texture = textures[0]
|
|
|
|
}
|
2018-03-29 02:48:11 +08:00
|
|
|
var texture = new Texture({
|
|
|
|
mode: 'bitmap',
|
2018-04-05 23:11:29 +08:00
|
|
|
keep_size: true,
|
2019-07-18 00:02:07 +08:00
|
|
|
res: options.resolution,
|
2018-03-29 02:48:11 +08:00
|
|
|
name: options.name ? options.name : 'texture',
|
2019-07-26 19:33:29 +08:00
|
|
|
folder: options.folder ? options.folder : 'block'
|
2018-12-29 19:26:02 +08:00
|
|
|
})
|
2018-03-29 02:48:11 +08:00
|
|
|
function makeTexture(dataUrl) {
|
2019-04-13 00:44:18 +08:00
|
|
|
texture.fromDataURL(dataUrl).add(false)
|
2018-03-29 02:48:11 +08:00
|
|
|
switch (options.particle) {
|
|
|
|
case 'auto':
|
|
|
|
texture.fillParticle();
|
|
|
|
break;
|
|
|
|
case true:
|
|
|
|
texture.enableParticle();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (typeof after === 'function') {
|
|
|
|
after(texture)
|
|
|
|
}
|
2019-08-18 00:26:14 +08:00
|
|
|
if (!options.template) {
|
2019-02-04 04:09:35 +08:00
|
|
|
Undo.finishEdit('create blank texture', {textures: [texture], bitmap: true})
|
|
|
|
}
|
2019-04-13 00:44:18 +08:00
|
|
|
return texture;
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-08-18 00:26:14 +08:00
|
|
|
if (options.template === true) {
|
2019-03-10 05:06:35 +08:00
|
|
|
Undo.initEdit({
|
2019-07-18 00:02:07 +08:00
|
|
|
textures: Format.single_texture ? textures : [],
|
2019-07-26 19:33:29 +08:00
|
|
|
elements: Format.single_texture ? Cube.all : Cube.selected,
|
2019-03-10 05:06:35 +08:00
|
|
|
uv_only: true,
|
2019-07-26 19:33:29 +08:00
|
|
|
uv_mode: true
|
2019-03-10 05:06:35 +08:00
|
|
|
})
|
2019-02-04 04:09:35 +08:00
|
|
|
Painter.generateTemplate(options, makeTexture)
|
2018-03-29 02:48:11 +08:00
|
|
|
} else {
|
2018-12-27 21:03:04 +08:00
|
|
|
Undo.initEdit({textures: []})
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.generateBlank(options.resolution, options.resolution, options.color, makeTexture)
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2018-03-29 02:48:11 +08:00
|
|
|
generateBlank(height, width, color, cb) {
|
2018-12-27 21:03:04 +08:00
|
|
|
var canvas = document.createElement('canvas')
|
|
|
|
canvas.width = width;
|
|
|
|
canvas.height = height;
|
|
|
|
var ctx = canvas.getContext('2d')
|
|
|
|
|
|
|
|
ctx.fillStyle = new tinycolor(color).toRgbString()
|
|
|
|
ctx.fillRect(0, 0, width, height)
|
|
|
|
|
|
|
|
cb(canvas.toDataURL())
|
2019-07-18 00:02:07 +08:00
|
|
|
},
|
2019-02-04 04:09:35 +08:00
|
|
|
generateTemplate(options, cb) {
|
2019-07-18 00:02:07 +08:00
|
|
|
var res = options.resolution;
|
|
|
|
var background_color = options.color;
|
|
|
|
var texture = options.texture;
|
|
|
|
var min_size = Project.box_uv ? 0 : 1;
|
2018-03-29 02:48:11 +08:00
|
|
|
function cubeTempl(obj) {
|
2019-07-18 00:02:07 +08:00
|
|
|
this.x = obj.size(0, true) || min_size;
|
|
|
|
this.y = obj.size(1, true) || min_size;
|
|
|
|
this.z = obj.size(2, true) || min_size;
|
|
|
|
this.obj = obj;
|
|
|
|
this.template_size = (obj.size(2, true) + obj.size(1, true))+ (obj.size(2, true) + obj.size(0, true))*2;
|
2018-03-29 02:48:11 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
this.height = this.z + this.y;
|
|
|
|
this.width = 2* (this.x + this.z);
|
2018-03-29 02:48:11 +08:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2018-12-03 02:37:06 +08:00
|
|
|
var res_multiple = res / 16
|
2019-07-18 00:02:07 +08:00
|
|
|
var templates = [];
|
|
|
|
var doubles = {};
|
2019-02-04 04:09:35 +08:00
|
|
|
var extend_x = 0;
|
|
|
|
var extend_y = 0;
|
2018-10-18 01:50:25 +08:00
|
|
|
var avg_size = 0;
|
2019-07-26 19:33:29 +08:00
|
|
|
var cubes = Format.single_texture ? Cube.all.slice() : Cube.selected.slice()
|
2018-10-18 01:50:25 +08:00
|
|
|
|
|
|
|
var i = cubes.length-1
|
|
|
|
while (i >= 0) {
|
|
|
|
let obj = cubes[i]
|
2019-02-04 04:09:35 +08:00
|
|
|
if (obj.visibility === true) {
|
2019-07-18 00:02:07 +08:00
|
|
|
var template = new cubeTempl(obj)
|
2019-07-19 23:31:22 +08:00
|
|
|
if (options.double_use && Project.box_uv && textures.length) {
|
|
|
|
var double_key = [...obj.uv_offset, ...obj.size(undefined, true), ].join('_')
|
2019-07-18 00:02:07 +08:00
|
|
|
if (doubles[double_key]) {
|
|
|
|
doubles[double_key].push(template)
|
|
|
|
doubles[double_key][0].duplicates = doubles[double_key];
|
|
|
|
i--;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
doubles[double_key] = [template]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
templates.push(template)
|
2019-02-04 04:09:35 +08:00
|
|
|
avg_size += templates[templates.length-1].template_size
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
|
|
|
i--;
|
|
|
|
}
|
2019-02-04 04:09:35 +08:00
|
|
|
templates.sort(function(a,b) {
|
|
|
|
return b.template_size - a.template_size;
|
2018-03-29 02:48:11 +08:00
|
|
|
})
|
|
|
|
//Cancel if no cubes
|
2019-02-04 04:09:35 +08:00
|
|
|
if (templates.length == 0) {
|
2018-03-29 02:48:11 +08:00
|
|
|
Blockbench.showMessage('No valid cubes', 'center')
|
|
|
|
return;
|
|
|
|
}
|
2019-04-08 00:53:33 +08:00
|
|
|
|
|
|
|
function getNextPower(num, min) {
|
|
|
|
var i = min ? min : 2
|
|
|
|
while (i < num && i < 4000) {
|
|
|
|
i *= 2
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2019-02-04 04:09:35 +08:00
|
|
|
if (options.compress) {
|
|
|
|
|
|
|
|
var fill_map = {}
|
|
|
|
function occupy(x, y) {
|
|
|
|
if (!fill_map[x]) fill_map[x] = {}
|
|
|
|
fill_map[x][y] = true
|
2018-10-18 01:50:25 +08:00
|
|
|
}
|
2019-02-04 04:09:35 +08:00
|
|
|
function check(x, y) {
|
|
|
|
return fill_map[x] && fill_map[x][y]
|
|
|
|
}
|
|
|
|
function forTemplatePixel(tpl, sx, sy, cb) {
|
|
|
|
for (var x = 0; x < tpl.width; x++) {
|
|
|
|
for (var y = 0; y < tpl.height; y++) {
|
|
|
|
if (y >= tpl.z || (x >= tpl.z && x < (tpl.z + 2*tpl.x))) {
|
|
|
|
if (cb(sx+x, sy+y)) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function place(tpl, x, y) {
|
|
|
|
var works = true;
|
|
|
|
forTemplatePixel(tpl, x, y, (tx, ty) => {
|
|
|
|
if (check(tx, ty)) {
|
|
|
|
works = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
})
|
|
|
|
if (works) {
|
|
|
|
forTemplatePixel(tpl, x, y, occupy)
|
|
|
|
tpl.posx = x;
|
|
|
|
tpl.posy = y;
|
|
|
|
extend_x = Math.max(extend_x, x + tpl.width);
|
|
|
|
extend_y = Math.max(extend_y, y + tpl.height);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
templates.forEach(tpl => {
|
|
|
|
var vert = extend_x > extend_y;
|
|
|
|
//Scan for empty spot
|
|
|
|
for (var line = 0; line < 2e3; line++) {
|
2019-07-18 00:02:07 +08:00
|
|
|
for (var x = 0; x <= line; x++) {
|
2019-02-04 04:09:35 +08:00
|
|
|
if (place(tpl, x, line)) return;
|
|
|
|
}
|
|
|
|
for (var y = 0; y < line; y++) {
|
|
|
|
if (place(tpl, line, y)) return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
//OLD -------------------------------------------
|
|
|
|
var lines = [[]]
|
2019-07-26 19:33:29 +08:00
|
|
|
var line_length = Math.sqrt(cubes.length/2)
|
2019-02-04 04:09:35 +08:00
|
|
|
avg_size /= templates.length
|
|
|
|
var o = 0
|
|
|
|
var i = 0
|
|
|
|
var ox = 0
|
|
|
|
templates.forEach(function(tpl) {
|
|
|
|
if (ox >= line_length) {
|
|
|
|
o = ox = 0
|
|
|
|
i++
|
|
|
|
lines[i] = []
|
|
|
|
}
|
|
|
|
lines[i][o] = tpl
|
|
|
|
o++;
|
|
|
|
ox += tpl.template_size/avg_size
|
|
|
|
})
|
|
|
|
|
|
|
|
lines.forEach(function(temps) {
|
|
|
|
|
|
|
|
var x_pos = 0
|
|
|
|
var y_pos = 0 //Y Position of current area relative to this bone
|
|
|
|
var filled_x_pos = 0;
|
|
|
|
var max_height = 0
|
|
|
|
//Find the maximum height of the line
|
|
|
|
temps.forEach(function(t) {
|
|
|
|
max_height = Math.max(max_height, t.height)
|
|
|
|
})
|
|
|
|
//Place
|
|
|
|
temps.forEach(function(t) {
|
|
|
|
if (y_pos > 0 && (y_pos + t.height) <= max_height) {
|
|
|
|
//same column
|
|
|
|
t.posx = x_pos
|
|
|
|
t.posy = y_pos + extend_y
|
|
|
|
filled_x_pos = Math.max(filled_x_pos, x_pos+t.width)
|
|
|
|
y_pos += t.height
|
|
|
|
} else {
|
|
|
|
//new column
|
|
|
|
x_pos = filled_x_pos
|
|
|
|
y_pos = t.height
|
|
|
|
t.posx = x_pos
|
|
|
|
t.posy = extend_y
|
|
|
|
filled_x_pos = Math.max(filled_x_pos, x_pos+t.width)
|
|
|
|
}
|
|
|
|
//size of widest bone
|
|
|
|
extend_x = Math.max(extend_x, filled_x_pos)
|
|
|
|
})
|
|
|
|
extend_y += max_height
|
|
|
|
})
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2019-04-08 00:53:33 +08:00
|
|
|
|
2019-02-04 04:09:35 +08:00
|
|
|
var max_size = Math.max(extend_x, extend_y)
|
2019-04-08 00:53:33 +08:00
|
|
|
if (options.power) {
|
|
|
|
max_size = getNextPower(max_size, 16);
|
|
|
|
} else {
|
|
|
|
max_size = Math.ceil(max_size/16)*16;
|
|
|
|
}
|
2018-10-18 09:32:42 +08:00
|
|
|
|
|
|
|
if (background_color.getAlpha() != 0) {
|
2019-04-08 00:53:33 +08:00
|
|
|
background_color = background_color.toRgbString()
|
2018-10-18 09:32:42 +08:00
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
var canvas = document.createElement('canvas')
|
|
|
|
canvas.width = canvas.height = max_size*res_multiple;
|
|
|
|
var ctx = canvas.getContext('2d')
|
|
|
|
ctx.imageSmoothingEnabled = false;
|
|
|
|
|
2018-03-29 02:48:11 +08:00
|
|
|
|
2019-04-08 00:53:33 +08:00
|
|
|
function drawTemplateRectangle(border_color, color, face, coords) {
|
|
|
|
if (typeof background_color === 'string') {
|
2018-10-18 01:50:25 +08:00
|
|
|
border_color = background_color
|
|
|
|
color = undefined
|
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
ctx.fillStyle = border_color
|
|
|
|
ctx.fillRect(
|
|
|
|
coords.x*res_multiple,
|
|
|
|
coords.y*res_multiple,
|
|
|
|
coords.w*res_multiple,
|
|
|
|
coords.h*res_multiple
|
|
|
|
)
|
2019-07-18 00:02:07 +08:00
|
|
|
if (coords.w*res_multiple > 2 && coords.h*res_multiple > 2 && color) {
|
|
|
|
ctx.fillStyle = color
|
|
|
|
ctx.fillRect(
|
|
|
|
coords.x * res_multiple + 1,
|
|
|
|
coords.y * res_multiple + 1,
|
|
|
|
coords.w * res_multiple - 2,
|
|
|
|
coords.h * res_multiple - 2
|
|
|
|
)
|
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
function drawTexture(face, coords) {
|
2019-07-18 00:02:07 +08:00
|
|
|
if (!Format.single_texture) {
|
2018-12-29 19:26:02 +08:00
|
|
|
if (face.texture === undefined || face.texture === null) return false;
|
2019-02-04 04:09:35 +08:00
|
|
|
texture = face.getTexture()
|
2018-12-27 21:03:04 +08:00
|
|
|
}
|
2018-12-29 19:26:02 +08:00
|
|
|
if (!texture || !texture.img) return false;
|
2019-07-18 00:02:07 +08:00
|
|
|
|
2019-02-04 04:09:35 +08:00
|
|
|
ctx.save()
|
|
|
|
var uv = face.uv.slice();
|
|
|
|
|
|
|
|
if (face.direction === 'up') {
|
|
|
|
uv = [uv[2], uv[3], uv[0], uv[1]]
|
|
|
|
} else if (face.direction === 'down') {
|
|
|
|
uv = [uv[2], uv[1], uv[0], uv[3]]
|
|
|
|
}
|
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
var src = getRectangle(uv[0], uv[1], uv[2], uv[3])
|
2019-02-04 04:09:35 +08:00
|
|
|
var flip = [
|
|
|
|
uv[0] > uv[2] ? -1 : 1,
|
|
|
|
uv[1] > uv[3] ? -1 : 1
|
|
|
|
]
|
|
|
|
if (flip[0] + flip[1] < 1) {
|
|
|
|
ctx.scale(flip[0], flip[1])
|
|
|
|
}
|
|
|
|
if (face.rotation) {
|
|
|
|
ctx.rotate(Math.degToRad(face.rotation))
|
|
|
|
let rot = face.rotation
|
|
|
|
|
|
|
|
if (rot <= 180) flip[1] *= -1;
|
|
|
|
if (rot >= 180) flip[0] *= -1;
|
|
|
|
|
|
|
|
while (rot > 0) {
|
|
|
|
[coords.x, coords.y] = [coords.y, coords.x];
|
|
|
|
[coords.w, coords.h] = [coords.h, coords.w];
|
|
|
|
rot -= 90;
|
|
|
|
}
|
|
|
|
}
|
2018-12-27 21:03:04 +08:00
|
|
|
ctx.drawImage(
|
|
|
|
texture.img,
|
|
|
|
src.ax/16 * texture.img.naturalWidth,
|
|
|
|
src.ay/16 * texture.img.naturalHeight,
|
|
|
|
src.x /16 * texture.img.naturalWidth,
|
|
|
|
src.y /16 * texture.img.naturalHeight,
|
2019-02-04 04:09:35 +08:00
|
|
|
coords.x*res_multiple*flip[0],
|
|
|
|
coords.y*res_multiple*flip[1],
|
|
|
|
coords.w*res_multiple*flip[0],
|
|
|
|
coords.h*res_multiple*flip[1]
|
2018-12-27 21:03:04 +08:00
|
|
|
)
|
2019-02-04 04:09:35 +08:00
|
|
|
ctx.restore()
|
2018-12-29 19:26:02 +08:00
|
|
|
return true;
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
2018-10-18 01:50:25 +08:00
|
|
|
|
2018-12-16 23:18:20 +08:00
|
|
|
var face_data = {
|
2018-12-27 21:03:04 +08:00
|
|
|
up: {c1: '#b4d4e1', c2: '#ecf8fd', place: t => {return {x: t.posx+t.z, y: t.posy, w: t.x, h: t.z}}},
|
|
|
|
down: {c1: '#536174', c2: '#6e788c', place: t => {return {x: t.posx+t.z+t.x, y: t.posy, w: t.x, h: t.z}}},
|
|
|
|
east: {c1: '#43e88d', c2: '#7BFFA3', place: t => {return {x: t.posx, y: t.posy+t.z, w: t.z, h: t.y}}},
|
|
|
|
north: {c1: '#5bbcf4', c2: '#7BD4FF', place: t => {return {x: t.posx+t.z, y: t.posy+t.z, w: t.x, h: t.y}}},
|
|
|
|
west: {c1: '#f48686', c2: '#FFA7A4', place: t => {return {x: t.posx+t.z+t.x, y: t.posy+t.z, w: t.z, h: t.y}}},
|
|
|
|
south: {c1: '#f8dd72', c2: '#FFF899', place: t => {return {x: t.posx+t.z+t.x+t.z,y: t.posy+t.z, w: t.x, h: t.y}}},
|
2018-12-16 23:18:20 +08:00
|
|
|
}
|
|
|
|
|
2018-03-29 02:48:11 +08:00
|
|
|
//Drawing
|
2018-12-27 21:03:04 +08:00
|
|
|
templates.forEach(function(t) {
|
2019-02-04 04:09:35 +08:00
|
|
|
let obj = t.obj
|
|
|
|
|
2018-12-27 21:03:04 +08:00
|
|
|
for (var face in face_data) {
|
|
|
|
let d = face_data[face]
|
2018-12-29 19:26:02 +08:00
|
|
|
|
|
|
|
if (!t.obj.faces[face].texture ||
|
|
|
|
!drawTexture(t.obj.faces[face], d.place(t))
|
|
|
|
) {
|
2019-04-08 00:53:33 +08:00
|
|
|
drawTemplateRectangle(d.c1, d.c2, t.obj.faces[face], d.place(t))
|
2018-12-27 21:03:04 +08:00
|
|
|
}
|
|
|
|
}
|
2019-07-26 19:33:29 +08:00
|
|
|
obj.uv_offset[0] = t.posx;
|
|
|
|
obj.uv_offset[1] = t.posy;
|
2018-12-27 21:03:04 +08:00
|
|
|
|
2019-07-18 00:02:07 +08:00
|
|
|
if (t.duplicates) {
|
|
|
|
t.duplicates.forEach(t_2 => {
|
|
|
|
t_2.obj.uv_offset[0] = t.posx;
|
|
|
|
t_2.obj.uv_offset[1] = t.posy;
|
2019-07-26 19:33:29 +08:00
|
|
|
if (t_2.obj !== obj) {
|
|
|
|
t_2.obj.mirror_uv = t_2.obj.mirror_uv != obj.mirror_uv;
|
|
|
|
}
|
2019-07-18 00:02:07 +08:00
|
|
|
})
|
|
|
|
}
|
2019-07-26 19:33:29 +08:00
|
|
|
obj.mirror_uv = false;
|
2019-07-18 00:02:07 +08:00
|
|
|
|
|
|
|
if (!Project.box_uv) {
|
2019-07-26 19:33:29 +08:00
|
|
|
var size = obj.size(undefined, true);
|
2019-07-18 00:02:07 +08:00
|
|
|
size.forEach((n, i) => {
|
|
|
|
size[i] = n || min_size;
|
|
|
|
})
|
2018-12-27 21:03:04 +08:00
|
|
|
|
|
|
|
var face_list = [
|
|
|
|
{face: 'north', fIndex: 10, from: [size[2], size[2]], size: [size[0], size[1]]},
|
|
|
|
{face: 'east', fIndex: 0, from: [0, size[2]], size: [size[2], size[1]]},
|
|
|
|
{face: 'south', fIndex: 8, from: [size[2]*2 + size[0], size[2]], size: [size[0], size[1]]},
|
|
|
|
{face: 'west', fIndex: 2, from: [size[2] + size[0], size[2]], size: [size[2], size[1]]},
|
|
|
|
{face: 'up', fIndex: 4, from: [size[2]+size[0], size[2]], size: [-size[0], -size[2]]},
|
|
|
|
{face: 'down', fIndex: 6, from: [size[2]+size[0]*2, 0], size: [-size[0], size[2]]}
|
|
|
|
]
|
|
|
|
face_list.forEach(function(f) {
|
|
|
|
|
2019-02-04 04:09:35 +08:00
|
|
|
obj.faces[f.face].uv[0] = (f.from[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16;
|
|
|
|
obj.faces[f.face].uv[1] = (f.from[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16;
|
|
|
|
obj.faces[f.face].uv[2] = (f.from[0] + f.size[0] + Math.floor(obj.uv_offset[0]+0.0000001)) / max_size * 16;
|
|
|
|
obj.faces[f.face].uv[3] = (f.from[1] + f.size[1] + Math.floor(obj.uv_offset[1]+0.0000001)) / max_size * 16;
|
|
|
|
obj.faces[f.face].rotation = 0;
|
2018-03-29 02:48:11 +08:00
|
|
|
})
|
2018-12-27 21:03:04 +08:00
|
|
|
}
|
2018-03-29 02:48:11 +08:00
|
|
|
})
|
2018-12-27 21:03:04 +08:00
|
|
|
var dataUrl = canvas.toDataURL()
|
|
|
|
var texture = cb(dataUrl)
|
2019-07-18 00:02:07 +08:00
|
|
|
Project.texture_width = Project.texture_height = max_size;
|
|
|
|
if (texture && !Project.box_uv) {
|
2018-12-27 21:03:04 +08:00
|
|
|
templates.forEach(function(t) {
|
|
|
|
t.obj.applyTexture(texture, true)
|
|
|
|
t.obj.autouv = 0
|
|
|
|
})
|
|
|
|
}
|
2019-07-26 19:33:29 +08:00
|
|
|
updateSelection()
|
|
|
|
Undo.finishEdit('create template', {
|
|
|
|
textures: [texture],
|
|
|
|
bitmap: true,
|
|
|
|
elements: Format.single_texture ? Cube.all : Cube.selected,
|
|
|
|
uv_only: true,
|
|
|
|
uv_mode: true
|
|
|
|
})
|
2018-03-29 02:48:11 +08:00
|
|
|
}
|
|
|
|
}
|
2019-04-08 00:53:33 +08:00
|
|
|
|
|
|
|
const ColorPanel = {
|
2019-07-18 00:02:07 +08:00
|
|
|
set(color) {
|
2019-04-08 00:53:33 +08:00
|
|
|
var value = new tinycolor(color)
|
|
|
|
$('#main_colorpicker').spectrum('set', value.toHex8String())
|
|
|
|
$('#main_colorpicker_preview > div').css('background-color', value.toRgbString())
|
|
|
|
return this;
|
|
|
|
},
|
2019-07-18 00:02:07 +08:00
|
|
|
get() {
|
2019-04-08 00:53:33 +08:00
|
|
|
var value = $('#main_colorpicker').spectrum('get');
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
}
|
2018-11-12 04:19:08 +08:00
|
|
|
|
|
|
|
BARS.defineActions(function() {
|
2018-12-03 02:37:06 +08:00
|
|
|
|
2019-08-18 00:26:14 +08:00
|
|
|
new Tool('brush_tool', {
|
2018-12-03 02:37:06 +08:00
|
|
|
icon: 'fa-paint-brush',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
2018-12-27 21:03:04 +08:00
|
|
|
alt_tool: 'color_picker',
|
2018-12-03 02:37:06 +08:00
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
allowWireframe: false,
|
|
|
|
keybind: new Keybind({key: 66}),
|
2018-12-29 19:26:02 +08:00
|
|
|
modes: ['paint'],
|
2018-12-27 21:03:04 +08:00
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startBrushCanvas(data, data.event)
|
|
|
|
},
|
|
|
|
onSelect: function() {
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.updateNslideValues()
|
2018-12-27 21:03:04 +08:00
|
|
|
$('.UVEditor').find('#uv_size').hide()
|
|
|
|
},
|
|
|
|
onUnselect: function() {
|
|
|
|
$('.UVEditor').find('#uv_size').show()
|
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new Tool('fill_tool', {
|
2018-12-27 21:03:04 +08:00
|
|
|
icon: 'format_color_fill',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
alt_tool: 'color_picker',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
allowWireframe: false,
|
2018-12-29 19:26:02 +08:00
|
|
|
modes: ['paint'],
|
2018-12-27 21:03:04 +08:00
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startBrushCanvas(data, data.event)
|
|
|
|
},
|
|
|
|
onSelect: function() {
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.updateNslideValues()
|
2018-12-27 21:03:04 +08:00
|
|
|
$('.UVEditor').find('#uv_size').hide()
|
|
|
|
},
|
|
|
|
onUnselect: function() {
|
|
|
|
$('.UVEditor').find('#uv_size').show()
|
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new Tool('eraser', {
|
2018-12-27 21:03:04 +08:00
|
|
|
icon: 'fa-eraser',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
allowWireframe: false,
|
2018-12-29 19:26:02 +08:00
|
|
|
modes: ['paint'],
|
2018-12-27 21:03:04 +08:00
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startBrushCanvas(data, data.event)
|
|
|
|
},
|
|
|
|
onSelect: function() {
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.updateNslideValues()
|
2018-12-27 21:03:04 +08:00
|
|
|
$('.UVEditor').find('#uv_size').hide()
|
|
|
|
},
|
|
|
|
onUnselect: function() {
|
|
|
|
$('.UVEditor').find('#uv_size').show()
|
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new Tool('color_picker', {
|
2018-12-27 21:03:04 +08:00
|
|
|
icon: 'colorize',
|
|
|
|
category: 'tools',
|
|
|
|
toolbar: 'brush',
|
|
|
|
selectFace: true,
|
|
|
|
transformerMode: 'hidden',
|
|
|
|
paintTool: true,
|
|
|
|
allowWireframe: false,
|
2018-12-29 19:26:02 +08:00
|
|
|
modes: ['paint'],
|
2018-12-03 02:37:06 +08:00
|
|
|
onCanvasClick: function(data) {
|
|
|
|
Painter.startBrushCanvas(data, data.event)
|
|
|
|
},
|
|
|
|
onSelect: function() {
|
2019-07-18 00:02:07 +08:00
|
|
|
Painter.updateNslideValues()
|
2018-12-03 02:37:06 +08:00
|
|
|
$('.UVEditor').find('#uv_size').hide()
|
|
|
|
},
|
|
|
|
onUnselect: function() {
|
|
|
|
$('.UVEditor').find('#uv_size').show()
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-08-18 00:26:14 +08:00
|
|
|
new BarSelect('brush_mode', {
|
2018-12-27 21:03:04 +08:00
|
|
|
condition: () => Toolbox && (Toolbox.selected.id === 'brush_tool' || Toolbox.selected.id === 'eraser'),
|
2019-07-18 00:02:07 +08:00
|
|
|
onChange() {
|
|
|
|
BARS.updateConditions();
|
|
|
|
Painter.updateNslideValues()
|
|
|
|
},
|
2018-11-12 04:19:08 +08:00
|
|
|
options: {
|
|
|
|
brush: true,
|
2018-12-27 21:03:04 +08:00
|
|
|
noise: true
|
2018-11-12 04:19:08 +08:00
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new BarSelect('fill_mode', {
|
2019-02-04 04:09:35 +08:00
|
|
|
condition: () => Toolbox && Toolbox.selected.id === 'fill_tool',
|
|
|
|
options: {
|
|
|
|
face: true,
|
|
|
|
color: true,
|
|
|
|
cube: true
|
|
|
|
}
|
|
|
|
})
|
2018-11-12 04:19:08 +08:00
|
|
|
|
2019-08-01 06:01:47 +08:00
|
|
|
|
2019-08-18 00:26:14 +08:00
|
|
|
new Action('painting_grid', {
|
2019-08-01 06:01:47 +08:00
|
|
|
name: tl('settings.painting_grid'),
|
|
|
|
description: tl('settings.painting_grid.desc'),
|
|
|
|
icon: 'check_box',
|
|
|
|
category: 'view',
|
|
|
|
condition: () => Modes.paint,
|
|
|
|
linked_setting: 'painting_grid',
|
|
|
|
click: function () {
|
|
|
|
BarItems.painting_grid.toggleLinkedSetting()
|
|
|
|
Cube.all.forEach(cube => {
|
|
|
|
Canvas.buildGridBox(cube)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2019-08-18 00:26:14 +08:00
|
|
|
new NumSlider('slider_brush_size', {
|
2018-12-27 21:03:04 +08:00
|
|
|
condition: () => (Toolbox && ['brush_tool', 'eraser'].includes(Toolbox.selected.id)),
|
2018-11-12 04:19:08 +08:00
|
|
|
settings: {
|
2019-07-18 00:02:07 +08:00
|
|
|
min: 1, max: 20, interval: 1, default: 1,
|
2018-11-12 04:19:08 +08:00
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new NumSlider('slider_brush_softness', {
|
2018-12-27 21:03:04 +08:00
|
|
|
condition: () => (Toolbox && ['brush_tool', 'eraser'].includes(Toolbox.selected.id)),
|
2018-11-12 04:19:08 +08:00
|
|
|
settings: {
|
|
|
|
min: 0, max: 100, default: 0,
|
|
|
|
interval: function(event) {
|
2019-08-18 00:26:14 +08:00
|
|
|
if (event.shiftKey && event.ctrlOrCmd) {
|
2018-11-12 04:19:08 +08:00
|
|
|
return 0.25;
|
|
|
|
} else if (event.shiftKey) {
|
|
|
|
return 5;
|
2019-08-18 00:26:14 +08:00
|
|
|
} else if (event.ctrlOrCmd) {
|
2018-11-12 04:19:08 +08:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new NumSlider('slider_brush_opacity', {
|
2018-12-27 21:03:04 +08:00
|
|
|
condition: () => (Toolbox && ['brush_tool', 'eraser'].includes(Toolbox.selected.id)),
|
2018-11-12 04:19:08 +08:00
|
|
|
settings: {
|
|
|
|
min: 0, max: 100, default: 100,
|
|
|
|
interval: function(event) {
|
2019-08-18 00:26:14 +08:00
|
|
|
if (event.shiftKey && event.ctrlOrCmd) {
|
2018-11-12 04:19:08 +08:00
|
|
|
return 0.25;
|
|
|
|
} else if (event.shiftKey) {
|
|
|
|
return 5;
|
2019-08-18 00:26:14 +08:00
|
|
|
} else if (event.ctrlOrCmd) {
|
2018-11-12 04:19:08 +08:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2019-08-18 00:26:14 +08:00
|
|
|
new NumSlider('slider_brush_min_opacity', {
|
2019-07-18 00:02:07 +08:00
|
|
|
condition: () => (Toolbox && ['brush_tool', 'eraser'].includes(Toolbox.selected.id) && BarItems.brush_mode.value == 'noise'),
|
|
|
|
settings: {
|
|
|
|
min: 0, max: 100, default: 50,
|
|
|
|
interval: function(event) {
|
2019-08-18 00:26:14 +08:00
|
|
|
if (event.shiftKey && event.ctrlOrCmd) {
|
2019-07-18 00:02:07 +08:00
|
|
|
return 0.25;
|
|
|
|
} else if (event.shiftKey) {
|
|
|
|
return 5;
|
2019-08-18 00:26:14 +08:00
|
|
|
} else if (event.ctrlOrCmd) {
|
2019-07-18 00:02:07 +08:00
|
|
|
return 1;
|
|
|
|
} else {
|
|
|
|
return 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
2018-11-12 04:19:08 +08:00
|
|
|
})
|