Skip to content

Commit 3842faa

Browse files
author
mehdi
committed
update to version 0.2
1 parent feb0283 commit 3842faa

3 files changed

Lines changed: 179 additions & 83 deletions

File tree

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
Easy Light Map
22
==============
33

4-
#### An addon for blender 2.73 to bake light map easily! ####
4+
#### An addon for blender 2.7 to bake light map easily! ####
55
![panel](https://github.com/mese79/easy_lightmap/raw/master/shot1.png)
66

7-
For baking your object light map to use in another engine like [three.js](http://threejs.org), first you have to add two uv layers and unwrap them. Then you must decide that do you want to bake diffuse color or object texture. if no then you have to un-check texture box and reset diffuse color into simple white color... . After baking was done you may want to revert your object material back to before baking state.
7+
For baking your object light map to use in another engine like [three.js](http://threejs.org), first you have to add two uv layers and unwrap them. Then you must decide that do you want to bake diffuse color or object texture. if no then you have to un-check texture box and reset diffuse color into pure white color... . After baking was done you may want to revert your object material back to before baking state.
88

9-
Well with this addon you can do all these steps just by one click!
9+
Well with this addon you can do all these steps just by one click!
10+
1011

12+
###### Changes in v0.2 ######
13+
- There is no need to Imagemagick anymore.
14+
- Bake process doesn't lock up UI and you can see baking progress.
15+
16+
17+
###### Note: ######
18+
Since i couldn't find any way to get when baking process has been finished normally and when it has been cancelled by user, you have to save the baked image manually.
1119

12-
###### Requirement: ######
13-
I'm using ImageMagick *mogrify* command to remove diffuse color from baked image.
14-

easy_lightmap.py

Lines changed: 168 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
import bpy
33

44
from bpy.props import StringProperty, BoolProperty, IntProperty
5-
from subprocess import Popen, PIPE
5+
from bpy.app.handlers import persistent
66

77

88
bl_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": "",
@@ -18,54 +18,60 @@
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("\ncontext.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

162202
class 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

194285
def 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()

shot1.png

-1.93 KB
Loading

0 commit comments

Comments
 (0)