Merge lightmapper 0.3.1.2

This commit is contained in:
Alexander 2020-08-23 23:11:49 +02:00
parent fbe144decc
commit 5d914b3bf5
48 changed files with 4537 additions and 27 deletions

View file

@ -0,0 +1 @@
__all__ = ('Operators', 'Panels', 'Properties', 'Preferences', 'Utility', 'Keymap')

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View file

@ -0,0 +1,7 @@
from . import keymap
def register():
keymap.register()
def unregister():
keymap.unregister()

View file

@ -0,0 +1,21 @@
import bpy
#from .. operators import build
#from .. operators import clean
tlm_keymaps = []
def register():
pass
# winman = bpy.context.window_manager
# keyman = winman.keyconfigs.addon.keymaps.new(name='Window', space_type='EMPTY', region_type="WINDOW")
# keyman.keymap_items.new(build.TLM_BuildLightmaps.bl_idname, type='F6', value='PRESS')
# keyman.keymap_items.new(clean.TLM_CleanLightmaps.bl_idname, type='F7', value='PRESS')
# tlm_keymaps.append(keyman)
def unregister():
pass
# winman = bpy.context.window_manager
# for keyman in tlm_keymaps:
# winman.keyconfigs.addon.keymaps.remove(keyman)
# del tlm_keymaps[:]

View file

@ -0,0 +1,21 @@
import bpy
from bpy.utils import register_class, unregister_class
from . import tlm, installopencv
classes = [
tlm.TLM_BuildLightmaps,
tlm.TLM_CleanLightmaps,
tlm.TLM_ExploreLightmaps,
tlm.TLM_EnableSelection,
tlm.TLM_DisableSelection,
tlm.TLM_RemoveLightmapUV,
installopencv.TLM_Install_OpenCV
]
def register():
for cls in classes:
register_class(cls)
def unregister():
for cls in classes:
unregister_class(cls)

View file

@ -0,0 +1,67 @@
import bpy, math, os, platform, subprocess, sys, re, shutil
def ShowMessageBox(message = "", title = "Message Box", icon = 'INFO'):
def draw(self, context):
self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw, title = title, icon = icon)
class TLM_Install_OpenCV(bpy.types.Operator):
"""Install OpenCV"""
bl_idname = "tlm.install_opencv_lightmaps"
bl_label = "Install OpenCV"
bl_description = "Install OpenCV"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
cycles = bpy.data.scenes[scene.name].cycles
print("Module OpenCV")
pythonbinpath = bpy.app.binary_path_python
if platform.system() == "Windows":
pythonlibpath = os.path.join(os.path.dirname(os.path.dirname(pythonbinpath)), "lib")
else:
pythonlibpath = os.path.join(os.path.dirname(os.path.dirname(pythonbinpath)), "lib", os.path.basename(pythonbinpath)[:-1])
ensurepippath = os.path.join(pythonlibpath, "ensurepip")
cmda = [pythonbinpath, ensurepippath, "--upgrade", "--user"]
pip = subprocess.run(cmda, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if pip.returncode == 0:
print("Sucessfully installed pip!\n")
else:
try:
import pip
module_pip = True
except ImportError:
#pip
module_pip = False
if not module_pip:
print("Failed to install pip!\n")
ShowMessageBox("Failed to install pip - Please start Blender as administrator", "Restart", 'PREFERENCES')
return{'FINISHED'}
cmdb = [pythonbinpath, "-m", "pip", "install", "opencv-python"]
opencv = subprocess.run(cmdb, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if opencv.returncode == 0:
print("Sucessfully installed OpenCV!\n")
else:
print("Failed to install OpenCV!\n")
ShowMessageBox("Failed to install opencv - Please start Blender as administrator", "Restart", 'PREFERENCES')
return{'FINISHED'}
module_opencv = True
print("Sucessfully installed OpenCV!\n")
ShowMessageBox("Please restart blender to enable OpenCV filtering", "Restart", 'PREFERENCES')
return{'FINISHED'}

View file

@ -0,0 +1,156 @@
import bpy, os, time, blf, webbrowser
from .. utility import build
from .. utility.cycles import cache
class TLM_BuildLightmaps(bpy.types.Operator):
bl_idname = "tlm.build_lightmaps"
bl_label = "Build Lightmaps"
bl_description = "Build Lightmaps"
bl_options = {'REGISTER', 'UNDO'}
def modal(self, context, event):
#Add progress bar from 0.15
print("MODAL")
return {'PASS_THROUGH'}
def invoke(self, context, event):
if not bpy.app.background:
build.prepare_build(self, False)
else:
print("Running in background mode. Contextual operator not available. Use command 'thelightmapper.addon.build.prepare_build()'")
return {'RUNNING_MODAL'}
def cancel(self, context):
pass
def draw_callback_px(self, context, event):
pass
class TLM_CleanLightmaps(bpy.types.Operator):
bl_idname = "tlm.clean_lightmaps"
bl_label = "Clean Lightmaps"
bl_description = "Clean Lightmaps"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
filepath = bpy.data.filepath
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
if os.path.isdir(dirpath):
for file in os.listdir(dirpath):
os.remove(os.path.join(dirpath + "/" + file))
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_restore(obj)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_rename(obj)
for mat in bpy.data.materials:
if mat.users < 1:
bpy.data.materials.remove(mat)
for mat in bpy.data.materials:
if mat.name.startswith("."):
if "_Original" in mat.name:
bpy.data.materials.remove(mat)
for image in bpy.data.images:
if image.name.endswith("_baked"):
bpy.data.images.remove(image, do_unlink=True)
return {'FINISHED'}
class TLM_ExploreLightmaps(bpy.types.Operator):
bl_idname = "tlm.explore_lightmaps"
bl_label = "Explore Lightmaps"
bl_description = "Explore Lightmaps"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
cycles = scene.cycles
if not bpy.data.is_saved:
self.report({'INFO'}, "Please save your file first")
return {"CANCELLED"}
filepath = bpy.data.filepath
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
if os.path.isdir(dirpath):
webbrowser.open('file://' + dirpath)
else:
os.mkdir(dirpath)
webbrowser.open('file://' + dirpath)
return {'FINISHED'}
class TLM_EnableSelection(bpy.types.Operator):
"""Enable for selection"""
bl_idname = "tlm.enable_selection"
bl_label = "Enable for selection"
bl_description = "Enable for selection"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
scene = context.scene
for obj in bpy.context.selected_objects:
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
if scene.TLM_SceneProperties.tlm_override_object_settings:
obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_margin = scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
return{'FINISHED'}
class TLM_DisableSelection(bpy.types.Operator):
"""Disable for selection"""
bl_idname = "tlm.disable_selection"
bl_label = "Disable for selection"
bl_description = "Disable for selection"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in bpy.context.selected_objects:
obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False
return{'FINISHED'}
class TLM_RemoveLightmapUV(bpy.types.Operator):
"""Remove Lightmap UV for selection"""
bl_idname = "tlm.remove_uv_selection"
bl_label = "Remove Lightmap UV"
bl_description = "Remove Lightmap UV for selection"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in bpy.context.selected_objects:
if obj.type == "MESH":
uv_layers = obj.data.uv_layers
for uvlayer in uv_layers:
if uvlayer.name == "UVMap_Lightmap":
uv_layers.remove(uvlayer)
return{'FINISHED'}

View file

@ -0,0 +1,24 @@
import bpy, os
from bpy.utils import register_class, unregister_class
from . import scene, object, light, world
classes = [
scene.TLM_PT_Panel,
scene.TLM_PT_Settings,
scene.TLM_PT_Denoise,
scene.TLM_PT_Filtering,
scene.TLM_PT_Encoding,
scene.TLM_PT_Selection,
scene.TLM_PT_Additional,
object.TLM_PT_ObjectMenu,
light.TLM_PT_LightMenu,
world.TLM_PT_WorldMenu
]
def register():
for cls in classes:
register_class(cls)
def unregister():
for cls in classes:
unregister_class(cls)

View file

@ -0,0 +1,17 @@
import bpy
from bpy.props import *
from bpy.types import Menu, Panel
class TLM_PT_LightMenu(bpy.types.Panel):
bl_label = "The Lightmapper"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "light"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
obj = bpy.context.object
layout.use_property_split = True
layout.use_property_decorate = False

View file

@ -0,0 +1,59 @@
import bpy
from bpy.props import *
from bpy.types import Menu, Panel
class TLM_PT_ObjectMenu(bpy.types.Panel):
bl_label = "The Lightmapper"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "object"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
obj = bpy.context.object
layout.use_property_split = True
layout.use_property_decorate = False
if obj.type == "MESH":
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_use")
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
pass
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_unwrap_margin")
row = layout.row()
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filter_override")
row = layout.row()
if obj.TLM_ObjectProperties.tlm_mesh_filter_override:
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_mode")
row = layout.row(align=True)
if obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Gaussian":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_gaussian_strength")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Box":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_box_strength")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
elif obj.TLM_ObjectProperties.tlm_mesh_filtering_mode == "Bilateral":
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_diameter")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_color_deviation")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_bilateral_coordinate_deviation")
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")
else:
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_median_kernel", expand=True)
row = layout.row(align=True)
row.prop(obj.TLM_ObjectProperties, "tlm_mesh_filtering_iterations")

View file

