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