# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### import os import json import logging import bpy from bpy_extras.io_utils import ExportHelper from bpy.props import ( EnumProperty, BoolProperty, FloatProperty, IntProperty, StringProperty ) from . import constants logging.basicConfig( format='%(levelname)s:THREE:%(message)s', level=logging.DEBUG) bl_info = { 'name': "Three.js Format", 'author': "repsac, mrdoob, yomotsu, mpk, jpweeks, rkusa, tschw, jackcaron, bhouston", 'version': (1, 5, 0), 'blender': (2, 74, 0), 'location': "File > Export", 'description': "Export Three.js formatted JSON files.", 'warning': "Importer not included.", 'wiki_url': "https://github.com/mrdoob/three.js/tree/"\ "master/utils/exporters/blender", 'tracker_url': "https://github.com/mrdoob/three.js/issues", 'category': 'Import-Export' } def _geometry_types(): """The valid geometry types that are supported by Three.js :return: list of tuples """ keys = (constants.GLOBAL, constants.GEOMETRY, constants.BUFFER_GEOMETRY) types = [] for key in keys: types.append((key, key.title(), key)) return types bpy.types.Mesh.THREE_geometry_type = EnumProperty( name="Geometry type", description="Geometry type", items=_geometry_types(), default=constants.GLOBAL) class ThreeMesh(bpy.types.Panel): """Creates custom properties on a mesh node""" bl_label = 'THREE' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'data' def draw(self, context): """ :param context: """ row = self.layout.row() if context.mesh: row.prop(context.mesh, 'THREE_geometry_type', text="Type") def _blending_types(index): """Supported blending types for Three.js :param index: :type index: int :returns: tuple if types (str, str, str) """ types = (constants.BLENDING_TYPES.NONE, constants.BLENDING_TYPES.NORMAL, constants.BLENDING_TYPES.ADDITIVE, constants.BLENDING_TYPES.SUBTRACTIVE, constants.BLENDING_TYPES.MULTIPLY, constants.BLENDING_TYPES.CUSTOM) return (types[index], types[index], types[index]) bpy.types.Material.THREE_blending_type = EnumProperty( name="Blending type", description="Blending type", items=[_blending_types(x) for x in range(5)], default=constants.BLENDING_TYPES.NORMAL) bpy.types.Material.THREE_depth_write = BoolProperty(default=True) bpy.types.Material.THREE_depth_test = BoolProperty(default=True) bpy.types.Material.THREE_double_sided = BoolProperty(default=False) class ThreeMaterial(bpy.types.Panel): """Adds custom properties to the Materials of an object""" bl_label = 'THREE' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'material' def draw(self, context): """ :param context: """ layout = self.layout mat = context.material if mat is not None: row = layout.row() row.label(text="Selected material: %s" % mat.name) row = layout.row() row.prop(mat, 'THREE_blending_type', text="Blending type") row = layout.row() row.prop(mat, 'THREE_depth_write', text="Enable depth writing") row = layout.row() row.prop(mat, 'THREE_depth_test', text="Enable depth testing") row = layout.row() row.prop(mat, 'THREE_double_sided', text="Double-sided") def _mag_filters(index): """Three.js mag filters :param index: :type index: int :returns: tuple with the filter values """ types = (constants.LINEAR_FILTERS.LINEAR, constants.NEAREST_FILTERS.NEAREST) return (types[index], types[index], types[index]) bpy.types.Texture.THREE_mag_filter = EnumProperty( name="Mag Filter", items=[_mag_filters(x) for x in range(2)], default=constants.LINEAR_FILTERS.LINEAR) def _min_filters(index): """Three.js min filters :param index: :type index: int :returns: tuple with the filter values """ types = (constants.LINEAR_FILTERS.LINEAR, constants.LINEAR_FILTERS.MIP_MAP_NEAREST, constants.LINEAR_FILTERS.MIP_MAP_LINEAR, constants.NEAREST_FILTERS.NEAREST, constants.NEAREST_FILTERS.MIP_MAP_NEAREST, constants.NEAREST_FILTERS.MIP_MAP_LINEAR) return (types[index], types[index], types[index]) bpy.types.Texture.THREE_min_filter = EnumProperty( name="Min Filter", items=[_min_filters(x) for x in range(6)], default=constants.LINEAR_FILTERS.MIP_MAP_LINEAR) def _mapping(index): """Three.js texture mappings types :param index: :type index: int :returns: tuple with the mapping values """ types = (constants.MAPPING_TYPES.UV, constants.MAPPING_TYPES.CUBE_REFLECTION, constants.MAPPING_TYPES.CUBE_REFRACTION, constants.MAPPING_TYPES.SPHERICAL_REFLECTION) return (types[index], types[index], types[index]) bpy.types.Texture.THREE_mapping = EnumProperty( name="Mapping", items=[_mapping(x) for x in range(4)], default=constants.MAPPING_TYPES.UV) class ThreeTexture(bpy.types.Panel): """Adds custom properties to a texture""" bl_label = 'THREE' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'texture' #@TODO: possible to make cycles compatible? def draw(self, context): """ :param context: """ layout = self.layout tex = context.texture if tex is not None: row = layout.row() row.prop(tex, 'THREE_mapping', text="Mapping") row = layout.row() row.prop(tex, 'THREE_mag_filter', text="Mag Filter") row = layout.row() row.prop(tex, 'THREE_min_filter', text="Min Filter") bpy.types.Object.THREE_export = bpy.props.BoolProperty(default=True) class ThreeObject(bpy.types.Panel): """Adds custom properties to an object""" bl_label = 'THREE' bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = 'object' def draw(self, context): """ :param context: """ layout = self.layout obj = context.object row = layout.row() row.prop(obj, 'THREE_export', text='Export') class ThreeExportSettings(bpy.types.Operator): """Save the current export settings (gets saved in .blend)""" bl_label = "Save Settings" bl_idname = "scene.three_export_settings_set" def execute(self, context): cycles = context.scene.cycles cycles.use_samples_final = True context.scene[constants.EXPORT_SETTINGS_KEY] = set_settings(context.active_operator.properties) self.report({"INFO"}, "Three Export Settings Saved") return {"FINISHED"} def restore_export_settings(properties, settings): """Restore the settings :param properties: """ ## Geometry { properties.option_vertices = settings.get( constants.VERTICES, constants.EXPORT_OPTIONS[constants.VERTICES]) properties.option_faces = settings.get( constants.FACES, constants.EXPORT_OPTIONS[constants.FACES]) properties.option_normals = settings.get( constants.NORMALS, constants.EXPORT_OPTIONS[constants.NORMALS]) properties.option_skinning = settings.get( constants.SKINNING, constants.EXPORT_OPTIONS[constants.SKINNING]) properties.option_bones = settings.get( constants.BONES, constants.EXPORT_OPTIONS[constants.BONES]) properties.option_influences = settings.get( constants.INFLUENCES_PER_VERTEX, constants.EXPORT_OPTIONS[constants.INFLUENCES_PER_VERTEX]) properties.option_apply_modifiers = settings.get( constants.APPLY_MODIFIERS, constants.EXPORT_OPTIONS[constants.APPLY_MODIFIERS]) properties.option_extra_vgroups = settings.get( constants.EXTRA_VGROUPS, constants.EXPORT_OPTIONS[constants.EXTRA_VGROUPS]) properties.option_geometry_type = settings.get( constants.GEOMETRY_TYPE, constants.EXPORT_OPTIONS[constants.GEOMETRY_TYPE]) properties.option_index_type = settings.get( constants.INDEX_TYPE, constants.EXPORT_OPTIONS[constants.INDEX_TYPE]) ## } ## Materials { properties.option_materials = settings.get( constants.MATERIALS, constants.EXPORT_OPTIONS[constants.MATERIALS]) properties.option_uv_coords = settings.get( constants.UVS, constants.EXPORT_OPTIONS[constants.UVS]) properties.option_face_materials = settings.get( constants.FACE_MATERIALS, constants.EXPORT_OPTIONS[constants.FACE_MATERIALS]) properties.option_maps = settings.get( constants.MAPS, constants.EXPORT_OPTIONS[constants.MAPS]) properties.option_colors = settings.get( constants.COLORS, constants.EXPORT_OPTIONS[constants.COLORS]) properties.option_mix_colors = settings.get( constants.MIX_COLORS, constants.EXPORT_OPTIONS[constants.MIX_COLORS]) ## } ## Settings { properties.option_scale = settings.get( constants.SCALE, constants.EXPORT_OPTIONS[constants.SCALE]) properties.option_round_off = settings.get( constants.ENABLE_PRECISION, constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION]) properties.option_round_value = settings.get( constants.PRECISION, constants.EXPORT_OPTIONS[constants.PRECISION]) properties.option_custom_properties = settings.get( constants.CUSTOM_PROPERTIES, constants.EXPORT_OPTIONS[constants.CUSTOM_PROPERTIES]) properties.option_logging = settings.get( constants.LOGGING, constants.EXPORT_OPTIONS[constants.LOGGING]) properties.option_compression = settings.get( constants.COMPRESSION, constants.NONE) properties.option_indent = settings.get( constants.INDENT, constants.EXPORT_OPTIONS[constants.INDENT]) properties.option_export_textures = settings.get( constants.EXPORT_TEXTURES, constants.EXPORT_OPTIONS[constants.EXPORT_TEXTURES]) properties.option_embed_textures = settings.get( constants.EMBED_TEXTURES, constants.EXPORT_OPTIONS[constants.EMBED_TEXTURES]) properties.option_texture_folder = settings.get( constants.TEXTURE_FOLDER, constants.EXPORT_OPTIONS[constants.TEXTURE_FOLDER]) properties.option_embed_animation = settings.get( constants.EMBED_ANIMATION, constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION]) ## } ## Scene { properties.option_export_scene = settings.get( constants.SCENE, constants.EXPORT_OPTIONS[constants.SCENE]) #properties.option_embed_geometry = settings.get( # constants.EMBED_GEOMETRY, # constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY]) properties.option_lights = settings.get( constants.LIGHTS, constants.EXPORT_OPTIONS[constants.LIGHTS]) properties.option_cameras = settings.get( constants.CAMERAS, constants.EXPORT_OPTIONS[constants.CAMERAS]) properties.option_hierarchy = settings.get( constants.HIERARCHY, constants.EXPORT_OPTIONS[constants.HIERARCHY]) ## } ## Animation { properties.option_animation_morph = settings.get( constants.MORPH_TARGETS, constants.EXPORT_OPTIONS[constants.MORPH_TARGETS]) properties.option_blend_shape = settings.get( constants.BLEND_SHAPES, constants.EXPORT_OPTIONS[constants.BLEND_SHAPES]) properties.option_animation_skeletal = settings.get( constants.ANIMATION, constants.EXPORT_OPTIONS[constants.ANIMATION]) properties.option_keyframes = settings.get( constants.KEYFRAMES, constants.EXPORT_OPTIONS[constants.KEYFRAMES]) properties.option_frame_step = settings.get( constants.FRAME_STEP, constants.EXPORT_OPTIONS[constants.FRAME_STEP]) properties.option_frame_index_as_time = settings.get( constants.FRAME_INDEX_AS_TIME, constants.EXPORT_OPTIONS[constants.FRAME_INDEX_AS_TIME]) ## } def set_settings(properties): """Set the export settings to the correct keys. :param properties: :returns: settings :rtype: dict """ settings = { constants.VERTICES: properties.option_vertices, constants.FACES: properties.option_faces, constants.NORMALS: properties.option_normals, constants.SKINNING: properties.option_skinning, constants.BONES: properties.option_bones, constants.EXTRA_VGROUPS: properties.option_extra_vgroups, constants.APPLY_MODIFIERS: properties.option_apply_modifiers, constants.GEOMETRY_TYPE: properties.option_geometry_type, constants.INDEX_TYPE: properties.option_index_type, constants.MATERIALS: properties.option_materials, constants.UVS: properties.option_uv_coords, constants.FACE_MATERIALS: properties.option_face_materials, constants.MAPS: properties.option_maps, constants.COLORS: properties.option_colors, constants.MIX_COLORS: properties.option_mix_colors, constants.SCALE: properties.option_scale, constants.ENABLE_PRECISION: properties.option_round_off, constants.PRECISION: properties.option_round_value, constants.CUSTOM_PROPERTIES: properties.option_custom_properties, constants.LOGGING: properties.option_logging, constants.COMPRESSION: properties.option_compression, constants.INDENT: properties.option_indent, constants.EXPORT_TEXTURES: properties.option_export_textures, constants.EMBED_TEXTURES: properties.option_embed_textures, constants.TEXTURE_FOLDER: properties.option_texture_folder, constants.SCENE: properties.option_export_scene, #constants.EMBED_GEOMETRY: properties.option_embed_geometry, constants.EMBED_ANIMATION: properties.option_embed_animation, constants.LIGHTS: properties.option_lights, constants.CAMERAS: properties.option_cameras, constants.HIERARCHY: properties.option_hierarchy, constants.MORPH_TARGETS: properties.option_animation_morph, constants.BLEND_SHAPES: properties.option_blend_shape, constants.ANIMATION: properties.option_animation_skeletal, constants.KEYFRAMES: properties.option_keyframes, constants.FRAME_STEP: properties.option_frame_step, constants.FRAME_INDEX_AS_TIME: properties.option_frame_index_as_time, constants.INFLUENCES_PER_VERTEX: properties.option_influences } return settings def compression_types(): """Supported compression formats :rtype: tuple """ types = [(constants.NONE, constants.NONE, constants.NONE)] try: import msgpack types.append((constants.MSGPACK, constants.MSGPACK, constants.MSGPACK)) except ImportError: pass return types def animation_options(): """The supported skeletal animation types :returns: list of tuples """ anim = [ (constants.OFF, constants.OFF.title(), constants.OFF), (constants.POSE, constants.POSE.title(), constants.POSE), (constants.REST, constants.REST.title(), constants.REST) ] return anim def resolve_conflicts(self, context): if(not self.option_export_textures): self.option_embed_textures = False; class ExportThree(bpy.types.Operator, ExportHelper): """Class that handles the export properties""" bl_idname = 'export.three' bl_label = 'Export THREE' bl_options = {'PRESET'} filename_ext = constants.EXTENSION option_vertices = BoolProperty( name="Vertices", description="Export vertices", default=constants.EXPORT_OPTIONS[constants.VERTICES]) option_faces = BoolProperty( name="Faces", description="Export faces", default=constants.EXPORT_OPTIONS[constants.FACES]) option_normals = BoolProperty( name="Normals", description="Export normals", default=constants.EXPORT_OPTIONS[constants.NORMALS]) option_colors = BoolProperty( name="Vertex Colors", description="Export vertex colors", default=constants.EXPORT_OPTIONS[constants.COLORS]) option_mix_colors = BoolProperty( name="Mix Colors", description="Mix material and vertex colors", default=constants.EXPORT_OPTIONS[constants.MIX_COLORS]) option_uv_coords = BoolProperty( name="UVs", description="Export texture coordinates", default=constants.EXPORT_OPTIONS[constants.UVS]) option_materials = BoolProperty( name="Materials", description="Export materials", default=constants.EXPORT_OPTIONS[constants.MATERIALS]) option_face_materials = BoolProperty( name="Face Materials", description="Face mapping materials", default=constants.EXPORT_OPTIONS[constants.FACE_MATERIALS]) option_maps = BoolProperty( name="Textures", description="Include texture maps", default=constants.EXPORT_OPTIONS[constants.MAPS]) option_skinning = BoolProperty( name="Skinning", description="Export skin data", default=constants.EXPORT_OPTIONS[constants.SKINNING]) option_bones = BoolProperty( name="Bones", description="Export bones", default=constants.EXPORT_OPTIONS[constants.BONES]) option_extra_vgroups = StringProperty( name="Extra Vertex Groups", description="Non-skinning vertex groups to export (comma-separated, w/ star wildcard, BufferGeometry only).", default=constants.EXPORT_OPTIONS[constants.EXTRA_VGROUPS]) option_apply_modifiers = BoolProperty( name="Apply Modifiers", description="Apply Modifiers to mesh objects", default=constants.EXPORT_OPTIONS[constants.APPLY_MODIFIERS] ) index_buffer_types = [ (constants.NONE,) * 3, (constants.UINT_16,) * 3, (constants.UINT_32,) * 3] option_index_type = EnumProperty( name="Index Buffer", description="Index buffer type that will be used for BufferGeometry objects.", items=index_buffer_types, default=constants.EXPORT_OPTIONS[constants.INDEX_TYPE]) option_scale = FloatProperty( name="Scale", description="Scale vertices", min=0.01, max=1000.0, soft_min=0.01, soft_max=1000.0, default=constants.EXPORT_OPTIONS[constants.SCALE]) option_round_off = BoolProperty( name="Enable Precision", description="Round off floating point values", default=constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION]) option_round_value = IntProperty( name="Precision", min=0, max=16, description="Floating point precision", default=constants.EXPORT_OPTIONS[constants.PRECISION]) option_custom_properties = BoolProperty( name="Custom Props", description="Export custom properties as userData", default=False) logging_types = [ (constants.DISABLED, constants.DISABLED, constants.DISABLED), (constants.DEBUG, constants.DEBUG, constants.DEBUG), (constants.INFO, constants.INFO, constants.INFO), (constants.WARNING, constants.WARNING, constants.WARNING), (constants.ERROR, constants.ERROR, constants.ERROR), (constants.CRITICAL, constants.CRITICAL, constants.CRITICAL)] option_logging = EnumProperty( name="", description="Logging verbosity level", items=logging_types, default=constants.DISABLED) option_geometry_type = EnumProperty( name="Type", description="Geometry type", items=_geometry_types()[1:], default=constants.EXPORT_OPTIONS[constants.GEOMETRY_TYPE]) option_export_scene = BoolProperty( name="Scene", description="Export scene", default=constants.EXPORT_OPTIONS[constants.SCENE]) #@TODO: removing this option since the ObjectLoader doesn't have # support for handling external geometry data #option_embed_geometry = BoolProperty( # name="Embed geometry", # description="Embed geometry", # default=constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY]) option_embed_animation = BoolProperty( name="Embed animation", description="Embed animation data with the geometry data", default=constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION]) option_export_textures = BoolProperty( name="Export textures", description="Export textures", default=constants.EXPORT_OPTIONS[constants.EXPORT_TEXTURES], update=resolve_conflicts) option_embed_textures = BoolProperty( name="Embed textures", description="Embed base64 textures in .json", default=constants.EXPORT_OPTIONS[constants.EMBED_TEXTURES]) option_texture_folder = StringProperty( name="Texture folder", description="add this folder to textures path", default=constants.EXPORT_OPTIONS[constants.TEXTURE_FOLDER]) option_lights = BoolProperty( name="Lights", description="Export default scene lights", default=False) option_cameras = BoolProperty( name="Cameras", description="Export default scene cameras", default=False) option_hierarchy = BoolProperty( name="Hierarchy", description="Export object hierarchy", default=False) option_animation_morph = BoolProperty( name="Morph animation", description="Export animation (morphs)", default=constants.EXPORT_OPTIONS[constants.MORPH_TARGETS]) option_blend_shape = BoolProperty( name="Blend Shape animation", description="Export Blend Shapes", default=constants.EXPORT_OPTIONS[constants.BLEND_SHAPES]) option_animation_skeletal = EnumProperty( name="", description="Export animation (skeletal)", items=animation_options(), default=constants.OFF) option_keyframes = BoolProperty( name="Keyframe animation", description="Export animation (keyframes)", default=constants.EXPORT_OPTIONS[constants.KEYFRAMES]) option_frame_index_as_time = BoolProperty( name="Frame index as time", description="Use (original) frame index as frame time", default=constants.EXPORT_OPTIONS[constants.FRAME_INDEX_AS_TIME]) option_frame_step = IntProperty( name="Frame step", description="Animation frame step", min=1, max=1000, soft_min=1, soft_max=1000, default=1) option_indent = BoolProperty( name="Indent JSON", description="Disable this to reduce the file size", default=constants.EXPORT_OPTIONS[constants.INDENT]) option_compression = EnumProperty( name="", description="Compression options", items=compression_types(), default=constants.NONE) option_influences = IntProperty( name="Influences", description="Maximum number of bone influences", min=1, max=4, default=2) def invoke(self, context, event): settings = context.scene.get(constants.EXPORT_SETTINGS_KEY) if settings: try: restore_export_settings(self.properties, settings) except AttributeError as e: logging.error("Loading export settings failed:") logging.exception(e) logging.debug("Removed corrupted settings") del context.scene[constants.EXPORT_SETTINGS_KEY] return ExportHelper.invoke(self, context, event) @classmethod def poll(cls, context): """ :param context: """ return context.active_object is not None def execute(self, context): """ :param context: """ if not self.properties.filepath: raise Exception("filename not set") settings = set_settings(self.properties) settings['addon_version'] = bl_info['version'] filepath = self.filepath if settings[constants.COMPRESSION] == constants.MSGPACK: filepath = "%s%s" % (filepath[:-4], constants.PACK) from io_three import exporter if settings[constants.SCENE]: exporter.export_scene(filepath, settings) else: exporter.export_geometry(filepath, settings) return {'FINISHED'} def draw(self, context): """ :param context: """ layout = self.layout ## Geometry { row = layout.row() row.label(text="GEOMETRY:") row = layout.row() row.prop(self.properties, 'option_vertices') row.prop(self.properties, 'option_faces') row = layout.row() row.prop(self.properties, 'option_normals') row.prop(self.properties, 'option_uv_coords') row = layout.row() row.prop(self.properties, 'option_bones') row.prop(self.properties, 'option_skinning') row = layout.row() row.prop(self.properties, 'option_extra_vgroups') row = layout.row() row.prop(self.properties, 'option_apply_modifiers') row = layout.row() row.prop(self.properties, 'option_geometry_type') row = layout.row() row.prop(self.properties, 'option_index_type') ## } layout.separator() ## Materials { row = layout.row() row.label(text="- Shading:") row = layout.row() row.prop(self.properties, 'option_face_materials') row = layout.row() row.prop(self.properties, 'option_colors') row = layout.row() row.prop(self.properties, 'option_mix_colors') ## } layout.separator() ## Animation { row = layout.row() row.label(text="- Animation:") row = layout.row() row.prop(self.properties, 'option_animation_morph') row = layout.row() row.prop(self.properties, 'option_blend_shape') row = layout.row() row.label(text="Skeletal animations:") row = layout.row() row.prop(self.properties, 'option_animation_skeletal') row = layout.row() row.label(text="Keyframe animations:") row = layout.row() row.prop(self.properties, 'option_keyframes') layout.row() row = layout.row() row.prop(self.properties, 'option_influences') row = layout.row() row.prop(self.properties, 'option_frame_step') row = layout.row() row.prop(self.properties, 'option_frame_index_as_time') row = layout.row() row.prop(self.properties, 'option_embed_animation') ## } layout.separator() ## Scene { row = layout.row() row.label(text="SCENE:") row = layout.row() row.prop(self.properties, 'option_export_scene') row.prop(self.properties, 'option_materials') #row = layout.row() #row.prop(self.properties, 'option_embed_geometry') row = layout.row() row.prop(self.properties, 'option_lights') row.prop(self.properties, 'option_cameras') ## } row = layout.row() row.prop(self.properties, 'option_hierarchy') layout.separator() ## Settings { row = layout.row() row.label(text="SETTINGS:") row = layout.row() row.prop(self.properties, 'option_maps') row = layout.row() row.prop(self.properties, 'option_export_textures') row = layout.row() row.prop(self.properties, 'option_embed_textures') row.enabled = self.properties.option_export_textures row = layout.row() row.prop(self.properties, 'option_texture_folder') row = layout.row() row.prop(self.properties, 'option_scale') layout.row() row = layout.row() row.prop(self.properties, 'option_round_off') row = layout.row() row.prop(self.properties, 'option_round_value') layout.row() row = layout.row() row.label(text="Custom Properties") row = layout.row() row.prop(self.properties, 'option_custom_properties') layout.row() row = layout.row() row.label(text="Logging verbosity:") row = layout.row() row.prop(self.properties, 'option_logging') row = layout.row() row.label(text="File compression format:") row = layout.row() row.prop(self.properties, 'option_compression') row = layout.row() row.prop(self.properties, 'option_indent') ## } ## Operators { has_settings = context.scene.get(constants.EXPORT_SETTINGS_KEY, False) row = layout.row() row.operator( ThreeExportSettings.bl_idname, ThreeExportSettings.bl_label, icon="%s" % "PINNED" if has_settings else "UNPINNED") ## } def menu_func_export(self, context): """ :param self: :param context: """ default_path = bpy.data.filepath.replace('.blend', constants.EXTENSION) text = "Three.js (%s)" % constants.EXTENSION operator = self.layout.operator(ExportThree.bl_idname, text=text) operator.filepath = default_path def register(): """Registers the addon (Blender boilerplate)""" bpy.utils.register_module(__name__) bpy.types.INFO_MT_file_export.append(menu_func_export) def unregister(): """Unregisters the addon (Blender boilerplate)""" bpy.utils.unregister_module(__name__) bpy.types.INFO_MT_file_export.remove(menu_func_export) if __name__ == '__main__': register()