material.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. from .logger import *
  2. from .package_level import *
  3. import bpy
  4. from base64 import b64encode
  5. from mathutils import Color
  6. from os import path, remove
  7. from shutil import copy
  8. from sys import exc_info # for writing errors to log file
  9. # used in Texture constructor, defined in BABYLON.Texture
  10. CLAMP_ADDRESSMODE = 0
  11. WRAP_ADDRESSMODE = 1
  12. MIRROR_ADDRESSMODE = 2
  13. # used in Texture constructor, defined in BABYLON.Texture
  14. EXPLICIT_MODE = 0
  15. SPHERICAL_MODE = 1
  16. #PLANAR_MODE = 2
  17. CUBIC_MODE = 3
  18. #PROJECTION_MODE = 4
  19. #SKYBOX_MODE = 5
  20. DEFAULT_MATERIAL_NAMESPACE = 'Same as Filename'
  21. #===============================================================================
  22. class MultiMaterial:
  23. def __init__(self, material_slots, idx, nameSpace):
  24. self.name = nameSpace + '.' + 'Multimaterial#' + str(idx)
  25. Logger.log('processing begun of multimaterial: ' + self.name, 2)
  26. self.material_slots = material_slots
  27. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  28. def to_scene_file(self, file_handler):
  29. file_handler.write('{')
  30. write_string(file_handler, 'name', self.name, True)
  31. write_string(file_handler, 'id', self.name)
  32. file_handler.write(',"materials":[')
  33. first = True
  34. for material in self.material_slots:
  35. if first != True:
  36. file_handler.write(',')
  37. file_handler.write('"' + material.name +'"')
  38. first = False
  39. file_handler.write(']')
  40. file_handler.write('}')
  41. #===============================================================================
  42. class Texture:
  43. def __init__(self, slot, level, textureOrImage, mesh, exporter):
  44. wasBaked = not hasattr(textureOrImage, 'uv_layer')
  45. if wasBaked:
  46. image = textureOrImage
  47. texture = None
  48. repeat = False
  49. self.hasAlpha = False
  50. self.coordinatesIndex = 0
  51. else:
  52. texture = textureOrImage
  53. image = texture.texture.image
  54. repeat = texture.texture.extension == 'REPEAT'
  55. self.hasAlpha = texture.texture.use_alpha
  56. usingMap = texture.uv_layer
  57. if len(usingMap) == 0:
  58. usingMap = mesh.data.uv_textures[0].name
  59. Logger.log('Image texture found, type: ' + slot + ', mapped using: "' + usingMap + '"', 4)
  60. if mesh.data.uv_textures[0].name == usingMap:
  61. self.coordinatesIndex = 0
  62. elif mesh.data.uv_textures[1].name == usingMap:
  63. self.coordinatesIndex = 1
  64. else:
  65. Logger.warn('Texture is not mapped as UV or UV2, assigned 1', 5)
  66. self.coordinatesIndex = 0
  67. # always write the file out, since base64 encoding is easiest from a file
  68. try:
  69. imageFilepath = path.normpath(bpy.path.abspath(image.filepath))
  70. basename = path.basename(imageFilepath)
  71. internalImage = image.packed_file or wasBaked
  72. # when coming from either a packed image or a baked image, then save_render
  73. if internalImage:
  74. if exporter.scene.inlineTextures:
  75. textureFile = path.join(exporter.textureDir, basename + "temp")
  76. else:
  77. textureFile = path.join(exporter.textureDir, basename)
  78. image.save_render(textureFile)
  79. # when backed by an actual file, copy to target dir, unless inlining
  80. else:
  81. textureFile = bpy.path.abspath(image.filepath)
  82. if not exporter.scene.inlineTextures:
  83. copy(textureFile, exporter.textureDir)
  84. except:
  85. ex = exc_info()
  86. Logger.warn('Error encountered processing image file: ' + ', Error: '+ str(ex[1]))
  87. if exporter.scene.inlineTextures:
  88. # base64 is easiest from a file, so sometimes a temp file was made above; need to delete those
  89. with open(textureFile, "rb") as image_file:
  90. asString = b64encode(image_file.read()).decode()
  91. self.encoded_URI = 'data:image/' + image.file_format + ';base64,' + asString
  92. if internalImage:
  93. remove(textureFile)
  94. # capture texture attributes
  95. self.slot = slot
  96. self.name = basename
  97. self.level = level
  98. if (texture and texture.mapping == 'CUBE'):
  99. self.coordinatesMode = CUBIC_MODE
  100. if (texture and texture.mapping == 'SPHERE'):
  101. self.coordinatesMode = SPHERICAL_MODE
  102. else:
  103. self.coordinatesMode = EXPLICIT_MODE
  104. self.uOffset = texture.offset.x if texture else 0.0
  105. self.vOffset = texture.offset.y if texture else 0.0
  106. self.uScale = texture.scale.x if texture else 1.0
  107. self.vScale = texture.scale.y if texture else 1.0
  108. self.uAng = 0
  109. self.vAng = 0
  110. self.wAng = 0
  111. if (repeat):
  112. if (texture.texture.use_mirror_x):
  113. self.wrapU = MIRROR_ADDRESSMODE
  114. else:
  115. self.wrapU = WRAP_ADDRESSMODE
  116. if (texture.texture.use_mirror_y):
  117. self.wrapV = MIRROR_ADDRESSMODE
  118. else:
  119. self.wrapV = WRAP_ADDRESSMODE
  120. else:
  121. self.wrapU = CLAMP_ADDRESSMODE
  122. self.wrapV = CLAMP_ADDRESSMODE
  123. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  124. def to_scene_file(self, file_handler):
  125. file_handler.write(', \n"' + self.slot + '":{')
  126. write_string(file_handler, 'name', self.name, True)
  127. write_float(file_handler, 'level', self.level)
  128. write_float(file_handler, 'hasAlpha', self.hasAlpha)
  129. write_int(file_handler, 'coordinatesMode', self.coordinatesMode)
  130. write_float(file_handler, 'uOffset', self.uOffset)
  131. write_float(file_handler, 'vOffset', self.vOffset)
  132. write_float(file_handler, 'uScale', self.uScale)
  133. write_float(file_handler, 'vScale', self.vScale)
  134. write_float(file_handler, 'uAng', self.uAng)
  135. write_float(file_handler, 'vAng', self.vAng)
  136. write_float(file_handler, 'wAng', self.wAng)
  137. write_int(file_handler, 'wrapU', self.wrapU)
  138. write_int(file_handler, 'wrapV', self.wrapV)
  139. write_int(file_handler, 'coordinatesIndex', self.coordinatesIndex)
  140. if hasattr(self,'encoded_URI'):
  141. write_string(file_handler, 'base64String', self.encoded_URI)
  142. file_handler.write('}')
  143. #===============================================================================
  144. # need to evaluate the need to bake a mesh before even starting; class also stores specific types of bakes
  145. class BakingRecipe:
  146. def __init__(self, mesh):
  147. # transfer from Mesh custom properties
  148. self.bakeSize = mesh.data.bakeSize
  149. self.bakeQuality = mesh.data.bakeQuality # for lossy compression formats
  150. self.forceBaking = mesh.data.forceBaking # in mesh, but not currently exposed
  151. self.usePNG = mesh.data.usePNG # in mesh, but not currently exposed
  152. # initialize all members
  153. self.needsBaking = self.forceBaking
  154. self.diffuseBaking = self.forceBaking
  155. self.ambientBaking = False
  156. self.opacityBaking = False
  157. self.reflectionBaking = False
  158. self.emissiveBaking = False
  159. self.bumpBaking = False
  160. self.specularBaking = False
  161. # need to make sure a single render
  162. self.cyclesRender = False
  163. blenderRender = False
  164. # accumulators set by Blender Game
  165. self.backFaceCulling = True # used only when baking
  166. self.isBillboard = len(mesh.material_slots) == 1 and mesh.material_slots[0].material.game_settings.face_orientation == 'BILLBOARD'
  167. # Cycles specific, need to get the node trees of each material
  168. self.nodeTrees = []
  169. for material_slot in mesh.material_slots:
  170. # a material slot is not a reference to an actual material; need to look up
  171. material = material_slot.material
  172. self.backFaceCulling &= material.game_settings.use_backface_culling
  173. # testing for Cycles renderer has to be different
  174. if material.use_nodes == True:
  175. self.needsBaking = True
  176. self.cyclesRender = True
  177. self.nodeTrees.append(material.node_tree)
  178. for node in material.node_tree.nodes:
  179. id = node.bl_idname
  180. if id == 'ShaderNodeBsdfDiffuse':
  181. self.diffuseBaking = True
  182. if id == 'ShaderNodeAmbientOcclusion':
  183. self.ambientBaking = True
  184. # there is no opacity baking for Cycles AFAIK
  185. if id == '':
  186. self.opacityBaking = True
  187. if id == 'ShaderNodeEmission':
  188. self.emissiveBaking = True
  189. if id == 'ShaderNodeNormal' or id == 'ShaderNodeNormalMap':
  190. self.bumpBaking = True
  191. if id == '':
  192. self.specularBaking = True
  193. else:
  194. blenderRender = True
  195. nDiffuseImages = 0
  196. nReflectionImages = 0
  197. nAmbientImages = 0
  198. nOpacityImages = 0
  199. nEmissiveImages = 0
  200. nBumpImages = 0
  201. nSpecularImages = 0
  202. textures = [mtex for mtex in material.texture_slots if mtex and mtex.texture]
  203. for mtex in textures:
  204. # ignore empty slots
  205. if mtex.texture.type == 'NONE':
  206. continue
  207. # for images, just need to make sure there is only 1 per type
  208. if mtex.texture.type == 'IMAGE' and not self.forceBaking:
  209. if mtex.use_map_diffuse or mtex.use_map_color_diffuse:
  210. if mtex.texture_coords == 'REFLECTION':
  211. nReflectionImages += 1
  212. else:
  213. nDiffuseImages += 1
  214. if mtex.use_map_ambient:
  215. nAmbientImages += 1
  216. if mtex.use_map_alpha:
  217. nOpacityImages += 1
  218. if mtex.use_map_emit:
  219. nEmissiveImages += 1
  220. if mtex.use_map_normal:
  221. nBumpImages += 1
  222. if mtex.use_map_color_spec:
  223. nSpecularImages += 1
  224. else:
  225. self.needsBaking = True
  226. if mtex.use_map_diffuse or mtex.use_map_color_diffuse:
  227. if mtex.texture_coords == 'REFLECTION':
  228. self.reflectionBaking = True
  229. else:
  230. self.diffuseBaking = True
  231. if mtex.use_map_ambient:
  232. self.ambientBaking = True
  233. if mtex.use_map_alpha and material.alpha > 0:
  234. self.opacityBaking = True
  235. if mtex.use_map_emit:
  236. self.emissiveBaking = True
  237. if mtex.use_map_normal:
  238. self.bumpBaking = True
  239. if mtex.use_map_color_spec:
  240. self.specularBaking = True
  241. # 2nd pass 2 check for multiples of a given image type
  242. if nDiffuseImages > 1:
  243. self.needsBaking = self.diffuseBaking = True
  244. if nReflectionImages > 1:
  245. self.needsBaking = self.nReflectionImages = True
  246. if nAmbientImages > 1:
  247. self.needsBaking = self.ambientBaking = True
  248. if nOpacityImages > 1:
  249. self.needsBaking = self.opacityBaking = True
  250. if nEmissiveImages > 1:
  251. self.needsBaking = self.emissiveBaking = True
  252. if nBumpImages > 1:
  253. self.needsBaking = self.bumpBaking = True
  254. if nSpecularImages > 1:
  255. self.needsBaking = self.specularBaking = True
  256. self.multipleRenders = blenderRender and self.cyclesRender
  257. # check for really old .blend file, eg. 2.49, to ensure that everything requires exists
  258. if self.needsBaking and bpy.data.screens.find('UV Editing') == -1:
  259. Logger.warn('Contains material requiring baking, but resources not available. Probably .blend very old', 2)
  260. self.needsBaking = False
  261. #===============================================================================
  262. # Not intended to be instanced directly
  263. class Material:
  264. def __init__(self, checkReadyOnlyOnce, maxSimultaneousLights):
  265. self.checkReadyOnlyOnce = checkReadyOnlyOnce
  266. self.maxSimultaneousLights = maxSimultaneousLights
  267. # first pass of textures, either appending image type or recording types of bakes to do
  268. self.textures = []
  269. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  270. def to_scene_file(self, file_handler):
  271. file_handler.write('{')
  272. write_string(file_handler, 'name', self.name, True)
  273. write_string(file_handler, 'id', self.name)
  274. write_color(file_handler, 'ambient', self.ambient)
  275. write_color(file_handler, 'diffuse', self.diffuse)
  276. write_color(file_handler, 'specular', self.specular)
  277. write_color(file_handler, 'emissive', self.emissive)
  278. write_float(file_handler, 'specularPower', self.specularPower)
  279. write_float(file_handler, 'alpha', self.alpha)
  280. write_bool(file_handler, 'backFaceCulling', self.backFaceCulling)
  281. write_bool(file_handler, 'checkReadyOnlyOnce', self.checkReadyOnlyOnce)
  282. write_int(file_handler, 'maxSimultaneousLights', self.maxSimultaneousLights)
  283. for texSlot in self.textures:
  284. texSlot.to_scene_file(file_handler)
  285. file_handler.write('}')
  286. #===============================================================================
  287. class StdMaterial(Material):
  288. def __init__(self, material_slot, exporter, mesh):
  289. super().__init__(mesh.data.checkReadyOnlyOnce, mesh.data.maxSimultaneousLights)
  290. nameSpace = exporter.nameSpace if mesh.data.materialNameSpace == DEFAULT_MATERIAL_NAMESPACE or len(mesh.data.materialNameSpace) == 0 else mesh.data.materialNameSpace
  291. self.name = nameSpace + '.' + material_slot.name
  292. Logger.log('processing begun of Standard material: ' + material_slot.name, 2)
  293. # a material slot is not a reference to an actual material; need to look up
  294. material = material_slot.material
  295. self.ambient = material.ambient * material.diffuse_color
  296. self.diffuse = material.diffuse_intensity * material.diffuse_color
  297. self.specular = material.specular_intensity * material.specular_color
  298. self.emissive = material.emit * material.diffuse_color
  299. self.specularPower = material.specular_hardness
  300. self.alpha = material.alpha
  301. self.backFaceCulling = material.game_settings.use_backface_culling
  302. textures = [mtex for mtex in material.texture_slots if mtex and mtex.texture]
  303. for mtex in textures:
  304. # test should be un-neccessary, since should be a BakedMaterial; just for completeness
  305. if (mtex.texture.type != 'IMAGE'):
  306. continue
  307. elif not mtex.texture.image:
  308. Logger.warn('Material has un-assigned image texture: "' + mtex.name + '" ignored', 3)
  309. continue
  310. elif len(mesh.data.uv_textures) == 0:
  311. Logger.warn('Mesh has no UV maps, material: "' + mtex.name + '" ignored', 3)
  312. continue
  313. if mtex.use_map_diffuse or mtex.use_map_color_diffuse:
  314. if mtex.texture_coords == 'REFLECTION':
  315. Logger.log('Reflection texture found "' + mtex.name + '"', 3)
  316. self.textures.append(Texture('reflectionTexture', mtex.diffuse_color_factor, mtex, mesh, exporter))
  317. else:
  318. Logger.log('Diffuse texture found "' + mtex.name + '"', 3)
  319. self.textures.append(Texture('diffuseTexture', mtex.diffuse_color_factor, mtex, mesh, exporter))
  320. if mtex.use_map_ambient:
  321. Logger.log('Ambient texture found "' + mtex.name + '"', 3)
  322. self.textures.append(Texture('ambientTexture', mtex.ambient_factor, mtex, mesh, exporter))
  323. if mtex.use_map_alpha:
  324. if self.alpha > 0:
  325. Logger.log('Opacity texture found "' + mtex.name + '"', 3)
  326. self.textures.append(Texture('opacityTexture', mtex.alpha_factor, mtex, mesh, exporter))
  327. else:
  328. Logger.warn('Opacity non-std way to indicate opacity, use material alpha to also use Opacity texture', 4)
  329. self.alpha = 1
  330. if mtex.use_map_emit:
  331. Logger.log('Emissive texture found "' + mtex.name + '"', 3)
  332. self.textures.append(Texture('emissiveTexture', mtex.emit_factor, mtex, mesh, exporter))
  333. if mtex.use_map_normal:
  334. Logger.log('Bump texture found "' + mtex.name + '"', 3)
  335. self.textures.append(Texture('bumpTexture', 1.0 / mtex.normal_factor, mtex, mesh, exporter))
  336. if mtex.use_map_color_spec:
  337. Logger.log('Specular texture found "' + mtex.name + '"', 3)
  338. self.textures.append(Texture('specularTexture', mtex.specular_color_factor, mtex, mesh, exporter))
  339. #===============================================================================
  340. class BakedMaterial(Material):
  341. def __init__(self, exporter, mesh, recipe):
  342. super().__init__(mesh.data.checkReadyOnlyOnce, mesh.data.maxSimultaneousLights)
  343. nameSpace = exporter.nameSpace if mesh.data.materialNameSpace == DEFAULT_MATERIAL_NAMESPACE or len(mesh.data.materialNameSpace) == 0 else mesh.data.materialNameSpace
  344. self.name = nameSpace + '.' + mesh.name
  345. Logger.log('processing begun of baked material: ' + mesh.name, 2)
  346. # changes to cycles & smart_project occurred in 2.77; need to know what we are running
  347. bVersion = blenderMajorMinorVersion()
  348. # any baking already took in the values. Do not want to apply them again, but want shadows to show.
  349. # These are the default values from StandardMaterials
  350. self.ambient = Color((0, 0, 0))
  351. self.diffuse = Color((0.8, 0.8, 0.8)) # needed for shadows, but not change anything else
  352. self.specular = Color((1, 1, 1))
  353. self.emissive = Color((0, 0, 0))
  354. self.specularPower = 64
  355. self.alpha = 1.0
  356. self.backFaceCulling = recipe.backFaceCulling
  357. # texture is baked from selected mesh(es), need to insure this mesh is only one selected
  358. bpy.ops.object.select_all(action='DESELECT')
  359. mesh.select = True
  360. # store setting to restore
  361. engine = exporter.scene.render.engine
  362. # mode_set's only work when there is an active object
  363. exporter.scene.objects.active = mesh
  364. # UV unwrap operates on mesh in only edit mode, procedurals can also give error of 'no images to be found' when not done
  365. # select all verticies of mesh, since smart_project works only with selected verticies
  366. bpy.ops.object.mode_set(mode='EDIT')
  367. bpy.ops.mesh.select_all(action='SELECT')
  368. # you need UV on a mesh in order to bake image. This is not reqd for procedural textures, so may not exist
  369. # need to look if it might already be created, if so use the first one
  370. uv = mesh.data.uv_textures[0] if len(mesh.data.uv_textures) > 0 else None
  371. if uv == None or recipe.forceBaking:
  372. mesh.data.uv_textures.new('BakingUV')
  373. uv = mesh.data.uv_textures['BakingUV']
  374. uv.active = True
  375. uv.active_render = not recipe.forceBaking # want the other uv's for the source when combining
  376. if bVersion <= 2.76:
  377. bpy.ops.uv.smart_project(angle_limit = 66.0, island_margin = 0.0, user_area_weight = 1.0, use_aspect = True)
  378. else:
  379. bpy.ops.uv.smart_project(angle_limit = 66.0, island_margin = 0.0, user_area_weight = 1.0, use_aspect = True, stretch_to_bounds = True)
  380. # syntax for using unwrap enstead of smart project
  381. # bpy.ops.uv.unwrap(margin = 1.0) # defaulting on all
  382. uvName = 'BakingUV' # issues with cycles when not done this way
  383. else:
  384. uvName = uv.name
  385. format = 'PNG' if recipe.usePNG else 'JPEG'
  386. # create a temporary image & link it to the UV/Image Editor so bake_image works
  387. image = bpy.data.images.new(name = mesh.name + '_BJS_BAKE', width = recipe.bakeSize, height = recipe.bakeSize, alpha = recipe.usePNG, float_buffer = False)
  388. image.file_format = format
  389. image.mapping = 'UV' # default value
  390. image_settings = exporter.scene.render.image_settings
  391. image_settings.file_format = format
  392. image_settings.color_mode = 'RGBA' if recipe.usePNG else 'RGB'
  393. image_settings.quality = recipe.bakeQuality # for lossy compression formats
  394. image_settings.compression = recipe.bakeQuality # Amount of time to determine best compression: 0 = no compression with fast file output, 100 = maximum lossless compression with slow file output
  395. # now go thru all the textures that need to be baked
  396. if recipe.diffuseBaking:
  397. cycles_type = 'DIFFUSE_COLOR' if bVersion <= 2.76 else 'DIFFUSE'
  398. self.bake('diffuseTexture', cycles_type, 'TEXTURE', image, mesh, uvName, exporter, recipe)
  399. if recipe.ambientBaking:
  400. self.bake('ambientTexture', 'AO', 'AO', image, mesh, uvName, exporter, recipe)
  401. if recipe.opacityBaking: # no eqivalent found for cycles
  402. self.bake('opacityTexture', None, 'ALPHA', image, mesh, uvName, exporter, recipe)
  403. if recipe.reflectionBaking: # no eqivalent found for cycles
  404. self.bake('reflectionTexture', None, 'MIRROR_COLOR', image, mesh, uvName, exporter, recipe)
  405. if recipe.emissiveBaking:
  406. self.bake('emissiveTexture', 'EMIT', 'EMIT', image, mesh, uvName, exporter, recipe)
  407. if recipe.bumpBaking:
  408. self.bake('bumpTexture', 'NORMAL', 'NORMALS', image, mesh, uvName, exporter, recipe)
  409. if recipe.specularBaking:
  410. cycles_type = 'SPECULAR' if bVersion <= 2.76 else 'GLOSSY'
  411. self.bake('specularTexture', cycles_type, 'SPEC_COLOR', image, mesh, uvName, exporter, recipe)
  412. # Toggle vertex selection & mode, if setting changed their value
  413. bpy.ops.mesh.select_all(action='TOGGLE') # still in edit mode toggle select back to previous
  414. bpy.ops.object.mode_set(toggle=True) # change back to Object
  415. bpy.ops.object.select_all(action='TOGGLE') # change scene selection back, not seeming to work
  416. exporter.scene.render.engine = engine
  417. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  418. def bake(self, bjs_type, cycles_type, internal_type, image, mesh, uvName, exporter, recipe):
  419. extension = '.png' if recipe.usePNG else '.jpg'
  420. if recipe.cyclesRender:
  421. if cycles_type is None:
  422. return
  423. self.bakeCycles(cycles_type, image, uvName, recipe.nodeTrees, extension)
  424. else:
  425. self.bakeInternal(internal_type, image, uvName, extension)
  426. self.textures.append(Texture(bjs_type, 1.0, image, mesh, exporter))
  427. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  428. def bakeInternal(self, bake_type, image, uvName, extension):
  429. Logger.log('Internal baking texture, type: ' + bake_type + ', mapped using: ' + uvName, 3)
  430. # need to use the legal name, since this will become the file name, chars like ':' not legal
  431. legalName = legal_js_identifier(self.name)
  432. image.filepath = legalName + '_' + bake_type + extension
  433. scene = bpy.context.scene
  434. scene.render.engine = 'BLENDER_RENDER'
  435. scene.render.bake_type = bake_type
  436. # assign the image to the UV Editor, which does not have to shown
  437. bpy.data.screens['UV Editing'].areas[1].spaces[0].image = image
  438. renderer = scene.render
  439. renderer.use_bake_selected_to_active = False
  440. renderer.use_bake_to_vertex_color = False
  441. renderer.use_bake_clear = False
  442. renderer.bake_quad_split = 'AUTO'
  443. renderer.bake_margin = 5
  444. renderer.use_file_extension = True
  445. renderer.use_bake_normalize = True
  446. renderer.use_bake_antialiasing = True
  447. bpy.ops.object.bake_image()
  448. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  449. def bakeCycles(self, bake_type, image, uvName, nodeTrees, extension):
  450. Logger.log('Cycles baking texture, type: ' + bake_type + ', mapped using: ' + uvName, 3)
  451. legalName = legal_js_identifier(self.name)
  452. image.filepath = legalName + '_' + bake_type + extension
  453. scene = bpy.context.scene
  454. scene.render.engine = 'CYCLES'
  455. # create an unlinked temporary node to bake to for each material
  456. for tree in nodeTrees:
  457. bakeNode = tree.nodes.new(type='ShaderNodeTexImage')
  458. bakeNode.image = image
  459. bakeNode.select = True
  460. tree.nodes.active = bakeNode
  461. bpy.ops.object.bake(type = bake_type, use_clear = True, margin = 5, use_selected_to_active = False)
  462. for tree in nodeTrees:
  463. tree.nodes.remove(tree.nodes.active)
  464. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  465. @staticmethod
  466. def meshBakingClean(mesh):
  467. for uvMap in mesh.data.uv_textures:
  468. if uvMap.name == 'BakingUV':
  469. mesh.data.uv_textures.remove(uvMap)
  470. break
  471. # remove an image if it was baked
  472. for image in bpy.data.images:
  473. if image.name == mesh.name + '_BJS_BAKE':
  474. image.user_clear() # cannot remove image unless 0 references
  475. bpy.data.images.remove(image)
  476. break