2 # ##### BEGIN GPL LICENSE BLOCK #####
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software Foundation,
16 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # ##### END GPL LICENSE BLOCK #####
21 'name': 'NP 020 Shader Brush',
22 'author': 'Okavango & the Blenderartists community',
24 'blender': (2, 75, 0),
27 'description': 'Transfers materials between objects in 3D view',
29 'category': '3D View'}
33 import mathutils
, bmesh
, math
34 from math
import sin
, cos
, tan
, atan
, degrees
, radians
, asin
, acos
37 from mathutils
import Vector
, Matrix
38 from bpy_extras
import view3d_utils
40 from .utils_geometry
import *
41 from .utils_graphics
import *
42 from .utils_function
import *
44 # Defining the first graphical set that will be shown during first phase and updated along:
45 def draw_callback1_px(self
, context
):
47 # Getting the first object and face that raycast hits:
49 np_print('mode:', mode
)
51 np_print('region', region
)
53 np_print('rv3d', rv3d
)
55 np_print('self.co2d', co2d
)
57 center
= Vector((0,0,0))
58 normal
= Vector((0.0, 0.0, 1.0))
59 scenecast
= scene_cast(region
, rv3d
, co2d
)
60 np_print('scenecast', scenecast
)
62 np_print('hitob', hitob
)
66 instruct
= 'paint material on object / objects'
69 instruct
= 'pick material to paint with'
72 instruct
= 'pick material to paint with'
75 instruct
= 'paint material on single face'
78 instruct
= 'paint material on object / objects'
80 acob
= bpy
.context
.view_layer
.objects
.active
81 bpy
.context
.view_layer
.objects
.active
= hitob
82 slots
= hitob
.material_slots
83 for i
, s
in enumerate(slots
):
84 hitob
.active_material_index
= i
85 bpy
.ops
.object.material_slot_remove()
86 if self
.shader
is not None:
87 hitob
.data
.materials
.append(self
.shader
)
88 np_print(hitob
.select
)
89 if hitob
.select_get() is True:
92 bpy
.context
.view_layer
.objects
.active
= ob
93 slots
= ob
.material_slots
94 for i
, s
in enumerate(slots
):
95 ob
.active_material_index
= i
96 bpy
.ops
.object.material_slot_remove()
97 if self
.shader
is not None:
98 ob
.data
.materials
.append(self
.shader
)
99 bpy
.context
.view_layer
.objects
.active
= acob
100 #bpy.context.view_layer.update()
104 instruct
= 'pick material to paint with'
105 if hitob
is not None:
107 findex
= scenecast
[3]
108 np_print('findex', findex
)
113 bme
.faces
.ensure_lookup_table()
114 np_print('faces', bme
.faces
)
115 np_print('face', bme
.faces
[findex
])
116 bmeface
= bme
.faces
[findex
]
117 matindex
= bmeface
.material_index
118 np_print('material_index', matindex
)
119 slots
= list(hitob
.material_slots
)
120 np_print('slots', slots
)
121 np_print('len slots', len(slots
))
122 np_print('slots',slots
)
125 self
.shadername
= 'None'
126 elif slots
[matindex
].material
is not None:
127 self
.shader
= slots
[matindex
].material
128 self
.shadername
= slots
[matindex
].material
.name
131 self
.shadername
= 'None'
132 bpy
.context
.view_layer
.objects
.active
= hitob
133 hitob
.active_material_index
= matindex
134 np_print('self.shader', self
.shader
)
138 self
.shadername
= 'None'
141 instruct
= 'pick material to paint with'
144 instruct
= 'paint material on single face'
145 if hitob
is not None:
146 acob
= bpy
.context
.view_layer
.objects
.active
147 bpy
.context
.view_layer
.objects
.active
= hitob
148 findex
= scenecast
[3]
149 np_print('findex', findex
)
152 bpy
.ops
.object.mode_set(mode
= 'EDIT')
153 bme
= bmesh
.from_edit_mesh(me
)
154 bme
.faces
.ensure_lookup_table()
155 np_print('faces', bme
.faces
)
156 np_print('face', bme
.faces
[findex
])
157 bmeface
= bme
.faces
[findex
]
158 slots
= list(hitob
.material_slots
)
159 np_print('slots', list(slots
))
161 if list(slots
) == []:
162 hitob
.data
.materials
.append(None)
163 slots
= list(hitob
.material_slots
)
164 for i
, s
in enumerate(slots
):
166 if s
.name
== self
.shadername
:
169 elif s
.material
== None and self
.shader
== None:
173 hitob
.data
.materials
.append(self
.shader
)
174 matindex
= len(slots
) - 1
175 hitob
.active_material_index
= matindex
176 bpy
.context
.tool_settings
.mesh_select_mode
= False, False, True
177 bpy
.ops
.mesh
.select_all(action
= 'DESELECT')
178 bmeface
.select
= True
179 bmesh
.update_edit_mesh(me
, True)
180 bpy
.ops
.object.material_slot_assign()
181 bpy
.ops
.mesh
.select_all(action
= 'DESELECT')
182 bpy
.ops
.object.mode_set(mode
= 'OBJECT')
183 bpy
.context
.view_layer
.objects
.active
= acob
186 # ON-SCREEN INSTRUCTIONS:
188 keys_aff
= 'LMB - paint object / objects, CTRL - take material, ALT - paint single face'
189 keys_nav
= 'MMB, SCROLL - navigate'
190 keys_neg
= 'ESC - quit'
192 display_instructions(region
, rv3d
, instruct
, keys_aff
, keys_nav
, keys_neg
)
195 col_bg_fill_main_run
= addon_settings_graph('col_bg_fill_main_run')
196 col_bg_fill_main_nav
= addon_settings_graph('col_bg_fill_main_nav')
198 # Drawing the small icon near the cursor and the shader name:
199 # First color is for lighter themes, second for default and darker themes:
200 if mode
in {1,2,5,6}:
201 square
= [[17, 30], [17, 39], [26, 39], [26, 30]]
202 cur
= [[17, 36], [18, 35], [14, 31], [14, 30], [15, 30], [19, 34], [18, 35], [20, 37], [21, 37], [21, 36], [19, 34], [20, 33]]
203 curpipe
= [[18, 35], [14, 31], [14, 30], [15, 30], [19, 34], [18, 35]]
204 curcap
= [[18, 35], [20, 37], [21, 37], [21, 36], [19, 34], [18, 35]]
206 col_square
= col_bg_fill_main_run
208 co
[0] = round((co
[0] * scale
), 0) -35 + co2d
[0]
209 co
[1] = round((co
[1] * scale
), 0) -88 + co2d
[1]
211 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
212 co
[1] = round((co
[1] * scale
),0) -86 + co2d
[1]
214 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
215 co
[1] = round((co
[1] * scale
),0) -86 + co2d
[1]
217 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
218 co
[1] = round((co
[1] * scale
),0) -86 + co2d
[1]
219 bgl
.glColor4f(*col_square
)
220 bgl
.glBegin(bgl
.GL_TRIANGLE_FAN
)
224 #bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
225 bgl
.glColor4f(1.0, 1.0, 1.0, 0.8)
226 bgl
.glBegin(bgl
.GL_LINE_STRIP
)
231 bgl
.glColor4f(1.0, 0.5, 0.5, 1.0)
232 bgl
.glBegin(bgl
.GL_TRIANGLE_FAN
)
238 square
= [[17, 30], [17, 39], [26, 39], [26, 30]]
239 cur
= [[18, 30], [21, 33], [18, 36], [14, 32], [16, 32], [18, 30]]
240 curtip
= [[14, 32], [16, 32], [15, 33], [14, 32]]
241 curbag
= [[18, 30], [15, 33], [21, 33], [18, 30]]
243 if mode
in (3, 7): col_square
= col_bg_fill_main_nav
244 else: col_square
= (0.25, 0.35, 0.4, 0.87)
246 co
[0] = round((co
[0] * scale
), 0) -35 + co2d
[0]
247 co
[1] = round((co
[1] * scale
), 0) -88 + co2d
[1]
249 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
250 co
[1] = round((co
[1] * scale
),0) -84 + co2d
[1]
252 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
253 co
[1] = round((co
[1] * scale
),0) -84 + co2d
[1]
255 co
[0] = round((co
[0] * scale
),0) -25 + co2d
[0]
256 co
[1] = round((co
[1] * scale
),0) -84 + co2d
[1]
257 bgl
.glColor4f(*col_square
)
258 bgl
.glBegin(bgl
.GL_TRIANGLE_FAN
)
262 #bgl.glColor4f(1.0, 1.0, 1.0, 1.0)
263 bgl
.glColor4f(1.0, 1.0, 1.0, 0.8)
264 bgl
.glBegin(bgl
.GL_LINE_STRIP
)
270 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
275 bgl.glBegin(bgl.GL_LINE_STRIP)
281 if self
.shader
is not None:
282 bgl
.glBegin(bgl
.GL_TRIANGLE_FAN
)
287 bgl
.glColor4f(0.25, 0.35, 0.4, 0.87)
289 blf
.size(font_id
, 15, 72)
290 blf
.position(font_id
, co2d
[0] + 47, co2d
[1] - 3, 0)
291 blf
.draw(font_id
, self
.shadername
)
293 bgl
.glColor4f(1.0, 1.0, 1.0, 0.8)
295 blf
.size(font_id
, 15, 72)
296 blf
.position(font_id
, co2d
[0] + 46, co2d
[1] - 2, 0)
297 blf
.draw(font_id
, self
.shadername
)
299 # restore opengl defaults
301 bgl
.glDisable(bgl
.GL_BLEND
)
302 bgl
.glColor4f(0.0, 0.0, 0.0, 1.0)
304 # Defining the operator that will use the graphical sets and make the drawing of the base:
305 class NP020ShaderBrush(bpy
.types
.Operator
):
306 bl_idname
= "object.np_020_shader_brush"
307 bl_label
= "NP 020 Shader Brush"
308 bl_options
={'REGISTER', 'UNDO', 'PRESET'}
310 def modal(self
, context
, event
):
311 context
.area
.tag_redraw()
313 if not event
.ctrl
and not event
.shift
and not event
.alt
and event
.type == 'MOUSEMOVE':
315 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
317 if event
.ctrl
and event
.type == 'MOUSEMOVE':
319 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
321 if event
.ctrl
and event
.shift
and event
.type == 'MOUSEMOVE':
323 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
325 if event
.alt
and event
.type == 'MOUSEMOVE':
327 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
329 if event
.type == 'LEFT_CTRL' and event
.value
== 'PRESS':
331 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
333 if event
.shift
and event
.type == 'LEFT_CTRL' and event
.value
== 'PRESS':
335 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
337 if event
.type == 'LEFT_CTRL' and event
.value
== 'RELEASE':
339 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
341 if event
.type == 'LEFT_SHIFT' and event
.value
== 'PRESS' and self
.mode
== 1:
343 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
345 if event
.ctrl
and event
.type == 'LEFT_SHIFT' and event
.value
== 'RELEASE':
347 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
349 if event
.type == 'LEFT_CTRL' and event
.type == 'LEFT_SHIFT' and event
.value
== 'RELEASE':
351 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
353 if event
.type == 'LEFT_ALT' and event
.value
== 'PRESS':
355 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
357 if event
.type == 'LEFT_ALT' and event
.value
== 'RELEASE':
359 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
361 if event
.type == 'LEFTMOUSE' and event
.value
== 'PRESS':
363 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
365 if event
.ctrl
and event
.type == 'LEFTMOUSE' and event
.value
== 'PRESS':
367 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
369 if event
.ctrl
and event
.shift
and event
.type == 'LEFTMOUSE' and event
.value
== 'PRESS':
371 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
373 if event
.alt
and event
.type == 'LEFTMOUSE' and event
.value
== 'PRESS':
375 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
377 if event
.type == 'LEFTMOUSE' and event
.value
== 'RELEASE':
379 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
381 if event
.ctrl
and event
.type == 'LEFTMOUSE' and event
.value
== 'RELEASE':
383 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
385 if event
.ctrl
and event
.shift
and event
.type == 'LEFTMOUSE' and event
.value
== 'RELEASE':
387 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
389 if event
.alt
and event
.type == 'LEFTMOUSE' and event
.value
== 'RELEASE':
391 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
393 elif event
.type == 'RIGHTMOUSE':
394 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
397 elif event
.type == 'ESC':
398 bpy
.types
.SpaceView3D
.draw_handler_remove(self
._handle
, 'WINDOW')
401 elif event
.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
402 return {'PASS_THROUGH'}
404 return {'RUNNING_MODAL'}
406 def invoke(self
, context
, event
):
407 if context
.area
.type == 'VIEW_3D':
408 self
.co2d
= ((event
.mouse_region_x
, event
.mouse_region_y
))
409 args
= (self
, context
)
410 self
._handle
= bpy
.types
.SpaceView3D
.draw_handler_add(draw_callback1_px
, args
, 'WINDOW', 'POST_PIXEL')
412 self
.region
= bpy
.context
.region
413 self
.rv3d
= bpy
.context
.region_data
415 ob
= bpy
.context
.active_object
416 if ob
is not None and ob
.active_material
is not None:
417 self
.shader
= ob
.active_material
418 self
.shadername
= ob
.active_material
.name
421 self
.shadername
= 'None'
422 self
.selob
= bpy
.context
.selected_objects
424 context
.window_manager
.modal_handler_add(self
)
425 return {'RUNNING_MODAL'}
427 self
.report({'WARNING'}, "View3D not found, cannot run operator")