Skip to content

Commit

Permalink
version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonNordon4 committed Sep 12, 2024
1 parent 7a7a7bf commit 45236f1
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 77 deletions.
78 changes: 69 additions & 9 deletions lightmapper/lightmapper_operators.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import bpy
import bpy.utils
import bmesh

import bpy.ops

import os.path
import re



Expand Down Expand Up @@ -154,6 +153,7 @@ def __init__(self):
self.bake_iterator = None

self.bake_object = None
self.bake_name = None

self.scene_state = SceneState()

Expand Down Expand Up @@ -228,6 +228,23 @@ def _select_correct_uv(self, mesh_objects):
# Ensure the lightmap is selected, as that's the UV we're baking to.
obj.data.uv_layers["Lightmap"].active = True

def _create_bake_image(self, context):
# get the resolution from the props
width = self.lightmapper_props.lightmap_width
height = self.lightmapper_props.lightmap_height
new_image = bpy.data.images.new(name="BakeImage", width=width, height=height)

# set the float depth
new_image.use_generated_float = True
# EXR for high dynamic range.
new_image.file_format = 'OPEN_EXR'

# color space
# new_image.color_space = 'sRGB' # Don't need to set for now.
return new_image



def _create_bakeable_object(self, mesh_objects):
""" Create a combined mesh from the selected objects, including UVs and materials. """
# Create a new mesh
Expand Down Expand Up @@ -363,7 +380,7 @@ def _prepare_object_for_bake(self, mesh_objects, bake_object):
def _setup_bake_settings(self):
""" Set up bake settings for diffuse lightmap baking. """
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.samples = 128
bpy.context.scene.cycles.samples = self.lightmapper_props.num_samples
bpy.context.scene.cycles.use_denoising = False

bpy.context.scene.cycles.bake_type = 'DIFFUSE'
Expand Down Expand Up @@ -393,8 +410,11 @@ def _setup_compositor_for_denoising(self):
output_node = tree.nodes.new(type='CompositorNodeOutputFile')
output_node.format.file_format = 'HDR'
output_node.format.color_depth = '32'
output_node.base_path = "//denoised_lightmap"
output_node.file_slots[0].path = "denoised_lightmap_####.hdr"
output_node.format.exr_codec = 'ZIP'


output_node.base_path = bpy.context.scene.lightmapper_properties.export_path
output_node.file_slots[0].path = self.bake_name

# Link nodes
tree.links.new(input_node.outputs[0], denoise_node.inputs[0])
Expand All @@ -404,14 +424,47 @@ def _render_denoised_image(self):
# Run the compositor
bpy.ops.render.render(write_still=True)

def _clean_up_exported_name(self):
export_path = bpy.context.scene.lightmapper_properties.export_path
exported_file = None
cleaned_file = os.path.join(export_path, f"{self.bake_name}.hdr")

print(f"Export path: {export_path}")
print(f"Expected cleaned file: {cleaned_file}")

for file in os.listdir(export_path):
print(f"Checking file: {file}")
if file.startswith(self.bake_name) and file.endswith(".hdr"):
if re.match(rf"{re.escape(self.bake_name)}\d{{4}}\.hdr$", file):
exported_file = os.path.join(export_path, file)
print(f"Found exported file: {exported_file}")
break

if exported_file is None:
print("Exported file not found.")
return

if os.path.exists(cleaned_file):
print(f"Removing existing cleaned file: {cleaned_file}")
os.remove(cleaned_file)

print(f"Renaming {exported_file} to {cleaned_file}")
os.rename(exported_file, cleaned_file)

def save_state(self, context):
self.scene_state.save(context)

def restore_state(self, context):
self.scene_state.restore(context)

# remove the bake object
if self.bake_object is not None:
if self.bake_object.data is not None:
bpy.data.meshes.remove(self.bake_object.data, do_unlink=True)

# remove the bake_image
if self.bake_image is not None:
bpy.data.images.remove(self.bake_image, do_unlink=True)

def execute(self, context):
# Run the update loop as an iterator.
Expand All @@ -420,6 +473,13 @@ def execute(self, context):
# We'll update every 0.5 seconds.
self._timer = context.window_manager.event_timer_add(0.5, window=context.window) # Check every 0.5 seconds
context.window_manager.modal_handler_add(self)

bake_name_target = bpy.context.scene.lightmapper_properties.bake_name
if bake_name_target is 'ACTIVE_OBJECT':
self.bake_name = context.active_object.name
else:
self.bake_name = context.view_layer.active_layer_collection.collection.name


return {'RUNNING_MODAL'}

Expand All @@ -445,7 +505,7 @@ def bake(self, context):


# 2. Create an image to bake to, and a new bake object to be baked to.
self.bake_image = bpy.data.images.new("BakeImage", width=1024, height=1024)
self.bake_image = self._create_bake_image(context)
self.bake_object = self._create_bakeable_object(mesh_objects)
yield 1
self._apply_bake_image(self.bake_object)
Expand All @@ -460,14 +520,14 @@ def bake(self, context):
while not self.bake_image.is_dirty:
yield 1


self._setup_compositor_for_denoising()
yield 1
bpy.ops.render.render(write_still=False)

