1 # Copyright 2018 The glTF-Blender-IO authors.
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
21 from bpy_extras
.io_utils
import ImportHelper
, ExportHelper
22 from bpy
.types
import Operator
, AddonPreferences
24 from .io
.com
.gltf2_io_debug
import Log
26 from bpy
.props
import (CollectionProperty
,
38 'name': 'glTF 2.0 format',
39 'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann & Moritz Becher',
41 'blender': (2, 80, 0),
42 'location': 'File > Import-Export',
43 'description': 'Import-Export as glTF 2.0',
45 'wiki_url': "https://github.com/KhronosGroup/glTF-Blender-IO",
46 'tracker_url': "https://github.com/KhronosGroup/glTF-Blender-IO/issues/",
47 'support': 'OFFICIAL',
48 'category': 'Import-Export'}
52 # Functions / Classes.
56 class GLTF2ExportSettings(bpy
.types
.Operator
):
57 """Save the export settings on export (saved in .blend). """
58 """Toggle off to clear settings"""
59 bl_label
= "Save Settings"
60 bl_idname
= "scene.gltf2_export_settings_set"
62 def execute(self
, context
):
63 operator
= context
.active_operator
64 operator
.will_save_settings
= not operator
.will_save_settings
65 if not operator
.will_save_settings
:
67 context
.scene
.pop(operator
.scene_key
)
71 class ExportGLTF2_Base
:
73 # TODO: refactor to avoid boilerplate
75 export_format
: EnumProperty(
77 items
=(('GLB', 'glTF Binary (.glb)',
78 'Exports a single file, with all data packed in binary form. '
79 'Most efficient and portable, but more difficult to edit later'),
80 ('GLTF_EMBEDDED', 'glTF Embedded (.gltf)',
81 'Exports a single file, with all data packed in JSON. '
82 'Less efficient than binary, but easier to edit later'),
83 ('GLTF_SEPARATE', 'glTF Separate (.gltf + .bin + textures)',
84 'Exports multiple files, with separate JSON, binary and texture data. '
85 'Easiest to edit later')),
87 'Output format and embedding options. Binary is most efficient, '
88 'but JSON (embedded or separate) may be easier to edit later'
93 export_copyright
: StringProperty(
95 description
='Legal rights and conditions for the model',
99 export_texcoords
: BoolProperty(
101 description
='Export UVs (texture coordinates) with meshes',
105 export_normals
: BoolProperty(
107 description
='Export vertex normals with meshes',
111 export_tangents
: BoolProperty(
113 description
='Export vertex tangents with meshes',
117 export_materials
: BoolProperty(
119 description
='Export materials',
123 export_colors
: BoolProperty(
124 name
='Vertex Colors',
125 description
='Export vertex colors with meshes',
129 export_cameras
: BoolProperty(
131 description
='Export cameras',
135 export_selected
: BoolProperty(
136 name
='Selected Objects',
137 description
='Export selected objects only',
141 # export_layers: BoolProperty(
143 # description='Export all layers, rather than just the first',
147 export_extras
: BoolProperty(
148 name
='Custom Properties',
149 description
='Export custom properties as glTF extras',
153 export_yup
: BoolProperty(
155 description
='Export using glTF convention, +Y up',
159 export_apply
: BoolProperty(
160 name
='Apply Modifiers',
161 description
='Apply modifiers to mesh objects',
165 export_animations
: BoolProperty(
167 description
='Exports active actions and NLA tracks as glTF animations',
171 export_frame_range
: BoolProperty(
172 name
='Limit to Playback Range',
173 description
='Clips animations to selected playback range',
177 export_frame_step
: IntProperty(
178 name
='Sampling Rate',
179 description
='How often to evaluate animated values (in frames)',
185 export_move_keyframes
: BoolProperty(
186 name
='Keyframes Start at 0',
187 description
='Keyframes start at 0, instead of 1',
191 export_force_sampling
: BoolProperty(
192 name
='Always Sample Animations',
193 description
='Apply sampling to all animations',
197 export_current_frame
: BoolProperty(
198 name
='Use Current Frame',
199 description
='Export the scene in the current animation frame',
203 export_skins
: BoolProperty(
205 description
='Export skinning (armature) data',
209 export_bake_skins
: BoolProperty(
210 name
='Bake Skinning Constraints',
211 description
='Apply skinning constraints to armatures',
215 export_all_influences
: BoolProperty(
216 name
='Include All Bone Influences',
217 description
='Allow >4 joint vertex influences. Models may appear' \
218 ' incorrectly in many viewers',
222 export_morph
: BoolProperty(
224 description
='Export shape keys (morph targets)',
228 export_morph_normal
: BoolProperty(
229 name
='Shape Key Normals',
230 description
='Export vertex normals with shape keys (morph targets)',
234 export_morph_tangent
: BoolProperty(
235 name
='Shape Key Tangents',
236 description
='Export vertex tangents with shape keys (morph targets)',
240 export_lights
: BoolProperty(
241 name
='Punctual Lights',
242 description
='Export directional, point, and spot lights. Uses ' \
243 ' "KHR_lights_punctual" glTF extension',
247 export_texture_transform
: BoolProperty(
248 name
='Texture Transforms',
249 description
='Export texture or UV position, rotation, and scale.' \
250 ' Uses "KHR_texture_transform" glTF extension',
254 export_displacement
: BoolProperty(
255 name
='Displacement Textures (EXPERIMENTAL)',
256 description
='EXPERIMENTAL: Export displacement textures. Uses' \
257 ' incomplete "KHR_materials_displacement" glTF extension',
261 will_save_settings
: BoolProperty(default
=False)
263 # Custom scene property for saving settings
264 scene_key
= "glTF2ExportSettings"
268 def invoke(self
, context
, event
):
269 settings
= context
.scene
.get(self
.scene_key
)
270 self
.will_save_settings
= False
273 for (k
, v
) in settings
.items():
275 self
.will_save_settings
= True
277 except AttributeError:
278 self
.report({"ERROR"}, "Loading export settings failed. Removed corrupted settings")
279 del context
.scene
[self
.scene_key
]
281 return ExportHelper
.invoke(self
, context
, event
)
283 def save_settings(self
, context
):
284 # find all export_ props
285 all_props
= self
.properties
286 export_props
= {x
: all_props
.get(x
) for x
in dir(all_props
)
287 if x
.startswith("export_") and all_props
.get(x
) is not None}
289 context
.scene
[self
.scene_key
] = export_props
291 def execute(self
, context
):
293 from .blender
.exp
import gltf2_blender_export
295 if self
.will_save_settings
:
296 self
.save_settings(context
)
298 if self
.export_format
== 'GLB':
299 self
.filename_ext
= '.glb'
301 self
.filename_ext
= '.gltf'
303 # All custom export settings are stored in this container.
306 export_settings
['timestamp'] = datetime
.datetime
.now()
308 export_settings
['gltf_filepath'] = bpy
.path
.ensure_ext(self
.filepath
, self
.filename_ext
)
309 export_settings
['gltf_filedirectory'] = os
.path
.dirname(export_settings
['gltf_filepath']) + '/'
311 export_settings
['gltf_format'] = self
.export_format
312 export_settings
['gltf_copyright'] = self
.export_copyright
313 export_settings
['gltf_texcoords'] = self
.export_texcoords
314 export_settings
['gltf_normals'] = self
.export_normals
315 export_settings
['gltf_tangents'] = self
.export_tangents
and self
.export_normals
316 export_settings
['gltf_materials'] = self
.export_materials
317 export_settings
['gltf_colors'] = self
.export_colors
318 export_settings
['gltf_cameras'] = self
.export_cameras
319 export_settings
['gltf_selected'] = self
.export_selected
320 export_settings
['gltf_layers'] = True #self.export_layers
321 export_settings
['gltf_extras'] = self
.export_extras
322 export_settings
['gltf_yup'] = self
.export_yup
323 export_settings
['gltf_apply'] = self
.export_apply
324 export_settings
['gltf_animations'] = self
.export_animations
325 if self
.export_animations
:
326 export_settings
['gltf_current_frame'] = False
327 export_settings
['gltf_frame_range'] = self
.export_frame_range
328 export_settings
['gltf_move_keyframes'] = self
.export_move_keyframes
329 export_settings
['gltf_force_sampling'] = self
.export_force_sampling
331 export_settings
['gltf_current_frame'] = self
.export_current_frame
332 export_settings
['gltf_frame_range'] = False
333 export_settings
['gltf_move_keyframes'] = False
334 export_settings
['gltf_force_sampling'] = False
335 export_settings
['gltf_skins'] = self
.export_skins
336 if self
.export_skins
:
337 export_settings
['gltf_bake_skins'] = self
.export_bake_skins
338 export_settings
['gltf_all_vertex_influences'] = self
.export_all_influences
340 export_settings
['gltf_bake_skins'] = False
341 export_settings
['gltf_all_vertex_influences'] = False
342 export_settings
['gltf_frame_step'] = self
.export_frame_step
343 export_settings
['gltf_morph'] = self
.export_morph
344 if self
.export_morph
:
345 export_settings
['gltf_morph_normal'] = self
.export_morph_normal
347 export_settings
['gltf_morph_normal'] = False
348 if self
.export_morph
and self
.export_morph_normal
:
349 export_settings
['gltf_morph_tangent'] = self
.export_morph_tangent
351 export_settings
['gltf_morph_tangent'] = False
353 export_settings
['gltf_lights'] = self
.export_lights
354 export_settings
['gltf_texture_transform'] = self
.export_texture_transform
355 export_settings
['gltf_displacement'] = self
.export_displacement
357 export_settings
['gltf_binary'] = bytearray()
358 export_settings
['gltf_binaryfilename'] = os
.path
.splitext(os
.path
.basename(self
.filepath
))[0] + '.bin'
360 return gltf2_blender_export
.save(context
, export_settings
)
362 def draw(self
, context
):
367 col
= layout
.box().column()
368 col
.label(text
='General:', icon
='PREFERENCES')
369 col
.prop(self
, 'export_format')
370 col
.prop(self
, 'export_selected')
371 #col.prop(self, 'export_layers')
372 col
.prop(self
, 'export_apply')
373 col
.prop(self
, 'export_yup')
374 col
.prop(self
, 'export_extras')
375 col
.prop(self
, 'export_copyright')
377 col
= layout
.box().column()
378 col
.label(text
='Meshes:', icon
='MESH_DATA')
379 col
.prop(self
, 'export_texcoords')
380 col
.prop(self
, 'export_normals')
381 if self
.export_normals
:
382 col
.prop(self
, 'export_tangents')
383 col
.prop(self
, 'export_colors')
385 col
= layout
.box().column()
386 col
.label(text
='Objects:', icon
='OBJECT_DATA')
387 col
.prop(self
, 'export_cameras')
388 col
.prop(self
, 'export_lights')
390 col
= layout
.box().column()
391 col
.label(text
='Materials:', icon
='MATERIAL_DATA')
392 col
.prop(self
, 'export_materials')
393 col
.prop(self
, 'export_texture_transform')
395 col
= layout
.box().column()
396 col
.label(text
='Animation:', icon
='ARMATURE_DATA')
397 col
.prop(self
, 'export_animations')
398 if self
.export_animations
:
399 col
.prop(self
, 'export_frame_range')
400 col
.prop(self
, 'export_frame_step')
401 col
.prop(self
, 'export_move_keyframes')
402 col
.prop(self
, 'export_force_sampling')
404 col
.prop(self
, 'export_current_frame')
405 col
.prop(self
, 'export_skins')
406 if self
.export_skins
:
407 col
.prop(self
, 'export_bake_skins')
408 col
.prop(self
, 'export_all_influences')
409 col
.prop(self
, 'export_morph')
410 if self
.export_morph
:
411 col
.prop(self
, 'export_morph_normal')
412 if self
.export_morph_normal
:
413 col
.prop(self
, 'export_morph_tangent')
417 GLTF2ExportSettings
.bl_idname
,
418 text
=GLTF2ExportSettings
.bl_label
,
419 icon
="%s" % "PINNED" if self
.will_save_settings
else "UNPINNED")
422 class ExportGLTF2(bpy
.types
.Operator
, ExportGLTF2_Base
, ExportHelper
):
423 """Export scene as glTF 2.0 file"""
424 bl_idname
= 'export_scene.gltf'
425 bl_label
= 'glTF 2.0 (.glb/.gltf)'
429 filter_glob
: StringProperty(default
='*.glb;*.gltf', options
={'HIDDEN'})
432 def menu_func_export(self
, context
):
433 self
.layout
.operator(ExportGLTF2
.bl_idname
, text
='glTF 2.0 (.glb/.gltf)')
436 class ImportGLTF2(Operator
, ImportHelper
):
437 bl_idname
= 'import_scene.gltf'
438 bl_label
= 'glTF 2.0 (.glb/.gltf)'
440 filter_glob
: StringProperty(default
="*.glb;*.gltf", options
={'HIDDEN'})
442 loglevel
: EnumProperty(
443 items
=Log
.get_levels(),
445 description
="Set level of log to display",
446 default
=Log
.default())
448 import_pack_images
: BoolProperty(
450 description
='Pack all images into .blend file',
454 import_shading
: EnumProperty(
456 items
=(("NORMALS", "Use Normal Data", ""),
457 ("FLAT", "Flat Shading", ""),
458 ("SMOOTH", "Smooth Shading", "")),
459 description
="How normals are computed during import",
462 def draw(self
, context
):
465 layout
.prop(self
, 'loglevel')
466 layout
.prop(self
, 'import_pack_images')
467 layout
.prop(self
, 'import_shading')
469 def execute(self
, context
):
470 return self
.import_gltf2(context
)
472 def import_gltf2(self
, context
):
473 from .io
.imp
.gltf2_io_gltf
import glTFImporter
474 from .blender
.imp
.gltf2_blender_gltf
import BlenderGlTF
476 import_settings
= self
.as_keywords()
478 self
.gltf_importer
= glTFImporter(self
.filepath
, import_settings
)
479 success
, txt
= self
.gltf_importer
.read()
481 self
.report({'ERROR'}, txt
)
483 success
, txt
= self
.gltf_importer
.checks()
485 self
.report({'ERROR'}, txt
)
487 self
.gltf_importer
.log
.critical("Data are loaded, start creating Blender stuff")
488 BlenderGlTF
.create(self
.gltf_importer
)
489 self
.gltf_importer
.log
.critical("glTF import is now finished")
490 self
.gltf_importer
.log
.removeHandler(self
.gltf_importer
.log_handler
)
495 def menu_func_import(self
, context
):
496 self
.layout
.operator(ImportGLTF2
.bl_idname
, text
=ImportGLTF2
.bl_label
)
508 bpy
.utils
.register_class(c
)
509 # bpy.utils.register_module(__name__)
511 # add to the export / import menu
512 bpy
.types
.TOPBAR_MT_file_export
.append(menu_func_export
)
513 bpy
.types
.TOPBAR_MT_file_import
.append(menu_func_import
)
518 bpy
.utils
.unregister_class(c
)
519 # bpy.utils.unregister_module(__name__)
521 # remove from the export / import menu
522 bpy
.types
.TOPBAR_MT_file_export
.remove(menu_func_export
)
523 bpy
.types
.TOPBAR_MT_file_import
.remove(menu_func_import
)