22import bpy
33
44from bpy .props import StringProperty , BoolProperty , IntProperty
5- from subprocess import Popen , PIPE
5+ from bpy . app . handlers import persistent
66
77
88bl_info = {
99 "name" : "Easy Light Map" ,
1010 "author" : "Mehdi Seifi" ,
11- "version" : (0 , 1 ),
12- "blender" : (2 , 73 ),
11+ "version" : (0 , 2 ),
12+ "blender" : (2 , 7 ),
1313 "location" : "Render Tab > Easy Light Map" ,
1414 "description" : "Bakes light map for selected object easily!" ,
1515 "warning" : "" ,
1818}
1919
2020
21- class EasyLightMapProperties ( bpy . types . PropertyGroup ):
21+ class General ( object ):
2222
23- bake_path = StringProperty (name = "Bake folder:" , default = "" , subtype = "DIR_PATH" , description = "Path for saving baked maps." )
23+ is_baking_started = False
24+ original_color = None
25+ textures_use = []
26+
27+ def __init__ (self ):
28+ pass
29+
30+
31+ class EasyLightMapProperties (bpy .types .PropertyGroup ):
32+
33+ bake_path = StringProperty (
34+ name = "Bake folder:" , default = "" , subtype = "DIR_PATH" , description = "Path for saving baked maps." )
2435 image_w = IntProperty (name = "Width" , default = 1024 , min = 1 , description = "Image width" )
2536 image_h = IntProperty (name = "Height" , default = 1024 , min = 1 , description = "Image height" )
26- check_uv = BoolProperty (name = "Check/Create UV Layers" , default = True , description = "Create two uv layers if there is not any." )
2737 bake_diffuse = BoolProperty (name = "Bake diffuse color" , default = False , description = "Bake material diffuse color into map." )
2838 bake_textures = BoolProperty (name = "Bake textures" , default = False , description = "Bake material textures into map." )
2939
3040
31- class EasyLightMap (bpy .types .Operator ):
41+ class EasyLightMapPrepare (bpy .types .Operator ):
3242
33- """ Main operation happens here. """
34- bl_idname = "object.easy_light_map"
35- bl_label = "Bake It!"
36- bl_description = "Bake light map into new texture and add it into object material."
43+ bl_idname = "object.easy_light_map_prepare"
44+ bl_label = "Only Prepare For Baking"
45+ bl_description = "Create two uv layers if there is not any then add an empty texture slot for baking."
3746
3847 settings = None
3948 selected_object = None
4049 material = None
41- textures_use = []
4250
4351 @classmethod
4452 def poll (cls , context ):
45- if context .scene .render .engine == "BLENDER_RENDER" :
46- return True
53+ return True
4754
4855 def execute (self , context ):
49- print ("\n context.object:" , context .object )
5056 self .selected_object = context .active_object
5157 if self .selected_object is None or self .selected_object .type != "MESH" :
5258 self .report ({"WARNING" }, "No mesh object was selected." )
53-
59+
5460 self .material = self .selected_object .active_material
5561
5662 self .settings = context .scene .easyLightMap
5763
58- self . get_used_textures ()
59- self . check_uv_layers ()
64+ # Check/Add UVs.
65+ check_uv_layers (self . selected_object )
6066
6167 # Add new texture slot and new image for baking.
6268 name = "Baked_" + self .material .name
63-
69+
6470 img_path = bpy .path .abspath (self .settings .bake_path )
6571 if not img_path :
6672 img_path = bpy .path .abspath ("//" )
6773 img_path = os .path .join (img_path , name + ".png" )
68-
74+
6975 img = bpy .data .images .get (name )
7076 if img is None :
7177 img = bpy .data .images .new (name , width = self .settings .image_w , height = self .settings .image_h , alpha = True )
@@ -79,84 +85,118 @@ def execute(self, context):
7985 baked_tex = bpy .data .textures .new (name , type = "IMAGE" )
8086 baked_tex .image = img
8187 baked_tex .use_alpha = True
82-
88+
8389 baked_slot = self .material .texture_slots .get (name )
8490 if baked_slot is None :
8591 baked_slot = self .material .texture_slots .add ()
8692 baked_slot .use = False
8793 baked_slot .texture = baked_tex
8894 baked_slot .texture_coords = "UV"
8995 baked_slot .uv_layer = self .selected_object .data .uv_textures .active .name
96+ baked_slot .blend_type = "MULTIPLY"
9097
91- # Bake It
98+ # Apply image to active UV layer.
9299 uv_layer = self .selected_object .data .uv_textures .active
93100 for uv in uv_layer .data :
94101 uv .image = baked_tex .image
95102
96- bpy . ops . object . bake_image ()
97- img . save ()
103+ # Done!
104+ return { "FINISHED" }
98105
99- # Removing diffuse color
100- if not self .settings .bake_diffuse :
101- self .remove_diffuse_color (img_path )
102106
103- # Revert back texture_slots.use check boxes.
104- for index in range (len (self .textures_use )):
105- if self .material .texture_slots [index ] is not None :
106- self .material .texture_slots [index ].use = self .textures_use .pop (0 )
107+ class EasyLightMapBake (bpy .types .Operator ):
108+
109+ bl_idname = "object.easy_light_map_bake"
110+ bl_label = "Bake It!"
111+ bl_description = "Bake light map into new texture and add it into object material."
112+
113+ settings = None
114+ selected_object = None
115+ material = None
116+ #original_color = None
117+ #textures_use = []
118+
119+ @classmethod
120+ def poll (cls , context ):
121+ return True
122+
123+ # def invoke(self, context, event):
124+ # return self.execute(context)
125+
126+ def execute (self , context ):
127+ self .selected_object = context .active_object
128+ if self .selected_object is None or self .selected_object .type != "MESH" :
129+ self .report ({"WARNING" }, "No mesh object was selected." )
130+
131+ self .settings = context .scene .easyLightMap
132+ self .material = self .selected_object .active_material
133+
134+ # Check UV layers.
135+ check_uv_layers (self .selected_object )
107136
108- # Use new baked texture
109- img .reload ()
110- baked_slot .use = True
137+ # Add a new texture slot and a new image for baking.
138+ name = "Baked_" + self .material .name
139+
140+ img_path = bpy .path .abspath (self .settings .bake_path )
141+ if not img_path :
142+ img_path = bpy .path .abspath ("//" )
143+ img_path = os .path .join (img_path , name + ".png" )
144+
145+ img = bpy .data .images .get (name )
146+ if img is None :
147+ img = bpy .data .images .new (name , width = self .settings .image_w , height = self .settings .image_h , alpha = True )
148+ img .file_format = "PNG"
149+ img .filepath = img_path
150+ elif img .size != [self .settings .image_w , self .settings .image_h ]:
151+ img .scale (self .settings .image_w , self .settings .image_h )
152+
153+ baked_tex = bpy .data .textures .get (name )
154+ if baked_tex is None :
155+ baked_tex = bpy .data .textures .new (name , type = "IMAGE" )
156+ baked_tex .image = img
157+ baked_tex .use_alpha = True
158+
159+ baked_slot = self .material .texture_slots .get (name )
160+ if baked_slot is None :
161+ baked_slot = self .material .texture_slots .add ()
162+ baked_slot .use = False
163+ baked_slot .texture = baked_tex
164+ baked_slot .texture_coords = "UV"
165+ baked_slot .uv_layer = self .selected_object .data .uv_textures .active .name
111166 baked_slot .blend_type = "MULTIPLY"
112167
168+ # Set UV layer image.
169+ uv_layer = self .selected_object .data .uv_textures .active
170+ for uv in uv_layer .data :
171+ uv .image = baked_tex .image
172+
173+ if not self .settings .bake_diffuse :
174+ # Save diffuse color
175+ General .original_color = self .material .diffuse_color .copy ()
176+ # Change it to pure white.
177+ self .material .diffuse_color = [1.0 , 1.0 , 1.0 ]
178+
179+ # Check which texture to use.
180+ General .textures_use = self .get_used_textures ()
181+
182+ # Bake it.
183+ bpy .app .handlers .scene_update_post .append (scene_update )
184+ bpy .ops .object .bake_image ("INVOKE_DEFAULT" )
185+ General .is_baking_started = True
186+
113187 # Done!
114188 return {"FINISHED" }
115189
116190 def get_used_textures (self ):
117191 """ UnCheck textures if bake_textures is false. """
192+ result = []
118193 for slot in self .material .texture_slots :
119194 if slot is not None and slot .use :
120- self . textures_use .append (slot .use )
195+ result .append (slot .use )
121196 if not self .settings .bake_textures :
122197 slot .use = False
123198
124- def check_uv_layers (self ):
125- """ Object must have two uv sets. """
126- if len (self .selected_object .data .uv_textures ) == 0 :
127- self .add_uv_map ("Diffuse" )
128- self .add_uv_map ("LightMap" )
129- elif len (self .selected_object .data .uv_textures ) == 1 :
130- self .add_uv_map ("LightMap" )
131-
132- def add_uv_map (self , name ):
133- """ Add new UV Map to object and unwrap it. """
134- uv = self .selected_object .data .uv_textures .new (name )
135- uv .active = True
136- bpy .ops .object .mode_set (mode = "EDIT" )
137- bpy .ops .mesh .select_all (action = "SELECT" )
138- bpy .ops .uv .smart_project (island_margin = 0.05 )
139- # Have to pass object, because context.object is None in render panel.
140- # (in unwrap function is_editmode = (context.object.mode == 'EDIT') will need it.)
141- #bpy.ops.uv.lightmap_pack({
142- # "object": self.selected_object,
143- # "PREF_CONTEXT": "ALL_OBJECTS",
144- # "PREF_PACK_IN_ONE": True,
145- # "PREF_IMG_PX_SIZE": self.settings.image_w
146- # })
147- bpy .ops .object .mode_set (mode = "OBJECT" )
148- uv .active = True
149-
150- return uv
151-
152- def remove_diffuse_color (self , file ):
153- """ Remove diffuse color by ImageMagick. """
154- params = [
155- "mogrify" , "-normalize" , "-grayscale" , "Rec709Luma" ,
156- "-type" , "TrueColorMatte" , "-define" , "png:color-type=6" , file
157- ]
158- process = Popen (params , stdin = PIPE , stdout = PIPE )
159- out , err = process .communicate ()
199+ return result
160200
161201
162202class EasyLightMapPanel (bpy .types .Panel ):
@@ -169,8 +209,8 @@ class EasyLightMapPanel(bpy.types.Panel):
169209
170210 @classmethod
171211 def poll (cls , context ):
172- if context .scene .render .engine == "BLENDER_RENDER" :
173- return True
212+ # if context.scene.render.engine == "BLENDER_RENDER":
213+ return True
174214
175215 def draw (self , context ):
176216 layout = self .layout
@@ -182,13 +222,64 @@ def draw(self, context):
182222 row .prop (props , "image_h" )
183223 layout .separator ()
184224 row = layout .row (True )
185- row .prop (props , "check_uv" )
186- row = layout .row (True )
187225 row .prop (props , "bake_diffuse" )
188226 row = layout .row (True )
189227 row .prop (props , "bake_textures" )
190228 layout .separator ()
191- layout .operator (EasyLightMap .bl_idname , text = "Bake it!" )
229+ layout .operator (EasyLightMapPrepare .bl_idname , text = "Only Prepare For Baking" )
230+ layout .operator (EasyLightMapBake .bl_idname , text = "Bake it!" )
231+
232+
233+
234+ def check_uv_layers (selected_object ):
235+ """ Object must have two uv sets. """
236+ if len (selected_object .data .uv_textures ) == 0 :
237+ add_uv_map ("Diffuse" , selected_object )
238+ add_uv_map ("LightMap" , selected_object )
239+ elif len (selected_object .data .uv_textures ) == 1 :
240+ add_uv_map ("LightMap" , selected_object )
241+
242+
243+ def add_uv_map (name , selected_object ):
244+ """ Add new UV Map to object and unwrap it. """
245+ uv = selected_object .data .uv_textures .new (name )
246+ uv .active = True
247+ bpy .ops .object .mode_set (mode = "EDIT" )
248+ bpy .ops .mesh .select_all (action = "SELECT" )
249+ bpy .ops .uv .smart_project (island_margin = 0.05 )
250+ # Have to pass object, because context.object is None in render panel.
251+ # (in unwrap function is_editmode = (context.object.mode == 'EDIT') will need it.)
252+ # bpy.ops.uv.lightmap_pack({
253+ # "object": selected_object,
254+ # "PREF_CONTEXT": "ALL_OBJECTS",
255+ # "PREF_PACK_IN_ONE": True,
256+ # "PREF_IMG_PX_SIZE": settings.image_w
257+ # })
258+ bpy .ops .object .mode_set (mode = "OBJECT" )
259+ uv .active = True
260+
261+ return uv
262+
263+
264+ @persistent
265+ def scene_update (context ):
266+ # Just when baking started texture.is_updated is true, after that it'll becomes false.
267+ if bpy .context .active_object :
268+ material = bpy .context .active_object .active_material
269+ name = "Baked_" + material .name
270+
271+ if General .is_baking_started and not bpy .data .textures [name ].is_updated :
272+ # Baking is finished, so revert back the material color and texture slots use check boxes.
273+ if General .original_color is not None :
274+ material .diffuse_color = General .original_color
275+
276+ for index in range (len (General .textures_use )):
277+ if material .texture_slots [index ] is not None :
278+ material .texture_slots [index ].use = General .textures_use .pop (0 )
279+
280+ # Remove handler
281+ bpy .app .handlers .scene_update_post .remove (scene_update )
282+
192283
193284
194285def register ():
@@ -201,5 +292,5 @@ def unregister():
201292 bpy .utils .unregister_module (__name__ )
202293
203294
204- if __name__ == ' __main__' :
295+ if __name__ == " __main__" :
205296 register ()
0 commit comments