properties_freestyle.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  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. # <pep8 compliant>
  19. import bpy
  20. from bpy.types import Menu, Panel, UIList
  21. # Render properties
  22. class RenderFreestyleButtonsPanel:
  23. bl_space_type = 'PROPERTIES'
  24. bl_region_type = 'WINDOW'
  25. bl_context = "render"
  26. # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
  27. @classmethod
  28. def poll(cls, context):
  29. scene = context.scene
  30. with_freestyle = bpy.app.build_options.freestyle
  31. return scene and with_freestyle and(context.engine in cls.COMPAT_ENGINES)
  32. class RENDER_PT_freestyle(RenderFreestyleButtonsPanel, Panel):
  33. bl_label = "Freestyle"
  34. bl_options = {'DEFAULT_CLOSED'}
  35. bl_order = 10
  36. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
  37. def draw_header(self, context):
  38. rd = context.scene.render
  39. self.layout.prop(rd, "use_freestyle", text="")
  40. def draw(self, context):
  41. layout = self.layout
  42. layout.use_property_split = True
  43. layout.use_property_decorate = False # No animation.
  44. rd = context.scene.render
  45. layout.active = rd.use_freestyle
  46. layout.prop(rd, "line_thickness_mode", expand=True)
  47. if (rd.line_thickness_mode == 'ABSOLUTE'):
  48. layout.prop(rd, "line_thickness")
  49. # Render layer properties
  50. class ViewLayerFreestyleButtonsPanel:
  51. bl_space_type = 'PROPERTIES'
  52. bl_region_type = 'WINDOW'
  53. bl_context = "view_layer"
  54. bl_order = 10
  55. # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
  56. @classmethod
  57. def poll(cls, context):
  58. scene = context.scene
  59. rd = scene.render
  60. with_freestyle = bpy.app.build_options.freestyle
  61. return (scene and with_freestyle and rd.use_freestyle and
  62. (context.engine in cls.COMPAT_ENGINES))
  63. class ViewLayerFreestyleEditorButtonsPanel(ViewLayerFreestyleButtonsPanel):
  64. # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
  65. @classmethod
  66. def poll(cls, context):
  67. if not super().poll(context):
  68. return False
  69. view_layer = context.view_layer
  70. return view_layer and view_layer.freestyle_settings.mode == 'EDITOR'
  71. class VIEWLAYER_UL_linesets(UIList):
  72. def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, index):
  73. lineset = item
  74. if self.layout_type in {'DEFAULT', 'COMPACT'}:
  75. layout.prop(lineset, "name", text="", emboss=False, icon_value=icon)
  76. layout.prop(lineset, "show_render", text="", index=index)
  77. elif self.layout_type == 'GRID':
  78. layout.alignment = 'CENTER'
  79. layout.label(text="", icon_value=icon)
  80. class RENDER_MT_lineset_context_menu(Menu):
  81. bl_label = "Lineset Specials"
  82. def draw(self, _context):
  83. layout = self.layout
  84. layout.operator("scene.freestyle_lineset_copy", icon='COPYDOWN')
  85. layout.operator("scene.freestyle_lineset_paste", icon='PASTEDOWN')
  86. class VIEWLAYER_PT_freestyle(ViewLayerFreestyleButtonsPanel, Panel):
  87. bl_label = "Freestyle"
  88. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
  89. def draw(self, context):
  90. layout = self.layout
  91. view_layer = context.view_layer
  92. freestyle = view_layer.freestyle_settings
  93. layout.active = view_layer.use_freestyle
  94. row = layout.row()
  95. layout.prop(freestyle, "mode", text="Control Mode")
  96. layout.prop(freestyle, "use_view_map_cache", text="View Map Cache")
  97. layout.label(text="Edge Detection Options:")
  98. split = layout.split()
  99. col = split.column()
  100. col.prop(freestyle, "crease_angle")
  101. col.prop(freestyle, "use_culling")
  102. col.prop(freestyle, "use_advanced_options")
  103. col = split.column()
  104. col.prop(freestyle, "use_smoothness")
  105. if freestyle.mode == 'SCRIPT':
  106. col.prop(freestyle, "use_material_boundaries")
  107. # Advanced options are hidden by default to warn new users
  108. if freestyle.use_advanced_options:
  109. if freestyle.mode == 'SCRIPT':
  110. row = layout.row()
  111. row.prop(freestyle, "use_ridges_and_valleys")
  112. row.prop(freestyle, "use_suggestive_contours")
  113. row = layout.row()
  114. row.prop(freestyle, "sphere_radius")
  115. row.prop(freestyle, "kr_derivative_epsilon")
  116. if freestyle.mode == 'SCRIPT':
  117. row = layout.row()
  118. row.label(text="Style modules:")
  119. row.operator("scene.freestyle_module_add", text="Add")
  120. for module in freestyle.modules:
  121. box = layout.box()
  122. box.context_pointer_set("freestyle_module", module)
  123. row = box.row(align=True)
  124. row.prop(module, "use", text="")
  125. row.prop(module, "script", text="")
  126. row.operator("scene.freestyle_module_open", icon='FILEBROWSER', text="")
  127. row.operator("scene.freestyle_module_remove", icon='X', text="")
  128. row.operator("scene.freestyle_module_move", icon='TRIA_UP', text="").direction = 'UP'
  129. row.operator("scene.freestyle_module_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
  130. class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel):
  131. bl_label = "Freestyle Line Set"
  132. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
  133. def draw_edge_type_buttons(self, box, lineset, edge_type):
  134. # property names
  135. select_edge_type = "select_" + edge_type
  136. exclude_edge_type = "exclude_" + edge_type
  137. # draw edge type buttons
  138. row = box.row(align=True)
  139. row.prop(lineset, select_edge_type)
  140. sub = row.column(align=True)
  141. sub.prop(lineset, exclude_edge_type, text="")
  142. sub.active = getattr(lineset, select_edge_type)
  143. def draw(self, context):
  144. layout = self.layout
  145. view_layer = context.view_layer
  146. freestyle = view_layer.freestyle_settings
  147. lineset = freestyle.linesets.active
  148. layout.active = view_layer.use_freestyle
  149. row = layout.row()
  150. rows = 4 if lineset else 2
  151. row.template_list(
  152. "VIEWLAYER_UL_linesets",
  153. "",
  154. freestyle,
  155. "linesets",
  156. freestyle.linesets,
  157. "active_index",
  158. rows=rows,
  159. )
  160. sub = row.column(align=True)
  161. sub.operator("scene.freestyle_lineset_add", icon='ADD', text="")
  162. sub.operator("scene.freestyle_lineset_remove", icon='REMOVE', text="")
  163. sub.menu("RENDER_MT_lineset_context_menu", icon='DOWNARROW_HLT', text="")
  164. if lineset:
  165. sub.separator()
  166. sub.separator()
  167. sub.operator("scene.freestyle_lineset_move", icon='TRIA_UP', text="").direction = 'UP'
  168. sub.operator("scene.freestyle_lineset_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
  169. col = layout.column()
  170. col.label(text="Selection By:")
  171. row = col.row(align=True)
  172. row.prop(lineset, "select_by_visibility", text="Visibility", toggle=True)
  173. row.prop(lineset, "select_by_edge_types", text="Edge Types", toggle=True)
  174. row.prop(lineset, "select_by_face_marks", text="Face Marks", toggle=True)
  175. row.prop(lineset, "select_by_collection", text="Collection", toggle=True)
  176. row.prop(lineset, "select_by_image_border", text="Image Border", toggle=True)
  177. if lineset.select_by_visibility:
  178. col.label(text="Visibility:")
  179. row = col.row(align=True)
  180. row.prop(lineset, "visibility", expand=True)
  181. if lineset.visibility == 'RANGE':
  182. row = col.row(align=True)
  183. row.prop(lineset, "qi_start")
  184. row.prop(lineset, "qi_end")
  185. if lineset.select_by_edge_types:
  186. col.label(text="Edge Types:")
  187. row = col.row()
  188. row.prop(lineset, "edge_type_negation", expand=True)
  189. row.prop(lineset, "edge_type_combination", expand=True)
  190. split = col.split()
  191. sub = split.column()
  192. self.draw_edge_type_buttons(sub, lineset, "silhouette")
  193. self.draw_edge_type_buttons(sub, lineset, "border")
  194. self.draw_edge_type_buttons(sub, lineset, "contour")
  195. self.draw_edge_type_buttons(sub, lineset, "suggestive_contour")
  196. self.draw_edge_type_buttons(sub, lineset, "ridge_valley")
  197. sub = split.column()
  198. self.draw_edge_type_buttons(sub, lineset, "crease")
  199. self.draw_edge_type_buttons(sub, lineset, "edge_mark")
  200. self.draw_edge_type_buttons(sub, lineset, "external_contour")
  201. self.draw_edge_type_buttons(sub, lineset, "material_boundary")
  202. if lineset.select_by_face_marks:
  203. col.label(text="Face Marks:")
  204. row = col.row()
  205. row.prop(lineset, "face_mark_negation", expand=True)
  206. row.prop(lineset, "face_mark_condition", expand=True)
  207. if lineset.select_by_collection:
  208. col.label(text="Collection:")
  209. row = col.row()
  210. row.prop(lineset, "collection", text="")
  211. row.prop(lineset, "collection_negation", expand=True)
  212. class VIEWLAYER_PT_freestyle_linestyle(ViewLayerFreestyleEditorButtonsPanel, Panel):
  213. bl_label = "Freestyle Line Style"
  214. bl_options = {'DEFAULT_CLOSED'}
  215. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
  216. def draw_modifier_box_header(self, box, modifier):
  217. row = box.row()
  218. row.context_pointer_set("modifier", modifier)
  219. if modifier.expanded:
  220. icon = 'TRIA_DOWN'
  221. else:
  222. icon = 'TRIA_RIGHT'
  223. row.prop(modifier, "expanded", text="", icon=icon, emboss=False)
  224. # TODO: Use icons rather than text label, would save some room!
  225. row.label(text=modifier.rna_type.name)
  226. row.prop(modifier, "name", text="")
  227. if modifier.use:
  228. icon = 'RESTRICT_RENDER_OFF'
  229. else:
  230. icon = 'RESTRICT_RENDER_ON'
  231. row.prop(modifier, "use", text="", icon=icon)
  232. sub = row.row(align=True)
  233. sub.operator("scene.freestyle_modifier_copy", icon='NONE', text="Copy")
  234. sub.operator("scene.freestyle_modifier_move", icon='TRIA_UP', text="").direction = 'UP'
  235. sub.operator("scene.freestyle_modifier_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
  236. sub.operator("scene.freestyle_modifier_remove", icon='X', text="")
  237. def draw_modifier_box_error(self, box, _modifier, message):
  238. row = box.row()
  239. row.label(text=message, icon='ERROR')
  240. def draw_modifier_common(self, box, modifier):
  241. row = box.row()
  242. row.prop(modifier, "blend", text="")
  243. row.prop(modifier, "influence")
  244. def draw_modifier_color_ramp_common(self, box, modifier, has_range):
  245. box.template_color_ramp(modifier, "color_ramp", expand=True)
  246. if has_range:
  247. row = box.row(align=True)
  248. row.prop(modifier, "range_min")
  249. row.prop(modifier, "range_max")
  250. def draw_modifier_curve_common(self, box, modifier, has_range, has_value):
  251. row = box.row()
  252. row.prop(modifier, "mapping", text="")
  253. sub = row.column()
  254. sub.prop(modifier, "invert")
  255. if modifier.mapping == 'CURVE':
  256. sub.active = False
  257. box.template_curve_mapping(modifier, "curve")
  258. if has_range:
  259. row = box.row(align=True)
  260. row.prop(modifier, "range_min")
  261. row.prop(modifier, "range_max")
  262. if has_value:
  263. row = box.row(align=True)
  264. row.prop(modifier, "value_min")
  265. row.prop(modifier, "value_max")
  266. def draw_color_modifier(self, context, modifier):
  267. layout = self.layout
  268. col = layout.column(align=True)
  269. self.draw_modifier_box_header(col.box(), modifier)
  270. if modifier.expanded:
  271. box = col.box()
  272. self.draw_modifier_common(box, modifier)
  273. if modifier.type == 'ALONG_STROKE':
  274. self.draw_modifier_color_ramp_common(box, modifier, False)
  275. elif modifier.type == 'DISTANCE_FROM_OBJECT':
  276. box.prop(modifier, "target")
  277. self.draw_modifier_color_ramp_common(box, modifier, True)
  278. prop = box.operator("scene.freestyle_fill_range_by_selection")
  279. prop.type = 'COLOR'
  280. prop.name = modifier.name
  281. elif modifier.type == 'DISTANCE_FROM_CAMERA':
  282. self.draw_modifier_color_ramp_common(box, modifier, True)
  283. prop = box.operator("scene.freestyle_fill_range_by_selection")
  284. prop.type = 'COLOR'
  285. prop.name = modifier.name
  286. elif modifier.type == 'MATERIAL':
  287. row = box.row()
  288. row.prop(modifier, "material_attribute", text="")
  289. sub = row.column()
  290. sub.prop(modifier, "use_ramp")
  291. if modifier.material_attribute in {'LINE', 'DIFF', 'SPEC'}:
  292. sub.active = True
  293. show_ramp = modifier.use_ramp
  294. else:
  295. sub.active = False
  296. show_ramp = True
  297. if show_ramp:
  298. self.draw_modifier_color_ramp_common(box, modifier, False)
  299. elif modifier.type == 'TANGENT':
  300. self.draw_modifier_color_ramp_common(box, modifier, False)
  301. elif modifier.type == 'NOISE':
  302. self.draw_modifier_color_ramp_common(box, modifier, False)
  303. row = box.row(align=False)
  304. row.prop(modifier, "amplitude")
  305. row.prop(modifier, "period")
  306. row.prop(modifier, "seed")
  307. elif modifier.type == 'CREASE_ANGLE':
  308. self.draw_modifier_color_ramp_common(box, modifier, False)
  309. row = box.row(align=True)
  310. row.prop(modifier, "angle_min")
  311. row.prop(modifier, "angle_max")
  312. elif modifier.type == 'CURVATURE_3D':
  313. self.draw_modifier_color_ramp_common(box, modifier, False)
  314. row = box.row(align=True)
  315. row.prop(modifier, "curvature_min")
  316. row.prop(modifier, "curvature_max")
  317. freestyle = context.view_layer.freestyle_settings
  318. if not freestyle.use_smoothness:
  319. message = "Enable Face Smoothness to use this modifier"
  320. self.draw_modifier_box_error(col.box(), modifier, message)
  321. def draw_alpha_modifier(self, context, modifier):
  322. layout = self.layout
  323. col = layout.column(align=True)
  324. self.draw_modifier_box_header(col.box(), modifier)
  325. if modifier.expanded:
  326. box = col.box()
  327. self.draw_modifier_common(box, modifier)
  328. if modifier.type == 'ALONG_STROKE':
  329. self.draw_modifier_curve_common(box, modifier, False, False)
  330. elif modifier.type == 'DISTANCE_FROM_OBJECT':
  331. box.prop(modifier, "target")
  332. self.draw_modifier_curve_common(box, modifier, True, False)
  333. prop = box.operator("scene.freestyle_fill_range_by_selection")
  334. prop.type = 'ALPHA'
  335. prop.name = modifier.name
  336. elif modifier.type == 'DISTANCE_FROM_CAMERA':
  337. self.draw_modifier_curve_common(box, modifier, True, False)
  338. prop = box.operator("scene.freestyle_fill_range_by_selection")
  339. prop.type = 'ALPHA'
  340. prop.name = modifier.name
  341. elif modifier.type == 'MATERIAL':
  342. box.prop(modifier, "material_attribute", text="")
  343. self.draw_modifier_curve_common(box, modifier, False, False)
  344. elif modifier.type == 'TANGENT':
  345. self.draw_modifier_curve_common(box, modifier, False, False)
  346. elif modifier.type == 'NOISE':
  347. self.draw_modifier_curve_common(box, modifier, False, False)
  348. row = box.row(align=False)
  349. row.prop(modifier, "amplitude")
  350. row.prop(modifier, "period")
  351. row.prop(modifier, "seed")
  352. elif modifier.type == 'CREASE_ANGLE':
  353. self.draw_modifier_curve_common(box, modifier, False, False)
  354. row = box.row(align=True)
  355. row.prop(modifier, "angle_min")
  356. row.prop(modifier, "angle_max")
  357. elif modifier.type == 'CURVATURE_3D':
  358. self.draw_modifier_curve_common(box, modifier, False, False)
  359. row = box.row(align=True)
  360. row.prop(modifier, "curvature_min")
  361. row.prop(modifier, "curvature_max")
  362. freestyle = context.view_layer.freestyle_settings
  363. if not freestyle.use_smoothness:
  364. message = "Enable Face Smoothness to use this modifier"
  365. self.draw_modifier_box_error(col.box(), modifier, message)
  366. def draw_thickness_modifier(self, context, modifier):
  367. layout = self.layout
  368. col = layout.column(align=True)
  369. self.draw_modifier_box_header(col.box(), modifier)
  370. if modifier.expanded:
  371. box = col.box()
  372. self.draw_modifier_common(box, modifier)
  373. if modifier.type == 'ALONG_STROKE':
  374. self.draw_modifier_curve_common(box, modifier, False, True)
  375. elif modifier.type == 'DISTANCE_FROM_OBJECT':
  376. box.prop(modifier, "target")
  377. self.draw_modifier_curve_common(box, modifier, True, True)
  378. prop = box.operator("scene.freestyle_fill_range_by_selection")
  379. prop.type = 'THICKNESS'
  380. prop.name = modifier.name
  381. elif modifier.type == 'DISTANCE_FROM_CAMERA':
  382. self.draw_modifier_curve_common(box, modifier, True, True)
  383. prop = box.operator("scene.freestyle_fill_range_by_selection")
  384. prop.type = 'THICKNESS'
  385. prop.name = modifier.name
  386. elif modifier.type == 'MATERIAL':
  387. box.prop(modifier, "material_attribute", text="")
  388. self.draw_modifier_curve_common(box, modifier, False, True)
  389. elif modifier.type == 'CALLIGRAPHY':
  390. box.prop(modifier, "orientation")
  391. row = box.row(align=True)
  392. row.prop(modifier, "thickness_min")
  393. row.prop(modifier, "thickness_max")
  394. elif modifier.type == 'TANGENT':
  395. self.draw_modifier_curve_common(box, modifier, False, False)
  396. self.mapping = 'CURVE'
  397. row = box.row(align=True)
  398. row.prop(modifier, "thickness_min")
  399. row.prop(modifier, "thickness_max")
  400. elif modifier.type == 'NOISE':
  401. row = box.row(align=False)
  402. row.prop(modifier, "amplitude")
  403. row.prop(modifier, "period")
  404. row = box.row(align=False)
  405. row.prop(modifier, "seed")
  406. row.prop(modifier, "use_asymmetric")
  407. elif modifier.type == 'CREASE_ANGLE':
  408. self.draw_modifier_curve_common(box, modifier, False, False)
  409. row = box.row(align=True)
  410. row.prop(modifier, "thickness_min")
  411. row.prop(modifier, "thickness_max")
  412. row = box.row(align=True)
  413. row.prop(modifier, "angle_min")
  414. row.prop(modifier, "angle_max")
  415. elif modifier.type == 'CURVATURE_3D':
  416. self.draw_modifier_curve_common(box, modifier, False, False)
  417. row = box.row(align=True)
  418. row.prop(modifier, "thickness_min")
  419. row.prop(modifier, "thickness_max")
  420. row = box.row(align=True)
  421. row.prop(modifier, "curvature_min")
  422. row.prop(modifier, "curvature_max")
  423. freestyle = context.view_layer.freestyle_settings
  424. if not freestyle.use_smoothness:
  425. message = "Enable Face Smoothness to use this modifier"
  426. self.draw_modifier_box_error(col.box(), modifier, message)
  427. def draw_geometry_modifier(self, _context, modifier):
  428. layout = self.layout
  429. col = layout.column(align=True)
  430. self.draw_modifier_box_header(col.box(), modifier)
  431. if modifier.expanded:
  432. box = col.box()
  433. if modifier.type == 'SAMPLING':
  434. box.prop(modifier, "sampling")
  435. elif modifier.type == 'BEZIER_CURVE':
  436. box.prop(modifier, "error")
  437. elif modifier.type == 'SINUS_DISPLACEMENT':
  438. split = box.split()
  439. col = split.column()
  440. col.prop(modifier, "wavelength")
  441. col.prop(modifier, "amplitude")
  442. col = split.column()
  443. col.prop(modifier, "phase")
  444. elif modifier.type == 'SPATIAL_NOISE':
  445. split = box.split()
  446. col = split.column()
  447. col.prop(modifier, "amplitude")
  448. col.prop(modifier, "scale")
  449. col.prop(modifier, "octaves")
  450. col = split.column()
  451. col.prop(modifier, "smooth")
  452. col.prop(modifier, "use_pure_random")
  453. elif modifier.type == 'PERLIN_NOISE_1D':
  454. split = box.split()
  455. col = split.column()
  456. col.prop(modifier, "frequency")
  457. col.prop(modifier, "amplitude")
  458. col.prop(modifier, "seed")
  459. col = split.column()
  460. col.prop(modifier, "octaves")
  461. col.prop(modifier, "angle")
  462. elif modifier.type == 'PERLIN_NOISE_2D':
  463. split = box.split()
  464. col = split.column()
  465. col.prop(modifier, "frequency")
  466. col.prop(modifier, "amplitude")
  467. col.prop(modifier, "seed")
  468. col = split.column()
  469. col.prop(modifier, "octaves")
  470. col.prop(modifier, "angle")
  471. elif modifier.type == 'BACKBONE_STRETCHER':
  472. box.prop(modifier, "backbone_length")
  473. elif modifier.type == 'TIP_REMOVER':
  474. box.prop(modifier, "tip_length")
  475. elif modifier.type == 'POLYGONIZATION':
  476. box.prop(modifier, "error")
  477. elif modifier.type == 'GUIDING_LINES':
  478. box.prop(modifier, "offset")
  479. elif modifier.type == 'BLUEPRINT':
  480. row = box.row()
  481. row.prop(modifier, "shape", expand=True)
  482. box.prop(modifier, "rounds")
  483. row = box.row()
  484. if modifier.shape in {'CIRCLES', 'ELLIPSES'}:
  485. row.prop(modifier, "random_radius")
  486. row.prop(modifier, "random_center")
  487. elif modifier.shape == 'SQUARES':
  488. row.prop(modifier, "backbone_length")
  489. row.prop(modifier, "random_backbone")
  490. elif modifier.type == '2D_OFFSET':
  491. row = box.row(align=True)
  492. row.prop(modifier, "start")
  493. row.prop(modifier, "end")
  494. row = box.row(align=True)
  495. row.prop(modifier, "x")
  496. row.prop(modifier, "y")
  497. elif modifier.type == '2D_TRANSFORM':
  498. box.prop(modifier, "pivot")
  499. if modifier.pivot == 'PARAM':
  500. box.prop(modifier, "pivot_u")
  501. elif modifier.pivot == 'ABSOLUTE':
  502. row = box.row(align=True)
  503. row.prop(modifier, "pivot_x")
  504. row.prop(modifier, "pivot_y")
  505. row = box.row(align=True)
  506. row.prop(modifier, "scale_x")
  507. row.prop(modifier, "scale_y")
  508. box.prop(modifier, "angle")
  509. elif modifier.type == 'SIMPLIFICATION':
  510. box.prop(modifier, "tolerance")
  511. def draw(self, context):
  512. layout = self.layout
  513. view_layer = context.view_layer
  514. lineset = view_layer.freestyle_settings.linesets.active
  515. layout.active = view_layer.use_freestyle
  516. if lineset is None:
  517. return
  518. linestyle = lineset.linestyle
  519. layout.template_ID(lineset, "linestyle", new="scene.freestyle_linestyle_new")
  520. if linestyle is None:
  521. return
  522. row = layout.row(align=True)
  523. row.prop(linestyle, "panel", expand=True)
  524. if linestyle.panel == 'STROKES':
  525. # Chaining
  526. layout.prop(linestyle, "use_chaining", text="Chaining:")
  527. split = layout.split(align=True)
  528. split.active = linestyle.use_chaining
  529. # First column
  530. col = split.column()
  531. col.active = linestyle.use_chaining
  532. col.prop(linestyle, "chaining", text="")
  533. if linestyle.chaining == 'SKETCHY':
  534. col.prop(linestyle, "rounds")
  535. # Second column
  536. col = split.column()
  537. col.prop(linestyle, "use_same_object")
  538. # Splitting
  539. layout.label(text="Splitting:")
  540. split = layout.split(align=True)
  541. # First column
  542. col = split.column()
  543. row = col.row(align=True)
  544. row.prop(linestyle, "use_angle_min", text="")
  545. sub = row.row()
  546. sub.active = linestyle.use_angle_min
  547. sub.prop(linestyle, "angle_min")
  548. row = col.row(align=True)
  549. row.prop(linestyle, "use_angle_max", text="")
  550. sub = row.row()
  551. sub.active = linestyle.use_angle_max
  552. sub.prop(linestyle, "angle_max")
  553. # Second column
  554. col = split.column()
  555. row = col.row(align=True)
  556. row.prop(linestyle, "use_split_length", text="")
  557. sub = row.row()
  558. sub.active = linestyle.use_split_length
  559. sub.prop(linestyle, "split_length", text="2D Length")
  560. row = col.row(align=True)
  561. row.prop(linestyle, "material_boundary")
  562. # End of columns
  563. row = layout.row(align=True)
  564. row.prop(linestyle, "use_split_pattern", text="")
  565. sub = row.row(align=True)
  566. sub.active = linestyle.use_split_pattern
  567. sub.prop(linestyle, "split_dash1", text="D1")
  568. sub.prop(linestyle, "split_gap1", text="G1")
  569. sub.prop(linestyle, "split_dash2", text="D2")
  570. sub.prop(linestyle, "split_gap2", text="G2")
  571. sub.prop(linestyle, "split_dash3", text="D3")
  572. sub.prop(linestyle, "split_gap3", text="G3")
  573. # Sorting
  574. layout.prop(linestyle, "use_sorting", text="Sorting:")
  575. col = layout.column()
  576. col.active = linestyle.use_sorting
  577. row = col.row(align=True)
  578. row.prop(linestyle, "sort_key", text="")
  579. sub = row.row()
  580. sub.active = linestyle.sort_key in {'DISTANCE_FROM_CAMERA',
  581. 'PROJECTED_X',
  582. 'PROJECTED_Y'}
  583. sub.prop(linestyle, "integration_type", text="")
  584. row = col.row(align=True)
  585. row.prop(linestyle, "sort_order", expand=True)
  586. # Selection
  587. layout.label(text="Selection:")
  588. split = layout.split(align=True)
  589. # First column
  590. col = split.column()
  591. row = col.row(align=True)
  592. row.prop(linestyle, "use_length_min", text="")
  593. sub = row.row()
  594. sub.active = linestyle.use_length_min
  595. sub.prop(linestyle, "length_min")
  596. row = col.row(align=True)
  597. row.prop(linestyle, "use_length_max", text="")
  598. sub = row.row()
  599. sub.active = linestyle.use_length_max
  600. sub.prop(linestyle, "length_max")
  601. # Second column
  602. col = split.column()
  603. row = col.row(align=True)
  604. row.prop(linestyle, "use_chain_count", text="")
  605. sub = row.row()
  606. sub.active = linestyle.use_chain_count
  607. sub.prop(linestyle, "chain_count")
  608. # Caps
  609. layout.label(text="Caps:")
  610. row = layout.row(align=True)
  611. row.prop(linestyle, "caps", expand=True)
  612. # Dashed lines
  613. layout.prop(linestyle, "use_dashed_line", text="Dashed Line:")
  614. row = layout.row(align=True)
  615. row.active = linestyle.use_dashed_line
  616. row.prop(linestyle, "dash1", text="D1")
  617. row.prop(linestyle, "gap1", text="G1")
  618. row.prop(linestyle, "dash2", text="D2")
  619. row.prop(linestyle, "gap2", text="G2")
  620. row.prop(linestyle, "dash3", text="D3")
  621. row.prop(linestyle, "gap3", text="G3")
  622. elif linestyle.panel == 'COLOR':
  623. col = layout.column()
  624. row = col.row()
  625. row.label(text="Base Color:")
  626. row.prop(linestyle, "color", text="")
  627. col.label(text="Modifiers:")
  628. col.operator_menu_enum("scene.freestyle_color_modifier_add", "type", text="Add Modifier")
  629. for modifier in linestyle.color_modifiers:
  630. self.draw_color_modifier(context, modifier)
  631. elif linestyle.panel == 'ALPHA':
  632. col = layout.column()
  633. row = col.row()
  634. row.label(text="Base Transparency:")
  635. row.prop(linestyle, "alpha")
  636. col.label(text="Modifiers:")
  637. col.operator_menu_enum("scene.freestyle_alpha_modifier_add", "type", text="Add Modifier")
  638. for modifier in linestyle.alpha_modifiers:
  639. self.draw_alpha_modifier(context, modifier)
  640. elif linestyle.panel == 'THICKNESS':
  641. col = layout.column()
  642. row = col.row()
  643. row.label(text="Base Thickness:")
  644. row.prop(linestyle, "thickness")
  645. subcol = col.column()
  646. subcol.active = linestyle.chaining == 'PLAIN' and linestyle.use_same_object
  647. row = subcol.row()
  648. row.prop(linestyle, "thickness_position", expand=True)
  649. row = subcol.row()
  650. row.prop(linestyle, "thickness_ratio")
  651. row.active = (linestyle.thickness_position == 'RELATIVE')
  652. col = layout.column()
  653. col.label(text="Modifiers:")
  654. col.operator_menu_enum("scene.freestyle_thickness_modifier_add", "type", text="Add Modifier")
  655. for modifier in linestyle.thickness_modifiers:
  656. self.draw_thickness_modifier(context, modifier)
  657. elif linestyle.panel == 'GEOMETRY':
  658. col = layout.column()
  659. col.label(text="Modifiers:")
  660. col.operator_menu_enum("scene.freestyle_geometry_modifier_add", "type", text="Add Modifier")
  661. for modifier in linestyle.geometry_modifiers:
  662. self.draw_geometry_modifier(context, modifier)
  663. elif linestyle.panel == 'TEXTURE':
  664. layout.separator()
  665. row = layout.row()
  666. row.prop(linestyle, "use_nodes")
  667. row.prop(linestyle, "texture_spacing", text="Spacing Along Stroke")
  668. row = layout.row()
  669. props = row.operator(
  670. "wm.properties_context_change",
  671. text="Go to Linestyle Textures Properties",
  672. icon='TEXTURE',
  673. )
  674. props.context = 'TEXTURE'
  675. elif linestyle.panel == 'MISC':
  676. pass
  677. # Material properties
  678. class MaterialFreestyleButtonsPanel:
  679. bl_space_type = 'PROPERTIES'
  680. bl_region_type = 'WINDOW'
  681. bl_context = "material"
  682. # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
  683. @classmethod
  684. def poll(cls, context):
  685. scene = context.scene
  686. material = context.material
  687. with_freestyle = bpy.app.build_options.freestyle
  688. return (
  689. with_freestyle and material and scene and scene.render.use_freestyle and
  690. (context.engine in cls.COMPAT_ENGINES)
  691. )
  692. class MATERIAL_PT_freestyle_line(MaterialFreestyleButtonsPanel, Panel):
  693. bl_label = "Freestyle Line"
  694. bl_options = {'DEFAULT_CLOSED'}
  695. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
  696. def draw(self, context):
  697. layout = self.layout
  698. mat = context.material
  699. row = layout.row()
  700. row.prop(mat, "line_color", text="")
  701. row.prop(mat, "line_priority", text="Priority")
  702. classes = (
  703. RENDER_PT_freestyle,
  704. VIEWLAYER_UL_linesets,
  705. RENDER_MT_lineset_context_menu,
  706. VIEWLAYER_PT_freestyle,
  707. VIEWLAYER_PT_freestyle_lineset,
  708. VIEWLAYER_PT_freestyle_linestyle,
  709. MATERIAL_PT_freestyle_line,
  710. )
  711. if __name__ == "__main__": # only for live edit.
  712. from bpy.utils import register_class
  713. for cls in classes:
  714. register_class(cls)