self._render_denoised_image()
while bpy.ops.render.render() == {'RUNNING_MODAL'}:
yield 1

self._render_denoised_image()
self._clean_up_exported_name()

yield 0

Expand Down
112 changes: 44 additions & 68 deletions lightmapper/lightmapper_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,53 @@
import bpy.utils

class LIGHTMAPPER_PT_main_panel(bpy.types.Panel):
bl_idname = "OBJECT_PT_lightmapper_panel"
bl_label = "Lightmapper Panel"
bl_description = "This is a lightmapper panel, for starting a new addon."
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Lightmapper"
bl_order = 0
bl_idname = "OBJECT_PT_lightmapper_panel"
bl_label = "Lightmapper Panel"
bl_description = "This is a lightmapper panel, for starting a new addon."
bl_space_type = "VIEW_3D"
bl_region_type = "UI"
bl_category = "Lightmapper"
bl_order = 0

def draw_header(self, context):
layout = self.layout
layout.label(text="", icon="OUTLINER_DATA_LIGHTPROBE")
def draw_header(self, context):
layout = self.layout
layout.label(text="", icon="OUTLINER_DATA_LIGHTPROBE")

def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.lightmapper_properties

# Create UV Lightmap
box = layout.box()
box.label(text="Lightmap UV", icon='UV')
box.operator("lightmapper.create_lightmap_uv", icon='ADD')

# Lightmap Resolution
box = layout.box()
box.label(text="Lightmap Resolution", icon='TEXTURE')
col = box.column(align=True)
row = col.row(align=True)
row.prop(props, "lightmap_width", text="Width")
row.prop(props, "lightmap_height", text="Height")

# Resolution Presets
row = col.row(align=True)
row.operator("lightmapper.set_resolution", text="1K").resolution = 1024
row.operator("lightmapper.set_resolution", text="2K").resolution = 2048
row.operator("lightmapper.set_resolution", text="4K").resolution = 4096
row.operator("lightmapper.set_resolution", text="8K").resolution = 8192

# Export Settings
box = layout.box()
box.label(text="Export Settings", icon='EXPORT')
box.prop(props, "export_path")
box.label(text="Bake Name", icon='TEXTURE')
export_row = box.row(align=True)
export_row.prop(props, "bake_name", expand=True)

# Bake Button
layout.separator()
row = layout.row()
row.scale_y = 2
row.operator("lightmapper.bake_lightmap", text="Bake Lightmap", icon='RENDER_STILL')
def draw(self, context):
layout = self.layout
scene = context.scene
props = scene.lightmapper_properties

# Create UV Lightmap
box = layout.box()
box.label(text="Lightmap UV", icon='UV')
box.operator("lightmapper.create_lightmap_uv", icon='ADD')

# Lightmap Resolution
box = layout.box()
box.label(text="Lightmap Settings", icon='TEXTURE')
col = box.column(align=True)
row = col.row(align=True)
row.prop(props, "lightmap_width", text="Width")
row.prop(props, "lightmap_height", text="Height")
col.prop(props, "num_samples", text="Samples")

# Export Settings
box = layout.box()
box.label(text="Export Settings", icon='EXPORT')
box.prop(props, "export_path")
box.label(text="Bake Name", icon='TEXTURE')
export_row = box.row(align=True)
export_row.prop(props, "bake_name", expand=True)

# Bake Button
layout.separator()
row = layout.row()
row.scale_y = 2
row.operator("lightmapper.bake_lightmap", text="Bake Lightmap", icon='RENDER_STILL')

class LIGHTMAPPER_OT_set_resolution(bpy.types.Operator):
bl_idname = "lightmapper.set_resolution"
bl_label = "Set Resolution"
bl_description = "Set the resolution for the lightmap"

resolution: bpy.props.IntProperty()

def execute(self, context):
scene = context.scene
scene.lightmapper_properties.lightmap_width = self.resolution
scene.lightmapper_properties.lightmap_height = self.resolution
self.report({'INFO'}, f"Resolution set to {self.resolution}x{self.resolution}")
return {'FINISHED'}

def register():
bpy.utils.register_class(LIGHTMAPPER_OT_set_resolution)
bpy.utils.register_class(LIGHTMAPPER_PT_main_panel)
bpy.utils.register_class(LIGHTMAPPER_PT_main_panel)

def unregister():
bpy.utils.unregister_class(LIGHTMAPPER_PT_main_panel)
bpy.utils.unregister_class(LIGHTMAPPER_OT_set_resolution)


bpy.utils.unregister_class(LIGHTMAPPER_PT_main_panel)
8 changes: 8 additions & 0 deletions lightmapper/lightmapper_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class LIGHTMAPPER_PT_properties(bpy.types.PropertyGroup):
],
default='ACTIVE_OBJECT'
) # type: ignore

num_samples: bpy.props.IntProperty(
name="Number of Samples",
description="Number of samples for baking",
default=64,
min=8,
max=256
) # type: ignore



Expand Down
Binary file added lightmapper/tests/lightmap_test.blend
Binary file not shown.

0 comments on commit 45236f1

Please sign in to comment.