Merge lightmapper 0.3.1.2
This commit is contained in:
parent
fbe144decc
commit
5d914b3bf5
1
blender/arm/lightmapper/__init__.py
Normal file
1
blender/arm/lightmapper/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
__all__ = ('Operators', 'Panels', 'Properties', 'Preferences', 'Utility', 'Keymap')
|
BIN
blender/arm/lightmapper/assets/sound.ogg
Normal file
BIN
blender/arm/lightmapper/assets/sound.ogg
Normal file
Binary file not shown.
BIN
blender/arm/lightmapper/assets/tlm_data.blend
Normal file
BIN
blender/arm/lightmapper/assets/tlm_data.blend
Normal file
Binary file not shown.
BIN
blender/arm/lightmapper/icons/bake.png
Normal file
BIN
blender/arm/lightmapper/icons/bake.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
blender/arm/lightmapper/icons/clean.png
Normal file
BIN
blender/arm/lightmapper/icons/clean.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3 KiB |
BIN
blender/arm/lightmapper/icons/explore.png
Normal file
BIN
blender/arm/lightmapper/icons/explore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
7
blender/arm/lightmapper/keymap/__init__.py
Normal file
7
blender/arm/lightmapper/keymap/__init__.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from . import keymap
|
||||
|
||||
def register():
|
||||
keymap.register()
|
||||
|
||||
def unregister():
|
||||
keymap.unregister()
|
21
blender/arm/lightmapper/keymap/keymap.py
Normal file
21
blender/arm/lightmapper/keymap/keymap.py
Normal 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[:]
|
21
blender/arm/lightmapper/operators/__init__.py
Normal file
21
blender/arm/lightmapper/operators/__init__.py
Normal 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)
|
67
blender/arm/lightmapper/operators/installopencv.py
Normal file
67
blender/arm/lightmapper/operators/installopencv.py
Normal 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'}
|
156
blender/arm/lightmapper/operators/tlm.py
Normal file
156
blender/arm/lightmapper/operators/tlm.py
Normal 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'}
|
24
blender/arm/lightmapper/panels/__init__.py
Normal file
24
blender/arm/lightmapper/panels/__init__.py
Normal 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)
|
17
blender/arm/lightmapper/panels/light.py
Normal file
17
blender/arm/lightmapper/panels/light.py
Normal 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
|
59
blender/arm/lightmapper/panels/object.py
Normal file
59
blender/arm/lightmapper/panels/object.py
Normal 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")
|
322
blender/arm/lightmapper/panels/scene.py
Normal file
322
blender/arm/lightmapper/panels/scene.py
Normal 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
|
17
blender/arm/lightmapper/panels/world.py
Normal file
17
blender/arm/lightmapper/panels/world.py
Normal 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
|
16
blender/arm/lightmapper/preferences/__init__.py
Normal file
16
blender/arm/lightmapper/preferences/__init__.py
Normal 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)
|
72
blender/arm/lightmapper/preferences/addon_preferences.py
Normal file
72
blender/arm/lightmapper/preferences/addon_preferences.py
Normal 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")
|
33
blender/arm/lightmapper/properties/__init__.py
Normal file
33
blender/arm/lightmapper/properties/__init__.py
Normal 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
|
|
@ -0,0 +1,4 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
class TLM_IntegratedDenoiseEngineProperties(bpy.types.PropertyGroup):
|
39
blender/arm/lightmapper/properties/denoiser/oidn.py
Normal file
39
blender/arm/lightmapper/properties/denoiser/oidn.py
Normal 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")
|
21
blender/arm/lightmapper/properties/denoiser/optix.py
Normal file
21
blender/arm/lightmapper/properties/denoiser/optix.py
Normal 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.")
|
4
blender/arm/lightmapper/properties/filtering.py
Normal file
4
blender/arm/lightmapper/properties/filtering.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import bpy
|
||||
from bpy.props import *
|
||||
|
||||
class TLM_FilteringProperties(bpy.types.PropertyGroup):
|
121
blender/arm/lightmapper/properties/object.py
Normal file
121
blender/arm/lightmapper/properties/object.py
Normal 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)
|
||||
|
87
blender/arm/lightmapper/properties/renderer/cycles.py
Normal file
87
blender/arm/lightmapper/properties/renderer/cycles.py
Normal 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")
|
264
blender/arm/lightmapper/properties/scene.py
Normal file
264
blender/arm/lightmapper/properties/scene.py
Normal 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)
|
0
blender/arm/lightmapper/utility/__init__.py
Normal file
0
blender/arm/lightmapper/utility/__init__.py
Normal file
585
blender/arm/lightmapper/utility/build.py
Normal file
585
blender/arm/lightmapper/utility/build.py
Normal 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
|
0
blender/arm/lightmapper/utility/cycles/ao.py
Normal file
0
blender/arm/lightmapper/utility/cycles/ao.py
Normal file
74
blender/arm/lightmapper/utility/cycles/cache.py
Normal file
74
blender/arm/lightmapper/utility/cycles/cache.py
Normal 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
|
0
blender/arm/lightmapper/utility/cycles/indirect.py
Normal file
0
blender/arm/lightmapper/utility/cycles/indirect.py
Normal file
49
blender/arm/lightmapper/utility/cycles/lightmap.py
Normal file
49
blender/arm/lightmapper/utility/cycles/lightmap.py
Normal 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()
|
182
blender/arm/lightmapper/utility/cycles/nodes.py
Normal file
182
blender/arm/lightmapper/utility/cycles/nodes.py
Normal 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
|
452
blender/arm/lightmapper/utility/cycles/prepare.py
Normal file
452
blender/arm/lightmapper/utility/cycles/prepare.py
Normal 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
|
||||
]
|
79
blender/arm/lightmapper/utility/denoiser/integrated.py
Normal file
79
blender/arm/lightmapper/utility/denoiser/integrated.py
Normal 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)
|
200
blender/arm/lightmapper/utility/denoiser/oidn.py
Normal file
200
blender/arm/lightmapper/utility/denoiser/oidn.py
Normal 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))
|
7
blender/arm/lightmapper/utility/denoiser/optix.py
Normal file
7
blender/arm/lightmapper/utility/denoiser/optix.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
import bpy, os
|
||||
|
||||
class TLM_OIDN_Denoise:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
pass
|
245
blender/arm/lightmapper/utility/encoding.py
Normal file
245
blender/arm/lightmapper/utility/encoding.py
Normal 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)
|
49
blender/arm/lightmapper/utility/filtering/numpy.py
Normal file
49
blender/arm/lightmapper/utility/filtering/numpy.py
Normal 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)
|
160
blender/arm/lightmapper/utility/filtering/opencv.py
Normal file
160
blender/arm/lightmapper/utility/filtering/opencv.py
Normal 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)
|
160
blender/arm/lightmapper/utility/filtering/shader.py
Normal file
160
blender/arm/lightmapper/utility/filtering/shader.py
Normal 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)
|
31
blender/arm/lightmapper/utility/icon.py
Normal file
31
blender/arm/lightmapper/utility/icon.py
Normal 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)
|
|
@ -0,0 +1,5 @@
|
|||
import bpy, os, re, sys
|
||||
|
||||
def prepare(obj):
|
||||
print("Preparing: " + obj.name)
|
||||
pass
|
620
blender/arm/lightmapper/utility/utility.py
Normal file
620
blender/arm/lightmapper/utility/utility.py
Normal 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----------------------------------------")
|
|
@ -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'''
|
||||
|
|
Loading…
Reference in a new issue