obj_export.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  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. # <pep8 compliant>
  19. import os
  20. import bpy
  21. from mathutils import Matrix, Vector, Color
  22. from bpy_extras import io_utils, node_shader_utils
  23. def write_mtl(filepath, materials, path_mode):
  24. C = bpy.context
  25. scene = C.scene
  26. world = scene.world
  27. world_amb = Color((0.8, 0.8, 0.8))
  28. source_dir = os.path.dirname(bpy.data.filepath)
  29. dest_dir = os.path.dirname(filepath)
  30. # Set of images topotentially copy
  31. copy_set = set()
  32. with open(filepath, "w", encoding="utf8", newline="\n") as f:
  33. fw = f.write
  34. fw('# Blender MTL File: %r\n' % (os.path.basename(bpy.data.filepath) or "None"))
  35. fw('# Material Count: %i\n' % len(materials))
  36. for material in materials:
  37. fw('newmtl %s\n' % material)
  38. mat = bpy.data.materials[material]
  39. mat_wrap = node_shader_utils.PrincipledBSDFWrapper(mat) if mat else None
  40. if mat_wrap:
  41. use_mirror = mat_wrap.metallic != 0.0
  42. use_transparency = mat_wrap.alpha != 1.0
  43. # XXX Totally empirical conversion, trying to adapt it
  44. # (from 1.0 - 0.0 Principled BSDF range to 0.0 - 900.0 OBJ specular exponent range)...
  45. spec = (1.0 - mat_wrap.roughness) * 30
  46. spec *= spec
  47. fw('Ns %.6f\n' % spec)
  48. # Ambient
  49. if use_mirror:
  50. fw('Ka %.6f %.6f %.6f\n' % (mat_wrap.metallic, mat_wrap.metallic, mat_wrap.metallic))
  51. else:
  52. fw('Ka %.6f %.6f %.6f\n' % (1.0, 1.0, 1.0))
  53. fw('Kd %.6f %.6f %.6f\n' % mat_wrap.base_color[:3]) # Diffuse
  54. # XXX TODO Find a way to handle tint and diffuse color, in a consistent way with import...
  55. fw('Ks %.6f %.6f %.6f\n' % (mat_wrap.specular, mat_wrap.specular, mat_wrap.specular)) # Specular
  56. # Emission, not in original MTL standard but seems pretty common, see T45766.
  57. fw('Ke 0.0 0.0 0.0\n') # XXX Not supported by current Principled-based shader.
  58. fw('Ni %.6f\n' % mat_wrap.ior) # Refraction index
  59. fw('d %.6f\n' % mat_wrap.alpha) # Alpha (obj uses 'd' for dissolve)
  60. # See http://en.wikipedia.org/wiki/Wavefront_.obj_file for whole list of values...
  61. # Note that mapping is rather fuzzy sometimes, trying to do our best here.
  62. if mat_wrap.specular == 0:
  63. fw('illum 1\n') # no specular.
  64. elif use_mirror:
  65. if use_transparency:
  66. fw('illum 6\n') # Reflection, Transparency, Ray trace
  67. else:
  68. fw('illum 3\n') # Reflection and Ray trace
  69. elif use_transparency:
  70. fw('illum 9\n') # 'Glass' transparency and no Ray trace reflection... fuzzy matching, but...
  71. else:
  72. fw('illum 2\n') # light normally
  73. #### And now, the image textures...
  74. image_map = {
  75. "map_Kd": "base_color_texture",
  76. "map_Ka": None, # ambient...
  77. "map_Ks": "specular_texture",
  78. "map_Ns": "roughness_texture",
  79. "map_d": "alpha_texture",
  80. "map_Tr": None, # transmission roughness?
  81. "map_Bump": "normalmap_texture",
  82. "disp": None, # displacement...
  83. "refl": "metallic_texture",
  84. "map_Ke": None # emission...
  85. }
  86. for key, mat_wrap_key in sorted(image_map.items()):
  87. if mat_wrap_key is None:
  88. continue
  89. tex_wrap = getattr(mat_wrap, mat_wrap_key, None)
  90. if tex_wrap is None:
  91. continue
  92. image = tex_wrap.image
  93. if image is None:
  94. continue
  95. filepath = io_utils.path_reference(image.filepath, source_dir, dest_dir,
  96. path_mode, "", copy_set, image.library)
  97. options = []
  98. if key == "map_Bump":
  99. if mat_wrap.normalmap_strength != 1.0:
  100. options.append('-bm %.6f' % mat_wrap.normalmap_strength)
  101. if tex_wrap.translation != Vector((0.0, 0.0, 0.0)):
  102. options.append('-o %.6f %.6f %.6f' % tex_wrap.translation[:])
  103. if tex_wrap.scale != Vector((1.0, 1.0, 1.0)):
  104. options.append('-s %.6f %.6f %.6f' % tex_wrap.scale[:])
  105. if options:
  106. fw('%s %s %s\n' % (key, " ".join(options), repr(filepath)[1:-1]))
  107. else:
  108. fw('%s %s\n' % (key, repr(filepath)[1:-1]))
  109. else:
  110. # Write a dummy material here?
  111. fw('Ns 500\n')
  112. fw('Ka 0.8 0.8 0.8\n') # Ambient
  113. fw('Kd 0.8 0.8 0.8\n') # Diffuse
  114. fw('Ks 0.8 0.8 0.8\n') # Specular
  115. fw('d 1\n') # No alpha
  116. fw('illum 2\n') # light normally
  117. # After closing file
  118. io_utils.path_reference_copy(copy_set)