@ -0,0 +1,322 @@
import bpy, importlib
from bpy.props import *
from bpy.types import Menu, Panel
from .. utility import icon
from .. properties.denoiser import oidn, optix
class TLM_PT_Panel(bpy.types.Panel):
bl_label = "The Lightmapper"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
class TLM_PT_Settings(bpy.types.Panel):
bl_label = "Settings"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
row = layout.row(align=True)
#We list LuxCoreRender as available, by default we assume Cycles exists
row.prop(sceneProperties, "tlm_lightmap_engine")
if sceneProperties.tlm_lightmap_engine == "Cycles":
#CYCLES SETTINGS HERE
engineProperties = scene.TLM_EngineProperties
row = layout.row(align=True)
row.label(text="General Settings")
row = layout.row(align=True)
row.operator("tlm.build_lightmaps")
row = layout.row(align=True)
row.operator("tlm.clean_lightmaps")
row = layout.row(align=True)
row.operator("tlm.explore_lightmaps")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_apply_on_unwrap")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_headless")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_alert_on_finish")
row = layout.row(align=True)
row.label(text="Cycles Settings")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_quality")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_resolution_scale")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_bake_mode")
if scene.TLM_EngineProperties.tlm_bake_mode == "Background":
row = layout.row(align=True)
row.label(text="Warning! Background mode is currently unstable", icon_value=2)
row = layout.row(align=True)
row.prop(engineProperties, "tlm_caching_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_directional_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_lightmap_savedir")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_dilation_margin")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_exposure_multiplier")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_setting_supersample")
elif sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
#LUXCORE SETTINGS HERE
luxcore_available = False
#Look for Luxcorerender in the renderengine classes
for engine in bpy.types.RenderEngine.__subclasses__():
if engine.bl_idname == "LUXCORE":
luxcore_available = True
break
row = layout.row(align=True)
if not luxcore_available:
row.label(text="Please install BlendLuxCore.")
else:
row.label(text="LuxCoreRender not yet available.")
elif sceneProperties.tlm_lightmap_engine == "OctaneRender":
#LUXCORE SETTINGS HERE
octane_available = False
row = layout.row(align=True)
row.label(text="Octane Render not yet available.")
class TLM_PT_Denoise(bpy.types.Panel):
bl_label = "Denoise"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw_header(self, context):
scene = context.scene
sceneProperties = scene.TLM_SceneProperties
self.layout.prop(sceneProperties, "tlm_denoise_use", text="")
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
layout.active = sceneProperties.tlm_denoise_use
row = layout.row(align=True)
#row.prop(sceneProperties, "tlm_denoiser", expand=True)
#row = layout.row(align=True)
row.prop(sceneProperties, "tlm_denoise_engine", expand=True)
row = layout.row(align=True)
if sceneProperties.tlm_denoise_engine == "Integrated":
row.label(text="No options for Integrated.")
elif sceneProperties.tlm_denoise_engine == "OIDN":
denoiseProperties = scene.TLM_OIDNEngineProperties
row.prop(denoiseProperties, "tlm_oidn_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_threads")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_affinity")
# row = layout.row(align=True)
# row.prop(denoiseProperties, "tlm_denoise_ao")
elif sceneProperties.tlm_denoise_engine == "Optix":
denoiseProperties = scene.TLM_OptixEngineProperties
row.prop(denoiseProperties, "tlm_optix_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_denoise_ao")
class TLM_PT_Filtering(bpy.types.Panel):
bl_label = "Filtering"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw_header(self, context):
scene = context.scene
sceneProperties = scene.TLM_SceneProperties
self.layout.prop(sceneProperties, "tlm_filtering_use", text="")
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
layout.active = sceneProperties.tlm_filtering_use
#row = layout.row(align=True)
#row.label(text="TODO MAKE CHECK")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_filtering_engine", expand=True)
row = layout.row(align=True)
if sceneProperties.tlm_filtering_engine == "OpenCV":
cv2 = importlib.util.find_spec("cv2")
if cv2 is None:
row = layout.row(align=True)
row.label(text="OpenCV is not installed. Install it through preferences.")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_mode")
row = layout.row(align=True)
if scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_gaussian_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_box_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_diameter")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_color_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_coordinate_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row.prop(scene.TLM_SceneProperties, "tlm_filtering_median_kernel", expand=True)
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_numpy_filtering_mode")
class TLM_PT_Encoding(bpy.types.Panel):
bl_label = "Encoding"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw_header(self, context):
scene = context.scene
sceneProperties = scene.TLM_SceneProperties
self.layout.prop(sceneProperties, "tlm_encoding_use", text="")
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
layout.active = sceneProperties.tlm_encoding_use
sceneProperties = scene.TLM_SceneProperties
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_mode", expand=True)
if sceneProperties.tlm_encoding_mode == "RGBM" or sceneProperties.tlm_encoding_mode == "RGBD":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_range")
if sceneProperties.tlm_encoding_mode == "LogLuv":
pass
if sceneProperties.tlm_encoding_mode == "HDR":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_format")
class TLM_PT_Selection(bpy.types.Panel):
bl_label = "Selection"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw(self, context):
layout = self.layout
scene = context.scene
layout.use_property_split = True
layout.use_property_decorate = False
sceneProperties = scene.TLM_SceneProperties
row = layout.row(align=True)
row.operator("tlm.enable_selection")
row = layout.row(align=True)
row.operator("tlm.disable_selection")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_override_object_settings")
if sceneProperties.tlm_override_object_settings:
row = layout.row(align=True)
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if sceneProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
if scene.TLM_AtlasList_index >= 0 and len(scene.TLM_AtlasList) > 0:
row = layout.row()
item = scene.TLM_AtlasList[scene.TLM_AtlasList_index]
row.prop_search(sceneProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
else:
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
else:
row.prop(sceneProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_unwrap_margin")
row = layout.row(align=True)
row.operator("tlm.remove_uv_selection")
row = layout.row(align=True)
class TLM_PT_Additional(bpy.types.Panel):
bl_label = "Additional"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "render"
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = "TLM_PT_Panel"
def draw(self, context):
layout = self.layout
scene = context.scene
sceneProperties = scene.TLM_SceneProperties

View file

@ -0,0 +1,17 @@
import bpy
from bpy.props import *
from bpy.types import Menu, Panel
class TLM_PT_WorldMenu(bpy.types.Panel):
bl_label = "The Lightmapper"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "world"
bl_options = {'DEFAULT_CLOSED'}
def draw(self, context):
layout = self.layout
scene = context.scene
obj = bpy.context.object
layout.use_property_split = True
layout.use_property_decorate = False

View file

@ -0,0 +1,16 @@
import bpy
from bpy.utils import register_class, unregister_class
from . import addon_preferences
#from . import build, clean, explore, encode, installopencv
classes = [
addon_preferences.TLM_AddonPreferences
]
def register():
for cls in classes:
register_class(cls)
def unregister():
for cls in classes:
unregister_class(cls)

View file

@ -0,0 +1,72 @@
import bpy
from os.path import basename, dirname
from bpy.types import AddonPreferences
from .. operators import installopencv
import importlib
class TLM_AddonPreferences(AddonPreferences):
bl_idname = "thelightmapper"
addon_keys = bpy.context.preferences.addons.keys()
def draw(self, context):
layout = self.layout
box = layout.box()
row = box.row()
row.label(text="OpenCV")
cv2 = importlib.util.find_spec("cv2")
if cv2 is not None:
row.label(text="OpenCV installed")
else:
row.label(text="OpenCV not found - Install as administrator!", icon_value=2)
row = box.row()
row.operator("tlm.install_opencv_lightmaps", icon="PREFERENCES")
box = layout.box()
row = box.row()
row.label(text="Blender Xatlas")
if "blender_xatlas" in self.addon_keys:
row.label(text="Blender Xatlas installed and available")
else:
row.label(text="Blender Xatlas not installed", icon_value=2)
row = box.row()
row.label(text="Github: https://github.com/mattedicksoncom/blender-xatlas")
box = layout.box()
row = box.row()
row.label(text="RizomUV Bridge")
row.label(text="Coming soon")
box = layout.box()
row = box.row()
row.label(text="UVPackmaster")
row.label(text="Coming soon")
box = layout.box()
row = box.row()
row.label(text="Texel Density Checker")
row.label(text="Coming soon")
box = layout.box()
row = box.row()
row.label(text="LuxCoreRender")
row.label(text="Coming soon")
box = layout.box()
row = box.row()
row.label(text="OctaneRender")
row.label(text="Coming soon")
# row = layout.row()
# row.label(text="PIP")
# row = layout.row()
# row.label(text="OIDN / Optix")
# row = layout.row()
# row.label(text="UVPackmaster")
# row = layout.row()
# row.label(text="Texel Density")

View file

@ -0,0 +1,33 @@
import bpy
from bpy.utils import register_class, unregister_class
from . import scene, object
from . renderer import cycles
from . denoiser import oidn, optix
classes = [
scene.TLM_SceneProperties,
object.TLM_ObjectProperties,
cycles.TLM_CyclesSceneProperties,
oidn.TLM_OIDNEngineProperties,
optix.TLM_OptixEngineProperties
]
def register():
for cls in classes:
register_class(cls)
bpy.types.Scene.TLM_SceneProperties = bpy.props.PointerProperty(type=scene.TLM_SceneProperties)
bpy.types.Object.TLM_ObjectProperties = bpy.props.PointerProperty(type=object.TLM_ObjectProperties)
bpy.types.Scene.TLM_EngineProperties = bpy.props.PointerProperty(type=cycles.TLM_CyclesSceneProperties)
bpy.types.Scene.TLM_OIDNEngineProperties = bpy.props.PointerProperty(type=oidn.TLM_OIDNEngineProperties)
bpy.types.Scene.TLM_OptixEngineProperties = bpy.props.PointerProperty(type=optix.TLM_OptixEngineProperties)
def unregister():
for cls in classes:
unregister_class(cls)
del bpy.types.Scene.TLM_SceneProperties
del bpy.types.Object.TLM_ObjectProperties
del bpy.types.Scene.TLM_EngineProperties
del bpy.types.Scene.TLM_OIDNEngineProperties
del bpy.types.Scene.TLM_OptixEngineProperties

View file

@ -0,0 +1,4 @@
import bpy
from bpy.props import *
class TLM_IntegratedDenoiseEngineProperties(bpy.types.PropertyGroup):

View file

@ -0,0 +1,39 @@
import bpy
from bpy.props import *
class TLM_OIDNEngineProperties(bpy.types.PropertyGroup):
tlm_oidn_path : StringProperty(
name="OIDN Path",
description="The path to the OIDN binaries",
default="",
subtype="FILE_PATH")
tlm_oidn_verbose : BoolProperty(
name="Verbose",
description="TODO")
tlm_oidn_threads : IntProperty(
name="Threads",
default=0,
min=0,
max=64,
description="Amount of threads to use. Set to 0 for auto-detect.")
tlm_oidn_maxmem : IntProperty(
name="Tiling max Memory",
default=0,
min=512,
max=32768,
description="Use tiling for memory conservation. Set to 0 to disable tiling.")
tlm_oidn_affinity : BoolProperty(
name="Set Affinity",
description="TODO")
tlm_oidn_use_albedo : BoolProperty(
name="Use albedo map",
description="TODO")
tlm_oidn_use_normal : BoolProperty(
name="Use normal map",
description="TODO")

View file

@ -0,0 +1,21 @@
import bpy
from bpy.props import *
class TLM_OptixEngineProperties(bpy.types.PropertyGroup):
tlm_optix_path : StringProperty(
name="Optix Path",
description="TODO",
default="",
subtype="FILE_PATH")
tlm_optix_verbose : BoolProperty(
name="Verbose",
description="TODO")
tlm_optix_maxmem : IntProperty(
name="Tiling max Memory",
default=0,
min=512,
max=32768,
description="Use tiling for memory conservation. Set to 0 to disable tiling.")

View file

@ -0,0 +1,4 @@
import bpy
from bpy.props import *
class TLM_FilteringProperties(bpy.types.PropertyGroup):

View file

@ -0,0 +1,121 @@
import bpy
from bpy.props import *
class TLM_ObjectProperties(bpy.types.PropertyGroup):
addon_keys = bpy.context.preferences.addons.keys()
tlm_atlas_pointer : StringProperty(
name = "Atlas Group",
description = "Atlas Lightmap Group",
default = "")
tlm_mesh_lightmap_use : BoolProperty(
name="Enable Lightmapping",
description="TODO",
default=False)
tlm_mesh_lightmap_resolution : EnumProperty(
items = [('32', '32', 'TODO'),
('64', '64', 'TODO'),
('128', '128', 'TODO'),
('256', '256', 'TODO'),
('512', '512', 'TODO'),
('1024', '1024', 'TODO'),
('2048', '2048', 'TODO'),
('4096', '4096', 'TODO'),
('8192', '8192', 'TODO')],
name = "Lightmap Resolution",
description="TODO",
default='256')
unwrap_modes = [('Lightmap', 'Lightmap', 'TODO'),('SmartProject', 'Smart Project', 'TODO'),('CopyExisting', 'Copy Existing', 'TODO'),('AtlasGroup', 'Atlas Group', 'TODO')]
if "blender_xatlas" in addon_keys:
unwrap_modes.append(('Xatlas', 'Xatlas', 'TODO'))
tlm_mesh_lightmap_unwrap_mode : EnumProperty(
items = unwrap_modes,
name = "Unwrap Mode",
description="TODO",
default='SmartProject')
tlm_mesh_unwrap_margin : FloatProperty(
name="Unwrap Margin",
default=0.1,
min=0.0,
max=1.0,
subtype='FACTOR')
tlm_mesh_filter_override : BoolProperty(
name="Override filtering",
description="Override the scene specific filtering",
default=False)
#FILTERING SETTINGS GROUP
tlm_mesh_filtering_engine : EnumProperty(
items = [('OpenCV', 'OpenCV', 'Make use of OpenCV based image filtering (Requires it to be installed first in the preferences panel)'),
('Numpy', 'Numpy', 'Make use of Numpy based image filtering (Integrated)')],
name = "Filtering library",
description="Select which filtering library to use.",
default='Numpy')
#Numpy Filtering options
tlm_mesh_numpy_filtering_mode : EnumProperty(
items = [('Blur', 'Blur', 'Basic blur filtering.')],
name = "Filter",
description="TODO",
default='Blur')
#OpenCV Filtering options
tlm_mesh_filtering_mode : EnumProperty(
items = [('Box', 'Box', 'Basic box blur'),
('Gaussian', 'Gaussian', 'Gaussian blurring'),
('Bilateral', 'Bilateral', 'Edge-aware filtering'),
('Median', 'Median', 'Median blur')],
name = "Filter",
description="TODO",
default='Median')
tlm_mesh_filtering_gaussian_strength : IntProperty(
name="Gaussian Strength",
default=3,
min=1,
max=50)
tlm_mesh_filtering_iterations : IntProperty(
name="Filter Iterations",
default=5,
min=1,
max=50)
tlm_mesh_filtering_box_strength : IntProperty(
name="Box Strength",
default=1,
min=1,
max=50)
tlm_mesh_filtering_bilateral_diameter : IntProperty(
name="Pixel diameter",
default=3,
min=1,
max=50)
tlm_mesh_filtering_bilateral_color_deviation : IntProperty(
name="Color deviation",
default=75,
min=1,
max=100)
tlm_mesh_filtering_bilateral_coordinate_deviation : IntProperty(
name="Color deviation",
default=75,
min=1,
max=100)
tlm_mesh_filtering_median_kernel : IntProperty(
name="Median kernel",
default=3,
min=1,
max=5)

View file

@ -0,0 +1,87 @@
import bpy
from bpy.props import *
class TLM_CyclesSceneProperties(bpy.types.PropertyGroup):
tlm_mode : EnumProperty(
items = [('CPU', 'CPU', 'Use the processor to bake textures'),
('GPU', 'GPU', 'Use the graphics card to bake textures')],
name = "Device",
description="Select whether to use the CPU or the GPU for baking",
default="CPU")
tlm_quality : EnumProperty(
items = [('0', 'Exterior Preview', 'Best for fast exterior previz'),
('1', 'Interior Preview', 'Best for fast interior previz with bounces'),
('2', 'Medium', 'Best for complicated interior preview and final for isometric environments'),
('3', 'High', 'Best used for final baking for 3rd person games'),
('4', 'Production', 'Best for first-person and Archviz'),
('5', 'Custom', 'Uses the cycles sample settings provided the user')],
name = "Quality",
description="Select baking quality",
default="0")
tlm_resolution_scale : EnumProperty(
items = [('1', '1/1', '1'),
('2', '1/2', '2'),
('4', '1/4', '4'),
('8', '1/8', '8')],
name = "Resolution scale",
description="Select resolution scale",
default="2")
tlm_setting_supersample : EnumProperty(
items = [('none', 'None', 'No supersampling'),
('2x', '2x', 'Double supersampling'),
('4x', '4x', 'Quadruple supersampling')],
name = "Supersampling",
description="Supersampling scale",
default="none")
tlm_bake_mode : EnumProperty(
items = [('Background', 'Background', 'More overhead; allows for network.'),
('Foreground', 'Foreground', 'Direct in-session bake')],
name = "Baking mode",
description="Select bake mode",
default="Foreground")
tlm_caching_mode : EnumProperty(
items = [('Copy', 'Copy', 'More overhead; allows for network.'),
('Cache', 'Cache', 'Cache in separate blend'),
('Node', 'Node restore', 'EXPERIMENTAL! Use with care')],
name = "Caching mode",
description="Select cache mode",
default="Copy")
tlm_directional_mode : EnumProperty(
items = [('None', 'None', 'No directional information'),
('Normal', 'Baked normal', 'Baked normal maps are taken into consideration')],
name = "Directional mode",
description="Select directional mode",
default="None")
tlm_lightmap_savedir : StringProperty(
name="Lightmap Directory",
description="TODO",
default="Lightmaps",
subtype="FILE_PATH")
tlm_dilation_margin : IntProperty(
name="Dilation margin",
default=4,
min=1,
max=64,
subtype='PIXEL')
tlm_exposure_multiplier : FloatProperty(
name="Exposure Multiplier",
default=0,
description="0 to disable. Multiplies GI value")
tlm_metallic_handling_mode : EnumProperty(
items = [('ignore', 'Ignore', 'No directional information'),
('clamp', 'Clamp', 'Clamp to value 0.9'),
('zero', 'Zero', 'Temporarily set to 0 during baking, and reapply after')],
name = "Metallic handling",
description="Set metallic handling mode to prevent black-baking.",
default="ignore")

View file

@ -0,0 +1,264 @@
import bpy
from bpy.props import *
class TLM_SceneProperties(bpy.types.PropertyGroup):
engines = [('Cycles', 'Cycles', 'Use Cycles for lightmapping')]
#engines.append(('LuxCoreRender', 'LuxCoreRender', 'Use LuxCoreRender for lightmapping'))
#engines.append(('OctaneRender', 'Octane Render', 'Use Octane Render for lightmapping'))
tlm_lightmap_engine : EnumProperty(
items = engines,
name = "Lightmap Engine",
description="Select which lightmap engine to use.",
default='Cycles')
#SETTINGS GROUP
tlm_setting_clean_option : EnumProperty(
items = [('Clean', 'Full Clean', 'Clean lightmap directory and revert all materials'),
('CleanMarked', 'Clean marked', 'Clean only the objects marked for lightmapping')],
name = "Clean mode",
description="The cleaning mode, either full or partial clean. Be careful that you don't delete lightmaps you don't intend to delete.",
default='Clean')
tlm_setting_keep_cache_files : BoolProperty(
name="Keep cache files",
description="Keep cache files (non-filtered and non-denoised)",
default=True)
tlm_setting_renderer : EnumProperty(
items = [('CPU', 'CPU', 'Bake using the processor'),
('GPU', 'GPU', 'Bake using the graphics card')],
name = "Device",
description="Select whether to use the CPU or the GPU",
default="CPU")
tlm_setting_scale : EnumProperty(
items = [('8', '1/8', '1/8th of set scale'),
('4', '1/4', '1/4th of set scale'),
('2', '1/2', 'Half of set scale'),
('1', '1/1', 'Full scale')],
name = "Lightmap Resolution scale",
description="Lightmap resolution scaling. Adjust for previewing.",
default="1")
tlm_setting_supersample : EnumProperty(
items = [('2x', '2x', 'Double the sampling resolution'),
('4x', '4x', 'Quadruple the sampling resolution')],
name = "Lightmap Supersampling",
description="Supersamples the baked lightmap. Increases bake time",
default="2x")
tlm_setting_savedir : StringProperty(
name="Lightmap Directory",
description="Your baked lightmaps will be stored here.",
default="Lightmaps",
subtype="FILE_PATH")
tlm_setting_exposure_multiplier : FloatProperty(
name="Exposure Multiplier",
default=0,
description="0 to disable. Multiplies GI value")
tlm_alert_on_finish : BoolProperty(
name="Alert on finish",
description="Play a sound when the lightmaps are done.",
default=False)
tlm_setting_apply_scale : BoolProperty(
name="Apply scale",
description="Apply the scale before unwrapping.",
default=True)
tlm_play_sound : BoolProperty(
name="Play sound on finish",
description="Play sound on finish",
default=False)
tlm_compile_statistics : BoolProperty(
name="Compile statistics",
description="Compile time statistics in the lightmap folder.",
default=True)
tlm_apply_on_unwrap : BoolProperty(
name="Apply scale",
description="TODO",
default=False)
#DENOISE SETTINGS GROUP
tlm_denoise_use : BoolProperty(
name="Enable denoising",
description="Enable denoising for lightmaps",
default=False)
tlm_denoise_engine : EnumProperty(
items = [('Integrated', 'Integrated', 'Use the Blender native denoiser (Compositor; Slow)'),
('OIDN', 'Intel Denoiser', 'Use Intel denoiser (CPU powered)'),
('Optix', 'Optix Denoiser', 'Use Nvidia Optix denoiser (GPU powered)')],
name = "Denoiser",
description="Select which denoising engine to use.",
default='Integrated')
#FILTERING SETTINGS GROUP
tlm_filtering_use : BoolProperty(
name="Enable filtering",
description="Enable filtering for lightmaps",
default=False)
tlm_filtering_engine : EnumProperty(
items = [('OpenCV', 'OpenCV', 'Make use of OpenCV based image filtering (Requires it to be installed first in the preferences panel)'),
('Numpy', 'Numpy', 'Make use of Numpy based image filtering (Integrated)')],
name = "Filtering library",
description="Select which filtering library to use.",
default='Numpy')
#Numpy Filtering options
tlm_numpy_filtering_mode : EnumProperty(
items = [('Blur', 'Blur', 'Basic blur filtering.')],
name = "Filter",
description="TODO",
default='Blur')
#OpenCV Filtering options
tlm_filtering_mode : EnumProperty(
items = [('Box', 'Box', 'Basic box blur'),
('Gaussian', 'Gaussian', 'Gaussian blurring'),
('Bilateral', 'Bilateral', 'Edge-aware filtering'),
('Median', 'Median', 'Median blur')],
name = "Filter",
description="TODO",
default='Median')
tlm_filtering_gaussian_strength : IntProperty(
name="Gaussian Strength",
default=3,
min=1,
max=50)
tlm_filtering_iterations : IntProperty(
name="Filter Iterations",
default=5,
min=1,
max=50)
tlm_filtering_box_strength : IntProperty(
name="Box Strength",
default=1,
min=1,
max=50)
tlm_filtering_bilateral_diameter : IntProperty(
name="Pixel diameter",
default=3,
min=1,
max=50)
tlm_filtering_bilateral_color_deviation : IntProperty(
name="Color deviation",
default=75,
min=1,
max=100)
tlm_filtering_bilateral_coordinate_deviation : IntProperty(
name="Color deviation",
default=75,
min=1,
max=100)
tlm_filtering_median_kernel : IntProperty(
name="Median kernel",
default=3,
min=1,
max=5)
#Encoding properties
tlm_encoding_use : BoolProperty(
name="Enable encoding",
description="Enable encoding for lightmaps",
default=False)
tlm_encoding_mode : EnumProperty(
items = [('RGBM', 'RGBM', '8-bit HDR encoding. Good for compatibility, good for memory but has banding issues.'),
('LogLuv', 'LogLuv', '8-bit HDR encoding. Different.'),
('HDR', 'HDR', '32-bit HDR encoding. Best quality, but high memory usage and not compatible with all devices.')],
name = "Encoding Mode",
description="TODO",
default='HDR')
tlm_encoding_range : IntProperty(
name="Encoding range",
description="Higher gives a larger HDR range, but also gives more banding.",
default=6,
min=1,
max=10)
tlm_encoding_armory_setup : BoolProperty(
name="Use Armory decoder",
description="TODO",
default=False)
tlm_encoding_colorspace : EnumProperty(
items = [('XYZ', 'XYZ', 'TODO'),
('sRGB', 'sRGB', 'TODO'),
('NonColor', 'Non-Color', 'TODO'),
('ACES', 'Linear ACES', 'TODO'),
('Linear', 'Linear', 'TODO'),
('FilmicLog', 'Filmic Log', 'TODO')],
name = "Color Space",
description="TODO",
default='Linear')
tlm_compression : IntProperty(
name="PNG Compression",
description="0 = No compression. 100 = Maximum compression.",
default=0,
min=0,
max=100)
tlm_format : EnumProperty(
items = [('RGBE', 'HDR', '32-bit RGBE encoded .hdr files. No compression available.'),
('EXR', 'EXR', '32-bit OpenEXR format.')],
name = "Format",
description="Select default 32-bit format",
default='RGBE')
tlm_override_object_settings : BoolProperty(
name="Override settings",
description="TODO",
default=False)
tlm_mesh_lightmap_resolution : EnumProperty(
items = [('32', '32', 'TODO'),
('64', '64', 'TODO'),
('128', '128', 'TODO'),
('256', '256', 'TODO'),
('512', '512', 'TODO'),
('1024', '1024', 'TODO'),
('2048', '2048', 'TODO'),
('4096', '4096', 'TODO'),
('8192', '8192', 'TODO')],
name = "Lightmap Resolution",
description="TODO",
default='256')
tlm_mesh_lightmap_unwrap_mode : EnumProperty(
items = [('Lightmap', 'Lightmap', 'TODO'),
('SmartProject', 'Smart Project', 'TODO'),
('CopyExisting', 'Copy Existing', 'TODO'),
('AtlasGroup', 'Atlas Group', 'TODO')],
name = "Unwrap Mode",
description="TODO",
default='SmartProject')
tlm_mesh_unwrap_margin : FloatProperty(
name="Unwrap Margin",
default=0.1,
min=0.0,
max=1.0,
subtype='FACTOR')
tlm_headless : BoolProperty(
name="Don't apply materials",
description="Headless; Do not apply baked materials on finish.",
default=False)

View file

@ -0,0 +1,585 @@
import bpy, os, importlib, subprocess, sys, threading, platform, aud
from . import encoding
from . cycles import lightmap, prepare, nodes, cache
from . denoiser import integrated, oidn
from . filtering import opencv
from os import listdir
from os.path import isfile, join
from time import time, sleep
previous_settings = {}
def prepare_build(self=0, background_mode=False):
if bpy.context.scene.TLM_EngineProperties.tlm_bake_mode == "Foreground" or background_mode==True:
global start_time
start_time = time()
scene = bpy.context.scene
sceneProperties = scene.TLM_SceneProperties
#We dynamically load the renderer and denoiser, instead of loading something we don't use
if sceneProperties.tlm_lightmap_engine == "Cycles":
pass
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
#Timer start here bound to global
if check_save():
print("Please save your file first")
self.report({'INFO'}, "Please save your file first")
return{'FINISHED'}
if check_denoiser():
print("No denoise OIDN path assigned")
self.report({'INFO'}, "No denoise OIDN path assigned")
return{'FINISHED'}
if check_materials():
print("Error with material")
self.report({'INFO'}, "Error with material")
return{'FINISHED'}
if opencv_check():
if sceneProperties.tlm_filtering_use:
print("Error:Filtering - OpenCV not installed")
self.report({'INFO'}, "Error:Filtering - OpenCV not installed")
return{'FINISHED'}
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
if not os.path.isdir(dirpath):
os.mkdir(dirpath)
#Naming check
naming_check()
## RENDER DEPENDENCY FROM HERE
if sceneProperties.tlm_lightmap_engine == "Cycles":
prepare.init(self, previous_settings)
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
#Renderer - Store settings
#Renderer - Set settings
#Renderer - Config objects, lights, world
begin_build()
else:
filepath = bpy.data.filepath
start_time = time()
scene = bpy.context.scene
sceneProperties = scene.TLM_SceneProperties
#We dynamically load the renderer and denoiser, instead of loading something we don't use
if sceneProperties.tlm_lightmap_engine == "Cycles":
pass
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
#Timer start here bound to global
if check_save():
print("Please save your file first")
self.report({'INFO'}, "Please save your file first")
return{'FINISHED'}
if check_denoiser():
print("No denoise OIDN path assigned")
self.report({'INFO'}, "No denoise OIDN path assigned")
return{'FINISHED'}
if check_materials():
print("Error with material")
self.report({'INFO'}, "Error with material")
return{'FINISHED'}
if opencv_check():
if sceneProperties.tlm_filtering_use:
print("Error:Filtering - OpenCV not installed")
self.report({'INFO'}, "Error:Filtering - OpenCV not installed")
return{'FINISHED'}
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
if not os.path.isdir(dirpath):
os.mkdir(dirpath)
#Naming check
naming_check()
pipe_open([sys.executable,"-b",filepath,"--python-expr",'import bpy; import thelightmapper; thelightmapper.addon.utility.build.prepare_build(0, True);'], finish_assemble)
def finish_assemble():
pass
#bpy.ops.wm.revert_mainfile() We cannot use this, as Blender crashes...
print("Background baking finished")
scene = bpy.context.scene
sceneProperties = scene.TLM_SceneProperties
if sceneProperties.tlm_lightmap_engine == "Cycles":
prepare.init(previous_settings)
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
manage_build(True)
def pipe_open(args, callback):
def thread_process(args, callback):
process = subprocess.Popen(args)
process.wait()
callback()
return
thread = threading.Thread(target=thread_process, args=(args, callback))
thread.start()
return thread
def begin_build():
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
scene = bpy.context.scene
sceneProperties = scene.TLM_SceneProperties
if sceneProperties.tlm_lightmap_engine == "Cycles":
lightmap.bake()
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
#Denoiser
if sceneProperties.tlm_denoise_use:
if sceneProperties.tlm_denoise_engine == "Integrated":
baked_image_array = []
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
for file in dirfiles:
if file.endswith("_baked.hdr"):
baked_image_array.append(file)
print(baked_image_array)
denoiser = integrated.TLM_Integrated_Denoise()
denoiser.load(baked_image_array)
denoiser.setOutputDir(dirpath)
denoiser.denoise()
elif sceneProperties.tlm_denoise_engine == "OIDN":
baked_image_array = []
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
for file in dirfiles:
if file.endswith("_baked.hdr"):
baked_image_array.append(file)
oidnProperties = scene.TLM_OIDNEngineProperties
denoiser = oidn.TLM_OIDN_Denoise(oidnProperties, baked_image_array, dirpath)
denoiser.denoise()
denoiser.clean()
del denoiser
else:
pass
#Filtering
if sceneProperties.tlm_filtering_use:
if sceneProperties.tlm_denoise_use:
useDenoise = True
else:
useDenoise = False
filter = opencv.TLM_CV_Filtering
filter.init(dirpath, useDenoise)
if sceneProperties.tlm_encoding_use:
if sceneProperties.tlm_encoding_mode == "HDR":
if sceneProperties.tlm_format == "EXR":
print("EXR Format")
ren = bpy.context.scene.render
ren.image_settings.file_format = "OPEN_EXR"
#ren.image_settings.exr_codec = "scene.TLM_SceneProperties.tlm_exr_codec"
end = "_baked"
baked_image_array = []
if sceneProperties.tlm_denoise_use:
end = "_denoised"
if sceneProperties.tlm_filtering_use:
end = "_filtered"
#For each image in folder ending in denoised/filtered
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
for file in dirfiles:
if file.endswith(end + ".hdr"):
img = bpy.data.images.load(os.path.join(dirpath,file))
img.save_render(img.filepath_raw[:-4] + ".exr")
if sceneProperties.tlm_encoding_mode == "LogLuv":
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
end = "_baked"
if sceneProperties.tlm_denoise_use:
end = "_denoised"
if sceneProperties.tlm_filtering_use:
end = "_filtered"
for file in dirfiles:
if file.endswith(end + ".hdr"):
img = bpy.data.images.load(os.path.join(dirpath, file), check_existing=False)
encoding.encodeLogLuv(img, dirpath, 0)
if sceneProperties.tlm_encoding_mode == "RGBM":
print("ENCODING RGBM")
dirfiles = [f for f in listdir(dirpath) if isfile(join(dirpath, f))]
end = "_baked"
if sceneProperties.tlm_denoise_use:
end = "_denoised"
if sceneProperties.tlm_filtering_use:
end = "_filtered"
for file in dirfiles:
if file.endswith(end + ".hdr"):
img = bpy.data.images.load(os.path.join(dirpath, file), check_existing=False)
print("Encoding:" + str(file))
encoding.encodeImageRGBM(img, sceneProperties.tlm_encoding_range, dirpath, 0)
manage_build()
def manage_build(background_pass=False):
scene = bpy.context.scene
sceneProperties = scene.TLM_SceneProperties
if sceneProperties.tlm_lightmap_engine == "Cycles":
if background_pass:
nodes.apply_lightmaps()
nodes.apply_materials() #From here the name is changed...
end = "_baked"
if sceneProperties.tlm_denoise_use:
end = "_denoised"
if sceneProperties.tlm_filtering_use:
end = "_filtered"
formatEnc = ".hdr"
if sceneProperties.tlm_encoding_use:
if sceneProperties.tlm_encoding_mode == "HDR":
if sceneProperties.tlm_format == "EXR":
formatEnc = ".exr"
if sceneProperties.tlm_encoding_mode == "LogLuv":
formatEnc = "_encoded.png"
if sceneProperties.tlm_encoding_mode == "RGBM":
formatEnc = "_encoded.png"
if not background_pass:
nodes.exchangeLightmapsToPostfix("_baked", end, formatEnc)
if sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
pass
if sceneProperties.tlm_lightmap_engine == "OctaneRender":
pass
if bpy.context.scene.TLM_EngineProperties.tlm_bake_mode == "Background":
pass
#bpy.ops.wm.save_as_mainfile(filepath=bpy.data.filepath + "baked") #Crashes Blender
if scene.TLM_EngineProperties.tlm_setting_supersample == "2x":
supersampling_scale = 2
elif scene.TLM_EngineProperties.tlm_setting_supersample == "4x":
supersampling_scale = 4
else:
supersampling_scale = 1
# for image in bpy.data.images:
# if image.name.endswith("_baked"):
# resolution = image.size[0]
# rescale = resolution / supersampling_scale
# image.scale(rescale, rescale)
# image.save()
for image in bpy.data.images:
if image.users < 1:
bpy.data.images.remove(image)
if scene.TLM_SceneProperties.tlm_headless:
filepath = bpy.data.filepath
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_restore(obj)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_rename(obj)
for mat in bpy.data.materials:
if mat.users < 1:
bpy.data.materials.remove(mat)
for mat in bpy.data.materials:
if mat.name.startswith("."):
if "_Original" in mat.name:
bpy.data.materials.remove(mat)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
img_name = obj.name + '_baked'
Lightmapimage = bpy.data.images[img_name]
obj["Lightmap"] = Lightmapimage.filepath_raw
for image in bpy.data.images:
if image.name.endswith("_baked"):
bpy.data.images.remove(image, do_unlink=True)
total_time = sec_to_hours((time() - start_time))
print(total_time)
reset_settings(previous_settings["settings"])
if scene.TLM_SceneProperties.tlm_alert_on_finish:
scriptDir = os.path.dirname(os.path.realpath(__file__))
sound_path = os.path.abspath(os.path.join(scriptDir, '..', 'assets/sound.ogg'))
device = aud.Device()
sound = aud.Sound.file(sound_path)
device.play(sound)
print("ALERT!")
def reset_settings(prev_settings):
scene = bpy.context.scene
cycles = scene.cycles
cycles.samples = int(prev_settings[0])
cycles.max_bounces = int(prev_settings[1])
cycles.diffuse_bounces = int(prev_settings[2])
cycles.glossy_bounces = int(prev_settings[3])
cycles.transparent_max_bounces = int(prev_settings[4])
cycles.transmission_bounces = int(prev_settings[5])
cycles.volume_bounces = int(prev_settings[6])
cycles.caustics_reflective = prev_settings[7]
cycles.caustics_refractive = prev_settings[8]
cycles.device = prev_settings[9]
scene.render.engine = prev_settings[10]
bpy.context.view_layer.objects.active = prev_settings[11]
#for obj in prev_settings[12]:
# obj.select_set(True)
def naming_check():
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
if obj.name != "":
if "_" in obj.name:
obj.name = obj.name.replace("_",".")
if " " in obj.name:
obj.name = obj.name.replace(" ",".")
if "[" in obj.name:
obj.name = obj.name.replace("[",".")
if "]" in obj.name:
obj.name = obj.name.replace("]",".")
if "ø" in obj.name:
obj.name = obj.name.replace("ø","oe")
if "æ" in obj.name:
obj.name = obj.name.replace("æ","ae")
if "å" in obj.name:
obj.name = obj.name.replace("å","aa")
for slot in obj.material_slots:
if "_" in slot.material.name:
slot.material.name = slot.material.name.replace("_",".")
if " " in slot.material.name:
slot.material.name = slot.material.name.replace(" ",".")
if "[" in slot.material.name:
slot.material.name = slot.material.name.replace("[",".")
if "[" in slot.material.name:
slot.material.name = slot.material.name.replace("]",".")
if "ø" in slot.material.name:
slot.material.name = slot.material.name.replace("ø","oe")
if "æ" in slot.material.name:
slot.material.name = slot.material.name.replace("æ","ae")
if "å" in slot.material.name:
slot.material.name = slot.material.name.replace("å","aa")
def opencv_check():
cv2 = importlib.util.find_spec("cv2")
if cv2 is not None:
return 0
else:
return 1
def check_save():
if not bpy.data.is_saved:
return 1
else:
return 0
def check_denoiser():
scene = bpy.context.scene
if scene.TLM_SceneProperties.tlm_denoise_use:
if scene.TLM_SceneProperties.tlm_denoise_engine == "OIDN":
oidnPath = scene.TLM_OIDNEngineProperties.tlm_oidn_path
if scene.TLM_OIDNEngineProperties.tlm_oidn_path == "":
return 1
if platform.system() == "Windows":
if not scene.TLM_OIDNEngineProperties.tlm_oidn_path.endswith(".exe"):
return 1
else:
return 0
# if scene.TLM_SceneProperties.tlm_denoise_use:
# if scene.TLM_SceneProperties.tlm_oidn_path == "":
# print("NO DENOISE PATH")
# return False
# else:
# return True
# else:
# return True
def check_materials():
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
for slot in obj.material_slots:
mat = slot.material
if mat is None:
print("MatNone")
mat = bpy.data.materials.new(name="Material")
mat.use_nodes = True
slot.material = mat
nodes = mat.node_tree.nodes
#TODO FINISH MATERIAL CHECK -> Nodes check
#Afterwards, redo build/utility
def sec_to_hours(seconds):
a=str(seconds//3600)
b=str((seconds%3600)//60)
c=str((seconds%3600)%60)
d=["{} hours {} mins {} seconds".format(a, b, c)]
return d

View file

@ -0,0 +1,74 @@
import bpy
#Todo - Check if already exists, in case multiple objects has the same material
def backup_material_copy(slot):
material = slot.material
dup = material.copy()
dup.name = "." + material.name + "_Original"
dup.use_fake_user = True
def backup_material_cache(slot, path):
bpy.ops.wm.save_as_mainfile(filepath=path, copy=True)
def backup_material_cache_restore(slot, path):
print("Restore cache")
def backup_material_rename(obj):
if "TLM_PrevMatArray" in obj:
print("Has PrevMat B")
for slot in obj.material_slots:
if slot.material.name.endswith("_Original"):
newname = slot.material.name[1:-9]
if newname in bpy.data.materials:
bpy.data.materials.remove(bpy.data.materials[newname])
slot.material.name = newname
del obj["TLM_PrevMatArray"]
def backup_material_restore(obj):
print("RESTORE")
if "TLM_PrevMatArray" in obj:
print("Has PrevMat A")
#Running through the slots
prevMatArray = obj["TLM_PrevMatArray"]
slotsLength = len(prevMatArray)
if len(prevMatArray) > 0:
for idx, slot in enumerate(obj.material_slots): #For each slot, we get the index
#We only need the index, corresponds to the array index
try:
originalMaterial = prevMatArray[idx]
except IndexError:
originalMaterial = ""
slot.material.user_clear()
if "." + originalMaterial + "_Original" in bpy.data.materials:
slot.material = bpy.data.materials["." + originalMaterial + "_Original"]
slot.material.use_fake_user = False
#slot.material =
#Remove material after changin
#We only rename after every change is complete
#if "." + material.name + "_Original" in bpy.data.materials:
# material = slot.material
# if "." + material.name + "_Original" in bpy.data.materials:
# original = bpy.data.materials["." + material.name + "_Original"]
# slot.material = original
# material.name = material.name
# original.name = original.name[1:-9]
# original.use_fake_user = False
# material.user_clear()
# bpy.data.materials.remove(material)
# #Reset number
# else:
# pass
#Check if material has nodes with lightmap prefix

View file

@ -0,0 +1,49 @@
import bpy, os
def bake():
for obj in bpy.data.objects:
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(False)
iterNum = 0
currentIterNum = 0
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
iterNum = iterNum + 1
iterNum = iterNum - 1
for obj in bpy.data.objects:
if obj.type == 'MESH':
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
scene = bpy.context.scene
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
obs = bpy.context.view_layer.objects
active = obs.active
obj.hide_render = False
scene.render.bake.use_clear = False
print("Baking " + str(currentIterNum) + "/" + str(iterNum) + " (" + str(round(currentIterNum/iterNum*100, 2)) + "%) : " + obj.name)
bpy.ops.object.bake(type="DIFFUSE", pass_filter={"DIRECT","INDIRECT"}, margin=scene.TLM_EngineProperties.tlm_dilation_margin, use_clear=False)
bpy.ops.object.select_all(action='DESELECT')
currentIterNum = currentIterNum + 1
for image in bpy.data.images:
if image.name.endswith("_baked"):
saveDir = os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_EngineProperties.tlm_lightmap_savedir)
bakemap_path = os.path.join(saveDir, image.name)
filepath_ext = ".hdr"
image.filepath_raw = bakemap_path + filepath_ext
image.file_format = "HDR"
print("Saving to: " + image.filepath_raw)
image.save()

View file

@ -0,0 +1,182 @@
import bpy, os
def apply_lightmaps():
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
for slot in obj.material_slots:
mat = slot.material
node_tree = mat.node_tree
nodes = mat.node_tree.nodes
scene = bpy.context.scene
dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
#Find nodes
for node in nodes:
if node.name == "Baked Image":
extension = ".hdr"
postfix = "_baked"
if scene.TLM_SceneProperties.tlm_denoise_use:
postfix = "_denoised"
if scene.TLM_SceneProperties.tlm_filtering_use:
postfix = "_filtered"
node.image.source = "FILE"
image_name = obj.name + postfix + extension #TODO FIX EXTENSION
node.image.filepath_raw = os.path.join(dirpath, image_name)
def apply_materials():
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
uv_layers = obj.data.uv_layers
uv_layers.active_index = 0
scene = bpy.context.scene
decoding = False
#Sort name
for slot in obj.material_slots:
mat = slot.material
if mat.name.endswith('_temp'):
old = slot.material
slot.material = bpy.data.materials[old.name.split('_' + obj.name)[0]]
if(scene.TLM_EngineProperties.tlm_exposure_multiplier > 0):
tlm_exposure = bpy.data.node_groups.get("Exposure")
if tlm_exposure == None:
load_library("Exposure")
#Apply materials
for slot in obj.material_slots:
mat = slot.material
node_tree = mat.node_tree
nodes = mat.node_tree.nodes
foundBakedNode = False
#Find nodes
for node in nodes:
if node.name == "Baked Image":
lightmapNode = node
lightmapNode.location = -800, 300
lightmapNode.name = "TLM_Lightmap"
foundBakedNode = True
img_name = obj.name + '_baked'
if not foundBakedNode:
lightmapNode = node_tree.nodes.new(type="ShaderNodeTexImage")
lightmapNode.location = -300, 300
lightmapNode.image = bpy.data.images[img_name]
lightmapNode.name = "TLM_Lightmap"
lightmapNode.interpolation = "Smart"
#Find output node
outputNode = nodes[0]
if(outputNode.type != "OUTPUT_MATERIAL"):
for node in node_tree.nodes:
if node.type == "OUTPUT_MATERIAL":
outputNode = node
break
#Find mainnode
mainNode = outputNode.inputs[0].links[0].from_node
#Add all nodes first
#Add lightmap multipliction texture
mixNode = node_tree.nodes.new(type="ShaderNodeMixRGB")
mixNode.name = "Lightmap_Multiplication"
mixNode.location = -300, 300
mixNode.blend_type = 'MULTIPLY'
mixNode.inputs[0].default_value = 1.0
UVLightmap = node_tree.nodes.new(type="ShaderNodeUVMap")
UVLightmap.uv_map = "UVMap_Lightmap"
UVLightmap.name = "Lightmap_UV"
UVLightmap.location = -1000, 300
if(scene.TLM_EngineProperties.tlm_exposure_multiplier > 0):
ExposureNode = node_tree.nodes.new(type="ShaderNodeGroup")
ExposureNode.node_tree = bpy.data.node_groups["Exposure"]
ExposureNode.inputs[1].default_value = scene.TLM_EngineProperties.tlm_exposure_multiplier
ExposureNode.location = -500, 300
ExposureNode.name = "Lightmap_Exposure"
#Add Basecolor node
if len(mainNode.inputs[0].links) == 0:
baseColorValue = mainNode.inputs[0].default_value
baseColorNode = node_tree.nodes.new(type="ShaderNodeRGB")
baseColorNode.outputs[0].default_value = baseColorValue
baseColorNode.location = ((mainNode.location[0] - 500, mainNode.location[1] - 300))
baseColorNode.name = "Lightmap_BasecolorNode_A"
else:
baseColorNode = mainNode.inputs[0].links[0].from_node
baseColorNode.name = "LM_P"
#Linking
if(scene.TLM_EngineProperties.tlm_exposure_multiplier > 0):
mat.node_tree.links.new(lightmapNode.outputs[0], ExposureNode.inputs[0]) #Connect lightmap node to mixnode
mat.node_tree.links.new(ExposureNode.outputs[0], mixNode.inputs[1]) #Connect lightmap node to mixnode
else:
mat.node_tree.links.new(lightmapNode.outputs[0], mixNode.inputs[1]) #Connect lightmap node to mixnode
mat.node_tree.links.new(baseColorNode.outputs[0], mixNode.inputs[2]) #Connect basecolor to pbr node
mat.node_tree.links.new(mixNode.outputs[0], mainNode.inputs[0]) #Connect mixnode to pbr node
mat.node_tree.links.new(UVLightmap.outputs[0], lightmapNode.inputs[0]) #Connect uvnode to lightmapnode
def exchangeLightmapsToPostfix(ext_postfix, new_postfix, formatHDR=".hdr"):
print(ext_postfix, new_postfix, formatHDR)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
for slot in obj.material_slots:
mat = slot.material
node_tree = mat.node_tree
nodes = mat.node_tree.nodes
for node in nodes:
if node.name == "Baked Image" or node.name == "TLM_Lightmap":
img_name = node.image.filepath_raw
cutLen = len(ext_postfix + formatHDR)
print("Len:" + str(len(ext_postfix + formatHDR)) + "|" + ext_postfix + ".." + formatHDR)
#Simple way to sort out objects with multiple materials
if formatHDR == ".hdr" or formatHDR == ".exr":
if not node.image.filepath_raw.endswith(new_postfix + formatHDR):
node.image.filepath_raw = img_name[:-cutLen] + new_postfix + formatHDR
else:
cutLen = len(ext_postfix + ".hdr")
if not node.image.filepath_raw.endswith(new_postfix + formatHDR):
node.image.filepath_raw = img_name[:-cutLen] + new_postfix + formatHDR
for image in bpy.data.images:
image.reload()
def load_library(asset_name):
scriptDir = os.path.dirname(os.path.realpath(__file__))
if bpy.data.filepath.endswith('tlm_data.blend'): # Prevent load in library itself
return
data_path = os.path.abspath(os.path.join(scriptDir, '..', '..', 'Assets/tlm_data.blend'))
data_names = [asset_name]
# Import
data_refs = data_names.copy()
with bpy.data.libraries.load(data_path, link=False) as (data_from, data_to):
data_to.node_groups = data_refs
for ref in data_refs:
ref.use_fake_user = True

View file

@ -0,0 +1,452 @@
import bpy
from . import cache
from .. utility import *
def assemble():
configure_world()
configure_lights()
configure_meshes()
def init(self, prev_container):
store_existing(prev_container)
set_settings()
configure_world()
configure_lights()
configure_meshes(self)
def configure_world():
pass
def configure_lights():
pass
def configure_meshes(self):
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_restore(obj)
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
cache.backup_material_rename(obj)
for mat in bpy.data.materials:
if mat.users < 1:
bpy.data.materials.remove(mat)
for mat in bpy.data.materials:
if mat.name.startswith("."):
if "_Original" in mat.name:
bpy.data.materials.remove(mat)
for image in bpy.data.images:
if image.name.endswith("_baked"):
bpy.data.images.remove(image, do_unlink=True)
iterNum = 0
currentIterNum = 0
scene = bpy.context.scene
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
for slot in obj.material_slots:
if "." + slot.name + '_Original' in bpy.data.materials:
print("The material: " + slot.name + " shifted to " + "." + slot.name + '_Original')
slot.material = bpy.data.materials["." + slot.name + '_Original']
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
iterNum = iterNum + 1
for obj in bpy.data.objects:
if obj.type == "MESH":
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
currentIterNum = currentIterNum + 1
#Configure selection
bpy.ops.object.select_all(action='DESELECT')
bpy.context.view_layer.objects.active = obj
obj.select_set(True)
obs = bpy.context.view_layer.objects
active = obs.active
#Provide material if none exists
preprocess_material(obj, scene)
#UV Layer management here
if not obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
uv_layers = obj.data.uv_layers
if not "UVMap_Lightmap" in uv_layers:
print("UVMap made B")
uvmap = uv_layers.new(name="UVMap_Lightmap")
uv_layers.active_index = len(uv_layers) - 1
#If lightmap
if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Lightmap":
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bpy.ops.uv.lightmap_pack('EXEC_SCREEN', PREF_CONTEXT='ALL_FACES', PREF_MARGIN_DIV=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin)
#If smart project
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "SmartProject":
print("Smart Project B")
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.uv.smart_project(angle_limit=45.0, island_margin=obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin, user_area_weight=1.0, use_aspect=True, stretch_to_bounds=False)
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "Xatlas":
if scene.TLM_SceneProperties.tlm_apply_on_unwrap:
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
#import blender_xatlas
#blender_xatlas.Unwrap_Lightmap_Group_Xatlas_2(bpy.context)
#bpy.ops.object.setup_unwrap()
Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj)
elif obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
print("ATLAS GROUP: " + obj.TLM_ObjectProperties.tlm_atlas_pointer)
else: #if copy existing
print("Copied Existing B")
#Here we copy an existing map
pass
else:
print("Existing found...skipping")
for i in range(0, len(uv_layers)):
if uv_layers[i].name == 'UVMap_Lightmap':
uv_layers.active_index = i
print("Lightmap shift B")
break
#Sort out nodes
for slot in obj.material_slots:
nodetree = slot.material.node_tree
outputNode = nodetree.nodes[0] #Presumed to be material output node
if(outputNode.type != "OUTPUT_MATERIAL"):
for node in nodetree.nodes:
if node.type == "OUTPUT_MATERIAL":
outputNode = node
break
mainNode = outputNode.inputs[0].links[0].from_node
if mainNode.type not in ['BSDF_PRINCIPLED','BSDF_DIFFUSE','GROUP']:
#TODO! FIND THE PRINCIPLED PBR
self.report({'INFO'}, "The primary material node is not supported. Seeking first principled.")
if len(find_node_by_type(nodetree.nodes, Node_Types.pbr_node)) > 0:
mainNode = find_node_by_type(nodetree.nodes, Node_Types.pbr_node)[0]
else:
self.report({'INFO'}, "No principled found. Seeking diffuse")
if len(find_node_by_type(nodetree.nodes, Node_Types.diffuse)) > 0:
mainNode = find_node_by_type(nodetree.nodes, Node_Types.diffuse)[0]
else:
self.report({'INFO'}, "No supported nodes. Continuing anyway.")
pass
if mainNode.type == 'GROUP':
if mainNode.node_tree != "Armory PBR":
print("The material group is not supported!")
pass
if (mainNode.type == "BSDF_PRINCIPLED"):
print("BSDF_Principled")
if scene.TLM_EngineProperties.tlm_directional_mode == "None":
print("Directional mode")
if not len(mainNode.inputs[19].links) == 0:
print("NOT LEN 0")
ninput = mainNode.inputs[19].links[0]
noutput = mainNode.inputs[19].links[0].from_node
nodetree.links.remove(noutput.outputs[0].links[0])
#Clamp metallic
# if(mainNode.inputs[4].default_value == 1 and scene.TLM_SceneProperties.tlm_clamp_metallic):
# mainNode.inputs[4].default_value = 0.99
if (mainNode.type == "BSDF_DIFFUSE"):
print("BSDF_Diffuse")
for slot in obj.material_slots:
nodetree = bpy.data.materials[slot.name].node_tree
nodes = nodetree.nodes
#First search to get the first output material type
for node in nodetree.nodes:
if node.type == "OUTPUT_MATERIAL":
mainNode = node
break
#Fallback to get search
if not mainNode.type == "OUTPUT_MATERIAL":
mainNode = nodetree.nodes.get("Material Output")
#Last resort to first node in list
if not mainNode.type == "OUTPUT_MATERIAL":
mainNode = nodetree.nodes[0].inputs[0].links[0].from_node
for node in nodes:
if "LM" in node.name:
nodetree.links.new(node.outputs[0], mainNode.inputs[0])
for node in nodes:
if "Lightmap" in node.name:
nodes.remove(node)
def preprocess_material(obj, scene):
if len(obj.material_slots) == 0:
single = False
number = 0
while single == False:
matname = obj.name + ".00" + str(number)
if matname in bpy.data.materials:
single = False
number = number + 1
else:
mat = bpy.data.materials.new(name=matname)
mat.use_nodes = True
obj.data.materials.append(mat)
single = True
#We copy the existing material slots to an ordered array, which corresponds to the slot index
matArray = []
for slot in obj.material_slots:
matArray.append(slot.name)
obj["TLM_PrevMatArray"] = matArray
for slot in obj.material_slots:
cache.backup_material_copy(slot)
mat = slot.material
if mat.users > 1:
copymat = mat.copy()
slot.material = copymat
# for slot in obj.material_slots:
# matname = slot.material.name
# originalName = "." + matname + "_Original"
# hasOriginal = False
# if originalName in bpy.data.materials:
# hasOriginal = True
# else:
# hasOriginal = False
# if hasOriginal:
# cache.backup_material_restore(slot)
# cache.backup_material_copy(slot)
############################
#Make a material backup and restore original if exists
# if scene.TLM_SceneProperties.tlm_caching_mode == "Copy":
# for slot in obj.material_slots:
# matname = slot.material.name
# originalName = "." + matname + "_Original"
# hasOriginal = False
# if originalName in bpy.data.materials:
# hasOriginal = True
# else:
# hasOriginal = False
# if hasOriginal:
# matcache.backup_material_restore(slot)
# matcache.backup_material_copy(slot)
# else: #Cache blend
# #TEST CACHE
# filepath = bpy.data.filepath
# dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_SceneProperties.tlm_lightmap_savedir)
# path = dirpath + "/cache.blend"
# bpy.ops.wm.save_as_mainfile(filepath=path, copy=True)
#print("Warning: Cache blend not supported")
# for mat in bpy.data.materials:
# if mat.name.endswith('_baked'):
# bpy.data.materials.remove(mat, do_unlink=True)
# for img in bpy.data.images:
# if img.name == obj.name + "_baked":
# bpy.data.images.remove(img, do_unlink=True)
#SOME ATLAS EXCLUSION HERE?
ob = obj
for slot in ob.material_slots:
#If temporary material already exists
if slot.material.name.endswith('_temp'):
continue
n = slot.material.name + '_' + ob.name + '_temp'
if not n in bpy.data.materials:
slot.material = slot.material.copy()
slot.material.name = n
#Add images for baking
img_name = obj.name + '_baked'
#Resolution is object lightmap resolution divided by global scaler
if scene.TLM_EngineProperties.tlm_setting_supersample == "2x":
supersampling_scale = 2
elif scene.TLM_EngineProperties.tlm_setting_supersample == "4x":
supersampling_scale = 4
else:
supersampling_scale = 1
res = int(obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution) / int(scene.TLM_EngineProperties.tlm_resolution_scale) * int(supersampling_scale)
#If image not in bpy.data.images or if size changed, make a new image
if img_name not in bpy.data.images or bpy.data.images[img_name].size[0] != res or bpy.data.images[img_name].size[1] != res:
img = bpy.data.images.new(img_name, res, res, alpha=True, float_buffer=True)
num_pixels = len(img.pixels)
result_pixel = list(img.pixels)
for i in range(0,num_pixels,4):
# result_pixel[i+0] = scene.TLM_SceneProperties.tlm_default_color[0]
# result_pixel[i+1] = scene.TLM_SceneProperties.tlm_default_color[1]
# result_pixel[i+2] = scene.TLM_SceneProperties.tlm_default_color[2]
result_pixel[i+0] = 0.0
result_pixel[i+1] = 0.0
result_pixel[i+2] = 0.0
result_pixel[i+3] = 1.0
img.pixels = result_pixel
img.name = img_name
else:
img = bpy.data.images[img_name]
for slot in obj.material_slots:
mat = slot.material
mat.use_nodes = True
nodes = mat.node_tree.nodes
if "Baked Image" in nodes:
img_node = nodes["Baked Image"]
else:
img_node = nodes.new('ShaderNodeTexImage')
img_node.name = 'Baked Image'
img_node.location = (100, 100)
img_node.image = img
img_node.select = True
nodes.active = img_node
def set_settings():
scene = bpy.context.scene
cycles = scene.cycles
scene.render.engine = "CYCLES"
sceneProperties = scene.TLM_SceneProperties
engineProperties = scene.TLM_EngineProperties
cycles.device = scene.TLM_EngineProperties.tlm_mode
if engineProperties.tlm_quality == "0":
cycles.samples = 32
cycles.max_bounces = 1
cycles.diffuse_bounces = 1
cycles.glossy_bounces = 1
cycles.transparent_max_bounces = 1
cycles.transmission_bounces = 1
cycles.volume_bounces = 1
cycles.caustics_reflective = False
cycles.caustics_refractive = False
elif engineProperties.tlm_quality == "1":
cycles.samples = 64
cycles.max_bounces = 2
cycles.diffuse_bounces = 2
cycles.glossy_bounces = 2
cycles.transparent_max_bounces = 2
cycles.transmission_bounces = 2
cycles.volume_bounces = 2
cycles.caustics_reflective = False
cycles.caustics_refractive = False
elif engineProperties.tlm_quality == "2":
cycles.samples = 512
cycles.max_bounces = 2
cycles.diffuse_bounces = 2
cycles.glossy_bounces = 2
cycles.transparent_max_bounces = 2
cycles.transmission_bounces = 2
cycles.volume_bounces = 2
cycles.caustics_reflective = False
cycles.caustics_refractive = False
elif engineProperties.tlm_quality == "3":
cycles.samples = 1024
cycles.max_bounces = 256
cycles.diffuse_bounces = 256
cycles.glossy_bounces = 256
cycles.transparent_max_bounces = 256
cycles.transmission_bounces = 256
cycles.volume_bounces = 256
cycles.caustics_reflective = False
cycles.caustics_refractive = False
elif engineProperties.tlm_quality == "4":
cycles.samples = 2048
cycles.max_bounces = 512
cycles.diffuse_bounces = 512
cycles.glossy_bounces = 512
cycles.transparent_max_bounces = 512
cycles.transmission_bounces = 512
cycles.volume_bounces = 512
cycles.caustics_reflective = True
cycles.caustics_refractive = True
else: #Custom
pass
def store_existing(prev_container):
scene = bpy.context.scene
cycles = scene.cycles
selected = []
for obj in bpy.data.objects:
if obj.select_get():
selected.append(obj.name)
prev_container["settings"] = [
cycles.samples,
cycles.max_bounces,
cycles.diffuse_bounces,
cycles.glossy_bounces,
cycles.transparent_max_bounces,
cycles.transmission_bounces,
cycles.volume_bounces,
cycles.caustics_reflective,
cycles.caustics_refractive,
cycles.device,
scene.render.engine,
bpy.context.view_layer.objects.active,
selected
]

View file

@ -0,0 +1,79 @@
import bpy, os
class TLM_Integrated_Denoise:
image_array = []
image_output_destination = ""
def load(self, images):
self.image_array = images
self.cull_undefined()
def setOutputDir(self, dir):
self.image_output_destination = dir
def cull_undefined(self):
#Do a validation check before denoising
cam = bpy.context.scene.camera
if not cam:
bpy.ops.object.camera_add()
#Just select the first camera we find, needed for the compositor
for obj in bpy.data.objects:
if obj.type == "CAMERA":
bpy.context.scene.camera = obj
return
def denoise(self):
if not bpy.context.scene.use_nodes:
bpy.context.scene.use_nodes = True
tree = bpy.context.scene.node_tree
for image in self.image_array:
print("Image...: " + image)
img = bpy.data.images.load(self.image_output_destination + "/" + image)
image_node = tree.nodes.new(type='CompositorNodeImage')
image_node.image = img
image_node.location = 0, 0
denoise_node = tree.nodes.new(type='CompositorNodeDenoise')
denoise_node.location = 300, 0
comp_node = tree.nodes.new('CompositorNodeComposite')
comp_node.location = 600, 0
links = tree.links
links.new(image_node.outputs[0], denoise_node.inputs[0])
links.new(denoise_node.outputs[0], comp_node.inputs[0])
# set output resolution to image res
bpy.context.scene.render.resolution_x = img.size[0]
bpy.context.scene.render.resolution_y = img.size[1]
bpy.context.scene.render.resolution_percentage = 100
filePath = bpy.data.filepath
path = os.path.dirname(filePath)
base = os.path.basename(image)
filename, file_extension = os.path.splitext(image)
filename = filename[:-6]
bpy.data.scenes["Scene"].render.filepath = self.image_output_destination + "/" + filename + "_denoised" + file_extension
denoised_image_path = self.image_output_destination
bpy.data.scenes["Scene"].render.image_settings.file_format = "HDR"
bpy.ops.render.render(write_still=True)
#Cleanup
comp_nodes = [image_node, denoise_node, comp_node]
for node in comp_nodes:
tree.nodes.remove(node)

View file

@ -0,0 +1,200 @@
import bpy, os, sys, re, platform, subprocess
import numpy as np
class TLM_OIDN_Denoise:
image_array = []
image_output_destination = ""
denoised_array = []
def __init__(self, oidnProperties, img_array, dirpath):
self.oidnProperties = oidnProperties
self.image_array = img_array
self.image_output_destination = dirpath
self.check_binary()
def check_binary(self):
oidnPath = self.oidnProperties.tlm_oidn_path
if oidnPath != "":
file = os.path.basename(os.path.realpath(oidnPath))
filename, file_extension = os.path.splitext(file)
if(file_extension == ".exe"):
#if file exists oidnDenoise or denoise
pass
else:
#if file exists oidnDenoise or denoise
self.oidnProperties.tlm_oidn_path = os.path.join(self.oidnProperties.tlm_oidn_path,"oidnDenoise.exe")
else:
print("Please provide OIDN path")
def denoise(self):
for image in self.image_array:
if image not in self.denoised_array:
image_path = os.path.join(self.image_output_destination, image)
#Save to pfm
loaded_image = bpy.data.images.load(image_path, check_existing=False)
width = loaded_image.size[0]
height = loaded_image.size[1]
image_output_array = np.zeros([width, height, 3], dtype="float32")
image_output_array = np.array(loaded_image.pixels)
image_output_array = image_output_array.reshape(height, width, 4)
image_output_array = np.float32(image_output_array[:,:,:3])
image_output_denoise_destination = image_path[:-4] + ".pfm"
image_output_denoise_result_destination = image_path[:-4] + "_denoised.pfm"
with open(image_output_denoise_destination, "wb") as fileWritePFM:
self.save_pfm(fileWritePFM, image_output_array)
#Denoise
print("Loaded image: " + str(loaded_image))
verbose = self.oidnProperties.tlm_oidn_verbose
affinity = self.oidnProperties.tlm_oidn_affinity
if verbose:
print("Denoiser search: " + bpy.path.abspath(self.oidnProperties.tlm_oidn_path))
v = "3"
else:
v = "0"
if affinity:
a = "1"
else:
a = "0"
threads = str(self.oidnProperties.tlm_oidn_threads)
maxmem = str(self.oidnProperties.tlm_oidn_maxmem)
if platform.system() == 'Windows':
oidnPath = bpy.path.abspath(self.oidnProperties.tlm_oidn_path)
pipePath = [oidnPath, '-f', 'RTLightmap', '-hdr', image_output_denoise_destination, '-o', image_output_denoise_result_destination, '-verbose', v, '-threads', threads, '-affinity', a, '-maxmem', maxmem]
elif platform.system() == 'Darwin':
oidnPath = bpy.path.abspath(self.oidnProperties.tlm_oidn_path)
pipePath = [oidnPath + ' -f ' + ' RTLightmap ' + ' -hdr ' + image_output_denoise_destination + ' -o ' + image_output_denoise_result_destination + ' -verbose ' + v]
else:
oidnPath = bpy.path.abspath(self.oidnProperties.tlm_oidn_path)
pipePath = [oidnPath + ' -f ' + ' RTLightmap ' + ' -hdr ' + image_output_denoise_destination + ' -o ' + image_output_denoise_result_destination + ' -verbose ' + v]
if not verbose:
denoisePipe = subprocess.Popen(pipePath, stdout=subprocess.PIPE, stderr=None, shell=True)
else:
denoisePipe = subprocess.Popen(pipePath, shell=True)
denoisePipe.communicate()[0]
with open(image_output_denoise_result_destination, "rb") as f:
denoise_data, scale = self.load_pfm(f)
ndata = np.array(denoise_data)
ndata2 = np.dstack((ndata, np.ones((width,height))))
img_array = ndata2.ravel()
loaded_image.pixels = img_array
loaded_image.filepath_raw = image_output_denoise_result_destination = image_path[:-10] + "_denoised.hdr"
loaded_image.file_format = "HDR"
loaded_image.save()
self.denoised_array.append(image)
print(image_path)
def clean(self):
self.denoised_array.clear()
self.image_array.clear()
for file in self.image_output_destination:
if file.endswith("_baked.hdr"):
baked_image_array.append(file)
#self.image_output_destination
#Clean temporary files here..
#...pfm
#...denoised.hdr
def load_pfm(self, file, as_flat_list=False):
#start = time()
header = file.readline().decode("utf-8").rstrip()
if header == "PF":
color = True
elif header == "Pf":
color = False
else:
raise Exception("Not a PFM file.")
dim_match = re.match(r"^(\d+)\s(\d+)\s$", file.readline().decode("utf-8"))
if dim_match:
width, height = map(int, dim_match.groups())
else:
raise Exception("Malformed PFM header.")
scale = float(file.readline().decode("utf-8").rstrip())
if scale < 0: # little-endian
endian = "<"
scale = -scale
else:
endian = ">" # big-endian
data = np.fromfile(file, endian + "f")
shape = (height, width, 3) if color else (height, width)
if as_flat_list:
result = data
else:
result = np.reshape(data, shape)
#print("PFM import took %.3f s" % (time() - start))
return result, scale
def save_pfm(self, file, image, scale=1):
#start = time()
if image.dtype.name != "float32":
raise Exception("Image dtype must be float32 (got %s)" % image.dtype.name)
if len(image.shape) == 3 and image.shape[2] == 3: # color image
color = True
elif len(image.shape) == 2 or len(image.shape) == 3 and image.shape[2] == 1: # greyscale
color = False
else:
raise Exception("Image must have H x W x 3, H x W x 1 or H x W dimensions.")
file.write(b"PF\n" if color else b"Pf\n")
file.write(b"%d %d\n" % (image.shape[1], image.shape[0]))
endian = image.dtype.byteorder
if endian == "<" or endian == "=" and sys.byteorder == "little":
scale = -scale
file.write(b"%f\n" % scale)
image.tofile(file)
#print("PFM export took %.3f s" % (time() - start))

View file

@ -0,0 +1,7 @@
import bpy, os
class TLM_OIDN_Denoise:
def __init__(self):
pass

View file

@ -0,0 +1,245 @@
import bpy, math, os, gpu, bgl
import numpy as np
from . import utility
from fractions import Fraction
from gpu_extras.batch import batch_for_shader
def encodeLogLuv(image, outDir, quality):
input_image = bpy.data.images[image.name]
image_name = input_image.name
offscreen = gpu.types.GPUOffScreen(input_image.size[0], input_image.size[1])
image = input_image
vertex_shader = '''
uniform mat4 ModelViewProjectionMatrix;
in vec2 texCoord;
in vec2 pos;
out vec2 texCoord_interp;
void main()
{
//gl_Position = ModelViewProjectionMatrix * vec4(pos.xy, 0.0f, 1.0f);
//gl_Position.z = 1.0;
gl_Position = vec4(pos.xy, 100, 100);
texCoord_interp = texCoord;
}
'''
fragment_shader = '''
in vec2 texCoord_interp;
out vec4 fragColor;
uniform sampler2D image;
const mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );
vec4 LinearToLogLuv( in vec4 value ) {
vec3 Xp_Y_XYZp = cLogLuvM * value.rgb;
Xp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );
vec4 vResult;
vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
vResult.w = fract( Le );
vResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;
return vResult;
//return vec4(Xp_Y_XYZp,1);
}
const mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );
vec4 LogLuvToLinear( in vec4 value ) {
float Le = value.z * 255.0 + value.w;
vec3 Xp_Y_XYZp;
Xp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );
Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;
Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;
vec3 vRGB = cLogLuvInverseM * Xp_Y_XYZp.rgb;
//return vec4( max( vRGB, 0.0 ), 1.0 );
return vec4( max( Xp_Y_XYZp, 0.0 ), 1.0 );
}
void main()
{
//fragColor = LinearToLogLuv(pow(texture(image, texCoord_interp), vec4(0.454)));
fragColor = LinearToLogLuv(texture(image, texCoord_interp));
//fragColor = LogLuvToLinear(LinearToLogLuv(texture(image, texCoord_interp)));
}
'''
x_screen = 0
off_x = -100
off_y = -100
y_screen_flip = 0
sx = 200
sy = 200
vertices = (
(x_screen + off_x, y_screen_flip - off_y),
(x_screen + off_x, y_screen_flip - sy - off_y),
(x_screen + off_x + sx, y_screen_flip - sy - off_y),
(x_screen + off_x + sx, y_screen_flip - off_x))
if input_image.colorspace_settings.name != 'Linear':
input_image.colorspace_settings.name = 'Linear'
# Removing .exr or .hdr prefix
if image_name[-4:] == '.exr' or image_name[-4:] == '.hdr':
image_name = image_name[:-4]
target_image = bpy.data.images.get(image_name + '_encoded')
print(image_name + '_encoded')
if not target_image:
target_image = bpy.data.images.new(
name = image_name + '_encoded',
width = input_image.size[0],
height = input_image.size[1],
alpha = True,
float_buffer = False
)
shader = gpu.types.GPUShader(vertex_shader, fragment_shader)
batch = batch_for_shader(
shader, 'TRI_FAN',
{
"pos": vertices,
"texCoord": ((0, 1), (0, 0), (1, 0), (1, 1)),
},
)
if image.gl_load():
raise Exception()
with offscreen.bind():
bgl.glActiveTexture(bgl.GL_TEXTURE0)
bgl.glBindTexture(bgl.GL_TEXTURE_2D, image.bindcode)
shader.bind()
shader.uniform_int("image", 0)
batch.draw(shader)
buffer = bgl.Buffer(bgl.GL_BYTE, input_image.size[0] * input_image.size[1] * 4)
bgl.glReadBuffer(bgl.GL_BACK)
bgl.glReadPixels(0, 0, input_image.size[0], input_image.size[1], bgl.GL_RGBA, bgl.GL_UNSIGNED_BYTE, buffer)
offscreen.free()
target_image.pixels = [v / 255 for v in buffer]
input_image = target_image
#Save LogLuv
print(input_image.name)
input_image.filepath_raw = outDir + "/" + input_image.name + ".png"
#input_image.filepath_raw = outDir + "_encoded.png"
input_image.file_format = "PNG"
bpy.context.scene.render.image_settings.quality = quality
#input_image.save_render(filepath = input_image.filepath_raw, scene = bpy.context.scene)
input_image.save()
#Todo - Find a way to save
#bpy.ops.image.save_all_modified()
def encodeImageRGBM(image, maxRange, outDir, quality):
input_image = bpy.data.images[image.name]
image_name = input_image.name
if input_image.colorspace_settings.name != 'Linear':
input_image.colorspace_settings.name = 'Linear'
# Removing .exr or .hdr prefix
if image_name[-4:] == '.exr' or image_name[-4:] == '.hdr':
image_name = image_name[:-4]
target_image = bpy.data.images.get(image_name + '_encoded')
print(image_name + '_encoded')
if not target_image:
target_image = bpy.data.images.new(
name = image_name + '_encoded',
width = input_image.size[0],
height = input_image.size[1],
alpha = True,
float_buffer = False
)
num_pixels = len(input_image.pixels)
result_pixel = list(input_image.pixels)
for i in range(0,num_pixels,4):
for j in range(3):
result_pixel[i+j] *= 1.0 / maxRange;
result_pixel[i+3] = saturate(max(result_pixel[i], result_pixel[i+1], result_pixel[i+2], 1e-6))
result_pixel[i+3] = math.ceil(result_pixel[i+3] * 255.0) / 255.0
for j in range(3):
result_pixel[i+j] /= result_pixel[i+3]
target_image.pixels = result_pixel
input_image = target_image
#Save RGBM
print(input_image.name)
input_image.filepath_raw = outDir + "/" + input_image.name + ".png"
input_image.file_format = "PNG"
bpy.context.scene.render.image_settings.quality = quality
input_image.save()
#input_image.save_render(filepath = input_image.filepath_raw, scene = bpy.context.scene)
# input_image.filepath_raw = outDir + "_encoded.png"
# input_image.file_format = "PNG"
# bpy.context.scene.render.image_settings.quality = quality
# input_image.save_render(filepath = input_image.filepath_raw, scene = bpy.context.scene)
#input_image.
#input_image.save()
def saturate(num, floats=True):
if num < 0:
num = 0
elif num > (1 if floats else 255):
num = (1 if floats else 255)
return num
def encodeImageRGBD(image, maxRange, outDir, quality):
input_image = bpy.data.images[image.name]
image_name = input_image.name
if input_image.colorspace_settings.name != 'Linear':
input_image.colorspace_settings.name = 'Linear'
# Removing .exr or .hdr prefix
if image_name[-4:] == '.exr' or image_name[-4:] == '.hdr':
image_name = image_name[:-4]
target_image = bpy.data.images.get(image_name + '_encoded')
if not target_image:
target_image = bpy.data.images.new(
name = image_name + '_encoded',
width = input_image.size[0],
height = input_image.size[1],
alpha = True,
float_buffer = False
)
num_pixels = len(input_image.pixels)
result_pixel = list(input_image.pixels)
for i in range(0,num_pixels,4):
m = utility.saturate(max(result_pixel[i], result_pixel[i+1], result_pixel[i+2], 1e-6))
d = max(maxRange / m, 1)
d = utility.saturate(math.floor(d) / 255 )
result_pixel[i] = result_pixel[i] * d * 255 / maxRange
result_pixel[i+1] = result_pixel[i+1] * d * 255 / maxRange
result_pixel[i+2] = result_pixel[i+2] * d * 255 / maxRange
result_pixel[i+3] = d
target_image.pixels = result_pixel
input_image = target_image
#Save RGBD
input_image.filepath_raw = outDir + "_encoded.png"
input_image.file_format = "PNG"
bpy.context.scene.render.image_settings.quality = quality
input_image.save_render(filepath = input_image.filepath_raw, scene = bpy.context.scene)

View file

@ -0,0 +1,49 @@
import bpy, os, importlib
from os import listdir
from os.path import isfile, join
class TLM_NP_Filtering:
image_output_destination = ""
def init(lightmap_dir, denoise):
scene = bpy.context.scene
print("Beginning filtering for files: ")
if denoise:
file_ending = "_denoised.hdr"
else:
file_ending = "_baked.hdr"
dirfiles = [f for f in listdir(lightmap_dir) if isfile(join(lightmap_dir, f))]
for file in dirfiles:
if denoise:
file_ending = "_denoised.hdr"
file_split = 13
else:
file_ending = "_baked.hdr"
file_split = 10
if file.endswith(file_ending):
file_input = os.path.join(lightmap_dir, file)
os.chdir(lightmap_dir)
#opencv_process_image = cv2.imread(file_input, -1)
print("Filtering: " + file_input)
print(os.path.join(lightmap_dir, file))
if scene.TLM_SceneProperties.tlm_numpy_filtering_mode == "3x3 blur":
pass
#filter_file_output = os.path.join(lightmap_dir, file[:-file_split] + "_filtered.hdr")
#cv2.imwrite(filter_file_output, opencv_bl_result)
print("Written to: " + filter_file_output)

View file

@ -0,0 +1,160 @@
import bpy, os, importlib
from os import listdir
from os.path import isfile, join
class TLM_CV_Filtering:
image_output_destination = ""
def init(lightmap_dir, denoise):
scene = bpy.context.scene
print("Beginning filtering for files: ")
if denoise:
file_ending = "_denoised.hdr"
else:
file_ending = "_baked.hdr"
dirfiles = [f for f in listdir(lightmap_dir) if isfile(join(lightmap_dir, f))]
cv2 = importlib.util.find_spec("cv2")
if cv2 is None:
print("CV2 not found - Ignoring filtering")
return 0
else:
cv2 = importlib.__import__("cv2")
for file in dirfiles:
if denoise:
file_ending = "_denoised.hdr"
file_split = 13
else:
file_ending = "_baked.hdr"
file_split = 10
if file.endswith(file_ending):
file_input = os.path.join(lightmap_dir, file)
os.chdir(lightmap_dir)
opencv_process_image = cv2.imread(file_input, -1)
print("Filtering: " + os.path.basename(file_input))
obj_name = os.path.basename(file_input).split("_")[0]
if bpy.data.objects[obj_name].TLM_ObjectProperties.tlm_mesh_filter_override:
print("OVERRIDE!")
print(os.path.join(lightmap_dir, file))
objectProperties = bpy.data.objects[obj_name].TLM_ObjectProperties
#TODO OVERRIDE FILTERING OPTION! REWRITE
if objectProperties.tlm_mesh_filtering_mode == "Box":
if objectProperties.tlm_mesh_filtering_box_strength % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_box_strength + 1, objectProperties.tlm_mesh_filtering_box_strength + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_box_strength, objectProperties.tlm_mesh_filtering_box_strength)
opencv_bl_result = cv2.blur(opencv_process_image, kernel_size)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.blur(opencv_bl_result, kernel_size)
elif objectProperties.tlm_mesh_filtering_mode == "Gaussian":
if objectProperties.tlm_mesh_filtering_gaussian_strength % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_gaussian_strength + 1, objectProperties.tlm_mesh_filtering_gaussian_strength + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_gaussian_strength, objectProperties.tlm_mesh_filtering_gaussian_strength)
sigma_size = 0
opencv_bl_result = cv2.GaussianBlur(opencv_process_image, kernel_size, sigma_size)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.GaussianBlur(opencv_bl_result, kernel_size, sigma_size)
elif objectProperties.tlm_mesh_filtering_mode == "Bilateral":
diameter_size = objectProperties.tlm_mesh_filtering_bilateral_diameter
sigma_color = objectProperties.tlm_mesh_filtering_bilateral_color_deviation
sigma_space = objectProperties.tlm_mesh_filtering_bilateral_coordinate_deviation
opencv_bl_result = cv2.bilateralFilter(opencv_process_image, diameter_size, sigma_color, sigma_space)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.bilateralFilter(opencv_bl_result, diameter_size, sigma_color, sigma_space)
else:
if objectProperties.tlm_mesh_filtering_median_kernel % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_median_kernel + 1, objectProperties.tlm_mesh_filtering_median_kernel + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_median_kernel, objectProperties.tlm_mesh_filtering_median_kernel)
opencv_bl_result = cv2.medianBlur(opencv_process_image, kernel_size[0])
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.medianBlur(opencv_bl_result, kernel_size[0])
filter_file_output = os.path.join(lightmap_dir, file[:-file_split] + "_filtered.hdr")
cv2.imwrite(filter_file_output, opencv_bl_result)
print("Written to: " + filter_file_output)
else:
print(os.path.join(lightmap_dir, file))
#TODO OVERRIDE FILTERING OPTION!
if scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
if scene.TLM_SceneProperties.tlm_filtering_box_strength % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_box_strength + 1,scene.TLM_SceneProperties.tlm_filtering_box_strength + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_box_strength,scene.TLM_SceneProperties.tlm_filtering_box_strength)
opencv_bl_result = cv2.blur(opencv_process_image, kernel_size)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.blur(opencv_bl_result, kernel_size)
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
if scene.TLM_SceneProperties.tlm_filtering_gaussian_strength % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_gaussian_strength + 1,scene.TLM_SceneProperties.tlm_filtering_gaussian_strength + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_gaussian_strength,scene.TLM_SceneProperties.tlm_filtering_gaussian_strength)
sigma_size = 0
opencv_bl_result = cv2.GaussianBlur(opencv_process_image, kernel_size, sigma_size)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.GaussianBlur(opencv_bl_result, kernel_size, sigma_size)
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
diameter_size = scene.TLM_SceneProperties.tlm_filtering_bilateral_diameter
sigma_color = scene.TLM_SceneProperties.tlm_filtering_bilateral_color_deviation
sigma_space = scene.TLM_SceneProperties.tlm_filtering_bilateral_coordinate_deviation
opencv_bl_result = cv2.bilateralFilter(opencv_process_image, diameter_size, sigma_color, sigma_space)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.bilateralFilter(opencv_bl_result, diameter_size, sigma_color, sigma_space)
else:
if scene.TLM_SceneProperties.tlm_filtering_median_kernel % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_median_kernel + 1 , scene.TLM_SceneProperties.tlm_filtering_median_kernel + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_median_kernel, scene.TLM_SceneProperties.tlm_filtering_median_kernel)
opencv_bl_result = cv2.medianBlur(opencv_process_image, kernel_size[0])
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.medianBlur(opencv_bl_result, kernel_size[0])
filter_file_output = os.path.join(lightmap_dir, file[:-file_split] + "_filtered.hdr")
cv2.imwrite(filter_file_output, opencv_bl_result)
print("Written to: " + filter_file_output)
# if file.endswith(file_ending):
# print()
# baked_image_array.append(file)

