sun_position: fix warning from deleted prop in User Preferences
[blender-addons.git] / io_mesh_uv_layout / __init__.py
blobd585c005ae9590cc8ac1a48a8d962626e08003d0
1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "UV Layout",
23 "author": "Campbell Barton, Matt Ebb",
24 "version": (1, 1, 1),
25 "blender": (2, 80, 0),
26 "location": "Image-Window > UVs > Export UV Layout",
27 "description": "Export the UV layout as a 2D graphic",
28 "warning": "",
29 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
30 "import_export/io_mesh_uv_layout.html",
31 "support": 'OFFICIAL',
32 "category": "Import-Export",
36 # @todo write the wiki page
38 if "bpy" in locals():
39 import importlib
40 if "export_uv_eps" in locals():
41 importlib.reload(export_uv_eps)
42 if "export_uv_png" in locals():
43 importlib.reload(export_uv_png)
44 if "export_uv_svg" in locals():
45 importlib.reload(export_uv_svg)
47 import os
48 import bpy
50 from bpy.props import (
51 StringProperty,
52 BoolProperty,
53 EnumProperty,
54 IntVectorProperty,
55 FloatProperty,
59 class ExportUVLayout(bpy.types.Operator):
60 """Export UV layout to file"""
62 bl_idname = "uv.export_layout"
63 bl_label = "Export UV Layout"
64 bl_options = {'REGISTER', 'UNDO'}
66 filepath: StringProperty(
67 subtype='FILE_PATH',
69 export_all: BoolProperty(
70 name="All UVs",
71 description="Export all UVs in this mesh (not just visible ones)",
72 default=False,
74 modified: BoolProperty(
75 name="Modified",
76 description="Exports UVs from the modified mesh",
77 default=False,
79 mode: EnumProperty(
80 items=(
81 ('SVG', "Scalable Vector Graphic (.svg)",
82 "Export the UV layout to a vector SVG file"),
83 ('EPS', "Encapsulate PostScript (.eps)",
84 "Export the UV layout to a vector EPS file"),
85 ('PNG', "PNG Image (.png)",
86 "Export the UV layout to a bitmap image"),
88 name="Format",
89 description="File format to export the UV layout to",
90 default='PNG',
92 size: IntVectorProperty(
93 size=2,
94 default=(1024, 1024),
95 min=8, max=32768,
96 description="Dimensions of the exported file",
98 opacity: FloatProperty(
99 name="Fill Opacity",
100 min=0.0, max=1.0,
101 default=0.25,
102 description="Set amount of opacity for exported UV layout",
105 @classmethod
106 def poll(cls, context):
107 obj = context.active_object
108 return obj is not None and obj.type == 'MESH' and obj.data.uv_layers
110 def invoke(self, context, event):
111 self.size = self.get_image_size(context)
112 self.filepath = self.get_default_file_name(context) + "." + self.mode.lower()
113 context.window_manager.fileselect_add(self)
114 return {'RUNNING_MODAL'}
116 def get_default_file_name(self, context):
117 AMOUNT = 3
118 objects = list(self.iter_objects_to_export(context))
119 name = " ".join(sorted([obj.name for obj in objects[:AMOUNT]]))
120 if len(objects) > AMOUNT:
121 name += " and more"
122 return name
124 def check(self, context):
125 if any(self.filepath.endswith(ext) for ext in (".png", ".eps", ".svg")):
126 self.filepath = self.filepath[:-4]
128 ext = "." + self.mode.lower()
129 self.filepath = bpy.path.ensure_ext(self.filepath, ext)
130 return True
132 def execute(self, context):
133 obj = context.active_object
134 is_editmode = (obj.mode == 'EDIT')
135 if is_editmode:
136 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
138 filepath = self.filepath
139 filepath = bpy.path.ensure_ext(filepath, "." + self.mode.lower())
141 meshes = list(self.iter_meshes_to_export(context))
142 polygon_data = list(self.iter_polygon_data_to_draw(context, meshes))
143 different_colors = set(color for _, color in polygon_data)
144 if self.modified:
145 depsgraph = context.evaluated_depsgraph_get()
146 for obj in self.iter_objects_to_export(context):
147 obj_eval = obj.evaluated_get(depsgraph)
148 obj_eval.to_mesh_clear()
150 export = self.get_exporter()
151 export(filepath, polygon_data, different_colors, self.size[0], self.size[1], self.opacity)
153 if is_editmode:
154 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
156 return {'FINISHED'}
158 def iter_meshes_to_export(self, context):
159 depsgraph = context.evaluated_depsgraph_get()
160 for obj in self.iter_objects_to_export(context):
161 if self.modified:
162 yield obj.evaluated_get(depsgraph).to_mesh()
163 else:
164 yield obj.data
166 @staticmethod
167 def iter_objects_to_export(context):
168 for obj in {*context.selected_objects, context.active_object}:
169 if obj.type != 'MESH':
170 continue
171 mesh = obj.data
172 if mesh.uv_layers.active is None:
173 continue
174 yield obj
176 @staticmethod
177 def currently_image_image_editor(context):
178 return isinstance(context.space_data, bpy.types.SpaceImageEditor)
180 def get_currently_opened_image(self, context):
181 if not self.currently_image_image_editor(context):
182 return None
183 return context.space_data.image
185 def get_image_size(self, context):
186 # fallback if not in image context
187 image_width = self.size[0]
188 image_height = self.size[1]
190 # get size of "active" image if some exist
191 image = self.get_currently_opened_image(context)
192 if image is not None:
193 width, height = image.size
194 if width and height:
195 image_width = width
196 image_height = height
198 return image_width, image_height
200 def iter_polygon_data_to_draw(self, context, meshes):
201 for mesh in meshes:
202 uv_layer = mesh.uv_layers.active.data
203 for polygon in mesh.polygons:
204 if self.export_all or polygon.select:
205 start = polygon.loop_start
206 end = start + polygon.loop_total
207 uvs = tuple(tuple(uv.uv) for uv in uv_layer[start:end])
208 yield (uvs, self.get_polygon_color(mesh, polygon))
210 @staticmethod
211 def get_polygon_color(mesh, polygon, default=(0.8, 0.8, 0.8)):
212 if polygon.material_index < len(mesh.materials):
213 material = mesh.materials[polygon.material_index]
214 if material is not None:
215 return tuple(material.diffuse_color)[:3]
216 return default
218 def get_exporter(self):
219 if self.mode == 'PNG':
220 from . import export_uv_png
221 return export_uv_png.export
222 elif self.mode == 'EPS':
223 from . import export_uv_eps
224 return export_uv_eps.export
225 elif self.mode == 'SVG':
226 from . import export_uv_svg
227 return export_uv_svg.export
228 else:
229 assert False
232 def menu_func(self, context):
233 self.layout.operator(ExportUVLayout.bl_idname)
236 def register():
237 bpy.utils.register_class(ExportUVLayout)
238 bpy.types.IMAGE_MT_uvs.append(menu_func)
241 def unregister():
242 bpy.utils.unregister_class(ExportUVLayout)
243 bpy.types.IMAGE_MT_uvs.remove(menu_func)
246 if __name__ == "__main__":
247 register()