Remove Blender Internal and legacy viewport from Blender 2.8.
[blender.git] / release / scripts / startup / bl_ui / properties_texture.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import Menu, Panel, UIList
22
23 from bpy.types import (
24     Brush,
25     FreestyleLineStyle,
26     Object,
27     ParticleSettings,
28     Texture,
29 )
30
31 from rna_prop_ui import PropertyPanel
32
33 from .properties_paint_common import brush_texture_settings
34
35
36 class TEXTURE_MT_specials(Menu):
37     bl_label = "Texture Specials"
38     COMPAT_ENGINES = {'BLENDER_RENDER'}
39
40     def draw(self, context):
41         layout = self.layout
42
43         layout.operator("texture.slot_copy", icon='COPYDOWN')
44         layout.operator("texture.slot_paste", icon='PASTEDOWN')
45
46
47 class TEXTURE_UL_texslots(UIList):
48
49     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
50         ma = data
51         slot = item
52         tex = slot.texture if slot else None
53         if self.layout_type in {'DEFAULT', 'COMPACT'}:
54             if tex:
55                 layout.prop(tex, "name", text="", emboss=False, icon_value=icon)
56             else:
57                 layout.label(text="", icon_value=icon)
58         elif self.layout_type == 'GRID':
59             layout.alignment = 'CENTER'
60             layout.label(text="", icon_value=icon)
61
62 def context_tex_datablock(context):
63     idblock = context.brush
64     if idblock:
65         return idblock
66
67     idblock = context.line_style
68     if idblock:
69         return idblock
70
71     if context.particle_system:
72         idblock = context.particle_system.settings
73
74     return idblock
75
76
77 class TextureButtonsPanel:
78     bl_space_type = 'PROPERTIES'
79     bl_region_type = 'WINDOW'
80     bl_context = "texture"
81
82
83 class TEXTURE_PT_preview(TextureButtonsPanel, Panel):
84     bl_label = "Preview"
85     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
86
87     @classmethod
88     def poll(cls, context):
89         tex = context.texture
90         return tex and (tex.type != 'NONE' or tex.use_nodes) and (context.engine in cls.COMPAT_ENGINES)
91
92     def draw(self, context):
93         layout = self.layout
94
95         tex = context.texture
96         slot = getattr(context, "texture_slot", None)
97         idblock = context_tex_datablock(context)
98
99         if idblock:
100             layout.template_preview(tex, parent=idblock, slot=slot)
101         else:
102             layout.template_preview(tex, slot=slot)
103
104         # Show Alpha Button for Brush Textures, see #29502
105         idblock = context_tex_datablock(context)
106         if isinstance(idblock, Brush):
107             layout.prop(tex, "use_preview_alpha")
108
109 class TEXTURE_PT_context(TextureButtonsPanel, Panel):
110     bl_label = ""
111     bl_context = "texture"
112     bl_options = {'HIDE_HEADER'}
113     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
114
115     def draw(self, context):
116         layout = self.layout
117
118         tex = context.texture
119         space = context.space_data
120         pin_id = space.pin_id
121         use_pin_id = space.use_pin_id
122         user = context.texture_user
123
124         if not (use_pin_id and isinstance(pin_id, bpy.types.Texture)):
125             pin_id = None
126
127         if not pin_id:
128             layout.template_texture_user()
129
130         if user or pin_id:
131             layout.separator()
132
133             split = layout.split(percentage=0.65)
134             col = split.column()
135
136             if pin_id:
137                 col.template_ID(space, "pin_id")
138             else:
139                 propname = context.texture_user_property.identifier
140                 col.template_ID(user, propname, new="texture.new")
141
142             if tex:
143                 split = layout.split(percentage=0.2)
144                 split.label(text="Type:")
145                 split.prop(tex, "type", text="")
146
147
148 class TEXTURE_PT_node(TextureButtonsPanel, Panel):
149     bl_label = "Node"
150     bl_context = "texture"
151     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
152
153     @classmethod
154     def poll(cls, context):
155         node = context.texture_node
156         return node and (context.engine in cls.COMPAT_ENGINES)
157
158     def draw(self, context):
159         layout = self.layout
160
161         node = context.texture_node
162         ntree = node.id_data
163         layout.template_node_view(ntree, node, None)
164
165
166 class TEXTURE_PT_node_mapping(TextureButtonsPanel, Panel):
167     bl_label = "Mapping"
168     bl_context = "texture"
169     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
170
171     @classmethod
172     def poll(cls, context):
173         node = context.texture_node
174         # TODO(sergey): perform a faster/nicer check?
175         return node and hasattr(node, 'texture_mapping') and (context.engine in cls.COMPAT_ENGINES)
176
177     def draw(self, context):
178         layout = self.layout
179
180         node = context.texture_node
181
182         mapping = node.texture_mapping
183
184         layout.prop(mapping, "vector_type", expand=True)
185
186         row = layout.row()
187
188         row.column().prop(mapping, "translation")
189         row.column().prop(mapping, "rotation")
190         row.column().prop(mapping, "scale")
191
192         layout.label(text="Projection:")
193
194         row = layout.row()
195         row.prop(mapping, "mapping_x", text="")
196         row.prop(mapping, "mapping_y", text="")
197         row.prop(mapping, "mapping_z", text="")
198
199
200 class TEXTURE_PT_colors(TextureButtonsPanel, Panel):
201     bl_label = "Colors"
202     bl_options = {'DEFAULT_CLOSED'}
203     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
204
205     @classmethod
206     def poll(cls, context):
207         tex = context.texture
208         return tex and (tex.type != 'NONE' or tex.use_nodes) and (context.engine in cls.COMPAT_ENGINES)
209
210     def draw(self, context):
211         layout = self.layout
212
213         tex = context.texture
214
215         layout.prop(tex, "use_color_ramp", text="Ramp")
216         if tex.use_color_ramp:
217             layout.template_color_ramp(tex, "color_ramp", expand=True)
218
219         split = layout.split()
220
221         col = split.column()
222         col.label(text="RGB Multiply:")
223         sub = col.column(align=True)
224         sub.prop(tex, "factor_red", text="R")
225         sub.prop(tex, "factor_green", text="G")
226         sub.prop(tex, "factor_blue", text="B")
227
228         col = split.column()
229         col.label(text="Adjust:")
230         col.prop(tex, "intensity")
231         col.prop(tex, "contrast")
232         col.prop(tex, "saturation")
233
234         col = layout.column()
235         col.prop(tex, "use_clamp", text="Clamp")
236
237
238 class TextureTypePanel(TextureButtonsPanel):
239
240     @classmethod
241     def poll(cls, context):
242         tex = context.texture
243         engine = context.engine
244         return tex and ((tex.type == cls.tex_type and not tex.use_nodes) and (engine in cls.COMPAT_ENGINES))
245
246
247 class TEXTURE_PT_clouds(TextureTypePanel, Panel):
248     bl_label = "Clouds"
249     tex_type = 'CLOUDS'
250     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
251
252     def draw(self, context):
253         layout = self.layout
254
255         tex = context.texture
256
257         layout.row().prop(tex, "cloud_type", expand=True)
258         layout.label(text="Noise:")
259         layout.row().prop(tex, "noise_type", text="Type", expand=True)
260         layout.prop(tex, "noise_basis", text="Basis")
261
262         split = layout.split()
263
264         col = split.column()
265         col.prop(tex, "noise_scale", text="Size")
266         col.prop(tex, "noise_depth", text="Depth")
267
268         split.prop(tex, "nabla", text="Nabla")
269
270
271 class TEXTURE_PT_wood(TextureTypePanel, Panel):
272     bl_label = "Wood"
273     tex_type = 'WOOD'
274     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
275
276     def draw(self, context):
277         layout = self.layout
278
279         tex = context.texture
280
281         layout.row().prop(tex, "noise_basis_2", expand=True)
282         layout.row().prop(tex, "wood_type", expand=True)
283
284         col = layout.column()
285         col.active = tex.wood_type in {'RINGNOISE', 'BANDNOISE'}
286         col.label(text="Noise:")
287         col.row().prop(tex, "noise_type", text="Type", expand=True)
288         layout.prop(tex, "noise_basis", text="Basis")
289
290         split = layout.split()
291         split.active = tex.wood_type in {'RINGNOISE', 'BANDNOISE'}
292
293         col = split.column()
294         col.prop(tex, "noise_scale", text="Size")
295         col.prop(tex, "turbulence")
296
297         split.prop(tex, "nabla")
298
299
300 class TEXTURE_PT_marble(TextureTypePanel, Panel):
301     bl_label = "Marble"
302     tex_type = 'MARBLE'
303     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
304
305     def draw(self, context):
306         layout = self.layout
307
308         tex = context.texture
309
310         layout.row().prop(tex, "marble_type", expand=True)
311         layout.row().prop(tex, "noise_basis_2", expand=True)
312         layout.label(text="Noise:")
313         layout.row().prop(tex, "noise_type", text="Type", expand=True)
314         layout.prop(tex, "noise_basis", text="Basis")
315
316         split = layout.split()
317
318         col = split.column()
319         col.prop(tex, "noise_scale", text="Size")
320         col.prop(tex, "noise_depth", text="Depth")
321
322         col = split.column()
323         col.prop(tex, "turbulence")
324         col.prop(tex, "nabla")
325
326
327 class TEXTURE_PT_magic(TextureTypePanel, Panel):
328     bl_label = "Magic"
329     tex_type = 'MAGIC'
330     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
331
332     def draw(self, context):
333         layout = self.layout
334
335         tex = context.texture
336
337         row = layout.row()
338         row.prop(tex, "noise_depth", text="Depth")
339         row.prop(tex, "turbulence")
340
341
342 class TEXTURE_PT_blend(TextureTypePanel, Panel):
343     bl_label = "Blend"
344     tex_type = 'BLEND'
345     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
346
347     def draw(self, context):
348         layout = self.layout
349
350         tex = context.texture
351
352         layout.prop(tex, "progression")
353
354         sub = layout.row()
355
356         sub.active = (tex.progression in {'LINEAR', 'QUADRATIC', 'EASING', 'RADIAL'})
357         sub.prop(tex, "use_flip_axis", expand=True)
358
359
360 class TEXTURE_PT_stucci(TextureTypePanel, Panel):
361     bl_label = "Stucci"
362     tex_type = 'STUCCI'
363     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
364
365     def draw(self, context):
366         layout = self.layout
367
368         tex = context.texture
369
370         layout.row().prop(tex, "stucci_type", expand=True)
371         layout.label(text="Noise:")
372         layout.row().prop(tex, "noise_type", text="Type", expand=True)
373         layout.prop(tex, "noise_basis", text="Basis")
374
375         row = layout.row()
376         row.prop(tex, "noise_scale", text="Size")
377         row.prop(tex, "turbulence")
378
379
380 class TEXTURE_PT_image(TextureTypePanel, Panel):
381     bl_label = "Image"
382     tex_type = 'IMAGE'
383     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
384
385     def draw(self, context):
386         layout = self.layout
387
388         tex = context.texture
389
390         layout.template_image(tex, "image", tex.image_user)
391
392
393 def texture_filter_common(tex, layout):
394     layout.label(text="Filter:")
395     layout.prop(tex, "filter_type", text="")
396     if tex.use_mipmap and tex.filter_type in {'AREA', 'EWA', 'FELINE'}:
397         if tex.filter_type == 'FELINE':
398             layout.prop(tex, "filter_lightprobes", text="Light Probes")
399         else:
400             layout.prop(tex, "filter_eccentricity", text="Eccentricity")
401
402     layout.prop(tex, "filter_size")
403     layout.prop(tex, "use_filter_size_min")
404
405
406 class TEXTURE_PT_image_sampling(TextureTypePanel, Panel):
407     bl_label = "Image Sampling"
408     bl_options = {'DEFAULT_CLOSED'}
409     tex_type = 'IMAGE'
410     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
411
412     def draw(self, context):
413         layout = self.layout
414
415         idblock = context_tex_datablock(context)
416         tex = context.texture
417         slot = getattr(context, "texture_slot", None)
418
419         split = layout.split()
420
421         col = split.column()
422         col.label(text="Alpha:")
423         row = col.row()
424         row.active = bool(tex.image and tex.image.use_alpha)
425         row.prop(tex, "use_alpha", text="Use")
426         col.prop(tex, "use_calculate_alpha", text="Calculate")
427         col.prop(tex, "invert_alpha", text="Invert")
428         col.separator()
429         col.prop(tex, "use_flip_axis", text="Flip X/Y Axis")
430
431         col = split.column()
432
433         col.prop(tex, "use_mipmap")
434         row = col.row()
435         row.active = tex.use_mipmap
436         row.prop(tex, "use_mipmap_gauss")
437         col.prop(tex, "use_interpolation")
438
439         texture_filter_common(tex, col)
440
441
442 class TEXTURE_PT_image_mapping(TextureTypePanel, Panel):
443     bl_label = "Image Mapping"
444     bl_options = {'DEFAULT_CLOSED'}
445     tex_type = 'IMAGE'
446     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
447
448     def draw(self, context):
449         layout = self.layout
450
451         tex = context.texture
452
453         layout.prop(tex, "extension")
454
455         split = layout.split()
456
457         if tex.extension == 'REPEAT':
458             col = split.column(align=True)
459             col.label(text="Repeat:")
460             col.prop(tex, "repeat_x", text="X")
461             col.prop(tex, "repeat_y", text="Y")
462
463             col = split.column(align=True)
464             col.label(text="Mirror:")
465             row = col.row(align=True)
466             row.prop(tex, "use_mirror_x", text="X")
467             row.active = (tex.repeat_x > 1)
468             row = col.row(align=True)
469             row.prop(tex, "use_mirror_y", text="Y")
470             row.active = (tex.repeat_y > 1)
471             layout.separator()
472
473         elif tex.extension == 'CHECKER':
474             col = split.column(align=True)
475             row = col.row(align=True)
476             row.prop(tex, "use_checker_even", text="Even")
477             row.prop(tex, "use_checker_odd", text="Odd")
478
479             col = split.column()
480             col.prop(tex, "checker_distance", text="Distance")
481
482             layout.separator()
483
484         split = layout.split()
485
486         col = split.column(align=True)
487         # col.prop(tex, "crop_rectangle")
488         col.label(text="Crop Minimum:")
489         col.prop(tex, "crop_min_x", text="X")
490         col.prop(tex, "crop_min_y", text="Y")
491
492         col = split.column(align=True)
493         col.label(text="Crop Maximum:")
494         col.prop(tex, "crop_max_x", text="X")
495         col.prop(tex, "crop_max_y", text="Y")
496
497
498 class TEXTURE_PT_musgrave(TextureTypePanel, Panel):
499     bl_label = "Musgrave"
500     tex_type = 'MUSGRAVE'
501     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
502
503     def draw(self, context):
504         layout = self.layout
505
506         tex = context.texture
507
508         layout.prop(tex, "musgrave_type")
509
510         split = layout.split()
511
512         col = split.column()
513         col.prop(tex, "dimension_max", text="Dimension")
514         col.prop(tex, "lacunarity")
515         col.prop(tex, "octaves")
516
517         musgrave_type = tex.musgrave_type
518         col = split.column()
519         if musgrave_type in {'HETERO_TERRAIN', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}:
520             col.prop(tex, "offset")
521         col.prop(tex, "noise_intensity", text="Intensity")
522         if musgrave_type in {'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}:
523             col.prop(tex, "gain")
524
525         layout.label(text="Noise:")
526
527         layout.prop(tex, "noise_basis", text="Basis")
528
529         row = layout.row()
530         row.prop(tex, "noise_scale", text="Size")
531         row.prop(tex, "nabla")
532
533
534 class TEXTURE_PT_voronoi(TextureTypePanel, Panel):
535     bl_label = "Voronoi"
536     tex_type = 'VORONOI'
537     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
538
539     def draw(self, context):
540         layout = self.layout
541
542         tex = context.texture
543
544         split = layout.split()
545
546         col = split.column()
547         col.label(text="Distance Metric:")
548         col.prop(tex, "distance_metric", text="")
549         sub = col.column()
550         sub.active = tex.distance_metric == 'MINKOVSKY'
551         sub.prop(tex, "minkovsky_exponent", text="Exponent")
552         col.label(text="Coloring:")
553         col.prop(tex, "color_mode", text="")
554         col.prop(tex, "noise_intensity", text="Intensity")
555
556         col = split.column()
557         sub = col.column(align=True)
558         sub.label(text="Feature Weights:")
559         sub.prop(tex, "weight_1", text="1", slider=True)
560         sub.prop(tex, "weight_2", text="2", slider=True)
561         sub.prop(tex, "weight_3", text="3", slider=True)
562         sub.prop(tex, "weight_4", text="4", slider=True)
563
564         layout.label(text="Noise:")
565         row = layout.row()
566         row.prop(tex, "noise_scale", text="Size")
567         row.prop(tex, "nabla")
568
569
570 class TEXTURE_PT_distortednoise(TextureTypePanel, Panel):
571     bl_label = "Distorted Noise"
572     tex_type = 'DISTORTED_NOISE'
573     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
574
575     def draw(self, context):
576         layout = self.layout
577
578         tex = context.texture
579
580         layout.prop(tex, "noise_distortion")
581         layout.prop(tex, "noise_basis", text="Basis")
582
583         split = layout.split()
584
585         col = split.column()
586         col.prop(tex, "distortion", text="Distortion")
587         col.prop(tex, "noise_scale", text="Size")
588
589         split.prop(tex, "nabla")
590
591
592 class TextureSlotPanel(TextureButtonsPanel):
593     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
594
595     @classmethod
596     def poll(cls, context):
597         if not hasattr(context, "texture_slot"):
598             return False
599
600         return (context.engine in cls.COMPAT_ENGINES)
601
602
603 class TEXTURE_PT_mapping(TextureSlotPanel, Panel):
604     bl_label = "Mapping"
605     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
606
607     @classmethod
608     def poll(cls, context):
609         idblock = context_tex_datablock(context)
610         if isinstance(idblock, Brush) and not context.sculpt_object:
611             return False
612
613         if not getattr(context, "texture_slot", None):
614             return False
615
616         engine = context.engine
617         return (engine in cls.COMPAT_ENGINES)
618
619     def draw(self, context):
620         layout = self.layout
621
622         idblock = context_tex_datablock(context)
623
624         tex = context.texture_slot
625
626         if not isinstance(idblock, Brush):
627             split = layout.split(percentage=0.3)
628             col = split.column()
629             col.label(text="Coordinates:")
630             col = split.column()
631             col.prop(tex, "texture_coords", text="")
632
633             if tex.texture_coords == 'ORCO':
634                 """
635                 ob = context.object
636                 if ob and ob.type == 'MESH':
637                     split = layout.split(percentage=0.3)
638                     split.label(text="Mesh:")
639                     split.prop(ob.data, "texco_mesh", text="")
640                 """
641             elif tex.texture_coords == 'UV':
642                 split = layout.split(percentage=0.3)
643                 split.label(text="Map:")
644                 ob = context.object
645                 if ob and ob.type == 'MESH':
646                     split.prop_search(tex, "uv_layer", ob.data, "uv_layers", text="")
647                 else:
648                     split.prop(tex, "uv_layer", text="")
649
650             elif tex.texture_coords == 'OBJECT':
651                 split = layout.split(percentage=0.3)
652                 split.label(text="Object:")
653                 split.prop(tex, "object", text="")
654
655             elif tex.texture_coords == 'ALONG_STROKE':
656                 split = layout.split(percentage=0.3)
657                 split.label(text="Use Tips:")
658                 split.prop(tex, "use_tips", text="")
659
660         if isinstance(idblock, Brush):
661             if context.sculpt_object or context.image_paint_object:
662                 brush_texture_settings(layout, idblock, context.sculpt_object)
663         else:
664             if isinstance(idblock, FreestyleLineStyle):
665                 split = layout.split(percentage=0.3)
666                 split.label(text="Projection:")
667                 split.prop(tex, "mapping", text="")
668
669                 split = layout.split(percentage=0.3)
670                 split.separator()
671                 row = split.row()
672                 row.prop(tex, "mapping_x", text="")
673                 row.prop(tex, "mapping_y", text="")
674                 row.prop(tex, "mapping_z", text="")
675
676             row = layout.row()
677             row.column().prop(tex, "offset")
678             row.column().prop(tex, "scale")
679
680
681 class TEXTURE_PT_influence(TextureSlotPanel, Panel):
682     bl_label = "Influence"
683     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
684
685     @classmethod
686     def poll(cls, context):
687         idblock = context_tex_datablock(context)
688         if isinstance(idblock, Brush):
689             return False
690
691         if not getattr(context, "texture_slot", None):
692             return False
693
694         engine = context.engine
695         return (engine in cls.COMPAT_ENGINES)
696
697     def draw(self, context):
698
699         layout = self.layout
700
701         idblock = context_tex_datablock(context)
702
703         tex = context.texture_slot
704
705         def factor_but(layout, toggle, factor, name):
706             row = layout.row(align=True)
707             row.prop(tex, toggle, text="")
708             sub = row.row(align=True)
709             sub.active = getattr(tex, toggle)
710             sub.prop(tex, factor, text=name, slider=True)
711             return sub  # XXX, temp. use_map_normal needs to override.
712
713         if isinstance(idblock, ParticleSettings):
714             split = layout.split()
715
716             col = split.column()
717             col.label(text="General:")
718             factor_but(col, "use_map_time", "time_factor", "Time")
719             factor_but(col, "use_map_life", "life_factor", "Lifetime")
720             factor_but(col, "use_map_density", "density_factor", "Density")
721             factor_but(col, "use_map_size", "size_factor", "Size")
722
723             col = split.column()
724             col.label(text="Physics:")
725             factor_but(col, "use_map_velocity", "velocity_factor", "Velocity")
726             factor_but(col, "use_map_damp", "damp_factor", "Damp")
727             factor_but(col, "use_map_gravity", "gravity_factor", "Gravity")
728             factor_but(col, "use_map_field", "field_factor", "Force Fields")
729
730             layout.label(text="Hair:")
731
732             split = layout.split()
733
734             col = split.column()
735             factor_but(col, "use_map_length", "length_factor", "Length")
736             factor_but(col, "use_map_clump", "clump_factor", "Clump")
737             factor_but(col, "use_map_twist", "twist_factor", "Twist")
738
739             col = split.column()
740             factor_but(col, "use_map_kink_amp", "kink_amp_factor", "Kink Amplitude")
741             factor_but(col, "use_map_kink_freq", "kink_freq_factor", "Kink Frequency")
742             factor_but(col, "use_map_rough", "rough_factor", "Rough")
743
744         elif isinstance(idblock, FreestyleLineStyle):
745             split = layout.split()
746
747             col = split.column()
748             factor_but(col, "use_map_color_diffuse", "diffuse_color_factor", "Color")
749             col = split.column()
750             factor_but(col, "use_map_alpha", "alpha_factor", "Alpha")
751
752         layout.separator()
753
754         if not isinstance(idblock, ParticleSettings):
755             split = layout.split()
756
757             col = split.column()
758             col.prop(tex, "blend_type", text="Blend")
759             col.prop(tex, "use_rgb_to_intensity")
760             # color is used on gray-scale textures even when use_rgb_to_intensity is disabled.
761             col.prop(tex, "color", text="")
762
763             col = split.column()
764             col.prop(tex, "invert", text="Negative")
765             col.prop(tex, "use_stencil")
766
767
768 class TEXTURE_PT_custom_props(TextureButtonsPanel, PropertyPanel, Panel):
769     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
770     _context_path = "texture"
771     _property_type = Texture
772
773     @classmethod
774     def poll(cls, context):
775         return context.texture and (context.engine in cls.COMPAT_ENGINES)
776
777
778 classes = (
779     TEXTURE_MT_specials,
780     TEXTURE_UL_texslots,
781     TEXTURE_PT_preview,
782     TEXTURE_PT_context,
783     TEXTURE_PT_node,
784     TEXTURE_PT_node_mapping,
785     TEXTURE_PT_mapping,
786     TEXTURE_PT_influence,
787     TEXTURE_PT_colors,
788     TEXTURE_PT_clouds,
789     TEXTURE_PT_wood,
790     TEXTURE_PT_marble,
791     TEXTURE_PT_magic,
792     TEXTURE_PT_blend,
793     TEXTURE_PT_stucci,
794     TEXTURE_PT_image,
795     TEXTURE_PT_image_sampling,
796     TEXTURE_PT_image_mapping,
797     TEXTURE_PT_musgrave,
798     TEXTURE_PT_voronoi,
799     TEXTURE_PT_distortednoise,
800     TEXTURE_PT_custom_props,
801 )
802
803 if __name__ == "__main__":  # only for live edit.
804     from bpy.utils import register_class
805     for cls in classes:
806         register_class(cls)