Sun position: remove unused prop in HDRI mode
[blender-addons.git] / io_mesh_uv_layout / __init__.py
blobc7b62047f0e2b0477913f3617576af933d038977
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 "doc_url": "{BLENDER_MANUAL_URL}/addons/import_export/mesh_uv_layout.html",
30 "support": 'OFFICIAL',
31 "category": "Import-Export",
35 # @todo write the wiki page
37 if "bpy" in locals():
38 import importlib
39 if "export_uv_eps" in locals():
40 importlib.reload(export_uv_eps)
41 if "export_uv_png" in locals():
42 importlib.reload(export_uv_png)
43 if "export_uv_svg" in locals():
44 importlib.reload(export_uv_svg)
46 import os
47 import bpy
49 from bpy.props import (
50 StringProperty,
51 BoolProperty,
52 EnumProperty,
53 IntVectorProperty,
54 FloatProperty,
58 class ExportUVLayout(bpy.types.Operator):
59 """Export UV layout to file"""
61 bl_idname = "uv.export_layout"
62 bl_label = "Export UV Layout"
63 bl_options = {'REGISTER', 'UNDO'}
65 filepath: StringProperty(
66 subtype='FILE_PATH',
68 export_all: BoolProperty(
69 name="All UVs",
70 description="Export all UVs in this mesh (not just visible ones)",
71 default=False,
73 modified: BoolProperty(
74 name="Modified",
75 description="Exports UVs from the modified mesh",
76 default=False,
78 mode: EnumProperty(
79 items=(
80 ('SVG', "Scalable Vector Graphic (.svg)",
81 "Export the UV layout to a vector SVG file"),
82 ('EPS', "Encapsulate PostScript (.eps)",
83 "Export the UV layout to a vector EPS file"),
84 ('PNG', "PNG Image (.png)",
85 "Export the UV layout to a bitmap image"),
87 name="Format",
88 description="File format to export the UV layout to",
89 default='PNG',
91 size: IntVectorProperty(
92 size=2,
93 default=(1024, 1024),
94 min=8, max=32768,
95 description="Dimensions of the exported file",
97 opacity: FloatProperty(
98 name="Fill Opacity",
99 min=0.0, max=1.0,
100 default=0.25,
101 description="Set amount of opacity for exported UV layout",
103 # For the file-selector.
104 check_existing: BoolProperty(
105 default=True,
106 options={'HIDDEN'},
109 @classmethod
110 def poll(cls, context):
111 obj = context.active_object
112 return obj is not None and obj.type == 'MESH' and obj.data.uv_layers
114 def invoke(self, context, event):
115 self.size = self.get_image_size(context)
116 self.filepath = self.get_default_file_name(context) + "." + self.mode.lower()
117 context.window_manager.fileselect_add(self)
118 return {'RUNNING_MODAL'}
120 def get_default_file_name(self, context):
121 AMOUNT = 3
122 objects = list(self.iter_objects_to_export(context))
123 name = " ".join(sorted([obj.name for obj in objects[:AMOUNT]]))
124 if len(objects) > AMOUNT:
125 name += " and more"
126 return name
128 def check(self, context):
129 if any(self.filepath.endswith(ext) for ext in (".png", ".eps", ".svg")):
130 self.filepath = self.filepath[:-4]
132 ext = "." + self.mode.lower()
133 self.filepath = bpy.path.ensure_ext(self.filepath, ext)
134 return True
136 def execute(self, context):
137 obj = context.active_object
138 is_editmode = (obj.mode == 'EDIT')
139 if is_editmode:
140 bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
142 filepath = self.filepath
143 filepath = bpy.path.ensure_ext(filepath, "." + self.mode.lower())
145 meshes = list(self.iter_meshes_to_export(context))
146 polygon_data = list(self.iter_polygon_data_to_draw(context, meshes))
147 different_colors = set(color for _, color in polygon_data)
148 if self.modified:
149 depsgraph = context.evaluated_depsgraph_get()
150 for obj in self.iter_objects_to_export(context):
151 obj_eval = obj.evaluated_get(depsgraph)
152 obj_eval.to_mesh_clear()
154 export = self.get_exporter()
155 export(filepath, polygon_data, different_colors, self.size[0], self.size[1], self.opacity)
157 if is_editmode:
158 bpy.ops.object.mode_set(mode='EDIT', toggle=False)
160 return {'FINISHED'}
162 def iter_meshes_to_export(self, context):
163 depsgraph = context.evaluated_depsgraph_get()
164 for obj in self.iter_objects_to_export(context):
165 if self.modified:
166 yield obj.evaluated_get(depsgraph).to_mesh()
167 else:
168 yield obj.data
170 @staticmethod
171 def iter_objects_to_export(context):
172 for obj in {*context.selected_objects, context.active_object}:
173 if obj.type != 'MESH':
174 continue
175 mesh = obj.data
176 if mesh.uv_layers.active is None:
177 continue
178 yield obj
180 @staticmethod
181 def currently_image_image_editor(context):
182 return isinstance(context.space_data, bpy.types.SpaceImageEditor)
184 def get_currently_opened_image(self, context):
185 if not self.currently_image_image_editor(context):
186 return None
187 return context.space_data.image
189 def get_image_size(self, context):
190 # fallback if not in image context
191 image_width = self.size[0]
192 image_height = self.size[1]
194 # get size of "active" image if some exist
195 image = self.get_currently_opened_image(context)
196 if image is not None:
197 width, height = image.size
198 if width and height:
199 image_width = width
200 image_height = height
202 return image_width, image_height
204 def iter_polygon_data_to_draw(self, context, meshes):
205 for mesh in meshes:
206 uv_layer = mesh.uv_layers.active.data
207 for polygon in mesh.polygons:
208 if self.export_all or polygon.select:
209 start = polygon.loop_start
210 end = start + polygon.loop_total
211 uvs = tuple(tuple(uv.uv) for uv in uv_layer[start:end])
212 yield (uvs, self.get_polygon_color(mesh, polygon))
214 @staticmethod
215 def get_polygon_color(mesh, polygon, default=(0.8, 0.8, 0.8)):
216 if polygon.material_index < len(mesh.materials):
217 material = mesh.materials[polygon.material_index]
218 if material is not None:
219 return tuple(material.diffuse_color)[:3]
220 return default
222 def get_exporter(self):
223 if self.mode == 'PNG':
224 from . import export_uv_png
225 return export_uv_png.export
226 elif self.mode == 'EPS':
227 from . import export_uv_eps
228 return export_uv_eps.export
229 elif self.mode == 'SVG':
230 from . import export_uv_svg
231 return export_uv_svg.export
232 else:
233 assert False
236 def menu_func(self, context):
237 self.layout.operator(ExportUVLayout.bl_idname)
240 def register():
241 bpy.utils.register_class(ExportUVLayout)
242 bpy.types.IMAGE_MT_uvs.append(menu_func)
245 def unregister():
246 bpy.utils.unregister_class(ExportUVLayout)
247 bpy.types.IMAGE_MT_uvs.remove(menu_func)
250 if __name__ == "__main__":
251 register()