__init__.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  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.
  7. #
  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.
  12. #
  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.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. import os
  19. import json
  20. import bpy
  21. from bpy_extras.io_utils import ExportHelper
  22. from bpy.props import (
  23. EnumProperty,
  24. BoolProperty,
  25. FloatProperty,
  26. IntProperty
  27. )
  28. from . import constants
  29. SETTINGS_FILE_EXPORT = 'three_settings_export.js'
  30. bl_info = {
  31. 'name': 'Three.js Format',
  32. 'author': 'Ed Caspersen (repsac)',
  33. 'version': (1, 0, 0),
  34. 'blender': (2, 7, 2),
  35. 'location': 'File > Import-Export',
  36. 'description': 'Export Three.js formatted JSON files.',
  37. 'warning': '',
  38. 'wiki_url': 'https://github.com/mrdoob/three.js/tree/'\
  39. 'master/utils/exporters/blender',
  40. 'tracker_url': 'https://github.com/mrdoob/three.js/issues',
  41. 'category': 'Import-Export'
  42. }
  43. def _geometry_types():
  44. types = [
  45. (constants.GLOBAL, constants.GLOBAL.title(),
  46. constants.GLOBAL),
  47. (constants.GEOMETRY, constants.GEOMETRY.title(),
  48. constants.GEOMETRY),
  49. (constants.BUFFER_GEOMETRY, constants.BUFFER_GEOMETRY,
  50. constants.BUFFER_GEOMETRY),
  51. ]
  52. return types
  53. bpy.types.Mesh.THREE_geometry_type = EnumProperty(
  54. name='Geometry type',
  55. description='Geometry type',
  56. items=_geometry_types(),
  57. default=constants.GLOBAL)
  58. class MESH_PT_hello(bpy.types.Panel):
  59. bl_label = 'THREE'
  60. bl_space_type = 'PROPERTIES'
  61. bl_region_type = 'WINDOW'
  62. bl_context = 'data'
  63. def draw(self, context):
  64. row = self.layout.row()
  65. if context.mesh:
  66. row.prop(context.mesh, 'THREE_geometry_type', text='Type')
  67. def _blending_types(index):
  68. types = (
  69. constants.BLENDING_TYPES.NONE,
  70. constants.BLENDING_TYPES.NORMAL,
  71. constants.BLENDING_TYPES.ADDITIVE,
  72. constants.BLENDING_TYPES.SUBTRACTIVE,
  73. constants.BLENDING_TYPES.MULTIPLY,
  74. constants.BLENDING_TYPES.CUSTOM)
  75. return (types[index], types[index], types[index])
  76. bpy.types.Material.THREE_blending_type = EnumProperty(
  77. name='Blending type',
  78. description='Blending type',
  79. items=[_blending_types(x) for x in range(5)],
  80. default=constants.BLENDING_TYPES.NORMAL)
  81. bpy.types.Material.THREE_depth_write = BoolProperty(default=True)
  82. bpy.types.Material.THREE_depth_test = BoolProperty(default=True)
  83. class MATERIAL_PT_hello(bpy.types.Panel):
  84. bl_label = 'THREE'
  85. bl_space_type = 'PROPERTIES'
  86. bl_region_type = 'WINDOW'
  87. bl_context = 'material'
  88. def draw(self, context):
  89. layout = self.layout
  90. mat = context.material
  91. if mat is not None:
  92. row = layout.row()
  93. row.label(text='Selected material: %s' % mat.name )
  94. row = layout.row()
  95. row.prop(mat, 'THREE_blending_type',
  96. text='Blending type' )
  97. row = layout.row()
  98. row.prop(mat, 'THREE_depth_write',
  99. text='Enable depth writing' )
  100. row = layout.row()
  101. row.prop(mat, 'THREE_depth_test',
  102. text='Enable depth testing' )
  103. def _mag_filters(index):
  104. types = (constants.LINEAR_FILTERS.LINEAR,
  105. constants.NEAREST_FILTERS.NEAREST)
  106. return (types[index], types[index], types[index])
  107. bpy.types.Texture.THREE_mag_filter = EnumProperty(
  108. name='Mag Filter',
  109. items = [_mag_filters(x) for x in range(2)],
  110. default=constants.LINEAR_FILTERS.LINEAR)
  111. def _min_filters(index):
  112. types = (constants.LINEAR_FILTERS.LINEAR,
  113. constants.LINEAR_FILTERS.MIP_MAP_NEAREST,
  114. constants.LINEAR_FILTERS.MIP_MAP_LINEAR,
  115. constants.NEAREST_FILTERS.NEAREST,
  116. constants.NEAREST_FILTERS.MIP_MAP_NEAREST,
  117. constants.NEAREST_FILTERS.MIP_MAP_LINEAR)
  118. return (types[index], types[index], types[index])
  119. bpy.types.Texture.THREE_min_filter = EnumProperty(
  120. name='Min Filter',
  121. items = [_min_filters(x) for x in range(6)],
  122. default=constants.LINEAR_FILTERS.MIP_MAP_LINEAR)
  123. def _mapping(index):
  124. types = (constants.MAPPING_TYPES.UV,
  125. constants.MAPPING_TYPES.CUBE_REFLECTION,
  126. constants.MAPPING_TYPES.CUBE_REFRACTION,
  127. constants.MAPPING_TYPES.SPHERICAL_REFLECTION)
  128. return (types[index], types[index], types[index])
  129. bpy.types.Texture.THREE_mapping = EnumProperty(
  130. name='Mapping',
  131. items = [_mapping(x) for x in range(4)],
  132. default=constants.MAPPING_TYPES.UV)
  133. class TEXTURE_PT_hello(bpy.types.Panel):
  134. bl_label = 'THREE'
  135. bl_space_type = 'PROPERTIES'
  136. bl_region_type = 'WINDOW'
  137. bl_context = 'texture'
  138. #@TODO: possible to make cycles compatible?
  139. def draw(self, context):
  140. layout = self.layout
  141. tex = context.texture
  142. if tex is not None:
  143. row = layout.row()
  144. row.prop(tex, 'THREE_mapping', text='Mapping')
  145. row = layout.row()
  146. row.prop(tex, 'THREE_mag_filter', text='Mag Filter')
  147. row = layout.row()
  148. row.prop(tex, 'THREE_min_filter', text='Min Filter')
  149. bpy.types.Object.THREE_export = bpy.props.BoolProperty(default=True)
  150. class OBJECT_PT_hello(bpy.types.Panel):
  151. bl_label = 'THREE'
  152. bl_space_type = 'PROPERTIES'
  153. bl_region_type = 'WINDOW'
  154. bl_context = 'object'
  155. def draw(self, context):
  156. layout = self.layout
  157. obj = context.object
  158. row = layout.row()
  159. row.prop(obj, 'THREE_export', text='Export')
  160. def get_settings_fullpath():
  161. return os.path.join(bpy.app.tempdir, SETTINGS_FILE_EXPORT)
  162. def save_settings_export(properties):
  163. settings = {
  164. constants.VERTICES: properties.option_vertices,
  165. constants.FACES: properties.option_faces,
  166. constants.NORMALS: properties.option_normals,
  167. constants.SKINNING: properties.option_skinning,
  168. constants.BONES: properties.option_bones,
  169. constants.GEOMETRY_TYPE: properties.option_geometry_type,
  170. constants.MATERIALS: properties.option_materials,
  171. constants.UVS: properties.option_uv_coords,
  172. constants.FACE_MATERIALS: properties.option_face_materials,
  173. constants.MAPS: properties.option_maps,
  174. constants.COLORS: properties.option_colors,
  175. constants.MIX_COLORS: properties.option_mix_colors,
  176. constants.SCALE: properties.option_scale,
  177. constants.ENABLE_PRECISION: properties.option_round_off,
  178. constants.PRECISION: properties.option_round_value,
  179. constants.LOGGING: properties.option_logging,
  180. constants.COMPRESSION: properties.option_compression,
  181. constants.COPY_TEXTURES: properties.option_copy_textures,
  182. constants.SCENE: properties.option_export_scene,
  183. constants.EMBED_GEOMETRY: properties.option_embed_geometry,
  184. constants.EMBED_ANIMATION: properties.option_embed_animation,
  185. constants.LIGHTS: properties.option_lights,
  186. constants.CAMERAS: properties.option_cameras,
  187. constants.MORPH_TARGETS: properties.option_animation_morph,
  188. constants.ANIMATION: properties.option_animation_skeletal,
  189. constants.FRAME_STEP: properties.option_frame_step,
  190. constants.INFLUENCES_PER_VERTEX: properties.option_influences
  191. }
  192. fname = get_settings_fullpath()
  193. with open(fname, 'w') as stream:
  194. json.dump(settings, stream)
  195. return settings
  196. def restore_settings_export(properties):
  197. settings = {}
  198. fname = get_settings_fullpath()
  199. if os.path.exists(fname) and os.access(fname, os.R_OK):
  200. f = open(fname, 'r')
  201. settings = json.load(f)
  202. ## Geometry {
  203. properties.option_vertices = settings.get(
  204. constants.VERTICES,
  205. constants.EXPORT_OPTIONS[constants.VERTICES])
  206. properties.option_faces = settings.get(
  207. constants.FACES,
  208. constants.EXPORT_OPTIONS[constants.FACES])
  209. properties.option_normals = settings.get(
  210. constants.NORMALS,
  211. constants.EXPORT_OPTIONS[constants.NORMALS])
  212. properties.option_skinning = settings.get(
  213. constants.SKINNING,
  214. constants.EXPORT_OPTIONS[constants.SKINNING])
  215. properties.option_bones = settings.get(
  216. constants.BONES,
  217. constants.EXPORT_OPTIONS[constants.BONES])
  218. properties.option_influences = settings.get(
  219. constants.INFLUENCES_PER_VERTEX,
  220. constants.EXPORT_OPTIONS[constants.INFLUENCES_PER_VERTEX])
  221. properties.option_geometry_type = settings.get(
  222. constants.GEOMETRY_TYPE,
  223. constants.EXPORT_OPTIONS[constants.GEOMETRY_TYPE])
  224. ## }
  225. ## Materials {
  226. properties.option_materials = settings.get(
  227. constants.MATERIALS,
  228. constants.EXPORT_OPTIONS[constants.MATERIALS])
  229. properties.option_uv_coords = settings.get(
  230. constants.UVS,
  231. constants.EXPORT_OPTIONS[constants.UVS])
  232. properties.option_face_materials = settings.get(
  233. constants.FACE_MATERIALS,
  234. constants.EXPORT_OPTIONS[constants.FACE_MATERIALS])
  235. properties.option_maps = settings.get(
  236. constants.MAPS,
  237. constants.EXPORT_OPTIONS[constants.MAPS])
  238. properties.option_colors = settings.get(
  239. constants.COLORS,
  240. constants.EXPORT_OPTIONS[constants.COLORS])
  241. properties.option_mix_colors = settings.get(
  242. constants.MIX_COLORS,
  243. constants.EXPORT_OPTIONS[constants.MIX_COLORS])
  244. ## }
  245. ## Settings {
  246. properties.option_scale = settings.get(
  247. constants.SCALE,
  248. constants.EXPORT_OPTIONS[constants.SCALE])
  249. properties.option_round_off = settings.get(
  250. constants.ENABLE_PRECISION,
  251. constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION])
  252. properties.option_round_value = settings.get(
  253. constants.PRECISION,
  254. constants.EXPORT_OPTIONS[constants.PRECISION])
  255. properties.option_logging = settings.get(
  256. constants.LOGGING,
  257. constants.EXPORT_OPTIONS[constants.LOGGING])
  258. properties.option_compression = settings.get(
  259. constants.COMPRESSION,
  260. constants.NONE)
  261. properties.option_copy_textures = settings.get(
  262. constants.COPY_TEXTURES,
  263. constants.EXPORT_OPTIONS[constants.COPY_TEXTURES])
  264. properties.option_embed_animation = settings.get(
  265. constants.EMBED_ANIMATION,
  266. constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION])
  267. ## }
  268. ## Scene {
  269. properties.option_export_scene = settings.get(
  270. constants.SCENE,
  271. constants.EXPORT_OPTIONS[constants.SCENE])
  272. properties.option_embed_geometry = settings.get(
  273. constants.EMBED_GEOMETRY,
  274. constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY])
  275. properties.option_lights = settings.get(
  276. constants.LIGHTS,
  277. constants.EXPORT_OPTIONS[constants.LIGHTS])
  278. properties.option_cameras = settings.get(
  279. constants.CAMERAS,
  280. constants.EXPORT_OPTIONS[constants.CAMERAS])
  281. ## }
  282. ## Animation {
  283. properties.option_animation_morph = settings.get(
  284. constants.MORPH_TARGETS,
  285. constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
  286. properties.option_animation_skeletal = settings.get(
  287. constants.ANIMATION,
  288. constants.EXPORT_OPTIONS[constants.ANIMATION])
  289. properties.option_frame_step = settings.get(
  290. constants.FRAME_STEP,
  291. constants.EXPORT_OPTIONS[constants.FRAME_STEP])
  292. ## }
  293. def compression_types():
  294. types = [(constants.NONE, constants.NONE, constants.NONE)]
  295. try:
  296. import msgpack
  297. types.append((constants.MSGPACK, constants.MSGPACK,
  298. constants.MSGPACK))
  299. except ImportError:
  300. pass
  301. return types
  302. class ExportThree(bpy.types.Operator, ExportHelper):
  303. bl_idname='export.three'
  304. bl_label = 'Export THREE'
  305. filename_ext = constants.EXTENSION
  306. option_vertices = BoolProperty(
  307. name='Vertices',
  308. description='Export vertices',
  309. default=constants.EXPORT_OPTIONS[constants.VERTICES])
  310. option_faces = BoolProperty(
  311. name='Faces',
  312. description='Export faces',
  313. default=constants.EXPORT_OPTIONS[constants.FACES])
  314. option_normals = BoolProperty(
  315. name='Normals',
  316. description='Export normals',
  317. default=constants.EXPORT_OPTIONS[constants.NORMALS])
  318. option_colors = BoolProperty(
  319. name='Colors',
  320. description='Export vertex colors',
  321. default=constants.EXPORT_OPTIONS[constants.COLORS])
  322. option_mix_colors = BoolProperty(
  323. name='Mix Colors',
  324. description='Mix material and vertex colors',
  325. default=constants.EXPORT_OPTIONS[constants.MIX_COLORS])
  326. option_uv_coords = BoolProperty(
  327. name='UVs',
  328. description='Export texture coordinates',
  329. default=constants.EXPORT_OPTIONS[constants.UVS])
  330. option_materials = BoolProperty(
  331. name='Materials',
  332. description='Export materials',
  333. default=constants.EXPORT_OPTIONS[constants.MATERIALS])
  334. option_face_materials = BoolProperty(
  335. name='Face Materials',
  336. description='Face mapping materials',
  337. default=constants.EXPORT_OPTIONS[constants.FACE_MATERIALS])
  338. option_maps = BoolProperty(
  339. name='Textures',
  340. description='Include texture maps',
  341. default=constants.EXPORT_OPTIONS[constants.MAPS])
  342. option_skinning = BoolProperty(
  343. name='Skinning',
  344. description='Export skin data',
  345. default=constants.EXPORT_OPTIONS[constants.SKINNING])
  346. option_bones = BoolProperty(
  347. name='Bones',
  348. description='Export bones',
  349. default=constants.EXPORT_OPTIONS[constants.BONES])
  350. option_scale = FloatProperty(
  351. name='Scale',
  352. description='Scale vertices',
  353. min=0.01,
  354. max=1000.0,
  355. soft_min=0.01,
  356. soft_max=1000.0,
  357. default=constants.EXPORT_OPTIONS[constants.SCALE])
  358. option_round_off = BoolProperty(
  359. name='Enable Precision',
  360. description='Round off floating point values',
  361. default=constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION])
  362. option_round_value = IntProperty(
  363. name='Precision',
  364. min=0,
  365. max=16,
  366. description='Floating point precision',
  367. default=constants.EXPORT_OPTIONS[constants.PRECISION])
  368. logging_types = [
  369. (constants.DEBUG, constants.DEBUG, constants.DEBUG),
  370. (constants.INFO, constants.INFO, constants.INFO),
  371. (constants.WARNING, constants.WARNING, constants.WARNING),
  372. (constants.ERROR, constants.ERROR, constants.ERROR),
  373. (constants.CRITICAL, constants.CRITICAL, constants.CRITICAL)]
  374. option_logging = EnumProperty(
  375. name='Logging',
  376. description = 'Logging verbosity level',
  377. items=logging_types,
  378. default=constants.DEBUG)
  379. option_geometry_type = EnumProperty(
  380. name='Type',
  381. description='Geometry type',
  382. items=_geometry_types()[1:],
  383. default=constants.GEOMETRY)
  384. option_export_scene = BoolProperty(
  385. name='Scene',
  386. description='Export scene',
  387. default=constants.EXPORT_OPTIONS[constants.SCENE])
  388. option_embed_geometry = BoolProperty(
  389. name='Embed geometry',
  390. description='Embed geometry',
  391. default=constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY])
  392. option_embed_animation = BoolProperty(
  393. name='Embed animation',
  394. description='Embed animation data with the geometry data',
  395. default=constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION])
  396. option_copy_textures = BoolProperty(
  397. name='Copy textures',
  398. description='Copy textures',
  399. default=constants.EXPORT_OPTIONS[constants.COPY_TEXTURES])
  400. option_lights = BoolProperty(
  401. name='Lights',
  402. description='Export default scene lights',
  403. default=False)
  404. option_cameras = BoolProperty(
  405. name='Cameras',
  406. description='Export default scene cameras',
  407. default=False)
  408. option_animation_morph = BoolProperty(
  409. name='Morph animation',
  410. description='Export animation (morphs)',
  411. default=constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
  412. option_animation_skeletal = BoolProperty(
  413. name='Skeletal animation',
  414. description='Export animation (skeletal)',
  415. default=constants.EXPORT_OPTIONS[constants.ANIMATION])
  416. option_frame_step = IntProperty(
  417. name='Frame step',
  418. description='Animation frame step',
  419. min=1,
  420. max=1000,
  421. soft_min=1,
  422. soft_max=1000,
  423. default=1)
  424. option_compression = EnumProperty(
  425. name='Compression',
  426. description = 'Compression options',
  427. items=compression_types(),
  428. default=constants.NONE)
  429. option_influences = IntProperty(
  430. name='Influences',
  431. description='Maximum number of bone influences',
  432. min=1,
  433. max=4,
  434. default=2)
  435. def invoke(self, context, event):
  436. restore_settings_export(self.properties)
  437. return ExportHelper.invoke(self, context, event)
  438. @classmethod
  439. def poll(cls, context):
  440. return context.active_object is not None
  441. def execute(self, context):
  442. if not self.properties.filepath:
  443. raise Exception('filename not set')
  444. settings = save_settings_export(self.properties)
  445. filepath = self.filepath
  446. if settings[constants.COMPRESSION] == constants.MSGPACK:
  447. filepath = '%s%s' % (filepath[:-4], constants.PACK)
  448. from io_three import exporter
  449. if settings[constants.SCENE]:
  450. exporter.export_scene(filepath, settings)
  451. else:
  452. exporter.export_geometry(filepath, settings)
  453. return {'FINISHED'}
  454. def draw(self, context):
  455. layout = self.layout
  456. ## Geometry {
  457. row = layout.row()
  458. row.label(text='Geometry:')
  459. row = layout.row()
  460. row.prop(self.properties, 'option_vertices')
  461. row.prop(self.properties, 'option_faces')
  462. row = layout.row()
  463. row.prop(self.properties, 'option_normals')
  464. row = layout.row()
  465. row.prop(self.properties, 'option_bones')
  466. row.prop(self.properties, 'option_skinning')
  467. row = layout.row()
  468. row.prop(self.properties, 'option_geometry_type')
  469. row = layout.row()
  470. row.prop(self.properties, 'option_influences')
  471. ## }
  472. layout.separator()
  473. ## Materials {
  474. row = layout.row()
  475. row.label(text='Materials:')
  476. row = layout.row()
  477. row.prop(self.properties, 'option_materials')
  478. row.prop(self.properties, 'option_uv_coords')
  479. row = layout.row()
  480. row.prop(self.properties, 'option_face_materials')
  481. row.prop(self.properties, 'option_maps')
  482. row = layout.row()
  483. row.prop(self.properties, 'option_colors')
  484. row.prop(self.properties, 'option_mix_colors')
  485. ## }
  486. layout.separator()
  487. ## Settings {
  488. row = layout.row()
  489. row.label(text='Settings:')
  490. row = layout.row()
  491. row.prop(self.properties, 'option_scale')
  492. row = layout.row()
  493. row.prop(self.properties, 'option_round_off')
  494. row.prop(self.properties, 'option_round_value')
  495. row = layout.row()
  496. row.prop(self.properties, 'option_logging')
  497. row = layout.row()
  498. row.prop(self.properties, 'option_compression')
  499. row = layout.row()
  500. row.prop(self.properties, 'option_copy_textures')
  501. row = layout.row()
  502. row.prop(self.properties, 'option_embed_animation')
  503. ## }
  504. layout.separator()
  505. ## Scene {
  506. row = layout.row()
  507. row.label(text='Scene:')
  508. row = layout.row()
  509. row.prop(self.properties, 'option_export_scene')
  510. row = layout.row()
  511. row.prop(self.properties, 'option_embed_geometry')
  512. row = layout.row()
  513. row.prop(self.properties, 'option_lights')
  514. row.prop(self.properties, 'option_cameras')
  515. ## }
  516. layout.separator()
  517. ## Animation {
  518. row = layout.row()
  519. row.label(text='Animation:')
  520. row = layout.row()
  521. row.prop(self.properties, 'option_animation_morph')
  522. row = layout.row()
  523. row.prop(self.properties, 'option_animation_skeletal')
  524. row = layout.row()
  525. row.prop(self.properties, 'option_frame_step')
  526. ## }
  527. def menu_func_export(self, context):
  528. default_path = bpy.data.filepath.replace('.blend', constants.EXTENSION)
  529. text = 'Three (%s)' % constants.EXTENSION
  530. operator = self.layout.operator(ExportThree.bl_idname, text=text)
  531. operator.filepath = default_path
  532. def register():
  533. bpy.utils.register_module(__name__)
  534. bpy.types.INFO_MT_file_export.append(menu_func_export)
  535. def unregister():
  536. bpy.utils.unregister_module(__name__)
  537. bpy.types.INFO_MT_file_export.remove(menu_func_export)
  538. if __name__ == '__main__':
  539. register()