View file

@ -0,0 +1,160 @@
import bpy, os, importlib
from os import listdir
from os.path import isfile, join
class TLM_Shader_Filtering:
image_output_destination = ""
def init(lightmap_dir, denoise):
scene = bpy.context.scene
print("Beginning filtering for files: ")
if denoise:
file_ending = "_denoised.hdr"
else:
file_ending = "_baked.hdr"
dirfiles = [f for f in listdir(lightmap_dir) if isfile(join(lightmap_dir, f))]
cv2 = importlib.util.find_spec("cv2")
if cv2 is None:
print("CV2 not found - Ignoring filtering")
return 0
else:
cv2 = importlib.__import__("cv2")
for file in dirfiles:
if denoise:
file_ending = "_denoised.hdr"
file_split = 13
else:
file_ending = "_baked.hdr"
file_split = 10
if file.endswith(file_ending):
file_input = os.path.join(lightmap_dir, file)
os.chdir(lightmap_dir)
opencv_process_image = cv2.imread(file_input, -1)
print("Filtering: " + os.path.basename(file_input))
obj_name = os.path.basename(file_input).split("_")[0]
if bpy.data.objects[obj_name].TLM_ObjectProperties.tlm_mesh_filter_override:
print("OVERRIDE!")
print(os.path.join(lightmap_dir, file))
objectProperties = bpy.data.objects[obj_name].TLM_ObjectProperties
#TODO OVERRIDE FILTERING OPTION! REWRITE
if objectProperties.tlm_mesh_filtering_mode == "Box":
if objectProperties.tlm_mesh_filtering_box_strength % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_box_strength + 1, objectProperties.tlm_mesh_filtering_box_strength + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_box_strength, objectProperties.tlm_mesh_filtering_box_strength)
opencv_bl_result = cv2.blur(opencv_process_image, kernel_size)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.blur(opencv_bl_result, kernel_size)
elif objectProperties.tlm_mesh_filtering_mode == "Gaussian":
if objectProperties.tlm_mesh_filtering_gaussian_strength % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_gaussian_strength + 1, objectProperties.tlm_mesh_filtering_gaussian_strength + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_gaussian_strength, objectProperties.tlm_mesh_filtering_gaussian_strength)
sigma_size = 0
opencv_bl_result = cv2.GaussianBlur(opencv_process_image, kernel_size, sigma_size)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.GaussianBlur(opencv_bl_result, kernel_size, sigma_size)
elif objectProperties.tlm_mesh_filtering_mode == "Bilateral":
diameter_size = objectProperties.tlm_mesh_filtering_bilateral_diameter
sigma_color = objectProperties.tlm_mesh_filtering_bilateral_color_deviation
sigma_space = objectProperties.tlm_mesh_filtering_bilateral_coordinate_deviation
opencv_bl_result = cv2.bilateralFilter(opencv_process_image, diameter_size, sigma_color, sigma_space)
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.bilateralFilter(opencv_bl_result, diameter_size, sigma_color, sigma_space)
else:
if objectProperties.tlm_mesh_filtering_median_kernel % 2 == 0:
kernel_size = (objectProperties.tlm_mesh_filtering_median_kernel + 1, objectProperties.tlm_mesh_filtering_median_kernel + 1)
else:
kernel_size = (objectProperties.tlm_mesh_filtering_median_kernel, objectProperties.tlm_mesh_filtering_median_kernel)
opencv_bl_result = cv2.medianBlur(opencv_process_image, kernel_size[0])
if objectProperties.tlm_mesh_filtering_iterations > 1:
for x in range(objectProperties.tlm_mesh_filtering_iterations):
opencv_bl_result = cv2.medianBlur(opencv_bl_result, kernel_size[0])
filter_file_output = os.path.join(lightmap_dir, file[:-file_split] + "_filtered.hdr")
cv2.imwrite(filter_file_output, opencv_bl_result)
print("Written to: " + filter_file_output)
else:
print(os.path.join(lightmap_dir, file))
#TODO OVERRIDE FILTERING OPTION!
if scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
if scene.TLM_SceneProperties.tlm_filtering_box_strength % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_box_strength + 1,scene.TLM_SceneProperties.tlm_filtering_box_strength + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_box_strength,scene.TLM_SceneProperties.tlm_filtering_box_strength)
opencv_bl_result = cv2.blur(opencv_process_image, kernel_size)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.blur(opencv_bl_result, kernel_size)
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
if scene.TLM_SceneProperties.tlm_filtering_gaussian_strength % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_gaussian_strength + 1,scene.TLM_SceneProperties.tlm_filtering_gaussian_strength + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_gaussian_strength,scene.TLM_SceneProperties.tlm_filtering_gaussian_strength)
sigma_size = 0
opencv_bl_result = cv2.GaussianBlur(opencv_process_image, kernel_size, sigma_size)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.GaussianBlur(opencv_bl_result, kernel_size, sigma_size)
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
diameter_size = scene.TLM_SceneProperties.tlm_filtering_bilateral_diameter
sigma_color = scene.TLM_SceneProperties.tlm_filtering_bilateral_color_deviation
sigma_space = scene.TLM_SceneProperties.tlm_filtering_bilateral_coordinate_deviation
opencv_bl_result = cv2.bilateralFilter(opencv_process_image, diameter_size, sigma_color, sigma_space)
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.bilateralFilter(opencv_bl_result, diameter_size, sigma_color, sigma_space)
else:
if scene.TLM_SceneProperties.tlm_filtering_median_kernel % 2 == 0:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_median_kernel + 1 , scene.TLM_SceneProperties.tlm_filtering_median_kernel + 1)
else:
kernel_size = (scene.TLM_SceneProperties.tlm_filtering_median_kernel, scene.TLM_SceneProperties.tlm_filtering_median_kernel)
opencv_bl_result = cv2.medianBlur(opencv_process_image, kernel_size[0])
if scene.TLM_SceneProperties.tlm_filtering_iterations > 1:
for x in range(scene.TLM_SceneProperties.tlm_filtering_iterations):
opencv_bl_result = cv2.medianBlur(opencv_bl_result, kernel_size[0])
filter_file_output = os.path.join(lightmap_dir, file[:-file_split] + "_filtered.hdr")
cv2.imwrite(filter_file_output, opencv_bl_result)
print("Written to: " + filter_file_output)
# if file.endswith(file_ending):
# print()
# baked_image_array.append(file)

