forked from azeemba/blender-add-outline
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathadd_outline.py
107 lines (80 loc) · 3.2 KB
/
add_outline.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
bl_info = {
"name": "Outline",
"description": "Add NPR/cartoon outline to an object that works in eevee and cycles",
"author": "cyberMaestro, Azeem Bande-Ali",
"version": (0, 2),
"blender": (2, 93),
"category": "Material"
}
import bpy
from bpy.props import StringProperty
from bpy.types import Operator, AddonPreferences
def get_path_to_blendfile(context):
preferences = context.preferences
addon_prefs = preferences.addons[__name__].preferences
return addon_prefs.source
def main(context, thickness):
"""Handles the user action to actually add outline to object"""
material_name = "ToonOutline"
filepath = get_path_to_blendfile(context)
for obj in context.selected_objects:
obj["outline"] = 1.0
add_modifier(obj, thickness)
add_outline_material(obj, filepath, material_name)
def add_modifier(obj, thickness):
"""Add the solidify modifier to object"""
obj.modifiers.new("Outline", "SOLIDIFY")
modifier = obj.modifiers["Outline"]
modifier.thickness = thickness
modifier.use_rim = False
modifier.offset = 1
modifier.use_flip_normals = True
modifier.use_quality_normals = True
# We want the material offset to be the "next one"
# but if there is no material at all, we assign a default one first
if len(obj.data.materials) == 0:
default_material = bpy.data.materials.new(name="DefaultMaterial")
obj.data.materials.append(default_material)
modifier.material_offset = len(obj.data.materials)
def add_outline_material(obj, filepath, material_name):
"""Loads material from filepath and appends to object"""
outline_material = bpy.data.materials.get(material_name)
if not outline_material:
with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
source_index = data_from.materials.index(material_name)
data_to.materials.append(data_from.materials[source_index])
outline_material = bpy.data.materials.get(material_name)
obj.data.materials.append(outline_material)
class AddOutlineOperator(Operator):
"""
Add NPR/cartoon outline to an object that works in eevee and cycles
"""
bl_idname = "object.add_outline"
bl_label = "Add Outline"
bl_options = {'REGISTER', 'UNDO'}
thickness: bpy.props.FloatProperty(name="Thickness",default=0.01)
def execute(self, context):
main(context, self.thickness)
return {'FINISHED'}
def menu_func(self, context):
self.layout.operator(AddOutlineOperator.bl_idname)
class OutlinePreferences(AddonPreferences):
bl_idname = __name__
source: StringProperty(
name="Blend file",
subtype="FILE_PATH")
def draw(self, context):
layout = self.layout
layout.label(text="Select blend file containing ToonOutline material")
layout.prop(self, "source")
classes = (AddOutlineOperator, OutlinePreferences)
def register():
for c in classes:
bpy.utils.register_class(c)
bpy.types.VIEW3D_MT_object.append(menu_func)
def unregister():
for c in classes:
bpy.utils.unregister_class(c)
bpy.types.VIEW3D_MT_object.remove(menu_func)
if __name__ == "__main__":
register()