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 #####
23 "author": "Campbell Barton, Matt Ebb",
25 "blender": (2, 80, 0),
26 "location": "Image-Window > UVs > Export UV Layout",
27 "description": "Export the UV layout as a 2D graphic",
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
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
)
50 from bpy
.props
import (
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(
69 export_all
: BoolProperty(
71 description
="Export all UVs in this mesh (not just visible ones)",
74 modified
: BoolProperty(
76 description
="Exports UVs from the modified mesh",
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"),
89 description
="File format to export the UV layout to",
92 size
: IntVectorProperty(
96 description
="Dimensions of the exported file",
98 opacity
: FloatProperty(
102 description
="Set amount of opacity for exported UV layout",
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
):
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
:
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
)
132 def execute(self
, context
):
133 obj
= context
.active_object
134 is_editmode
= (obj
.mode
== 'EDIT')
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
)
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
)
154 bpy
.ops
.object.mode_set(mode
='EDIT', toggle
=False)
158 def iter_meshes_to_export(self
, context
):
159 depsgraph
= context
.evaluated_depsgraph_get()
160 for obj
in self
.iter_objects_to_export(context
):
162 yield obj
.evaluated_get(depsgraph
).to_mesh()
167 def iter_objects_to_export(context
):
168 for obj
in {*context
.selected_objects
, context
.active_object
}:
169 if obj
.type != 'MESH':
172 if mesh
.uv_layers
.active
is None:
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
):
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
196 image_height
= height
198 return image_width
, image_height
200 def iter_polygon_data_to_draw(self
, context
, 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
))
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]
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
232 def menu_func(self
, context
):
233 self
.layout
.operator(ExportUVLayout
.bl_idname
)
237 bpy
.utils
.register_class(ExportUVLayout
)
238 bpy
.types
.IMAGE_MT_uvs
.append(menu_func
)
242 bpy
.utils
.unregister_class(ExportUVLayout
)
243 bpy
.types
.IMAGE_MT_uvs
.remove(menu_func
)
246 if __name__
== "__main__":