View file

@ -0,0 +1,31 @@
import os
import bpy
from bpy.utils import previews
icons = None
directory = os.path.abspath(os.path.join(__file__, '..', '..', '..', 'icons'))
def id(identifier):
return image(identifier).icon_id
def image(identifier):
def icon(identifier):
if identifier in icons:
return icons[identifier]
return icons.load(identifier, os.path.join(directory, identifier + '.png'), 'IMAGE')
if icons:
return icon(identifier)
else:
create()
return icon(identifier)
def create():
global icons
icons = previews.new()
def remove():
previews.remove(icons)

View file

@ -0,0 +1,5 @@
import bpy, os, re, sys
def prepare(obj):
print("Preparing: " + obj.name)
pass

View file

@ -0,0 +1,620 @@
import bpy.ops as O
import bpy, os, re, sys, importlib, struct, platform, subprocess, threading, string, bmesh
from io import StringIO
from threading import Thread
from queue import Queue, Empty
from dataclasses import dataclass
from dataclasses import field
from typing import List
###########################################################
###########################################################
# This set of utility functions are courtesy of LorenzWieseke
#
# Modified by Naxela
#
# https://github.com/Naxela/The_Lightmapper/tree/Lightmap-to-GLB
###########################################################
class Node_Types:
output_node = 'OUTPUT_MATERIAL'
ao_node = 'AMBIENT_OCCLUSION'
image_texture = 'TEX_IMAGE'
pbr_node = 'BSDF_PRINCIPLED'
diffuse = 'BSDF_DIFFUSE'
mapping = 'MAPPING'
normal_map = 'NORMAL_MAP'
bump_map = 'BUMP'
attr_node = 'ATTRIBUTE'
class Shader_Node_Types:
emission = "ShaderNodeEmission"
image_texture = "ShaderNodeTexImage"
mapping = "ShaderNodeMapping"
normal = "ShaderNodeNormalMap"
ao = "ShaderNodeAmbientOcclusion"
uv = "ShaderNodeUVMap"
mix = "ShaderNodeMixRGB"
def select_object(self,obj):
C = bpy.context
try:
O.object.select_all(action='DESELECT')
C.view_layer.objects.active = obj
obj.select_set(True)
except:
self.report({'INFO'},"Object not in View Layer")
def select_obj_by_mat(self,mat):
D = bpy.data
for obj in D.objects:
if obj.type == "MESH":
object_materials = [
slot.material for slot in obj.material_slots]
if mat in object_materials:
select_object(self,obj)
def save_image(image):
filePath = bpy.data.filepath
path = os.path.dirname(filePath)
try:
os.mkdir(path + "/tex")
except FileExistsError:
pass
try:
os.mkdir(path + "/tex/" + str(image.size[0]))
except FileExistsError:
pass
if image.file_format == "JPEG" :
file_ending = ".jpg"
elif image.file_format == "PNG" :
file_ending = ".png"
savepath = path + "/tex/" + \
str(image.size[0]) + "/" + image.name + file_ending
image.filepath_raw = savepath
# if "Normal" in image.name:
# bpy.context.scene.render.image_settings.quality = 90
# image.save_render( filepath = image.filepath_raw, scene = bpy.context.scene )
# else:
image.save()
def get_file_size(filepath):
size = "Unpack Files"
try:
path = bpy.path.abspath(filepath)
size = os.path.getsize(path)
size /= 1024
except:
print("error getting file path for " + filepath)
return (size)
def scale_image(image, newSize):
if (image.org_filepath != ''):
image.filepath = image.org_filepath
image.org_filepath = image.filepath
image.scale(newSize[0], newSize[1])
save_image(image)
def check_only_one_pbr(self,material):
check_ok = True
# get pbr shader
nodes = material.node_tree.nodes
pbr_node_type = Node_Types.pbr_node
pbr_nodes = find_node_by_type(nodes,pbr_node_type)
# check only one pbr node
if len(pbr_nodes) == 0:
self.report({'INFO'}, 'No PBR Shader Found')
check_ok = False
if len(pbr_nodes) > 1:
self.report({'INFO'}, 'More than one PBR Node found ! Clean before Baking.')
check_ok = False
return check_ok
# is material already the baked one
def check_is_org_material(self,material):
check_ok = True
if "_Bake" in material.name:
self.report({'INFO'}, 'Change back to org. Material')
check_ok = False
return check_ok
def clean_empty_materials(self):
for obj in bpy.data.objects:
for slot in obj.material_slots:
mat = slot.material
if mat is None:
print("Removed Empty Materials from " + obj.name)
bpy.ops.object.select_all(action='DESELECT')
obj.select_set(True)
bpy.ops.object.material_slot_remove()
def get_pbr_inputs(pbr_node):
base_color_input = pbr_node.inputs["Base Color"]
metallic_input = pbr_node.inputs["Metallic"]
specular_input = pbr_node.inputs["Specular"]
roughness_input = pbr_node.inputs["Roughness"]
normal_input = pbr_node.inputs["Normal"]
pbr_inputs = {"base_color_input":base_color_input, "metallic_input":metallic_input,"specular_input":specular_input,"roughness_input":roughness_input,"normal_input":normal_input}
return pbr_inputs
def find_node_by_type(nodes, node_type):
nodes_found = [n for n in nodes if n.type == node_type]
return nodes_found
def find_node_by_type_recusivly(material, note_to_start, node_type, del_nodes_inbetween=False):
nodes = material.node_tree.nodes
if note_to_start.type == node_type:
return note_to_start
for input in note_to_start.inputs:
for link in input.links:
current_node = link.from_node
if (del_nodes_inbetween and note_to_start.type != Node_Types.normal_map and note_to_start.type != Node_Types.bump_map):
nodes.remove(note_to_start)
return find_node_by_type_recusivly(material, current_node, node_type, del_nodes_inbetween)
def find_node_by_name_recusivly(node, idname):
if node.bl_idname == idname:
return node
for input in node.inputs:
for link in input.links:
current_node = link.from_node
return find_node_by_name_recusivly(current_node, idname)
def make_link(material, socket1, socket2):
links = material.node_tree.links
links.new(socket1, socket2)
def add_gamma_node(material, pbrInput):
nodeToPrincipledOutput = pbrInput.links[0].from_socket
gammaNode = material.node_tree.nodes.new("ShaderNodeGamma")
gammaNode.inputs[1].default_value = 2.2
gammaNode.name = "Gamma Bake"
# link in gamma
make_link(material, nodeToPrincipledOutput, gammaNode.inputs["Color"])
make_link(material, gammaNode.outputs["Color"], pbrInput)
def remove_gamma_node(material, pbrInput):
nodes = material.node_tree.nodes
gammaNode = nodes.get("Gamma Bake")
nodeToPrincipledOutput = gammaNode.inputs[0].links[0].from_socket
make_link(material, nodeToPrincipledOutput, pbrInput)
material.node_tree.nodes.remove(gammaNode)
def apply_ao_toggle(self,context):
all_materials = bpy.data.materials
ao_toggle = context.scene.toggle_ao
for mat in all_materials:
nodes = mat.node_tree.nodes
ao_node = nodes.get("AO Bake")
if ao_node is not None:
if ao_toggle:
emission_setup(mat,ao_node.outputs["Color"])
else:
pbr_node = find_node_by_type(nodes,Node_Types.pbr_node)[0]
remove_node(mat,"Emission Bake")
reconnect_PBR(mat, pbr_node)
def emission_setup(material, node_output):
nodes = material.node_tree.nodes
emission_node = add_node(material,Shader_Node_Types.emission,"Emission Bake")
# link emission to whatever goes into current pbrInput
emission_input = emission_node.inputs[0]
make_link(material, node_output, emission_input)
# link emission to materialOutput
surface_input = nodes.get("Material Output").inputs[0]
emission_output = emission_node.outputs[0]
make_link(material, emission_output, surface_input)
def link_pbr_to_output(material,pbr_node):
nodes = material.node_tree.nodes
surface_input = nodes.get("Material Output").inputs[0]
make_link(material,pbr_node.outputs[0],surface_input)
def reconnect_PBR(material, pbrNode):
nodes = material.node_tree.nodes
pbr_output = pbrNode.outputs[0]
surface_input = nodes.get("Material Output").inputs[0]
make_link(material, pbr_output, surface_input)
def mute_all_texture_mappings(material, do_mute):
nodes = material.node_tree.nodes
for node in nodes:
if node.bl_idname == "ShaderNodeMapping":
node.mute = do_mute
def add_node(material,shader_node_type,node_name):
nodes = material.node_tree.nodes
new_node = nodes.get(node_name)
if new_node is None:
new_node = nodes.new(shader_node_type)
new_node.name = node_name
new_node.label = node_name
return new_node
def remove_node(material,node_name):
nodes = material.node_tree.nodes
node = nodes.get(node_name)
if node is not None:
nodes.remove(node)
def lightmap_to_ao(material,lightmap_node):
nodes = material.node_tree.nodes
# -----------------------AO SETUP--------------------#
# create group data
gltf_settings = bpy.data.node_groups.get('glTF Settings')
if gltf_settings is None:
bpy.data.node_groups.new('glTF Settings', 'ShaderNodeTree')
# add group to node tree
ao_group = nodes.get('glTF Settings')
if ao_group is None:
ao_group = nodes.new('ShaderNodeGroup')
ao_group.name = 'glTF Settings'
ao_group.node_tree = bpy.data.node_groups['glTF Settings']
# create group inputs
if ao_group.inputs.get('Occlusion') is None:
ao_group.inputs.new('NodeSocketFloat','Occlusion')
# mulitply to control strength
mix_node = add_node(material,Shader_Node_Types.mix,"Adjust Lightmap")
mix_node.blend_type = "MULTIPLY"
mix_node.inputs["Fac"].default_value = 1
mix_node.inputs["Color2"].default_value = [3,3,3,1]
# position node
ao_group.location = (lightmap_node.location[0]+600,lightmap_node.location[1])
mix_node.location = (lightmap_node.location[0]+300,lightmap_node.location[1])
make_link(material,lightmap_node.outputs['Color'],mix_node.inputs['Color1'])
make_link(material,mix_node.outputs['Color'],ao_group.inputs['Occlusion'])
###########################################################
###########################################################
# This utility function is modified from blender_xatlas
# and calls the object without any explicit object context
# thus allowing blender_xatlas to pack from background.
###########################################################
# Code is courtesy of mattedicksoncom
# Modified by Naxela
#
# https://github.com/mattedicksoncom/blender-xatlas/
###########################################################
def Unwrap_Lightmap_Group_Xatlas_2_headless_call(obj):
blender_xatlas = importlib.util.find_spec("blender_xatlas")
if blender_xatlas is not None:
import blender_xatlas
else:
return 0
packOptions = bpy.context.scene.pack_tool
chartOptions = bpy.context.scene.chart_tool
sharedProperties = bpy.context.scene.shared_properties
context = bpy.context
if obj.type == 'MESH':
context.view_layer.objects.active = obj
if obj.data.users > 1:
obj.data = obj.data.copy() #make single user copy
uv_layers = obj.data.uv_layers
#setup the lightmap uvs
uvName = "UVMap_Lightmap"
if sharedProperties.lightmapUVChoiceType == "NAME":
uvName = sharedProperties.lightmapUVName
elif sharedProperties.lightmapUVChoiceType == "INDEX":
if sharedProperties.lightmapUVIndex < len(uv_layers):
uvName = uv_layers[sharedProperties.lightmapUVIndex].name
if not uvName in uv_layers:
uvmap = uv_layers.new(name=uvName)
uv_layers.active_index = len(uv_layers) - 1
else:
for i in range(0, len(uv_layers)):
if uv_layers[i].name == uvName:
uv_layers.active_index = i
obj.select_set(True)
#save all the current edges
if sharedProperties.packOnly:
edgeDict = dict()
for obj in selected_objects:
if obj.type == 'MESH':
tempEdgeDict = dict()
tempEdgeDict['object'] = obj.name
tempEdgeDict['edges'] = []
print(len(obj.data.edges))
for i in range(0,len(obj.data.edges)):
setEdge = obj.data.edges[i]
tempEdgeDict['edges'].append(i)
edgeDict[obj.name] = tempEdgeDict
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='FIXED', ngon_method='BEAUTY')
else:
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.quads_convert_to_tris(quad_method='FIXED', ngon_method='BEAUTY')
bpy.ops.object.mode_set(mode='OBJECT')
fakeFile = StringIO()
blender_xatlas.export_obj_simple.save(
context=bpy.context,
filepath=fakeFile,
mainUVChoiceType=sharedProperties.mainUVChoiceType,
uvIndex=sharedProperties.mainUVIndex,
uvName=sharedProperties.mainUVName,
use_selection=True,
use_animation=False,
use_mesh_modifiers=True,
use_edges=True,
use_smooth_groups=False,
use_smooth_groups_bitflags=False,
use_normals=True,
use_uvs=True,
use_materials=False,
use_triangles=False,
use_nurbs=False,
use_vertex_groups=False,
use_blen_objects=True,
group_by_object=False,
group_by_material=False,
keep_vertex_order=False,
)
file_path = os.path.dirname(os.path.abspath(blender_xatlas.__file__))
if platform.system() == "Windows":
xatlas_path = os.path.join(file_path, "xatlas", "xatlas-blender.exe")
elif platform.system() == "Linux":
xatlas_path = os.path.join(file_path, "xatlas", "xatlas-blender")
#need to set permissions for the process on linux
subprocess.Popen(
'chmod u+x "' + xatlas_path + '"',
shell=True
)
#setup the arguments to be passed to xatlas-------------------
arguments_string = ""
for argumentKey in packOptions.__annotations__.keys():
key_string = str(argumentKey)
if argumentKey is not None:
print(getattr(packOptions,key_string))
attrib = getattr(packOptions,key_string)
if type(attrib) == bool:
if attrib == True:
arguments_string = arguments_string + " -" + str(argumentKey)
else:
arguments_string = arguments_string + " -" + str(argumentKey) + " " + str(attrib)
for argumentKey in chartOptions.__annotations__.keys():
if argumentKey is not None:
key_string = str(argumentKey)
print(getattr(chartOptions,key_string))
attrib = getattr(chartOptions,key_string)
if type(attrib) == bool:
if attrib == True:
arguments_string = arguments_string + " -" + str(argumentKey)
else:
arguments_string = arguments_string + " -" + str(argumentKey) + " " + str(attrib)
#add pack only option
if sharedProperties.packOnly:
arguments_string = arguments_string + " -packOnly"
arguments_string = arguments_string + " -atlasLayout" + " " + sharedProperties.atlasLayout
print(arguments_string)
#END setup the arguments to be passed to xatlas-------------------
#RUN xatlas process
xatlas_process = subprocess.Popen(
r'"{}"'.format(xatlas_path) + ' ' + arguments_string,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
shell=True
)
#shove the fake file in stdin
stdin = xatlas_process.stdin
value = bytes(fakeFile.getvalue() + "\n", 'UTF-8') #The \n is needed to end the input properly
stdin.write(value)
stdin.flush()
#Get the output from xatlas
outObj = ""
while True:
output = xatlas_process.stdout.readline()
if not output:
break
outObj = outObj + (output.decode().strip() + "\n")
#the objects after xatlas processing
# print(outObj)
#Setup for reading the output
@dataclass
class uvObject:
obName: string = ""
uvArray: List[float] = field(default_factory=list)
faceArray: List[int] = field(default_factory=list)
convertedObjects = []
uvArrayComplete = []
#search through the out put for STARTOBJ
#then start reading the objects
obTest = None
startRead = False
for line in outObj.splitlines():
line_split = line.split()
if not line_split:
continue
line_start = line_split[0] # we compare with this a _lot_
# print(line_start)
if line_start == "STARTOBJ":
print("Start reading the objects----------------------------------------")
startRead = True
# obTest = uvObject()
if startRead:
#if it's a new obj
if line_start == 'o':
#if there is already an object append it
if obTest is not None:
convertedObjects.append(obTest)
obTest = uvObject() #create new uv object
obTest.obName = line_split[1]
if obTest is not None:
#the uv coords
if line_start == 'vt':
newUv = [float(line_split[1]),float(line_split[2])]
obTest.uvArray.append(newUv)
uvArrayComplete.append(newUv)
#the face coords index
#faces are 1 indexed
if line_start == 'f':
#vert/uv/normal
#only need the uvs
newFace = [
int(line_split[1].split("/")[1]),
int(line_split[2].split("/")[1]),
int(line_split[3].split("/")[1])
]
obTest.faceArray.append(newFace)
#append the final object
convertedObjects.append(obTest)
# print(convertedObjects)
#apply the output-------------------------------------------------------------
#copy the uvs to the original objects
# objIndex = 0
print("Applying the UVs----------------------------------------")
# print(convertedObjects)
for importObject in convertedObjects:
bpy.ops.object.select_all(action='DESELECT')
obTest = importObject
bpy.context.scene.objects[obTest.obName].select_set(True)
context.view_layer.objects.active = bpy.context.scene.objects[obTest.obName]
bpy.ops.object.mode_set(mode = 'OBJECT')
obj = bpy.context.active_object
me = obj.data
#convert to bmesh to create the new uvs
bm = bmesh.new()
bm.from_mesh(me)
uv_layer = bm.loops.layers.uv.verify()
nFaces = len(bm.faces)
#need to ensure lookup table for some reason?
if hasattr(bm.faces, "ensure_lookup_table"):
bm.faces.ensure_lookup_table()
#loop through the faces
for faceIndex in range(nFaces):
faceGroup = obTest.faceArray[faceIndex]
bm.faces[faceIndex].loops[0][uv_layer].uv = (
uvArrayComplete[faceGroup[0] - 1][0],
uvArrayComplete[faceGroup[0] - 1][1])
bm.faces[faceIndex].loops[1][uv_layer].uv = (
uvArrayComplete[faceGroup[1] - 1][0],
uvArrayComplete[faceGroup[1] - 1][1])
bm.faces[faceIndex].loops[2][uv_layer].uv = (
uvArrayComplete[faceGroup[2] - 1][0],
uvArrayComplete[faceGroup[2] - 1][1])
# objIndex = objIndex + 3
# print(objIndex)
#assign the mesh back to the original mesh
bm.to_mesh(me)
#END apply the output-------------------------------------------------------------
#Start setting the quads back again-------------------------------------------------------------
if sharedProperties.packOnly:
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
bpy.ops.object.mode_set(mode='OBJECT')
for edges in edgeDict:
edgeList = edgeDict[edges]
currentObject = bpy.context.scene.objects[edgeList['object']]
bm = bmesh.new()
bm.from_mesh(currentObject.data)
if hasattr(bm.edges, "ensure_lookup_table"):
bm.edges.ensure_lookup_table()
#assume that all the triangulated edges come after the original edges
newEdges = []
for edge in range(len(edgeList['edges']), len(bm.edges)):
newEdge = bm.edges[edge]
newEdge.select = True
newEdges.append(newEdge)
bmesh.ops.dissolve_edges(bm, edges=newEdges, use_verts=False, use_face_split=False)
bpy.ops.object.mode_set(mode='OBJECT')
bm.to_mesh(currentObject.data)
bm.free()
bpy.ops.object.mode_set(mode='EDIT')
#End setting the quads back again------------------------------------------------------------
print("Finished Xatlas----------------------------------------")

