Relative imports are intentionally avoided in UI code (`scripts/startup/bl_ui`) because it makes it impossible to run individual files in isolation. Common workflow for making mock-ups and quick UI edits is to load the UI file in Blender's text editor with "Edit Source" operator, make changes, and run the script to see the changes. But in the current version it's impossible because files include relative imports, which don't allow Python to run scripts individually. Pull Request: https://projects.blender.org/blender/blender/pulls/138246
414 lines
12 KiB
Python
414 lines
12 KiB
Python
# SPDX-FileCopyrightText: 2009-2023 Blender Authors
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
from bpy.types import Panel
|
|
from bpy.app.translations import contexts as i18n_contexts
|
|
from bl_ui.space_properties import PropertiesAnimationMixin
|
|
|
|
|
|
class DataButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.engine
|
|
return context.lightprobe and (engine in cls.COMPAT_ENGINES)
|
|
|
|
|
|
class DATA_PT_context_lightprobe(DataButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_options = {'HIDE_HEADER'}
|
|
COMPAT_ENGINES = {
|
|
'BLENDER_RENDER',
|
|
'BLENDER_EEVEE_NEXT'
|
|
}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
ob = context.object
|
|
probe = context.lightprobe
|
|
space = context.space_data
|
|
|
|
if ob:
|
|
layout.template_ID(ob, "data")
|
|
elif probe:
|
|
layout.template_ID(space, "pin_id")
|
|
|
|
|
|
class DATA_PT_lightprobe(DataButtonsPanel, Panel):
|
|
bl_label = "Probe"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
probe = context.lightprobe
|
|
|
|
# layout.prop(probe, "type")
|
|
|
|
if probe.type == 'VOLUME':
|
|
col = layout.column()
|
|
col.prop(probe, "influence_distance", text="Distance")
|
|
col.prop(probe, "falloff")
|
|
col.prop(probe, "intensity")
|
|
|
|
sub = col.column(align=True)
|
|
sub.prop(probe, "resolution_x", text="Resolution X")
|
|
sub.prop(probe, "resolution_y", text="Y")
|
|
sub.prop(probe, "resolution_z", text="Z")
|
|
|
|
elif probe.type == 'PLANE':
|
|
col = layout.column()
|
|
col.prop(probe, "influence_distance", text="Distance")
|
|
col.prop(probe, "falloff")
|
|
else:
|
|
col = layout.column()
|
|
col.prop(probe, "influence_type")
|
|
|
|
if probe.influence_type == 'ELIPSOID':
|
|
col.prop(probe, "influence_distance", text="Radius")
|
|
else:
|
|
col.prop(probe, "influence_distance", text="Size")
|
|
|
|
col.prop(probe, "falloff")
|
|
col.prop(probe, "intensity")
|
|
|
|
sub = col.column(align=True)
|
|
if probe.type == 'PLANE':
|
|
sub.prop(probe, "clip_start", text="Clipping Offset")
|
|
else:
|
|
sub.prop(probe, "clip_start", text="Clipping Start")
|
|
sub.prop(probe, "clip_end", text="End", text_ctxt=i18n_contexts.id_camera)
|
|
|
|
|
|
class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel):
|
|
bl_label = "Probe"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
probe = context.lightprobe
|
|
|
|
if probe.type == 'VOLUME':
|
|
col = layout.column()
|
|
|
|
col.prop(probe, "intensity")
|
|
|
|
col.separator()
|
|
|
|
sub = col.column(align=True)
|
|
sub.prop(probe, "normal_bias")
|
|
sub.prop(probe, "view_bias")
|
|
sub.prop(probe, "facing_bias")
|
|
|
|
col.separator()
|
|
|
|
col.prop(probe, "validity_threshold")
|
|
sub = col.column(align=True)
|
|
sub.prop(probe, "dilation_threshold")
|
|
sub.prop(probe, "dilation_radius", text="Radius")
|
|
|
|
col.separator()
|
|
|
|
elif probe.type == 'SPHERE':
|
|
col = layout.column()
|
|
col.prop(probe, "influence_type")
|
|
|
|
if probe.influence_type == 'ELIPSOID':
|
|
influence_text = "Radius"
|
|
else:
|
|
influence_text = "Size"
|
|
|
|
col.prop(probe, "influence_distance", text=influence_text)
|
|
col.prop(probe, "falloff")
|
|
|
|
elif probe.type == 'PLANE':
|
|
col = layout.column()
|
|
col.prop(probe, "influence_distance", text="Distance")
|
|
pass
|
|
else:
|
|
# Currently unsupported
|
|
pass
|
|
|
|
|
|
class DATA_PT_lightprobe_visibility(DataButtonsPanel, Panel):
|
|
bl_label = "Visibility"
|
|
bl_parent_id = "DATA_PT_lightprobe"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
if probe.type == 'VOLUME':
|
|
col.prop(probe, "visibility_buffer_bias", text="Bias")
|
|
col.prop(probe, "visibility_bleed_bias", text="Bleed Bias")
|
|
col.prop(probe, "visibility_blur", text="Blur")
|
|
|
|
row = col.row(align=True)
|
|
row.prop(probe, "visibility_collection")
|
|
row.prop(probe, "invert_visibility_collection", text="", icon='ARROW_LEFTRIGHT')
|
|
|
|
|
|
class DATA_PT_lightprobe_capture(DataButtonsPanel, Panel):
|
|
bl_label = "Capture"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.engine
|
|
return context.lightprobe and context.lightprobe.type in {'SPHERE', 'PLANE'} and (engine in cls.COMPAT_ENGINES)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
if probe.type == 'SPHERE':
|
|
sub = col.column(align=True)
|
|
sub.prop(probe, "clip_start", text="Clipping Start")
|
|
sub.prop(probe, "clip_end", text="End", text_ctxt=i18n_contexts.id_camera)
|
|
elif probe.type == 'PLANE':
|
|
col.prop(probe, "clip_start", text="Clipping Offset")
|
|
|
|
|
|
class DATA_PT_lightprobe_bake(DataButtonsPanel, Panel):
|
|
bl_label = "Bake"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.engine
|
|
return context.lightprobe and context.lightprobe.type == 'VOLUME' and (engine in cls.COMPAT_ENGINES)
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
col = layout.column()
|
|
row = col.row(align=True)
|
|
row.operator("object.lightprobe_cache_bake").subset = 'ACTIVE'
|
|
row.operator("object.lightprobe_cache_free", text="", icon='TRASH').subset = 'ACTIVE'
|
|
|
|
|
|
class DATA_PT_lightprobe_bake_resolution(DataButtonsPanel, Panel):
|
|
bl_label = "Resolution"
|
|
bl_parent_id = "DATA_PT_lightprobe_bake"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
sub = col.column(align=True)
|
|
sub.prop(probe, "resolution_x", text="Resolution X")
|
|
sub.prop(probe, "resolution_y", text="Y")
|
|
sub.prop(probe, "resolution_z", text="Z")
|
|
|
|
col.prop(probe, "bake_samples")
|
|
col.prop(probe, "surfel_density")
|
|
|
|
|
|
class DATA_PT_lightprobe_bake_capture(DataButtonsPanel, Panel):
|
|
bl_label = "Capture"
|
|
bl_parent_id = "DATA_PT_lightprobe_bake"
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
col.prop(probe, "capture_distance", text="Distance")
|
|
|
|
col = layout.column(heading="Contributions", align=True)
|
|
col.prop(probe, "capture_world", text="World")
|
|
col.prop(probe, "capture_indirect", text="Indirect Light")
|
|
col.prop(probe, "capture_emission", text="Emission")
|
|
|
|
|
|
class DATA_PT_lightprobe_bake_offset(DataButtonsPanel, Panel):
|
|
bl_label = "Offset"
|
|
bl_parent_id = "DATA_PT_lightprobe_bake_capture"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(probe, "surface_bias")
|
|
col.prop(probe, "escape_bias")
|
|
|
|
|
|
class DATA_PT_lightprobe_bake_clamping(DataButtonsPanel, Panel):
|
|
bl_label = "Clamping"
|
|
bl_parent_id = "DATA_PT_lightprobe_bake_capture"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(probe, "clamp_direct", text="Direct Light")
|
|
col.prop(probe, "clamp_indirect", text="Indirect Light")
|
|
|
|
|
|
class DATA_PT_lightprobe_parallax(DataButtonsPanel, Panel):
|
|
bl_label = "Custom Parallax"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {
|
|
'BLENDER_RENDER',
|
|
'BLENDER_EEVEE_NEXT',
|
|
}
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.engine
|
|
return context.lightprobe and context.lightprobe.type == 'SPHERE' and (engine in cls.COMPAT_ENGINES)
|
|
|
|
def draw_header(self, context):
|
|
probe = context.lightprobe
|
|
self.layout.prop(probe, "use_custom_parallax", text="")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
col.active = probe.use_custom_parallax
|
|
|
|
col.prop(probe, "parallax_type")
|
|
|
|
if probe.parallax_type == 'ELIPSOID':
|
|
col.prop(probe, "parallax_distance", text="Radius")
|
|
else:
|
|
col.prop(probe, "parallax_distance", text="Size")
|
|
|
|
|
|
class DATA_PT_lightprobe_display(DataButtonsPanel, Panel):
|
|
bl_label = "Viewport Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
ob = context.object
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
if probe.type == 'PLANE':
|
|
col.prop(ob, "empty_display_size", text="Arrow Size")
|
|
col.prop(probe, "show_influence")
|
|
|
|
if probe.type in {'VOLUME', 'SPHERE'}:
|
|
col.prop(probe, "show_influence")
|
|
col.prop(probe, "show_clip")
|
|
|
|
if probe.type == 'SPHERE':
|
|
sub = col.column()
|
|
sub.active = probe.use_custom_parallax
|
|
sub.prop(probe, "show_parallax")
|
|
|
|
|
|
class DATA_PT_lightprobe_display_eevee_next(DataButtonsPanel, Panel):
|
|
bl_label = "Viewport Display"
|
|
bl_options = {'DEFAULT_CLOSED'}
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
ob = context.object
|
|
probe = context.lightprobe
|
|
|
|
col = layout.column()
|
|
|
|
if probe.type in {'VOLUME', 'SPHERE'}:
|
|
row = col.row(heading="Data")
|
|
row.prop(probe, "use_data_display", text="")
|
|
subrow = row.row()
|
|
subrow.active = probe.use_data_display
|
|
subrow.prop(probe, "data_display_size", text="Size", slider=True)
|
|
col.prop(probe, "show_clip")
|
|
col.prop(probe, "show_influence")
|
|
|
|
if probe.type == 'SPHERE':
|
|
sub = col.column()
|
|
sub.active = probe.use_custom_parallax
|
|
sub.prop(probe, "show_parallax")
|
|
|
|
if probe.type == 'PLANE':
|
|
col.prop(ob, "empty_display_size", text="Arrow Size")
|
|
col.prop(probe, "use_data_display", text="Capture")
|
|
col.prop(probe, "show_influence")
|
|
|
|
|
|
class DATA_PT_lightprobe_animation(DataButtonsPanel, PropertiesAnimationMixin, Panel):
|
|
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
|
|
_animated_id_context_property = "lightprobe"
|
|
|
|
|
|
classes = (
|
|
DATA_PT_context_lightprobe,
|
|
DATA_PT_lightprobe,
|
|
DATA_PT_lightprobe_eevee_next,
|
|
DATA_PT_lightprobe_capture,
|
|
DATA_PT_lightprobe_bake,
|
|
DATA_PT_lightprobe_bake_resolution,
|
|
DATA_PT_lightprobe_bake_capture,
|
|
DATA_PT_lightprobe_bake_clamping,
|
|
DATA_PT_lightprobe_bake_offset,
|
|
DATA_PT_lightprobe_visibility,
|
|
DATA_PT_lightprobe_parallax,
|
|
DATA_PT_lightprobe_display,
|
|
DATA_PT_lightprobe_display_eevee_next,
|
|
DATA_PT_lightprobe_animation,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|