Extensions: change the constant for the complete status
[blender-addons-contrib.git] / np_station / np_shader_brush.py
blob6047a65cc4389d736626c83bb89650f9c284567b
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 #####
20 bl_info = {
21 'name': 'NP 020 Shader Brush',
22 'author': 'Okavango & the Blenderartists community',
23 'version': (0, 2, 0),
24 'blender': (2, 75, 0),
25 'location': 'View3D',
26 'warning': '',
27 'description': 'Transfers materials between objects in 3D view',
28 'doc_url': '',
29 'category': '3D View'}
32 import bpy
33 import mathutils, bmesh, math
34 from math import sin, cos, tan, atan, degrees, radians, asin, acos
35 import bgl
36 import blf
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:
48 mode = self.mode
49 np_print('mode:', mode)
50 region = self.region
51 np_print('region', region)
52 rv3d = self.rv3d
53 np_print('rv3d', rv3d)
54 co2d = self.co2d
55 np_print('self.co2d', co2d)
56 co3d = 1
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)
61 hitob = scenecast[4]
62 np_print('hitob', hitob)
65 if mode == 0:
66 instruct = 'paint material on object / objects'
68 elif mode == 1:
69 instruct = 'pick material to paint with'
71 elif mode == 2:
72 instruct = 'pick material to paint with'
74 elif mode == 3:
75 instruct = 'paint material on single face'
77 elif mode == 4:
78 instruct = 'paint material on object / objects'
79 if hitob is not None:
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:
90 np_print('true')
91 for ob in self.selob:
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()
101 np_print('040')
103 elif mode == 5:
104 instruct = 'pick material to paint with'
105 if hitob is not None:
106 mat = None
107 findex = scenecast[3]
108 np_print('findex', findex)
109 me = hitob.data
110 np_print('me', me)
111 bme = bmesh.new()
112 bme.from_mesh(me)
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)
123 if len(slots) == 0:
124 self.shader = None
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
129 else:
130 self.shader = None
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)
135 np_print('050')
136 else:
137 self.shader = None
138 self.shadername = 'None'
140 elif mode == 6:
141 instruct = 'pick material to paint with'
143 elif mode == 7:
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)
150 me = hitob.data
151 np_print('me', me)
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))
160 m = 0
161 if list(slots) == []:
162 hitob.data.materials.append(None)
163 slots = list(hitob.material_slots)
164 for i, s in enumerate(slots):
165 np_print('s', s)
166 if s.name == self.shadername:
167 m = 1
168 matindex = i
169 elif s.material == None and self.shader == None:
170 m = 1
171 matindex = i
172 if m == 0:
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]]
205 scale = 2.8
206 col_square = col_bg_fill_main_run
207 for co in square:
208 co[0] = round((co[0] * scale), 0) -35 + co2d[0]
209 co[1] = round((co[1] * scale), 0) -88 + co2d[1]
210 for co in cur:
211 co[0] = round((co[0] * scale),0) -25 + co2d[0]
212 co[1] = round((co[1] * scale),0) -86 + co2d[1]
213 for co in curcap:
214 co[0] = round((co[0] * scale),0) -25 + co2d[0]
215 co[1] = round((co[1] * scale),0) -86 + co2d[1]
216 for co in curpipe:
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)
221 for x, y in square:
222 bgl.glVertex2f(x, y)
223 bgl.glEnd()
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)
227 for x,y in cur:
228 bgl.glVertex2f(x,y)
229 bgl.glEnd()
230 if mode in {2, 6}:
231 bgl.glColor4f(1.0, 0.5, 0.5, 1.0)
232 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
233 for x,y in curcap:
234 bgl.glVertex2f(x,y)
235 bgl.glEnd()
237 else:
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]]
242 scale = 2.8
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)
245 for co in square:
246 co[0] = round((co[0] * scale), 0) -35 + co2d[0]
247 co[1] = round((co[1] * scale), 0) -88 + co2d[1]
248 for co in cur:
249 co[0] = round((co[0] * scale),0) -25 + co2d[0]
250 co[1] = round((co[1] * scale),0) -84 + co2d[1]
251 for co in curtip:
252 co[0] = round((co[0] * scale),0) -25 + co2d[0]
253 co[1] = round((co[1] * scale),0) -84 + co2d[1]
254 for co in curbag:
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)
259 for x, y in square:
260 bgl.glVertex2f(x, y)
261 bgl.glEnd()
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)
265 for x,y in cur:
266 bgl.glVertex2f(x,y)
267 bgl.glEnd()
269 if mode in {3, 7}:
270 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
271 for x,y in curtip:
272 bgl.glVertex2f(x,y)
273 bgl.glEnd()
274 bgl.glLineWidth(2)
275 bgl.glBegin(bgl.GL_LINE_STRIP)
276 for x,y in curtip:
277 bgl.glVertex2f(x,y)
278 bgl.glEnd()
279 bgl.glLineWidth(1)
281 if self.shader is not None:
282 bgl.glBegin(bgl.GL_TRIANGLE_FAN)
283 for x,y in curbag:
284 bgl.glVertex2f(x,y)
285 bgl.glEnd()
287 bgl.glColor4f(0.25, 0.35, 0.4, 0.87)
288 font_id = 0
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)
294 font_id = 0
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
300 bgl.glLineWidth(1)
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':
314 self.mode = 0
315 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
317 if event.ctrl and event.type == 'MOUSEMOVE':
318 self.mode = 1
319 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
321 if event.ctrl and event.shift and event.type == 'MOUSEMOVE':
322 self.mode = 2
323 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
325 if event.alt and event.type == 'MOUSEMOVE':
326 self.mode = 3
327 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
329 if event.type == 'LEFT_CTRL' and event.value == 'PRESS':
330 self.mode = 1
331 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
333 if event.shift and event.type == 'LEFT_CTRL' and event.value == 'PRESS':
334 self.mode = 2
335 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
337 if event.type == 'LEFT_CTRL' and event.value == 'RELEASE':
338 self.mode = 0
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:
342 self.mode = 2
343 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
345 if event.ctrl and event.type == 'LEFT_SHIFT' and event.value == 'RELEASE':
346 self.mode = 1
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':
350 self.mode = 0
351 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
353 if event.type == 'LEFT_ALT' and event.value == 'PRESS':
354 self.mode = 3
355 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
357 if event.type == 'LEFT_ALT' and event.value == 'RELEASE':
358 self.mode = 0
359 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
361 if event.type == 'LEFTMOUSE' and event.value == 'PRESS':
362 self.mode = 4
363 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
365 if event.ctrl and event.type == 'LEFTMOUSE' and event.value == 'PRESS':
366 self.mode = 5
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':
370 self.mode = 6
371 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
373 if event.alt and event.type == 'LEFTMOUSE' and event.value == 'PRESS':
374 self.mode = 7
375 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
377 if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
378 self.mode = 0
379 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
381 if event.ctrl and event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
382 self.mode = 1
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':
386 self.mode = 2
387 self.co2d = ((event.mouse_region_x, event.mouse_region_y))
389 if event.alt and event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
390 self.mode = 3
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')
395 return {'FINISHED'}
397 elif event.type == 'ESC':
398 bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
399 return {'CANCELLED'}
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
419 else:
420 self.shader = None
421 self.shadername = 'None'
422 self.selob = bpy.context.selected_objects
424 context.window_manager.modal_handler_add(self)
425 return {'RUNNING_MODAL'}
426 else:
427 self.report({'WARNING'}, "View3D not found, cannot run operator")
428 return {'CANCELLED'}
430 def register():
431 pass
434 def unregister():
435 pass