@@ -120,6 +120,7 @@ export const Painter = {
120120 }
121121 let [ x , y ] = Painter . getCanvasToolPixelCoords ( data . intersects [ 0 ] . uv , texture ) ;
122122 UVEditor . vue . texture = texture ;
123+ Painter . current . control_scale = Preview . selected . calculateControlScale ( data . intersects [ 0 ] . point ) ;
123124
124125 Painter . startPaintTool ( texture , x , y , data . element . faces [ data . face ] . uv , e , data )
125126
@@ -136,6 +137,7 @@ export const Painter = {
136137
137138 let new_face ;
138139 let [ x , y ] = Painter . getCanvasToolPixelCoords ( data . intersects [ 0 ] . uv , texture ) ;
140+ Painter . current . control_scale = Preview . selected . calculateControlScale ( data . intersects [ 0 ] . point ) ;
139141
140142 let interval = Toolbox . selected . brush ?. interval || 1 ;
141143 let delta = [ x - Painter . current . x , y - Painter . current . y ] ;
@@ -159,6 +161,8 @@ export const Painter = {
159161 Painter . current . y = y
160162 Painter . current . face = data . face
161163 Painter . current . element = data . element
164+ Painter . current . client_mouse_x = event . clientX ;
165+ Painter . current . client_mouse_y = event . clientY ;
162166 new_face = true
163167 UVEditor . vue . texture = texture ;
164168 if ( texture !== Painter . current . texture && Undo . current_save ) {
@@ -223,6 +227,7 @@ export const Painter = {
223227 }
224228 Undo . initEdit ( undo_aspects ) ;
225229 Painter . current . start_event = event ;
230+ Painter . current . use_screen_projection = ! ! data && Toolbox . selected . brush ?. screen_space && BarItems . screen_space_brush_projection . value ;
226231 Painter . brushChanges = false ;
227232
228233 if ( Toolbox . selected . id === 'draw_shape_tool' || Toolbox . selected . id === 'gradient_tool' ) {
@@ -242,30 +247,34 @@ export const Painter = {
242247 } else {
243248 Painter . current . face_matrices = { } ;
244249
245- let is_line
250+ let is_line = ( event . shiftKey || Pressing . overrides . shift ) ;
251+ // Viewport only
246252 if ( data ) {
247- is_line = ( event . shiftKey || Pressing . overrides . shift )
248- && Painter . current . element == data . element
249- && ( Painter . current . face == data . face ||
253+ if ( is_line && ! Painter . current . use_screen_projection ) {
254+ // Check if on same face or UV island
255+ is_line = Painter . current . element == data . element
256+ && ( Painter . current . face == data . face ||
250257 ( data . element . faces [ data . face ] instanceof MeshFace && Painter . getMeshUVIsland ( data . face , data . element . faces [ data . face ] ) . includes ( Painter . current . face ) )
251258 )
259+ }
252260 Painter . current . element = data . element ;
253261 Painter . current . face = data . face ;
254- } else {
255- //uv editor
256- is_line = ( event . shiftKey || Pressing . overrides . shift ) ;
257262 }
258263 if ( Toolbox . selected . brush ?. line == false ) is_line = false ;
259264
260265 texture . edit ( canvas => {
261266 if ( is_line ) {
262267 Painter . drawBrushLine ( texture , x , y , event , false , uvTag ) ;
263268 } else {
269+ Painter . current . client_mouse_x = event . clientX ;
270+ Painter . current . client_mouse_y = event . clientY ;
264271 Painter . current . x = Painter . current . y = 0
265272 Painter . useBrushlike ( texture , x , y , event , uvTag )
266273 }
267274 Painter . current . x = x ;
268275 Painter . current . y = y ;
276+ Painter . current . client_mouse_x = event . clientX ;
277+ Painter . current . client_mouse_y = event . clientY ;
269278 } , { no_undo : true , use_cache : true } ) ;
270279 }
271280 } ,
@@ -298,12 +307,16 @@ export const Painter = {
298307 Painter . drawBrushLine ( texture , x , y , event , new_face , uv ) ;
299308 } else {
300309 Painter . current . x = Painter . current . y = 0 ;
310+ delete Painter . current . client_mouse_x ;
311+ delete Painter . current . client_mouse_y ;
301312 Painter . useBrushlike ( texture , x , y , event , uv )
302313 }
303314 } , { no_undo : true , use_cache : true } ) ;
304315 }
305316 Painter . current . x = x ;
306317 Painter . current . y = y ;
318+ Painter . current . client_mouse_x = event . clientX ;
319+ Painter . current . client_mouse_y = event . clientY ;
307320 } ,
308321 stopPaintTool ( ) {
309322 PointerTarget . endTarget ( ) ;
@@ -407,15 +420,15 @@ export const Painter = {
407420 return rect ;
408421 } ,
409422 useBrushlike ( texture , x , y , event , uvTag , no_update , is_opposite ) {
410- if ( Painter . currentPixel [ 0 ] === x && Painter . currentPixel [ 1 ] === y ) return ;
423+ let use_screen_projection = Painter . current . use_screen_projection ;
424+ if ( Painter . currentPixel [ 0 ] === x && Painter . currentPixel [ 1 ] === y && ! use_screen_projection ) return ;
411425 Painter . currentPixel = [ x , y ] ;
412426 Painter . brushChanges = true ;
413427 if ( ! is_opposite ) {
414428 UVEditor . vue . last_brush_position . V2_set ( x , y ) ;
415429 }
416430 let uvFactorX = texture . width / texture . getUVWidth ( ) ;
417431 let uvFactorY = texture . display_height / texture . getUVHeight ( ) ;
418- let use_screen_projection = Toolbox . selected . brush ?. screen_space && BarItems . screen_space_brush_projection . value ;
419432
420433 if ( Painter . mirror_painting && ! is_opposite && ! use_screen_projection ) {
421434 let targets = Painter . getMirrorPaintTargets ( texture , x , y , uvTag ) ;
@@ -526,13 +539,14 @@ export const Painter = {
526539 return tool . brush . changePixel ( px , py , pxcolor , local_opacity , { color, opacity : b_opacity , max_opacity, ctx, x, y, size, softness, texture, event} ) ;
527540 }
528541
529- let use_screen_projection = Toolbox . selected . brush ?. screen_space && BarItems . screen_space_brush_projection . value ;
530- if ( use_screen_projection ) {
542+ if ( Painter . current . use_screen_projection ) {
531543 Painter . projectScreenSpaceBrush ( ctx , {
532544 x, y, size, texture,
533545 softness : softness * 1.8 ,
534546 shape,
535547 event,
548+ client_x : Painter . current . client_mouse_x ?? event . clientX ,
549+ client_y : Painter . current . client_mouse_y ?? event . clientY ,
536550 } , run_per_pixel ) ;
537551 } else {
538552 if ( shape == 'square' ) {
@@ -928,57 +942,93 @@ export const Painter = {
928942 return targets ;
929943 } ,
930944 drawBrushLine ( texture , end_x , end_y , event , new_face , uv ) {
931- // TODO: Support screen space brush
932- var start_x = ( Painter . current . x == undefined ? end_x : Painter . current . x ) ;
933- var start_y = ( Painter . current . y == undefined ? end_y : Painter . current . y ) ;
934-
935- var diff_x = end_x - start_x ;
936- var diff_y = end_y - start_y ;
937-
938- var length = Math . sqrt ( diff_x * diff_x + diff_y * diff_y )
939-
940- if ( new_face && ! length ) {
941- length = 1
942- }
943945 let interval = Toolbox . selected . brush ?. interval || 1 ;
944- var i = Math . min ( interval , length ) ;
945- var x , y ;
946946 let { ctx, offset} = Painter . current ;
947- if ( interval == 1 ) {
948- if ( Math . abs ( diff_x ) > Math . abs ( diff_y ) ) {
949- interval = Math . sqrt ( Math . pow ( diff_y / diff_x , 2 ) + 1 )
950- } else {
951- interval = Math . sqrt ( Math . pow ( diff_x / diff_y , 2 ) + 1 )
947+
948+ if ( ! Painter . current . use_screen_projection ) {
949+ var start_x = ( Painter . current . x == undefined ? end_x : Painter . current . x ) ;
950+ var start_y = ( Painter . current . y == undefined ? end_y : Painter . current . y ) ;
951+
952+ var diff_x = end_x - start_x ;
953+ var diff_y = end_y - start_y ;
954+
955+ var length = Math . sqrt ( diff_x * diff_x + diff_y * diff_y )
956+
957+ if ( new_face && ! length ) {
958+ length = 1
959+ }
960+ var x , y ;
961+ var i = Math . min ( interval , length ) ;
962+ if ( interval == 1 ) {
963+ if ( Math . abs ( diff_x ) > Math . abs ( diff_y ) ) {
964+ interval = Math . sqrt ( Math . pow ( diff_y / diff_x , 2 ) + 1 )
965+ } else {
966+ interval = Math . sqrt ( Math . pow ( diff_x / diff_y , 2 ) + 1 )
967+ }
968+ }
969+
970+ if ( Toolbox . selected . brush ?. pixel_perfect && BarItems . pixel_perfect_drawing . value && BarItems . slider_brush_size . get ( ) == 1 ) {
971+ let direction = 0 ;
972+ if ( length == 1 && diff_x && ! diff_y ) { direction = 1 ; }
973+ if ( length == 1 && ! diff_x && diff_y ) { direction = 2 ; }
974+ let image_data = ctx . getImageData ( end_x - offset [ 0 ] , end_y - offset [ 1 ] , 1 , 1 ) ;
975+ let pixel = {
976+ direction,
977+ image_data,
978+ position : [ end_x - offset [ 0 ] , end_y - offset [ 1 ] ]
979+ } ;
980+ if ( length == 1 && Painter . current . last_pixel && Painter . current . last_pixel . direction && direction && Painter . current . last_pixel . direction != direction ) {
981+ ctx . putImageData ( Painter . current . last_pixel . image_data , ...Painter . current . last_pixel . position ) ;
982+ delete Painter . current . last_pixel ;
983+ } else {
984+ Painter . current . last_pixel = pixel ;
985+ }
952986 }
953- }
954987
955- if ( Toolbox . selected . brush ?. pixel_perfect && BarItems . pixel_perfect_drawing . value && BarItems . slider_brush_size . get ( ) == 1 ) {
956- let direction = 0 ;
957- if ( length == 1 && diff_x && ! diff_y ) { direction = 1 ; }
958- if ( length == 1 && ! diff_x && diff_y ) { direction = 2 ; }
959- let image_data = ctx . getImageData ( end_x - offset [ 0 ] , end_y - offset [ 1 ] , 1 , 1 ) ;
960- let pixel = {
961- direction,
962- image_data,
963- position : [ end_x - offset [ 0 ] , end_y - offset [ 1 ] ]
964- } ;
965- if ( length == 1 && Painter . current . last_pixel && Painter . current . last_pixel . direction && direction && Painter . current . last_pixel . direction != direction ) {
966- ctx . putImageData ( Painter . current . last_pixel . image_data , ...Painter . current . last_pixel . position ) ;
967- delete Painter . current . last_pixel ;
968- } else {
969- Painter . current . last_pixel = pixel ;
988+ while ( i <= length + 0.001 ) {
989+ x = length ? ( start_x + diff_x / length * i ) : end_x ;
990+ y = length ? ( start_y + diff_y / length * i ) : end_y ;
991+ if ( ! Toolbox . selected . brush || Condition ( Toolbox . selected . brush . floor_coordinates ) ) {
992+ x = Math . round ( x ) ;
993+ y = Math . round ( y ) ;
994+ }
995+ Painter . useBrushlike ( texture , x , y , event , uv , i < length - 1 ) ;
996+ i += interval ;
970997 }
971- }
998+
999+ } else {
1000+ let pixel_density = texture . width / texture . getUVWidth ( ) ;
1001+ const screen_pixel_size = ( 10 / Painter . current . control_scale ) / pixel_density ;
1002+ var start_x = ( Painter . current . client_mouse_x ?? event . clientX ) ;
1003+ var start_y = ( Painter . current . client_mouse_y ?? event . clientY ) ;
1004+
1005+ var diff_x = event . clientX - start_x ;
1006+ var diff_y = event . clientY - start_y ;
9721007
973- while ( i <= length + 0.001 ) {
974- x = length ? ( start_x + diff_x / length * i ) : end_x ;
975- y = length ? ( start_y + diff_y / length * i ) : end_y ;
976- if ( ! Toolbox . selected . brush || Condition ( Toolbox . selected . brush . floor_coordinates ) ) {
977- x = Math . round ( x ) ;
978- y = Math . round ( y ) ;
1008+ var length = Math . sqrt ( diff_x ** 2 + diff_y ** 2 ) / screen_pixel_size ;
1009+
1010+ if ( new_face && ! length ) {
1011+ length = 1
1012+ }
1013+ if ( ! length ) {
1014+ Painter . useBrushlike ( texture , end_x , end_y , event , uv , i < length - 1 ) ;
1015+ return ;
1016+ }
1017+ var i = Math . min ( interval , length ) ;
1018+ if ( interval == 1 ) {
1019+ if ( Math . abs ( diff_x ) > Math . abs ( diff_y ) ) {
1020+ interval = Math . sqrt ( Math . pow ( diff_y / diff_x , 2 ) + 1 )
1021+ } else {
1022+ interval = Math . sqrt ( Math . pow ( diff_x / diff_y , 2 ) + 1 )
1023+ }
1024+ }
1025+
1026+ while ( i <= length + 0.001 ) {
1027+ Painter . current . client_mouse_x = start_x + ( ( diff_x / length ) * i ) ;
1028+ Painter . current . client_mouse_y = start_y + ( ( diff_y / length ) * i ) ;
1029+ Painter . useBrushlike ( texture , end_x , end_y , event , uv , i < length - 1 ) ;
1030+ i += interval ;
9791031 }
980- Painter . useBrushlike ( texture , x , y , event , uv , i < length - 1 ) ;
981- i += interval ;
9821032 }
9831033 } ,
9841034 useShapeTool ( texture , x , y , event , uvTag ) {
@@ -1692,14 +1742,15 @@ export const Painter = {
16921742 projectScreenSpaceBrush ( ctx , args , editPx ) {
16931743 let texture = args . texture ;
16941744 let preview = Preview . selected ;
1695- let r = Math . round ( args . size + 1 ) / 2 ;
1745+ let r = args . size / 2 ;
16961746 let selection = Painter . current . texture . selection ;
1747+ let pixel_density = texture . width / texture . getUVWidth ( ) ;
16971748 const bounds = [ texture . width , texture . height , 0 , 0 ] ;
16981749
16991750 let canvas_offset = preview . canvas . getBoundingClientRect ( ) ;
17001751 let mouse_canvas_offset = [
1701- args . event . clientX - canvas_offset . left ,
1702- args . event . clientY - canvas_offset . top
1752+ args . client_x - canvas_offset . left ,
1753+ args . client_y - canvas_offset . top
17031754 ]
17041755 function filterObjects ( elements ) {
17051756 let objects = [ ] ;
@@ -1720,13 +1771,11 @@ export const Painter = {
17201771 preview . mouse . x = ( mouse_canvas_offset [ 0 ] / preview . width ) * 2 - 1 ;
17211772 preview . mouse . y = - ( mouse_canvas_offset [ 1 ] / preview . height ) * 2 + 1 ;
17221773 preview . raycaster . setFromCamera ( preview . mouse , preview . camera ) ;
1723- let intersect = preview . raycaster . intersectObjects ( objects , false ) [ 0 ] ;
1724- if ( ! intersect ) return ;
1725- const screen_radius = 10 * r / preview . calculateControlScale ( intersect . point ) ;
1774+ const screen_radius = ( 13.4 / Painter . current . control_scale ) * ( r / pixel_density ) ;
17261775
17271776 // Calculate sampling resolution
17281777 let rough_samples = r * 0.6 ;
1729- let samples = r * 5 ;
1778+ let samples = r * 4 ;
17301779
17311780 const pixel_intensities = { } ;
17321781 const pixel_hits = { } ;
@@ -2703,6 +2752,7 @@ BARS.defineActions(function() {
27032752 softness : true ,
27042753 opacity : true ,
27052754 offset_even_radius : true ,
2755+ screen_space : true ,
27062756 onStrokeStart ( { texture, event, x, y, raycast_data} ) {
27072757 if ( event . ctrlOrCmd || Pressing . overrides . ctrl ) {
27082758 let size = BarItems . slider_brush_size . get ( ) ;
@@ -2844,6 +2894,7 @@ BARS.defineActions(function() {
28442894 size : true ,
28452895 softness : true ,
28462896 opacity : true ,
2897+ screen_space : true ,
28472898 offset_even_radius : true ,
28482899 floor_coordinates : ( ) => BarItems . slider_brush_softness . get ( ) == 0 ,
28492900 get interval ( ) {
0 commit comments