This adds support for rendering motion blur for volumes, using their velocity field. This works for fluid simulations and imported VDB volumes. For the latter, the name of the velocity field can be set per volume object, with automatic detection of velocity fields that are split into 3 scalar grids. A new parameter is also added to scale velocity for more artistic control. Like for Alembic and USD caches, a parameter to set the unit of time in which the velocity vectors are expressed is also added. For Blender gas simulations, the velocity unit should always be in seconds, so this is only exposed for volume objects which may come from external OpenVDB files. These parameters are available under the `Render` panels for the fluid domain and the volume object data properties respectively. Credits: kernel advection code from Tangent Animation's Blackbird based on earlier work by Geraldine Chua Differential Revision: https://developer.blender.org/D14629
197 lines
5.6 KiB
Python
197 lines
5.6 KiB
Python
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
# <pep8 compliant>
|
|
import bpy
|
|
from bpy.types import Panel, UIList
|
|
from rna_prop_ui import PropertyPanel
|
|
|
|
|
|
class DataButtonsPanel:
|
|
bl_space_type = 'PROPERTIES'
|
|
bl_region_type = 'WINDOW'
|
|
bl_context = "data"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
engine = context.scene.render.engine
|
|
return context.volume and (engine in cls.COMPAT_ENGINES)
|
|
|
|
|
|
class DATA_PT_context_volume(DataButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_options = {'HIDE_HEADER'}
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
ob = context.object
|
|
volume = context.volume
|
|
space = context.space_data
|
|
|
|
if ob:
|
|
layout.template_ID(ob, "data")
|
|
elif volume:
|
|
layout.template_ID(space, "pin_id")
|
|
|
|
|
|
class DATA_PT_volume_file(DataButtonsPanel, Panel):
|
|
bl_label = "OpenVDB File"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
volume = context.volume
|
|
volume.grids.load()
|
|
|
|
layout.prop(volume, "filepath", text="")
|
|
|
|
if volume.filepath:
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(volume, "is_sequence")
|
|
if volume.is_sequence:
|
|
col.prop(volume, "frame_duration", text="Frames")
|
|
col.prop(volume, "frame_start", text="Start")
|
|
col.prop(volume, "frame_offset", text="Offset")
|
|
col.prop(volume, "sequence_mode", text="Mode")
|
|
|
|
error_msg = volume.grids.error_message
|
|
if error_msg:
|
|
layout.separator()
|
|
col = layout.column(align=True)
|
|
col.label(text="Failed to load volume:")
|
|
col.label(text=error_msg)
|
|
|
|
|
|
class VOLUME_UL_grids(UIList):
|
|
def draw_item(self, _context, layout, _data, grid, _icon, _active_data, _active_propname, _index):
|
|
name = grid.name
|
|
data_type = grid.bl_rna.properties['data_type'].enum_items[grid.data_type]
|
|
|
|
layout.emboss = 'NONE'
|
|
layout.label(text=name)
|
|
row = layout.row()
|
|
row.alignment = 'RIGHT'
|
|
row.active = False
|
|
row.label(text=data_type.name)
|
|
|
|
|
|
class DATA_PT_volume_grids(DataButtonsPanel, Panel):
|
|
bl_label = "Grids"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
volume = context.volume
|
|
volume.grids.load()
|
|
|
|
layout.template_list("VOLUME_UL_grids", "grids", volume, "grids", volume.grids, "active_index", rows=3)
|
|
|
|
|
|
class DATA_PT_volume_render(DataButtonsPanel, Panel):
|
|
bl_label = "Render"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
scene = context.scene
|
|
volume = context.volume
|
|
render = volume.render
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(render, "space")
|
|
|
|
if scene.render.engine == 'CYCLES':
|
|
col.prop(render, "step_size")
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(render, "clipping")
|
|
|
|
col = layout.column(align=False)
|
|
col.prop(volume, "velocity_grid")
|
|
|
|
col.prop(volume, "velocity_unit")
|
|
col.prop(volume, "velocity_scale")
|
|
|
|
|
|
class DATA_PT_volume_viewport_display(DataButtonsPanel, Panel):
|
|
bl_label = "Viewport Display"
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
volume = context.volume
|
|
display = volume.display
|
|
|
|
col = layout.column(align=True)
|
|
col.prop(display, "wireframe_type")
|
|
sub = col.row()
|
|
sub.active = display.wireframe_type in {'BOXES', 'POINTS'}
|
|
sub.prop(display, "wireframe_detail", text="Detail")
|
|
|
|
col = layout.column()
|
|
col.prop(display, "density")
|
|
col.prop(display, "interpolation_method")
|
|
|
|
|
|
class DATA_PT_volume_viewport_display_slicing(DataButtonsPanel, Panel):
|
|
bl_label = ""
|
|
bl_parent_id = 'DATA_PT_volume_viewport_display'
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
|
|
def draw_header(self, context):
|
|
layout = self.layout
|
|
|
|
volume = context.volume
|
|
display = volume.display
|
|
|
|
layout.prop(display, "use_slice")
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.use_property_split = True
|
|
layout.use_property_decorate = False
|
|
|
|
volume = context.volume
|
|
display = volume.display
|
|
|
|
layout.active = display.use_slice
|
|
|
|
col = layout.column()
|
|
col.prop(display, "slice_axis")
|
|
col.prop(display, "slice_depth")
|
|
|
|
|
|
class DATA_PT_custom_props_volume(DataButtonsPanel, PropertyPanel, Panel):
|
|
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
|
|
_context_path = "object.data"
|
|
_property_type = bpy.types.Volume
|
|
|
|
|
|
classes = (
|
|
DATA_PT_context_volume,
|
|
DATA_PT_volume_grids,
|
|
DATA_PT_volume_file,
|
|
DATA_PT_volume_viewport_display,
|
|
DATA_PT_volume_viewport_display_slicing,
|
|
DATA_PT_volume_render,
|
|
DATA_PT_custom_props_volume,
|
|
VOLUME_UL_grids,
|
|
)
|
|
|
|
if __name__ == "__main__": # only for live edit.
|
|
from bpy.utils import register_class
|
|
for cls in classes:
|
|
register_class(cls)
|