Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added 3D brush outline shearing and fixed triangle selection #2529

Open
wants to merge 32 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
afd621a
Ask for file type when opening file on android (workaround for #2522)
JannisX11 Oct 24, 2024
bfc75c9
Performed truncation in UVToLocal to fix 3D brush position
Nestorboy Oct 25, 2024
a22d2a9
Merge branch 'JannisX11:next' into next
Nestorboy Oct 25, 2024
e436fa3
Check if truncateOffset is defined instead
Nestorboy Oct 27, 2024
1fd9581
Added function to compute Texel To Local matrix
Nestorboy Oct 27, 2024
942fe88
Set matrix position in TexelToLocalMatrix
Nestorboy Oct 27, 2024
5a6dbb5
Used custom matrix for the brush preview instead
Nestorboy Oct 28, 2024
ac4acce
Reverted truncation fix in UVToLocal
Nestorboy Oct 28, 2024
bbee842
Disabled brush outline matrixAutoUpdate in Canvas
Nestorboy Oct 28, 2024
9fc5613
Merge branch 'JannisX11:next' into next
Nestorboy Oct 28, 2024
0c986ef
Merge branch 'next' into 3d-brush-matrix
Nestorboy Oct 28, 2024
b90ce28
Fixed warnings about old matrix multiply usage
Nestorboy Oct 30, 2024
8ce1565
Avoid modifying original uv in texelToLocalMatrix
Nestorboy Oct 31, 2024
5bc4462
Used proper naming conventions
Nestorboy Oct 31, 2024
a2bd69a
Fixed preview not scaling with texel density
Nestorboy Oct 31, 2024
e10d9e3
Deduplicated bary code in texelToLocalMatrix
Nestorboy Oct 31, 2024
541049b
Simplified uv calculations for brush outline
Nestorboy Oct 31, 2024
8081bd6
Passed vertices last to texelToLocalMatrix
Nestorboy Oct 31, 2024
8af8db5
Moved factor out of truncate_offset check
Nestorboy Oct 31, 2024
99f2a25
Fixed brush outline not working with cubes
Nestorboy Oct 31, 2024
31b0a70
Made texel axis calculations more concise
Nestorboy Oct 31, 2024
fe107e2
Used snake case for local variables
Nestorboy Oct 31, 2024
5204adb
Minor style cleanups in brush outline code
Nestorboy Oct 31, 2024
310adeb
Added default param values to texelToLocalMatrix
Nestorboy Oct 31, 2024
6713470
Renamed normalized_uv to uv_coord for clarity
Nestorboy Oct 31, 2024
2ce717b
Fixed soft brush snapping to texels
Nestorboy Nov 3, 2024
96e6079
Passed truncated uvs to texelToLocalMatrix instead
Nestorboy Nov 3, 2024
f98be22
Moved truncation factor out of texelToLocalMatrix
Nestorboy Nov 3, 2024
35e2e10
Subtract current frame height from truncated y
Nestorboy Nov 3, 2024
f9ee230
Always initialized truncated uvs
Nestorboy Nov 3, 2024
64a1673
Passed truncated uv as last optional argument
Nestorboy Nov 3, 2024
f60f4ab
Reverted caching floor_coordinates condition
Nestorboy Nov 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion js/file_system.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Object.assign(Blockbench, {
let isIOS = ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) ||
(navigator.userAgent.includes("Mac") && "ontouchend" in document);

if (isIOS && options.extensions && options.extensions.length > 1) {
if ((isIOS || Blockbench.isTouch) && options.extensions && options.extensions.length > 1) {
let ext_options = {};
options.extensions.forEach(extension => {
ext_options[extension] = extension;
Expand Down
15 changes: 15 additions & 0 deletions js/outliner/cube.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,21 @@ class CubeFace extends Face {
case 'down': return [7, 2, 3, 6];
}
}
texelToLocalMatrix(uv, truncate_factor = [1, 1], truncated_uv) {
uv = truncated_uv == null || truncated_uv[0] == null || truncated_uv[1] == null ? [...uv] : [...truncated_uv];

let texel_pos = this.UVToLocal(uv);
let texel_x_axis = this.UVToLocal([uv[0] + truncate_factor[0], uv[1]]);
let texel_y_axis = this.UVToLocal([uv[0], uv[1] + truncate_factor[1]]);

texel_x_axis.sub(texel_pos);
texel_y_axis.sub(texel_pos);

let matrix = new THREE.Matrix4();
matrix.makeBasis(texel_x_axis, texel_y_axis, new THREE.Vector3(0, 0, 1));
matrix.setPosition(texel_pos);
return matrix;
}
UVToLocal(point) {
let from = this.cube.from.slice()
let to = this.cube.to.slice()
Expand Down
50 changes: 50 additions & 0 deletions js/outliner/mesh.js
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,56 @@ class MeshFace extends Face {
if (this.mesh.faces[fkey] == this) return fkey;
}
}
texelToLocalMatrix(uv, truncate_factor = [1, 1], truncated_uv, vertices = this.getSortedVertices()) {
let vert_a = vertices[0];
let vert_b = vertices[1];
let vert_c = vertices[2];

// Use non-truncated uv coordinates to select the correct triangle of a face.
if (vertices[3]) {
let is_in_tri = pointInTriangle(uv, this.uv[vert_a], this.uv[vert_b], this.uv[vert_c]);

if (!is_in_tri) {
vert_a = vertices[0];
vert_b = vertices[2];
vert_c = vertices[3];
}
}
let p0 = this.uv[vert_a];
let p1 = this.uv[vert_b];
let p2 = this.uv[vert_c];

let vertexa = this.mesh.vertices[vert_a];
let vertexb = this.mesh.vertices[vert_b];
let vertexc = this.mesh.vertices[vert_c];

uv = truncated_uv == null || truncated_uv[0] == null || truncated_uv[1] == null ? [...uv] : [...truncated_uv];

function UVToLocal(uv) {
let b0 = (p1[0] - p0[0]) * (p2[1] - p0[1]) - (p2[0] - p0[0]) * (p1[1] - p0[1]);
let b1 = ((p1[0] - uv[0]) * (p2[1] - uv[1]) - (p2[0] - uv[0]) * (p1[1] - uv[1])) / b0;
let b2 = ((p2[0] - uv[0]) * (p0[1] - uv[1]) - (p0[0] - uv[0]) * (p2[1] - uv[1])) / b0;
let b3 = ((p0[0] - uv[0]) * (p1[1] - uv[1]) - (p1[0] - uv[0]) * (p0[1] - uv[1])) / b0;

return new THREE.Vector3(
vertexa[0] * b1 + vertexb[0] * b2 + vertexc[0] * b3,
vertexa[1] * b1 + vertexb[1] * b2 + vertexc[1] * b3,
vertexa[2] * b1 + vertexb[2] * b2 + vertexc[2] * b3
)
}

let texel_pos = UVToLocal(uv);
let texel_x_axis = UVToLocal([uv[0] + truncate_factor[0], uv[1]]);
let texel_y_axis = UVToLocal([uv[0], uv[1] + truncate_factor[1]]);

texel_x_axis.sub(texel_pos);
texel_y_axis.sub(texel_pos);

let matrix = new THREE.Matrix4();
matrix.makeBasis(texel_x_axis, texel_y_axis, new THREE.Vector3(0, 0, 1));
matrix.setPosition(texel_pos);
return matrix;
}
UVToLocal(uv, vertices = this.getSortedVertices()) {
let vert_a = vertices[0];
let vert_b = vertices[1];
Expand Down
1 change: 1 addition & 0 deletions js/preview/canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,7 @@ const Canvas = {
alphaTest: 0.2
})
Canvas.brush_outline = new THREE.Mesh(new THREE.PlaneBufferGeometry(1, 1), brush_outline_material);
Canvas.brush_outline.matrixAutoUpdate = false;
Canvas.gizmos.push(Canvas.brush_outline);

Canvas.gizmos.push(Canvas.hover_helper_line);
Expand Down
44 changes: 21 additions & 23 deletions js/preview/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -1085,39 +1085,42 @@ class Preview {
let offset = 0;
let x = intersect.uv.x * texture.width;
let y = (1-intersect.uv.y) * texture.height;
let truncated_x = x;
let truncated_y = y;
if (Condition(Toolbox.selected.brush.floor_coordinates)) {
offset = BarItems.slider_brush_size.get()%2 == 0 && Toolbox.selected.brush?.offset_even_radius ? 0 : 0.5;
x = Math.round(x + offset) - offset;
y = Math.round(y + offset) - offset;
truncated_x = Math.round(x + offset) - offset;
truncated_y = Math.round(y + offset) - offset;
}
if (texture.currentFrame) {
y -= texture.display_height * texture.currentFrame;
truncated_y -= texture.display_height * texture.currentFrame;
}

// Position
let brush_coord = face.UVToLocal([x * uv_factor_x, y * uv_factor_y]);
let brush_coord_difference_x = face.UVToLocal([(x+1) * uv_factor_x, y * uv_factor_y]);
let brush_coord_difference_y = face.UVToLocal([x * uv_factor_x, (y+1) * uv_factor_y]);
brush_coord_difference_x.sub(brush_coord);
brush_coord_difference_y.sub(brush_coord);
let brush_matrix = face.texelToLocalMatrix([x * uv_factor_x, y * uv_factor_y], [uv_factor_x, uv_factor_y], [truncated_x * uv_factor_x, truncated_y * uv_factor_y]);
let brush_coord = new THREE.Vector3().setFromMatrixPosition(brush_matrix);
intersect.object.localToWorld(brush_coord);
if (!Format.centered_grid) {
brush_coord.x += 8;
brush_coord.z += 8;
}
Canvas.brush_outline.position.copy(brush_coord);

// z fighting
// Z-fighting
let z_fight_offset = Preview.selected.calculateControlScale(brush_coord) / 8;
let camera_direction = Preview.selected.camera.getWorldDirection(Reusable.vec2);
if (camera_direction.angleTo(world_normal) < Math.PI / 2) {
world_normal.multiplyScalar(-1);
}
Canvas.brush_outline.position.addScaledVector(world_normal, z_fight_offset);

//size
let radius_x = BarItems.slider_brush_size.get() * (1+z_fight_offset) * brush_coord_difference_x.length();
let radius_y = BarItems.slider_brush_size.get() * (1+z_fight_offset) * brush_coord_difference_y.length();
Canvas.brush_outline.scale.set(radius_x, radius_y, radius_x);
let z_offset = world_normal.clone().multiplyScalar(z_fight_offset);
let matrix_offset = new THREE.Matrix4().makeTranslation(z_offset.x, z_offset.y, z_offset.z);
brush_matrix.multiplyMatrices(matrix_offset, brush_matrix);

// Size
let brush_scale = new THREE.Vector3().setFromMatrixScale(brush_matrix);
let radius_x = BarItems.slider_brush_size.get() * (1+z_fight_offset) * brush_scale.x;
let radius_y = BarItems.slider_brush_size.get() * (1+z_fight_offset) * brush_scale.y;

let uv = Canvas.brush_outline.geometry.attributes.uv;
if (BarItems.brush_shape.value == 'square') {
Expand All @@ -1134,16 +1137,11 @@ class Preview {
uv.needsUpdate = true;
}

// rotation
Canvas.brush_outline.quaternion.setFromUnitVectors(new THREE.Vector3(0, 0, 1), intersect.face.normal);
let scale = new THREE.Vector3(BarItems.slider_brush_size.get(), BarItems.slider_brush_size.get(), 1);
brush_matrix.scale(scale);

Canvas.brush_outline.rotation.z = 0;
let inverse = Reusable.quat2.copy(Canvas.brush_outline.quaternion).invert();
brush_coord_difference_y.applyQuaternion(inverse);
let rotation = Math.atan2(brush_coord_difference_y.x, -brush_coord_difference_y.y);
Canvas.brush_outline.rotation.z = rotation;

Canvas.brush_outline.quaternion.premultiply(world_quaternion);
brush_matrix.multiplyMatrices(intersect.object.matrix, brush_matrix);
Canvas.brush_outline.matrix = brush_matrix;
}

if (Toolbox.selected.onCanvasMouseMove) {
Expand Down