__init__.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  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. constants.MAPPING_TYPES.SPHERICAL_REFRACTION)
  129. return (types[index], types[index], types[index])
  130. bpy.types.Texture.THREE_mapping = EnumProperty(
  131. name='Mapping',
  132. items = [_mapping(x) for x in range(5)],
  133. default=constants.MAPPING_TYPES.UV)
  134. class TEXTURE_PT_hello(bpy.types.Panel):
  135. bl_label = 'THREE'
  136. bl_space_type = 'PROPERTIES'
  137. bl_region_type = 'WINDOW'
  138. bl_context = 'texture'
  139. #@TODO: possible to make cycles compatible?
  140. def draw(self, context):
  141. layout = self.layout
  142. tex = context.texture
  143. if tex is not None:
  144. row = layout.row()
  145. row.prop(tex, 'THREE_mapping', text='Mapping')
  146. row = layout.row()
  147. row.prop(tex, 'THREE_mag_filter', text='Mag Filter')
  148. row = layout.row()
  149. row.prop(tex, 'THREE_min_filter', text='Min Filter')
  150. bpy.types.Object.THREE_export = bpy.props.BoolProperty(default=True)
  151. class OBJECT_PT_hello(bpy.types.Panel):
  152. bl_label = 'THREE'
  153. bl_space_type = 'PROPERTIES'
  154. bl_region_type = 'WINDOW'
  155. bl_context = 'object'
  156. def draw(self, context):
  157. layout = self.layout
  158. obj = context.object
  159. row = layout.row()
  160. row.prop(obj, 'THREE_export', text='Export')
  161. def get_settings_fullpath():
  162. return os.path.join(bpy.app.tempdir, SETTINGS_FILE_EXPORT)
  163. def save_settings_export(properties):
  164. settings = {
  165. constants.VERTICES: properties.option_vertices,
  166. constants.FACES: properties.option_faces,
  167. constants.NORMALS: properties.option_normals,
  168. constants.SKINNING: properties.option_skinning,
  169. constants.BONES: properties.option_bones,
  170. constants.GEOMETRY_TYPE: properties.option_geometry_type,
  171. constants.MATERIALS: properties.option_materials,
  172. constants.UVS: properties.option_uv_coords,
  173. constants.FACE_MATERIALS: properties.option_face_materials,
  174. constants.MAPS: properties.option_maps,
  175. constants.COLORS: properties.option_colors,
  176. constants.MIX_COLORS: properties.option_mix_colors,
  177. constants.SCALE: properties.option_scale,
  178. constants.ENABLE_PRECISION: properties.option_round_off,
  179. constants.PRECISION: properties.option_round_value,
  180. constants.LOGGING: properties.option_logging,
  181. constants.COMPRESSION: properties.option_compression,
  182. constants.COPY_TEXTURES: properties.option_copy_textures,
  183. constants.SCENE: properties.option_export_scene,
  184. constants.EMBED_GEOMETRY: properties.option_embed_geometry,
  185. constants.EMBED_ANIMATION: properties.option_embed_animation,
  186. constants.LIGHTS: properties.option_lights,
  187. constants.CAMERAS: properties.option_cameras,
  188. constants.MORPH_TARGETS: properties.option_animation_morph,
  189. constants.ANIMATION: properties.option_animation_skeletal,
  190. constants.FRAME_STEP: properties.option_frame_step
  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, constants.EXPORT_OPTIONS[constants.VERTICES])
  205. properties.option_faces = settings.get(
  206. constants.FACES, constants.EXPORT_OPTIONS[constants.FACES])
  207. properties.option_normals = settings.get(
  208. constants.NORMALS, constants.EXPORT_OPTIONS[constants.NORMALS])
  209. properties.option_skinning = settings.get(
  210. constants.SKINNING, constants.EXPORT_OPTIONS[constants.SKINNING])
  211. properties.option_bones = settings.get(
  212. constants.BONES, constants.EXPORT_OPTIONS[constants.BONES])
  213. properties.option_geometry_type = settings.get(
  214. constants.GEOMETRY_TYPE,
  215. constants.EXPORT_OPTIONS[constants.GEOMETRY_TYPE])
  216. ## }
  217. ## Materials {
  218. properties.option_materials = settings.get(
  219. constants.MATERIALS, constants.EXPORT_OPTIONS[constants.MATERIALS])
  220. properties.option_uv_coords = settings.get(
  221. constants.UVS, constants.EXPORT_OPTIONS[constants.UVS])
  222. properties.option_face_materials = settings.get(
  223. constants.FACE_MATERIALS,
  224. constants.EXPORT_OPTIONS[constants.FACE_MATERIALS])
  225. properties.option_maps = settings.get(
  226. constants.MAPS, constants.EXPORT_OPTIONS[constants.MAPS])
  227. properties.option_colors = settings.get(
  228. constants.COLORS, constants.EXPORT_OPTIONS[constants.COLORS])
  229. properties.option_mix_colors = settings.get(
  230. constants.MIX_COLORS, constants.EXPORT_OPTIONS[constants.MIX_COLORS])
  231. ## }
  232. ## Settings {
  233. properties.option_scale = settings.get(
  234. constants.SCALE, constants.EXPORT_OPTIONS[constants.SCALE])
  235. properties.option_round_off = settings.get(
  236. constants.ENABLE_PRECISION,
  237. constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION])
  238. properties.option_round_value = settings.get(
  239. constants.PRECISION,
  240. constants.EXPORT_OPTIONS[constants.PRECISION])
  241. properties.option_logging = settings.get(
  242. constants.LOGGING, constants.EXPORT_OPTIONS[constants.LOGGING])
  243. properties.option_compression = settings.get(
  244. constants.COMPRESSION, constants.NONE)
  245. properties.option_copy_textures = settings.get(
  246. constants.COPY_TEXTURES,
  247. constants.EXPORT_OPTIONS[constants.COPY_TEXTURES])
  248. properties.option_embed_animation = settings.get(
  249. constants.EMBED_ANIMATION,
  250. constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION])
  251. ## }
  252. ## Scene {
  253. properties.option_export_scene = settings.get(
  254. constants.SCENE, constants.EXPORT_OPTIONS[constants.SCENE])
  255. properties.option_embed_geometry = settings.get(
  256. constants.EMBED_GEOMETRY,
  257. constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY])
  258. properties.option_lights = settings.get(
  259. constants.LIGHTS, constants.EXPORT_OPTIONS[constants.LIGHTS])
  260. properties.option_cameras = settings.get(
  261. constants.CAMERAS, constants.EXPORT_OPTIONS[constants.CAMERAS])
  262. ## }
  263. ## Animation {
  264. properties.option_animation_morph = settings.get(
  265. constants.MORPH_TARGETS, constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
  266. properties.option_animation_skeletal = settings.get(
  267. constants.ANIMATION, constants.EXPORT_OPTIONS[constants.ANIMATION])
  268. #properties.option_frame_index_as_time = settings.get(
  269. # 'option_frame_index_as_time', False)
  270. properties.option_frame_step = settings.get(
  271. constants.FRAME_STEP, constants.EXPORT_OPTIONS[constants.FRAME_STEP])
  272. ## }
  273. def compression_types():
  274. types = [(constants.NONE, constants.NONE, constants.NONE)]
  275. try:
  276. import msgpack
  277. types.append((constants.MSGPACK, constants.MSGPACK,
  278. constants.MSGPACK))
  279. except ImportError:
  280. pass
  281. return types
  282. class ExportThree(bpy.types.Operator, ExportHelper):
  283. bl_idname='export.three'
  284. bl_label = 'Export THREE'
  285. filename_ext = constants.EXTENSION
  286. option_vertices = BoolProperty(
  287. name='Vertices',
  288. description='Export vertices',
  289. default=constants.EXPORT_OPTIONS[constants.VERTICES])
  290. option_faces = BoolProperty(
  291. name='Faces',
  292. description='Export faces',
  293. default=constants.EXPORT_OPTIONS[constants.FACES])
  294. option_normals = BoolProperty(
  295. name='Normals',
  296. description='Export normals',
  297. default=constants.EXPORT_OPTIONS[constants.NORMALS])
  298. option_colors = BoolProperty(
  299. name='Colors',
  300. description='Export vertex colors',
  301. default=constants.EXPORT_OPTIONS[constants.COLORS])
  302. option_mix_colors = BoolProperty(
  303. name='Mix Colors',
  304. description='Mix material and vertex colors',
  305. default=constants.EXPORT_OPTIONS[constants.MIX_COLORS])
  306. option_uv_coords = BoolProperty(
  307. name='UVs',
  308. description='Export texture coordinates',
  309. default=constants.EXPORT_OPTIONS[constants.UVS])
  310. option_materials = BoolProperty(
  311. name='Materials',
  312. description='Export materials',
  313. default=constants.EXPORT_OPTIONS[constants.MATERIALS])
  314. option_face_materials = BoolProperty(
  315. name='Face Materials',
  316. description='Face mapping materials',
  317. default=constants.EXPORT_OPTIONS[constants.FACE_MATERIALS])
  318. option_maps = BoolProperty(
  319. name='Textures',
  320. description='Include texture maps',
  321. default=constants.EXPORT_OPTIONS[constants.MAPS])
  322. option_skinning = BoolProperty(
  323. name='Skinning',
  324. description='Export skin data',
  325. default=constants.EXPORT_OPTIONS[constants.SKINNING])
  326. option_bones = BoolProperty(
  327. name='Bones',
  328. description='Export bones',
  329. default=constants.EXPORT_OPTIONS[constants.BONES])
  330. option_scale = FloatProperty(
  331. name='Scale',
  332. description='Scale vertices',
  333. min=0.01,
  334. max=1000.0,
  335. soft_min=0.01,
  336. soft_max=1000.0,
  337. default=constants.EXPORT_OPTIONS[constants.SCALE])
  338. option_round_off = BoolProperty(
  339. name='Enable Precision',
  340. description='Round off floating point values',
  341. default=constants.EXPORT_OPTIONS[constants.ENABLE_PRECISION])
  342. option_round_value = IntProperty(
  343. name='Precision',
  344. min=0,
  345. max=16,
  346. description='Floating point precision',
  347. default=constants.EXPORT_OPTIONS[constants.PRECISION])
  348. logging_types = [
  349. (constants.DEBUG, constants.DEBUG, constants.DEBUG),
  350. (constants.INFO, constants.INFO, constants.INFO),
  351. (constants.WARNING, constants.WARNING, constants.WARNING),
  352. (constants.ERROR, constants.ERROR, constants.ERROR),
  353. (constants.CRITICAL, constants.CRITICAL, constants.CRITICAL)]
  354. option_logging = EnumProperty(
  355. name='Logging',
  356. description = 'Logging verbosity level',
  357. items=logging_types,
  358. default=constants.DEBUG)
  359. option_geometry_type = EnumProperty(
  360. name='Type',
  361. description='Geometry type',
  362. items=_geometry_types()[1:],
  363. default=constants.GEOMETRY)
  364. option_export_scene = BoolProperty(
  365. name='Scene',
  366. description='Export scene',
  367. default=constants.EXPORT_OPTIONS[constants.SCENE])
  368. option_embed_geometry = BoolProperty(
  369. name='Embed geometry',
  370. description='Embed geometry',
  371. default=constants.EXPORT_OPTIONS[constants.EMBED_GEOMETRY])
  372. option_embed_animation = BoolProperty(
  373. name='Embed animation',
  374. description='Embed animation data with the geometry data',
  375. default=constants.EXPORT_OPTIONS[constants.EMBED_ANIMATION])
  376. option_copy_textures = BoolProperty(
  377. name='Copy textures',
  378. description='Copy textures',
  379. default=constants.EXPORT_OPTIONS[constants.COPY_TEXTURES])
  380. option_lights = BoolProperty(
  381. name='Lights',
  382. description='Export default scene lights',
  383. default=False)
  384. option_cameras = BoolProperty(
  385. name='Cameras',
  386. description='Export default scene cameras',
  387. default=False)
  388. option_animation_morph = BoolProperty(
  389. name='Morph animation',
  390. description='Export animation (morphs)',
  391. default=constants.EXPORT_OPTIONS[constants.MORPH_TARGETS])
  392. option_animation_skeletal = BoolProperty(
  393. name='Skeletal animation',
  394. description='Export animation (skeletal)',
  395. default=constants.EXPORT_OPTIONS[constants.ANIMATION])
  396. option_frame_step = IntProperty(
  397. name='Frame step',
  398. description='Animation frame step',
  399. min=1,
  400. max=1000,
  401. soft_min=1,
  402. soft_max=1000,
  403. default=1)
  404. option_compression = EnumProperty(
  405. name='Compression',
  406. description = 'Compression options',
  407. items=compression_types(),
  408. default=constants.NONE)
  409. def invoke(self, context, event):
  410. restore_settings_export(self.properties)
  411. return ExportHelper.invoke(self, context, event)
  412. @classmethod
  413. def poll(cls, context):
  414. return context.active_object is not None
  415. def execute(self, context):
  416. if not self.properties.filepath:
  417. raise Exception('filename not set')
  418. settings = save_settings_export(self.properties)
  419. filepath = self.filepath
  420. if settings[constants.COMPRESSION] == constants.MSGPACK:
  421. filepath = '%s%s' % (filepath[:-4], constants.PACK)
  422. from io_three import exporter
  423. if settings[constants.SCENE]:
  424. exporter.export_scene(filepath, settings)
  425. else:
  426. exporter.export_geometry(filepath, settings)
  427. return {'FINISHED'}
  428. def draw(self, context):
  429. layout = self.layout
  430. ## Geometry {
  431. row = layout.row()
  432. row.label(text='Geometry:')
  433. row = layout.row()
  434. row.prop(self.properties, 'option_vertices')
  435. row.prop(self.properties, 'option_faces')
  436. row = layout.row()
  437. row.prop(self.properties, 'option_normals')
  438. row = layout.row()
  439. row.prop(self.properties, 'option_bones')
  440. row.prop(self.properties, 'option_skinning')
  441. row = layout.row()
  442. row.prop(self.properties, 'option_geometry_type')
  443. ## }
  444. layout.separator()
  445. ## Materials {
  446. row = layout.row()
  447. row.label(text='Materials:')
  448. row = layout.row()
  449. row.prop(self.properties, 'option_materials')
  450. row.prop(self.properties, 'option_uv_coords')
  451. row = layout.row()
  452. row.prop(self.properties, 'option_face_materials')
  453. row.prop(self.properties, 'option_maps')
  454. row = layout.row()
  455. row.prop(self.properties, 'option_colors')
  456. row.prop(self.properties, 'option_mix_colors')
  457. ## }
  458. layout.separator()
  459. ## Settings {
  460. row = layout.row()
  461. row.label(text='Settings:')
  462. row = layout.row()
  463. row.prop(self.properties, 'option_scale')
  464. row = layout.row()
  465. row.prop(self.properties, 'option_round_off')
  466. row.prop(self.properties, 'option_round_value')
  467. row = layout.row()
  468. row.prop(self.properties, 'option_logging')
  469. row = layout.row()
  470. row.prop(self.properties, 'option_compression')
  471. row = layout.row()
  472. row.prop(self.properties, 'option_copy_textures')
  473. row = layout.row()
  474. row.prop(self.properties, 'option_embed_animation')
  475. ## }
  476. layout.separator()
  477. ## Scene {
  478. row = layout.row()
  479. row.label(text='Scene:')
  480. row = layout.row()
  481. row.prop(self.properties, 'option_export_scene')
  482. row = layout.row()
  483. row.prop(self.properties, 'option_embed_geometry')
  484. row = layout.row()
  485. row.prop(self.properties, 'option_lights')
  486. row.prop(self.properties, 'option_cameras')
  487. ## }
  488. layout.separator()
  489. ## Animation {
  490. row = layout.row()
  491. row.label(text='Animation:')
  492. row = layout.row()
  493. row.prop(self.properties, 'option_animation_morph')
  494. row = layout.row()
  495. row.prop(self.properties, 'option_animation_skeletal')
  496. row = layout.row()
  497. row.prop(self.properties, 'option_frame_step')
  498. ## }
  499. def menu_func_export(self, context):
  500. default_path = bpy.data.filepath.replace('.blend', constants.EXTENSION)
  501. text = 'Three (%s)' % constants.EXTENSION
  502. operator = self.layout.operator(ExportThree.bl_idname, text=text)
  503. operator.filepath = default_path
  504. def register():
  505. bpy.utils.register_module(__name__)
  506. bpy.types.INFO_MT_file_export.append(menu_func_export)
  507. def unregister():
  508. bpy.utils.unregister_module(__name__)
  509. bpy.types.INFO_MT_file_export.remove(menu_func_export)
  510. if __name__ == '__main__':
  511. register()