View file

@ -12,6 +12,9 @@ import arm.props_properties
import arm.proxy
import arm.utils
from arm.lightmapper.utility import icon
from arm.lightmapper.properties.denoiser import oidn, optix
# Menu in object region
class ARM_PT_ObjectPropsPanel(bpy.types.Panel):
bl_label = "Armory Props"
@ -1030,38 +1033,274 @@ class ARM_PT_BakePanel(bpy.types.Panel):
scn = bpy.data.scenes[context.scene.name]
row = layout.row(align=True)
row.alignment = 'EXPAND'
row.operator("arm.bake_textures", icon="RENDER_STILL")
row.operator("arm.bake_apply")
row.prop(scn, "arm_bakemode", expand=True)
col = layout.column()
col.prop(scn, 'arm_bakelist_scale')
col.prop(scn.cycles, "samples")
if scn.arm_bakemode == "Static Map":
layout.prop(scn, 'arm_bakelist_unwrap')
row = layout.row(align=True)
row.alignment = 'EXPAND'
row.operator("arm.bake_textures", icon="RENDER_STILL")
row.operator("arm.bake_apply")
rows = 2
if len(scn.arm_bakelist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_BakeList", "The_List", scn, "arm_bakelist", scn, "arm_bakelist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_bakelist.new_item", icon='ADD', text="")
col.operator("arm_bakelist.delete_item", icon='REMOVE', text="")
col.menu("ARM_MT_BakeListSpecials", icon='DOWNARROW_HLT', text="")
col = layout.column()
col.prop(scn, 'arm_bakelist_scale')
col.prop(scn.cycles, "samples")
if len(scn.arm_bakelist) > 1:
col.separator()
op = col.operator("arm_bakelist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_bakelist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
layout.prop(scn, 'arm_bakelist_unwrap')
if scn.arm_bakelist_index >= 0 and len(scn.arm_bakelist) > 0:
item = scn.arm_bakelist[scn.arm_bakelist_index]
layout.prop_search(item, "obj", bpy.data, "objects", text="Object")
layout.prop(item, "res_x")
layout.prop(item, "res_y")
rows = 2
if len(scn.arm_bakelist) > 1:
rows = 4
row = layout.row()
row.template_list("ARM_UL_BakeList", "The_List", scn, "arm_bakelist", scn, "arm_bakelist_index", rows=rows)
col = row.column(align=True)
col.operator("arm_bakelist.new_item", icon='ADD', text="")
col.operator("arm_bakelist.delete_item", icon='REMOVE', text="")
col.menu("ARM_MT_BakeListSpecials", icon='DOWNARROW_HLT', text="")
if len(scn.arm_bakelist) > 1:
col.separator()
op = col.operator("arm_bakelist.move_item", icon='TRIA_UP', text="")
op.direction = 'UP'
op = col.operator("arm_bakelist.move_item", icon='TRIA_DOWN', text="")
op.direction = 'DOWN'
if scn.arm_bakelist_index >= 0 and len(scn.arm_bakelist) > 0:
item = scn.arm_bakelist[scn.arm_bakelist_index]
layout.prop_search(item, "obj", bpy.data, "objects", text="Object")
layout.prop(item, "res_x")
layout.prop(item, "res_y")
else:
scene = context.scene
sceneProperties = scene.TLM_SceneProperties
row = layout.row(align=True)
row = layout.row(align=True)
#We list LuxCoreRender as available, by default we assume Cycles exists
row.prop(sceneProperties, "tlm_lightmap_engine")
if sceneProperties.tlm_lightmap_engine == "Cycles":
#CYCLES SETTINGS HERE
engineProperties = scene.TLM_EngineProperties
row = layout.row(align=True)
row.label(text="General Settings")
row = layout.row(align=True)
row.operator("tlm.build_lightmaps")
row = layout.row(align=True)
row.operator("tlm.clean_lightmaps")
row = layout.row(align=True)
row.operator("tlm.explore_lightmaps")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_apply_on_unwrap")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_headless")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_alert_on_finish")
row = layout.row(align=True)
row.label(text="Cycles Settings")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_quality")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_resolution_scale")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_bake_mode")
if scene.TLM_EngineProperties.tlm_bake_mode == "Background":
row = layout.row(align=True)
row.label(text="Warning! Background mode is currently unstable", icon_value=2)
row = layout.row(align=True)
row.prop(engineProperties, "tlm_caching_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_directional_mode")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_lightmap_savedir")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_dilation_margin")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_exposure_multiplier")
row = layout.row(align=True)
row.prop(engineProperties, "tlm_setting_supersample")
elif sceneProperties.tlm_lightmap_engine == "LuxCoreRender":
#LUXCORE SETTINGS HERE
luxcore_available = False
#Look for Luxcorerender in the renderengine classes
for engine in bpy.types.RenderEngine.__subclasses__():
if engine.bl_idname == "LUXCORE":
luxcore_available = True
break
row = layout.row(align=True)
if not luxcore_available:
row.label(text="Please install BlendLuxCore.")
else:
row.label(text="LuxCoreRender not yet available.")
elif sceneProperties.tlm_lightmap_engine == "OctaneRender":
#LUXCORE SETTINGS HERE
octane_available = False
row = layout.row(align=True)
row.label(text="Octane Render not yet available.")
##################
#DENOISE SETTINGS!
row = layout.row(align=True)
row.label(text="Denoise Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_denoise_use")
row = layout.row(align=True)
if sceneProperties.tlm_denoise_use:
row.prop(sceneProperties, "tlm_denoise_engine", expand=True)
row = layout.row(align=True)
if sceneProperties.tlm_denoise_engine == "Integrated":
row.label(text="No options for Integrated.")
elif sceneProperties.tlm_denoise_engine == "OIDN":
denoiseProperties = scene.TLM_OIDNEngineProperties
row.prop(denoiseProperties, "tlm_oidn_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_threads")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_oidn_affinity")
# row = layout.row(align=True)
# row.prop(denoiseProperties, "tlm_denoise_ao")
elif sceneProperties.tlm_denoise_engine == "Optix":
denoiseProperties = scene.TLM_OptixEngineProperties
row.prop(denoiseProperties, "tlm_optix_path")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_verbose")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_optix_maxmem")
row = layout.row(align=True)
row.prop(denoiseProperties, "tlm_denoise_ao")
##################
#FILTERING SETTINGS!
row = layout.row(align=True)
row.label(text="Filtering Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_filtering_use")
row = layout.row(align=True)
if sceneProperties.tlm_filtering_use:
row.prop(sceneProperties, "tlm_filtering_engine", expand=True)
row = layout.row(align=True)
if sceneProperties.tlm_filtering_engine == "OpenCV":
cv2 = importlib.util.find_spec("cv2")
if cv2 is None:
row = layout.row(align=True)
row.label(text="OpenCV is not installed. Install it through preferences.")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_mode")
row = layout.row(align=True)
if scene.TLM_SceneProperties.tlm_filtering_mode == "Gaussian":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_gaussian_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Box":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_box_strength")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
elif scene.TLM_SceneProperties.tlm_filtering_mode == "Bilateral":
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_diameter")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_color_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_bilateral_coordinate_deviation")
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row.prop(scene.TLM_SceneProperties, "tlm_filtering_median_kernel", expand=True)
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_filtering_iterations")
else:
row = layout.row(align=True)
row.prop(scene.TLM_SceneProperties, "tlm_numpy_filtering_mode")
##################
#ENCODING SETTINGS!
row = layout.row(align=True)
row.label(text="Encoding Settings")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_use")
row = layout.row(align=True)
if sceneProperties.tlm_encoding_use:
row.prop(sceneProperties, "tlm_encoding_mode", expand=True)
if sceneProperties.tlm_encoding_mode == "RGBM" or sceneProperties.tlm_encoding_mode == "RGBD":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_encoding_range")
if sceneProperties.tlm_encoding_mode == "LogLuv":
pass
if sceneProperties.tlm_encoding_mode == "HDR":
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_format")
row = layout.row(align=True)
row.label(text="Encoding Settings")
row = layout.row(align=True)
row = layout.row(align=True)
row.operator("tlm.enable_selection")
row = layout.row(align=True)
row.operator("tlm.disable_selection")
row = layout.row(align=True)
row.prop(sceneProperties, "tlm_override_object_settings")
if sceneProperties.tlm_override_object_settings:
row = layout.row(align=True)
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_lightmap_unwrap_mode")
row = layout.row()
if sceneProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroup":
if scene.TLM_AtlasList_index >= 0 and len(scene.TLM_AtlasList) > 0:
row = layout.row()
item = scene.TLM_AtlasList[scene.TLM_AtlasList_index]
row.prop_search(sceneProperties, "tlm_atlas_pointer", scene, "TLM_AtlasList", text='Atlas Group')
else:
row = layout.label(text="Add Atlas Groups from the scene lightmapping settings.")
else:
row.prop(sceneProperties, "tlm_mesh_lightmap_resolution")
row = layout.row()
row.prop(sceneProperties, "tlm_mesh_unwrap_margin")
row = layout.row(align=True)
row.operator("tlm.remove_uv_selection")
row = layout.row(align=True)
class ArmGenLodButton(bpy.types.Operator):
'''Automatically generate LoD levels'''