Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2020-03-17 16:41:48 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2020-03-18 13:23:05 +0300
commitb0a1cf2c9ae696b07f7a236bc855a5ab4a493dcb (patch)
tree92295af11db5e984da42bfac7ca60190b8549a3f
parent8dcfd392e4e62f193b666304425bc5ae635ecffe (diff)
Objects: add Volume object type, and prototypes for Hair and PointCloud
Only the volume object is exposed in the user interface. It is based on OpenVDB internally. Drawing and rendering code will follow in another commit. https://wiki.blender.org/wiki/Source/Objects/Volume https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Volumes Hair and PointCloud object types are hidden behind a WITH_NEW_OBJECT_TYPES build option. These are unfinished, and included only to make it easier to cooperate on development in the future and avoid tricky merges. https://wiki.blender.org/wiki/Source/Objects/New_Object_Types Ref T73201, T68981 Differential Revision: https://developer.blender.org/D6945
-rw-r--r--CMakeLists.txt4
-rw-r--r--release/scripts/startup/bl_ui/__init__.py3
-rw-r--r--release/scripts/startup/bl_ui/properties_data_hair.py78
-rw-r--r--release/scripts/startup/bl_ui/properties_data_pointcloud.py78
-rw-r--r--release/scripts/startup/bl_ui/properties_data_volume.py173
-rw-r--r--release/scripts/startup/bl_ui/properties_object.py2
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py6
-rw-r--r--release/scripts/startup/bl_ui/space_filebrowser.py3
-rw-r--r--release/scripts/startup/bl_ui/space_node.py3
-rw-r--r--release/scripts/startup/bl_ui/space_outliner.py3
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py9
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py24
-rw-r--r--source/blender/blenkernel/BKE_hair.h71
-rw-r--r--source/blender/blenkernel/BKE_idtype.h5
-rw-r--r--source/blender/blenkernel/BKE_main.h5
-rw-r--r--source/blender/blenkernel/BKE_packedFile.h5
-rw-r--r--source/blender/blenkernel/BKE_pointcloud.h74
-rw-r--r--source/blender/blenkernel/BKE_volume.h168
-rw-r--r--source/blender/blenkernel/CMakeLists.txt21
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c30
-rw-r--r--source/blender/blenkernel/intern/bpath.c8
-rw-r--r--source/blender/blenkernel/intern/customdata.c16
-rw-r--r--source/blender/blenkernel/intern/effect.c2
-rw-r--r--source/blender/blenkernel/intern/hair.c292
-rw-r--r--source/blender/blenkernel/intern/idcode.c22
-rw-r--r--source/blender/blenkernel/intern/idtype.c3
-rw-r--r--source/blender/blenkernel/intern/lib_id.c54
-rw-r--r--source/blender/blenkernel/intern/lib_query.c30
-rw-r--r--source/blender/blenkernel/intern/lib_remap.c3
-rw-r--r--source/blender/blenkernel/intern/main.c9
-rw-r--r--source/blender/blenkernel/intern/material.c75
-rw-r--r--source/blender/blenkernel/intern/object.c77
-rw-r--r--source/blender/blenkernel/intern/object_update.c21
-rw-r--r--source/blender/blenkernel/intern/packedFile.c69
-rw-r--r--source/blender/blenkernel/intern/pointcloud.c257
-rw-r--r--source/blender/blenkernel/intern/volume.cc1242
-rw-r--r--source/blender/blenloader/intern/readblenentry.c6
-rw-r--r--source/blender/blenloader/intern/readfile.c211
-rw-r--r--source/blender/blenloader/intern/readfile.h3
-rw-r--r--source/blender/blenloader/intern/writefile.c104
-rw-r--r--source/blender/blentranslation/BLT_translation.h6
-rw-r--r--source/blender/depsgraph/CMakeLists.txt2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc27
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc24
-rw-r--r--source/blender/depsgraph/intern/depsgraph_tag.cc8
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc9
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h2
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc11
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc60
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h45
-rw-r--r--source/blender/draw/engines/overlay/overlay_pointcloud.c72
-rw-r--r--source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl16
-rw-r--r--source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl27
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c349
-rw-r--r--source/blender/draw/intern/draw_cache_impl_pointcloud.c176
-rw-r--r--source/blender/draw/intern/draw_cache_impl_volume.c297
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c286
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c25
-rw-r--r--source/blender/editors/animation/anim_filter.c72
-rw-r--r--source/blender/editors/include/ED_anim_api.h6
-rw-r--r--source/blender/editors/include/UI_icons.h21
-rw-r--r--source/blender/editors/interface/interface_icons.c6
-rw-r--r--source/blender/editors/interface/interface_templates.c14
-rw-r--r--source/blender/editors/object/CMakeLists.txt5
-rw-r--r--source/blender/editors/object/object_add.c78
-rw-r--r--source/blender/editors/object/object_intern.h6
-rw-r--r--source/blender/editors/object/object_modifier.c14
-rw-r--r--source/blender/editors/object/object_ops.c6
-rw-r--r--source/blender/editors/object/object_relations.c12
-rw-r--r--source/blender/editors/object/object_volume.c193
-rw-r--r--source/blender/editors/render/render_opengl.c3
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt4
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c41
-rw-r--r--source/blender/editors/space_file/filelist.c17
-rw-r--r--source/blender/editors/space_file/filesel.c3
-rw-r--r--source/blender/editors/space_info/info_stats.c5
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c5
-rw-r--r--source/blender/editors/space_nla/nla_channels.c5
-rw-r--r--source/blender/editors/space_node/node_edit.c11
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c18
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h5
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c21
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c22
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c9
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c4
-rw-r--r--source/blender/makesdna/DNA_ID.h75
-rw-r--r--source/blender/makesdna/DNA_action_types.h3
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h13
-rw-r--r--source/blender/makesdna/DNA_defaults.h8
-rw-r--r--source/blender/makesdna/DNA_hair_defaults.h40
-rw-r--r--source/blender/makesdna/DNA_hair_types.h82
-rw-r--r--source/blender/makesdna/DNA_object_types.h28
-rw-r--r--source/blender/makesdna/DNA_pointcloud_defaults.h40
-rw-r--r--source/blender/makesdna/DNA_pointcloud_types.h64
-rw-r--r--source/blender/makesdna/DNA_space_types.h1
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesdna/DNA_volume_defaults.h59
-rw-r--r--source/blender/makesdna/DNA_volume_types.h130
-rw-r--r--source/blender/makesdna/intern/CMakeLists.txt3
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c24
-rw-r--r--source/blender/makesdna/intern/makesdna.c6
-rw-r--r--source/blender/makesrna/RNA_access.h3
-rw-r--r--source/blender/makesrna/RNA_types.h6
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt12
-rw-r--r--source/blender/makesrna/intern/makesrna.c7
-rw-r--r--source/blender/makesrna/intern/rna_ID.c32
-rw-r--r--source/blender/makesrna/intern/rna_action.c23
-rw-r--r--source/blender/makesrna/intern/rna_hair.c244
-rw-r--r--source/blender/makesrna/intern/rna_internal.h6
-rw-r--r--source/blender/makesrna/intern/rna_main.c22
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c187
-rw-r--r--source/blender/makesrna/intern/rna_object.c13
-rw-r--r--source/blender/makesrna/intern/rna_pointcloud.c175
-rw-r--r--source/blender/makesrna/intern/rna_space.c39
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c17
-rw-r--r--source/blender/makesrna/intern/rna_volume.c557
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c6
-rw-r--r--source/creator/creator.c2
118 files changed, 7092 insertions, 155 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9f4d0c20886..f71646f4aba 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -319,6 +319,10 @@ mark_as_advanced(WITH_SYSTEM_GLOG)
# Freestyle
option(WITH_FREESTYLE "Enable Freestyle (advanced edges rendering)" ON)
+# New object types
+option(WITH_NEW_OBJECT_TYPES "Enable new hair and pointcloud objects (use for development only, don't save in files)" OFF)
+mark_as_advanced(WITH_NEW_OBJECT_TYPES)
+
# Misc
if(WIN32)
option(WITH_INPUT_IME "Enable Input Method Editor (IME) for complex Asian character input" ON)
diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py
index c2bcb7d5ea5..7d3ecceca41 100644
--- a/release/scripts/startup/bl_ui/__init__.py
+++ b/release/scripts/startup/bl_ui/__init__.py
@@ -35,14 +35,17 @@ _modules = [
"properties_data_curve",
"properties_data_empty",
"properties_data_gpencil",
+ "properties_data_hair",
"properties_data_light",
"properties_data_lattice",
"properties_data_mesh",
"properties_data_metaball",
"properties_data_modifier",
+ "properties_data_pointcloud",
"properties_data_shaderfx",
"properties_data_lightprobe",
"properties_data_speaker",
+ "properties_data_volume",
"properties_mask_common",
"properties_material",
"properties_material_gpencil",
diff --git a/release/scripts/startup/bl_ui/properties_data_hair.py b/release/scripts/startup/bl_ui/properties_data_hair.py
new file mode 100644
index 00000000000..6017765b83d
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_data_hair.py
@@ -0,0 +1,78 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <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 hasattr(context, 'hair') and context.hair and (engine in cls.COMPAT_ENGINES)
+
+
+class DATA_PT_context_hair(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
+ hair = context.hair
+ space = context.space_data
+
+ if ob:
+ layout.template_ID(ob, "data")
+ elif hair:
+ layout.template_ID(space, "pin_id")
+
+
+class DATA_PT_hair(DataButtonsPanel, Panel):
+ bl_label = "Hair"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ def draw(self, context):
+ layout = self.layout
+ hair = context.hair
+ pass
+
+class DATA_PT_custom_props_hair(DataButtonsPanel, PropertyPanel, Panel):
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ _context_path = "object.data"
+ _property_type = bpy.types.Hair if hasattr(bpy.types, "Hair") else None
+
+
+classes = (
+ DATA_PT_context_hair,
+ DATA_PT_hair,
+ DATA_PT_custom_props_hair,
+)
+
+if __name__ == "__main__": # only for live edit.
+ from bpy.utils import register_class
+ for cls in classes:
+ register_class(cls)
diff --git a/release/scripts/startup/bl_ui/properties_data_pointcloud.py b/release/scripts/startup/bl_ui/properties_data_pointcloud.py
new file mode 100644
index 00000000000..10ebdea3155
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_data_pointcloud.py
@@ -0,0 +1,78 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <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 hasattr(context, 'pointcloud') and context.pointcloud and (engine in cls.COMPAT_ENGINES)
+
+
+class DATA_PT_context_pointcloud(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
+ pointcloud = context.pointcloud
+ space = context.space_data
+
+ if ob:
+ layout.template_ID(ob, "data")
+ elif pointcloud:
+ layout.template_ID(space, "pin_id")
+
+
+class DATA_PT_pointcloud(DataButtonsPanel, Panel):
+ bl_label = "Point Cloud"
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+
+ def draw(self, context):
+ layout = self.layout
+ pointcloud = context.pointcloud
+ pass
+
+class DATA_PT_custom_props_pointcloud(DataButtonsPanel, PropertyPanel, Panel):
+ COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
+ _context_path = "object.data"
+ _property_type = bpy.types.PointCloud if hasattr(bpy.types, "PointCloud") else None
+
+
+classes = (
+ DATA_PT_context_pointcloud,
+ DATA_PT_pointcloud,
+ DATA_PT_custom_props_pointcloud,
+)
+
+if __name__ == "__main__": # only for live edit.
+ from bpy.utils import register_class
+ for cls in classes:
+ register_class(cls)
diff --git a/release/scripts/startup/bl_ui/properties_data_volume.py b/release/scripts/startup/bl_ui/properties_data_volume.py
new file mode 100644
index 00000000000..29e28aa2c02
--- /dev/null
+++ b/release/scripts/startup/bl_ui/properties_data_volume.py
@@ -0,0 +1,173 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <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 len(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 len(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.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")
+ col.prop(render, "step_size")
+
+ if scene.render.engine == 'CYCLES':
+ col = layout.column(align=True)
+ col.prop(render, "clipping")
+
+
+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")
+
+ layout.prop(display, "density")
+
+
+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_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)
diff --git a/release/scripts/startup/bl_ui/properties_object.py b/release/scripts/startup/bl_ui/properties_object.py
index 33ba981e235..91bd055741c 100644
--- a/release/scripts/startup/bl_ui/properties_object.py
+++ b/release/scripts/startup/bl_ui/properties_object.py
@@ -216,7 +216,7 @@ class OBJECT_PT_display(ObjectButtonsPanel, Panel):
obj = context.object
obj_type = obj.type
- is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'})
+ is_geometry = (obj_type in {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT', 'VOLUME', 'HAIR', 'POINTCLOUD'})
is_wire = (obj_type in {'CAMERA', 'EMPTY'})
is_empty_image = (obj_type == 'EMPTY' and obj.empty_display_type == 'IMAGE')
is_dupli = (obj.instance_type != 'NONE')
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index 76ba4dfb78e..3f8c41e4f21 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -126,6 +126,12 @@ class DopesheetFilterPopoverBase:
flow.prop(dopesheet, "show_lattices", text="Lattices")
if bpy.data.metaballs:
flow.prop(dopesheet, "show_metaballs", text="Metaballs")
+ if hasattr(bpy.data, "hairs") and bpy.data.hairs:
+ flow.prop(dopesheet, "show_hairs", text="Hairs")
+ if hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds:
+ flow.prop(dopesheet, "show_pointclouds", text="Point Clouds")
+ if bpy.data.volumes:
+ flow.prop(dopesheet, "show_volumes", text="Volumes")
# data types
flow.prop(dopesheet, "show_worlds", text="Worlds")
diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py
index 95046678b27..3bf5bbf7b46 100644
--- a/release/scripts/startup/bl_ui/space_filebrowser.py
+++ b/release/scripts/startup/bl_ui/space_filebrowser.py
@@ -137,6 +137,9 @@ class FILEBROWSER_PT_filter(Panel):
row = col.row()
row.label(icon='FILE_TEXT')
row.prop(params, "use_filter_text", text="Text Files", toggle=0)
+ row = col.row()
+ row.label(icon='FILE_VOLUME')
+ row.prop(params, "use_filter_volume", text="Volume Files", toggle=0)
col.separator()
diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py
index 8fe758b8bf6..bdda0ebbe9a 100644
--- a/release/scripts/startup/bl_ui/space_node.py
+++ b/release/scripts/startup/bl_ui/space_node.py
@@ -76,7 +76,8 @@ class NODE_HT_header(Header):
layout.separator_spacer()
- types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'GPENCIL'}
+ types_that_support_material = {'MESH', 'CURVE', 'SURFACE', 'FONT', 'META',
+ 'GPENCIL', 'VOLUME', 'HAIR', 'POINTCLOUD'}
# disable material slot buttons when pinned, cannot find correct slot within id_from (#36589)
# disable also when the selected object does not support materials
has_material_slots = not snode.pin and ob_type in types_that_support_material
diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py
index 6ac31aeb3d0..cc773300d9e 100644
--- a/release/scripts/startup/bl_ui/space_outliner.py
+++ b/release/scripts/startup/bl_ui/space_outliner.py
@@ -397,6 +397,9 @@ class OUTLINER_PT_filter(Panel):
if (
bpy.data.curves or
bpy.data.metaballs or
+ (hasattr(bpy.data, "hairs") and bpy.data.hairs) or
+ (hasattr(bpy.data, "pointclouds") and bpy.data.pointclouds) or
+ bpy.data.volumes or
bpy.data.lightprobes or
bpy.data.lattices or
bpy.data.fonts or
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 2ca0e73bd74..4cccf429179 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -390,18 +390,23 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa
col.prop(edit, "use_duplicate_armature", text="Armature")
col.prop(edit, "use_duplicate_curve", text="Curve")
# col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented.
+ col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil")
+ if hasattr(edit, "use_duplicate_hair"):
+ col.prop(edit, "use_duplicate_hair", text="Hair")
col.prop(edit, "use_duplicate_light", text="Light")
- col.prop(edit, "use_duplicate_lightprobe", text="Light Probe")
col = flow.column()
+ col.prop(edit, "use_duplicate_lightprobe", text="Light Probe")
col.prop(edit, "use_duplicate_material", text="Material")
col.prop(edit, "use_duplicate_mesh", text="Mesh")
col.prop(edit, "use_duplicate_metaball", text="Metaball")
col.prop(edit, "use_duplicate_particle", text="Particle")
col = flow.column()
+ if hasattr(edit, "use_duplicate_pointcloud"):
+ col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud")
col.prop(edit, "use_duplicate_surface", text="Surface")
col.prop(edit, "use_duplicate_text", text="Text")
# col.prop(edit, "use_duplicate_texture", text="Texture") # Not implemented.
- col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil")
+ col.prop(edit, "use_duplicate_volume", text="Volume")
class USERPREF_PT_edit_cursor(EditingPanel, CenterAlignMixIn, Panel):
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index f8b291c0c5f..a2bfd711c04 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2157,6 +2157,16 @@ class VIEW3D_MT_camera_add(Menu):
layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA')
+class VIEW3D_MT_volume_add(Menu):
+ bl_idname = "VIEW3D_MT_volume_add"
+ bl_label = "Volume"
+
+ def draw(self, _context):
+ layout = self.layout
+ layout.operator("object.volume_import", text="Import OpenVDB...", icon='OUTLINER_DATA_VOLUME')
+ layout.operator("object.volume_add", text="Empty", icon='OUTLINER_DATA_VOLUME')
+
+
class VIEW3D_MT_add(Menu):
bl_label = "Add"
bl_translation_context = i18n_contexts.operator_default
@@ -2179,6 +2189,11 @@ class VIEW3D_MT_add(Menu):
layout.menu("VIEW3D_MT_surface_add", icon='OUTLINER_OB_SURFACE')
layout.menu("VIEW3D_MT_metaball_add", text="Metaball", icon='OUTLINER_OB_META')
layout.operator("object.text_add", text="Text", icon='OUTLINER_OB_FONT')
+ if hasattr(bpy.data, "hairs"):
+ layout.operator("object.hair_add", text="Hair", icon='OUTLINER_OB_HAIR')
+ if hasattr(bpy.data, "pointclouds"):
+ layout.operator("object.pointcloud_add", text="Point Cloud", icon='OUTLINER_OB_POINTCLOUD')
+ layout.menu("VIEW3D_MT_volume_add", text="Volume", icon='OUTLINER_OB_VOLUME')
layout.operator_menu_enum("object.gpencil_add", "type", text="Grease Pencil", icon='OUTLINER_OB_GREASEPENCIL')
layout.separator()
@@ -5428,6 +5443,9 @@ class VIEW3D_PT_object_type_visibility(Panel):
("surf", "Surface"),
("meta", "Meta"),
("font", "Text"),
+ ("hair", "Hair"),
+ ("pointcloud", "Point Cloud"),
+ ("volume", "Volume"),
("grease_pencil", "Grease Pencil"),
(None, None),
# Other
@@ -5445,6 +5463,11 @@ class VIEW3D_PT_object_type_visibility(Panel):
col.separator()
continue
+ if attr == "hair" and not hasattr(bpy.data, "hairs"):
+ continue
+ elif attr == "pointcloud" and not hasattr(bpy.data, "pointclouds"):
+ continue
+
attr_v = "show_object_viewport_" f"{attr:s}"
attr_s = "show_object_select_" f"{attr:s}"
@@ -7254,6 +7277,7 @@ classes = (
VIEW3D_MT_light_add,
VIEW3D_MT_lightprobe_add,
VIEW3D_MT_camera_add,
+ VIEW3D_MT_volume_add,
VIEW3D_MT_add,
VIEW3D_MT_image_add,
VIEW3D_MT_object,
diff --git a/source/blender/blenkernel/BKE_hair.h b/source/blender/blenkernel/BKE_hair.h
new file mode 100644
index 00000000000..0cb26581aef
--- /dev/null
+++ b/source/blender/blenkernel/BKE_hair.h
@@ -0,0 +1,71 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_HAIR_H__
+#define __BKE_HAIR_H__
+
+/** \file BKE_hair.h
+ * \ingroup bke
+ * \brief General operations for hairs.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BoundBox;
+struct Depsgraph;
+struct Hair;
+struct Main;
+struct Object;
+struct Scene;
+
+void *BKE_hair_add(struct Main *bmain, const char *name);
+struct Hair *BKE_hair_copy(struct Main *bmain, const struct Hair *hair);
+
+struct BoundBox *BKE_hair_boundbox_get(struct Object *ob);
+
+void BKE_hair_update_customdata_pointers(struct Hair *hair);
+
+/* Depsgraph */
+
+struct Hair *BKE_hair_new_for_eval(const struct Hair *hair_src, int totpoint, int totcurve);
+struct Hair *BKE_hair_copy_for_eval(struct Hair *hair_src, bool reference);
+
+void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *object);
+
+/* Draw Cache */
+
+enum {
+ BKE_HAIR_BATCH_DIRTY_ALL = 0,
+};
+
+void BKE_hair_batch_cache_dirty_tag(struct Hair *hair, int mode);
+void BKE_hair_batch_cache_free(struct Hair *hair);
+
+extern void (*BKE_hair_batch_cache_dirty_tag_cb)(struct Hair *hair, int mode);
+extern void (*BKE_hair_batch_cache_free_cb)(struct Hair *hair);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h
index e6e82900f6d..60eee2035f5 100644
--- a/source/blender/blenkernel/BKE_idtype.h
+++ b/source/blender/blenkernel/BKE_idtype.h
@@ -26,6 +26,8 @@
* ID type structure, helping to factorize common operations and data for all data-block types.
*/
+#include "BLI_sys_types.h"
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -160,6 +162,9 @@ extern IDTypeInfo IDType_ID_PC;
extern IDTypeInfo IDType_ID_CF;
extern IDTypeInfo IDType_ID_WS;
extern IDTypeInfo IDType_ID_LP;
+extern IDTypeInfo IDType_ID_HA;
+extern IDTypeInfo IDType_ID_PT;
+extern IDTypeInfo IDType_ID_VO;
/* ********** Helpers/Utils API. ********** */
diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h
index 8aac09d8738..306d889fba4 100644
--- a/source/blender/blenkernel/BKE_main.h
+++ b/source/blender/blenkernel/BKE_main.h
@@ -144,6 +144,9 @@ typedef struct Main {
ListBase linestyles;
ListBase cachefiles;
ListBase workspaces;
+ ListBase hairs;
+ ListBase pointclouds;
+ ListBase volumes;
/**
* Must be generated, used and freed by same code - never assume this is valid data unless you
@@ -217,7 +220,7 @@ const char *BKE_main_blendfile_path_from_global(void);
struct ListBase *which_libbase(struct Main *mainlib, short type);
-#define MAX_LIBARRAY 37
+#define MAX_LIBARRAY 40
int set_listbasepointers(struct Main *main, struct ListBase *lb[MAX_LIBARRAY]);
#define MAIN_VERSION_ATLEAST(main, ver, subver) \
diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h
index 2b491a1919e..16d9b5b2c8b 100644
--- a/source/blender/blenkernel/BKE_packedFile.h
+++ b/source/blender/blenkernel/BKE_packedFile.h
@@ -37,6 +37,7 @@ struct PackedFile;
struct ReportList;
struct VFont;
struct bSound;
+struct Volume;
enum ePF_FileCompare {
PF_CMP_EQUAL = 0,
@@ -84,6 +85,10 @@ int BKE_packedfile_unpack_image(struct Main *bmain,
struct ReportList *reports,
struct Image *ima,
enum ePF_FileStatus how);
+int BKE_packedfile_unpack_volume(struct Main *bmain,
+ struct ReportList *reports,
+ struct Volume *volume,
+ enum ePF_FileStatus how);
void BKE_packedfile_unpack_all(struct Main *bmain,
struct ReportList *reports,
enum ePF_FileStatus how);
diff --git a/source/blender/blenkernel/BKE_pointcloud.h b/source/blender/blenkernel/BKE_pointcloud.h
new file mode 100644
index 00000000000..972795e2ae1
--- /dev/null
+++ b/source/blender/blenkernel/BKE_pointcloud.h
@@ -0,0 +1,74 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_POINTCLOUD_H__
+#define __BKE_POINTCLOUD_H__
+
+/** \file BKE_pointcloud.h
+ * \ingroup bke
+ * \brief General operations for pointclouds.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BoundBox;
+struct Depsgraph;
+struct Main;
+struct Object;
+struct PointCloud;
+struct Scene;
+
+void *BKE_pointcloud_add(struct Main *bmain, const char *name);
+struct PointCloud *BKE_pointcloud_copy(struct Main *bmain, const struct PointCloud *pointcloud);
+
+struct BoundBox *BKE_pointcloud_boundbox_get(struct Object *ob);
+
+void BKE_pointcloud_update_customdata_pointers(struct PointCloud *pointcloud);
+
+/* Dependency Graph */
+
+struct PointCloud *BKE_pointcloud_new_for_eval(const struct PointCloud *pointcloud_src,
+ int totpoint);
+struct PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool reference);
+
+void BKE_pointcloud_data_update(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *object);
+
+/* Draw Cache */
+
+enum {
+ BKE_POINTCLOUD_BATCH_DIRTY_ALL = 0,
+};
+
+void BKE_pointcloud_batch_cache_dirty_tag(struct PointCloud *pointcloud, int mode);
+void BKE_pointcloud_batch_cache_free(struct PointCloud *pointcloud);
+
+extern void (*BKE_pointcloud_batch_cache_dirty_tag_cb)(struct PointCloud *pointcloud, int mode);
+extern void (*BKE_pointcloud_batch_cache_free_cb)(struct PointCloud *pointcloud);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h
new file mode 100644
index 00000000000..b9845397d84
--- /dev/null
+++ b/source/blender/blenkernel/BKE_volume.h
@@ -0,0 +1,168 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Brecht Van Lommel.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __BKE_VOLUME_H__
+#define __BKE_VOLUME_H__
+
+/** \file BKE_volume.h
+ * \ingroup bke
+ * \brief Volume datablock.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct BoundBox;
+struct Depsgraph;
+struct Main;
+struct Object;
+struct Scene;
+struct Volume;
+struct VolumeGridVector;
+
+/* Module */
+
+void BKE_volumes_init(void);
+
+/* Datablock Management */
+
+void BKE_volume_init_grids(struct Volume *volume);
+void *BKE_volume_add(struct Main *bmain, const char *name);
+struct Volume *BKE_volume_copy(struct Main *bmain, const struct Volume *volume);
+
+struct BoundBox *BKE_volume_boundbox_get(struct Object *ob);
+
+bool BKE_volume_is_y_up(const struct Volume *volume);
+bool BKE_volume_is_points_only(const struct Volume *volume);
+
+/* Depsgraph */
+
+void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, struct Volume *volume);
+void BKE_volume_data_update(struct Depsgraph *depsgraph,
+ struct Scene *scene,
+ struct Object *object);
+
+void BKE_volume_grids_backup_restore(struct Volume *volume,
+ struct VolumeGridVector *grids,
+ const char *filepath);
+
+/* Draw Cache */
+
+enum {
+ BKE_VOLUME_BATCH_DIRTY_ALL = 0,
+};
+
+void BKE_volume_batch_cache_dirty_tag(struct Volume *volume, int mode);
+void BKE_volume_batch_cache_free(struct Volume *volume);
+
+extern void (*BKE_volume_batch_cache_dirty_tag_cb)(struct Volume *volume, int mode);
+extern void (*BKE_volume_batch_cache_free_cb)(struct Volume *volume);
+
+/* Grids
+ *
+ * For volumes referencing a file, the list of grids and metadata must be
+ * loaded before it can be accessed. This happens on-demand, only when needed
+ * by the user interface, dependency graph or render engine. */
+
+typedef struct VolumeGrid VolumeGrid;
+
+bool BKE_volume_load(struct Volume *volume, struct Main *bmain);
+void BKE_volume_unload(struct Volume *volume);
+bool BKE_volume_is_loaded(const struct Volume *volume);
+
+int BKE_volume_num_grids(const struct Volume *volume);
+const char *BKE_volume_grids_error_msg(const struct Volume *volume);
+VolumeGrid *BKE_volume_grid_get(const struct Volume *volume, int grid_index);
+VolumeGrid *BKE_volume_grid_active_get(const struct Volume *volume);
+VolumeGrid *BKE_volume_grid_find(const struct Volume *volume, const char *name);
+
+/* Grid
+ *
+ * By default only grid metadata is loaded, for access to the tree and voxels
+ * BKE_volume_grid_load must be called first. */
+
+typedef enum VolumeGridType {
+ VOLUME_GRID_UNKNOWN = 0,
+ VOLUME_GRID_BOOLEAN,
+ VOLUME_GRID_FLOAT,
+ VOLUME_GRID_DOUBLE,
+ VOLUME_GRID_INT,
+ VOLUME_GRID_INT64,
+ VOLUME_GRID_MASK,
+ VOLUME_GRID_STRING,
+ VOLUME_GRID_VECTOR_FLOAT,
+ VOLUME_GRID_VECTOR_DOUBLE,
+ VOLUME_GRID_VECTOR_INT,
+ VOLUME_GRID_POINTS,
+} VolumeGridType;
+
+bool BKE_volume_grid_load(const struct Volume *volume, struct VolumeGrid *grid);
+void BKE_volume_grid_unload(const struct Volume *volume, struct VolumeGrid *grid);
+bool BKE_volume_grid_is_loaded(const struct VolumeGrid *grid);
+
+/* Metadata */
+const char *BKE_volume_grid_name(const struct VolumeGrid *grid);
+VolumeGridType BKE_volume_grid_type(const struct VolumeGrid *grid);
+int BKE_volume_grid_channels(const struct VolumeGrid *grid);
+void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4][4]);
+
+/* Bounds */
+bool BKE_volume_grid_bounds(const struct VolumeGrid *grid, float min[3], float max[3]);
+
+/* Volume Editing
+ *
+ * These are intended for modifiers to use on evaluated datablocks.
+ *
+ * new_for_eval creates a volume datablock with no grids or file path, but
+ * preserves other settings such as viewport display options.
+ *
+ * copy_for_eval creates a volume datablock preserving everything except the
+ * file path. Grids are shared with the source datablock, not copied. */
+
+struct Volume *BKE_volume_new_for_eval(const struct Volume *volume_src);
+struct Volume *BKE_volume_copy_for_eval(struct Volume *volume_src, bool reference);
+
+struct VolumeGrid *BKE_volume_grid_add(struct Volume *volume,
+ const char *name,
+ VolumeGridType type);
+void BKE_volume_grid_remove(struct Volume *volume, struct VolumeGrid *grid);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* OpenVDB Grid Access
+ *
+ * Access to OpenVDB grid for C++. These will automatically load grids from
+ * file or copy shared grids to make them writeable. */
+
+#if defined(__cplusplus) && defined(WITH_OPENVDB)
+# include <openvdb/openvdb.h>
+openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const struct VolumeGrid *grid);
+openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const struct Volume *volume,
+ struct VolumeGrid *grid);
+openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const struct Volume *volume,
+ struct VolumeGrid *grid,
+ const bool clear);
+#endif
+
+#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 047901b4c81..d3dfa422ebd 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -120,6 +120,7 @@ set(SRC
intern/freestyle.c
intern/gpencil.c
intern/gpencil_modifier.c
+ intern/hair.c
intern/icons.c
intern/icons_rasterize.c
intern/idcode.c
@@ -196,6 +197,7 @@ set(SRC
intern/pbvh_bmesh.c
intern/pbvh_parallel.cc
intern/pointcache.c
+ intern/pointcloud.c
intern/report.c
intern/rigidbody.c
intern/scene.c
@@ -239,6 +241,7 @@ set(SRC
intern/tracking_util.c
intern/undo_system.c
intern/unit.c
+ intern/volume.cc
intern/workspace.c
intern/world.c
intern/writeavi.c
@@ -296,12 +299,12 @@ set(SRC
BKE_global.h
BKE_gpencil.h
BKE_gpencil_modifier.h
+ BKE_hair.h
BKE_icons.h
BKE_idcode.h
BKE_idprop.h
BKE_idtype.h
BKE_image.h
- BKE_image_save.h
BKE_ipo.h
BKE_kelvinlet.h
BKE_key.h
@@ -345,6 +348,7 @@ set(SRC
BKE_particle.h
BKE_pbvh.h
BKE_pointcache.h
+ BKE_pointcloud.h
BKE_report.h
BKE_rigidbody.h
BKE_scene.h
@@ -370,6 +374,7 @@ set(SRC
BKE_tracking.h
BKE_undo_system.h
BKE_unit.h
+ BKE_volume.h
BKE_workspace.h
BKE_world.h
BKE_writeavi.h
@@ -634,16 +639,14 @@ if(WITH_OPENVDB)
list(APPEND INC
../../../intern/openvdb
)
+ list(APPEND INC_SYS
+ ${OPENVDB_INCLUDE_DIRS}
+ )
list(APPEND LIB
- bf_intern_openvdb
+ bf_intern_openvdb
+ ${OPENVDB_LIBRARIES}
)
- add_definitions(-DWITH_OPENVDB)
-
- if(WITH_OPENVDB_BLOSC)
- add_definitions(
- -DWITH_OPENVDB_BLOSC
- )
- endif()
+ add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS})
endif()
if(WITH_QUADRIFLOW)
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 2f4d58a1992..ada104a7b5a 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -110,6 +110,9 @@ bool id_type_can_have_animdata(const short id_type)
case ID_MSK:
case ID_GD:
case ID_CF:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
return true;
/* no AnimData */
@@ -1327,6 +1330,15 @@ void BKE_animdata_main_cb(Main *bmain, ID_AnimData_Edit_Callback func, void *use
/* cache files */
ANIMDATA_IDS_CB(bmain->cachefiles.first);
+
+ /* hairs */
+ ANIMDATA_IDS_CB(bmain->hairs.first);
+
+ /* pointclouds */
+ ANIMDATA_IDS_CB(bmain->pointclouds.first);
+
+ /* volumes */
+ ANIMDATA_IDS_CB(bmain->volumes.first);
}
/* Fix all RNA-Paths throughout the database (directly access the Global.main version)
@@ -1427,6 +1439,15 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id,
/* cache files */
RENAMEFIX_ANIM_IDS(bmain->cachefiles.first);
+ /* hairs */
+ RENAMEFIX_ANIM_IDS(bmain->hairs.first);
+
+ /* pointclouds */
+ RENAMEFIX_ANIM_IDS(bmain->pointclouds.first);
+
+ /* volumes */
+ RENAMEFIX_ANIM_IDS(bmain->volumes.first);
+
/* scenes */
RENAMEFIX_ANIM_NODETREE_IDS(bmain->scenes.first, Scene);
}
@@ -4035,6 +4056,15 @@ void BKE_animsys_evaluate_all_animation(Main *main,
/* cache files */
EVAL_ANIM_IDS(main->cachefiles.first, ADT_RECALC_ANIM);
+ /* hairs */
+ EVAL_ANIM_IDS(main->hairs.first, ADT_RECALC_ANIM);
+
+ /* pointclouds */
+ EVAL_ANIM_IDS(main->pointclouds.first, ADT_RECALC_ANIM);
+
+ /* volumes */
+ EVAL_ANIM_IDS(main->volumes.first, ADT_RECALC_ANIM);
+
/* objects */
/* ADT_RECALC_ANIM doesn't need to be supplied here, since object AnimData gets
* this tagged by Depsgraph on framechange. This optimization means that objects
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index ef049afc0f3..7e3e629d328 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -61,6 +61,7 @@
#include "DNA_scene_types.h"
#include "DNA_fluid_types.h"
#include "DNA_freestyle_types.h"
+#include "DNA_volume_types.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
@@ -645,6 +646,13 @@ void BKE_bpath_traverse_id(
}
break;
}
+ case ID_VO: {
+ Volume *volume = (Volume *)id;
+ if (volume->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
+ rewrite_path_fixed(volume->filepath, visit_cb, absbase, bpath_user_data);
+ }
+ break;
+ }
case ID_TXT:
if (((Text *)id)->name) {
rewrite_path_alloc(&((Text *)id)->name, visit_cb, absbase, bpath_user_data);
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index 33707d3f18d..117c96e2932 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -30,7 +30,9 @@
#define DNA_DEPRECATED_ALLOW
#include "DNA_customdata_types.h"
+#include "DNA_hair_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_ID.h"
#include "BLI_utildefines.h"
@@ -1623,6 +1625,14 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
{sizeof(short[2]), "vec2s", 1, NULL, NULL, NULL, NULL, NULL, NULL},
/* 42: CD_SCULPT_FACE_SETS */
{sizeof(int), "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* 43: CD_LOCATION */
+ {sizeof(float[3]), "vec3f", 1, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* 44: CD_RADIUS */
+ {sizeof(float), "MFloatProperty", 1, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* 45: CD_HAIRCURVE */
+ {sizeof(HairCurve), "HairCurve", 1, NULL, NULL, NULL, NULL, NULL, NULL},
+ /* 46: CD_HAIR_MAPPING */
+ {sizeof(HairMapping), "HairMapping", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -1667,10 +1677,14 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDMVertSkin",
/* 37-38 */ "CDFreestyleEdge",
"CDFreestyleFace",
- /* 39-41 */ "CDMLoopTangent",
+ /* 39-42 */ "CDMLoopTangent",
"CDTessLoopNormal",
"CDCustomLoopNormal",
"CDSculptFaceGroups",
+ /* 43-46 */ "CDHairPoint",
+ "CDHairCurve",
+ "CDHairMapping",
+ "CDPoint",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c
index b12201df809..2cb91678893 100644
--- a/source/blender/blenkernel/intern/effect.c
+++ b/source/blender/blenkernel/intern/effect.c
@@ -656,6 +656,7 @@ int get_effector_data(EffectorCache *eff,
efd->size = 0.0f;
}
else if (eff->pd && eff->pd->shape == PFIELD_SHAPE_POINTS) {
+ /* TODO: hair and points object support */
Mesh *me_eval = BKE_object_get_evaluated_mesh(eff->ob);
if (me_eval != NULL) {
copy_v3_v3(efd->loc, me_eval->mvert[*efd->index].co);
@@ -769,6 +770,7 @@ static void get_effector_tot(
efd->index = p;
if (eff->pd->shape == PFIELD_SHAPE_POINTS) {
+ /* TODO: hair and points object support */
Mesh *me_eval = BKE_object_get_evaluated_mesh(eff->ob);
*tot = me_eval != NULL ? me_eval->totvert : 1;
diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c
new file mode 100644
index 00000000000..1553fb44108
--- /dev/null
+++ b/source/blender/blenkernel/intern/hair.c
@@ -0,0 +1,292 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/hair.c
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_defaults.h"
+#include "DNA_hair_types.h"
+#include "DNA_object_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_customdata.h"
+#include "BKE_idtype.h"
+#include "BKE_global.h"
+#include "BKE_hair.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_lib_remap.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+
+#include "BLT_translation.h"
+
+#include "DEG_depsgraph_query.h"
+
+/* Hair datablock */
+
+static void hair_random(Hair *hair)
+{
+ const int numpoints = 8;
+
+ hair->totcurve = 500;
+ hair->totpoint = hair->totcurve * numpoints;
+
+ CustomData_realloc(&hair->pdata, hair->totpoint);
+ CustomData_realloc(&hair->cdata, hair->totcurve);
+ BKE_hair_update_customdata_pointers(hair);
+
+ RNG *rng = BLI_rng_new(0);
+
+ for (int i = 0; i < hair->totcurve; i++) {
+ HairCurve *curve = &hair->curves[i];
+ curve->firstpoint = i * numpoints;
+ curve->numpoints = numpoints;
+
+ float theta = 2.0f * M_PI * BLI_rng_get_float(rng);
+ float phi = saacosf(2.0f * BLI_rng_get_float(rng) - 1.0f);
+
+ float no[3] = {sinf(theta) * sinf(phi), cosf(theta) * sinf(phi), cosf(phi)};
+ normalize_v3(no);
+
+ float co[3];
+ copy_v3_v3(co, no);
+
+ float(*curve_co)[3] = hair->co + curve->firstpoint;
+ float *curve_radius = hair->radius + curve->firstpoint;
+ for (int key = 0; key < numpoints; key++) {
+ float t = key / (float)(numpoints - 1);
+ copy_v3_v3(curve_co[key], co);
+ curve_radius[key] = 0.02f * (1.0f - t);
+
+ float offset[3] = {2.0f * BLI_rng_get_float(rng) - 1.0f,
+ 2.0f * BLI_rng_get_float(rng) - 1.0f,
+ 2.0f * BLI_rng_get_float(rng) - 1.0f};
+ add_v3_v3(offset, no);
+ madd_v3_v3fl(co, offset, 1.0f / numpoints);
+ }
+ }
+
+ BLI_rng_free(rng);
+}
+
+static void hair_init_data(ID *id)
+{
+ Hair *hair = (Hair *)id;
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(hair, id));
+
+ MEMCPY_STRUCT_AFTER(hair, DNA_struct_default_get(Hair), id);
+
+ CustomData_reset(&hair->pdata);
+ CustomData_reset(&hair->cdata);
+
+ CustomData_add_layer(&hair->pdata, CD_LOCATION, CD_CALLOC, NULL, hair->totpoint);
+ CustomData_add_layer(&hair->pdata, CD_RADIUS, CD_CALLOC, NULL, hair->totpoint);
+ CustomData_add_layer(&hair->cdata, CD_HAIRCURVE, CD_CALLOC, NULL, hair->totcurve);
+ BKE_hair_update_customdata_pointers(hair);
+
+ hair_random(hair);
+}
+
+void *BKE_hair_add(Main *bmain, const char *name)
+{
+ Hair *hair = BKE_libblock_alloc(bmain, ID_HA, name, 0);
+
+ hair_init_data(&hair->id);
+
+ return hair;
+}
+
+static void hair_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
+{
+ Hair *hair_dst = (Hair *)id_dst;
+ const Hair *hair_src = (const Hair *)id_src;
+ hair_dst->mat = MEM_dupallocN(hair_dst->mat);
+
+ const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
+ CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, alloc_type, hair_dst->totpoint);
+ CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, alloc_type, hair_dst->totcurve);
+ BKE_hair_update_customdata_pointers(hair_dst);
+
+ hair_dst->batch_cache = NULL;
+}
+
+Hair *BKE_hair_copy(Main *bmain, const Hair *hair)
+{
+ Hair *hair_copy;
+ BKE_id_copy(bmain, &hair->id, (ID **)&hair_copy);
+ return hair_copy;
+}
+
+static void hair_make_local(Main *bmain, ID *id, const int flags)
+{
+ BKE_lib_id_make_local_generic(bmain, id, flags);
+}
+
+static void hair_free_data(ID *id)
+{
+ Hair *hair = (Hair *)id;
+ BKE_animdata_free(&hair->id, false);
+
+ BKE_hair_batch_cache_free(hair);
+
+ CustomData_free(&hair->pdata, hair->totpoint);
+ CustomData_free(&hair->cdata, hair->totcurve);
+
+ MEM_SAFE_FREE(hair->mat);
+}
+
+IDTypeInfo IDType_ID_HA = {
+ .id_code = ID_HA,
+ .id_filter = FILTER_ID_HA,
+ .main_listbase_index = INDEX_ID_HA,
+ .struct_size = sizeof(Hair),
+ .name = "Hair",
+ .name_plural = "hairs",
+ .translation_context = BLT_I18NCONTEXT_ID_HAIR,
+ .flags = 0,
+
+ .init_data = hair_init_data,
+ .copy_data = hair_copy_data,
+ .free_data = hair_free_data,
+ .make_local = hair_make_local,
+};
+
+BoundBox *BKE_hair_boundbox_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_HAIR);
+ Hair *hair = ob->data;
+
+ if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ return ob->runtime.bb;
+ }
+
+ if (ob->runtime.bb == NULL) {
+ ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "hair boundbox");
+
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+
+ float(*hair_co)[3] = hair->co;
+ float *hair_radius = hair->radius;
+ for (int a = 0; a < hair->totpoint; a++) {
+ float *co = hair_co[a];
+ float radius = (hair_radius) ? hair_radius[a] : 0.0f;
+ float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
+ float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
+ DO_MIN(co_min, min);
+ DO_MAX(co_max, max);
+ }
+
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ }
+
+ return ob->runtime.bb;
+}
+
+void BKE_hair_update_customdata_pointers(Hair *hair)
+{
+ hair->co = CustomData_get_layer(&hair->pdata, CD_LOCATION);
+ hair->radius = CustomData_get_layer(&hair->pdata, CD_RADIUS);
+ hair->curves = CustomData_get_layer(&hair->cdata, CD_HAIRCURVE);
+ hair->mapping = CustomData_get_layer(&hair->cdata, CD_HAIRMAPPING);
+}
+
+/* Dependency Graph */
+
+Hair *BKE_hair_new_for_eval(const Hair *hair_src, int totpoint, int totcurve)
+{
+ Hair *hair_dst = BKE_id_new_nomain(ID_HA, NULL);
+
+ STRNCPY(hair_dst->id.name, hair_src->id.name);
+ hair_dst->mat = MEM_dupallocN(hair_src->mat);
+ hair_dst->totcol = hair_src->totcol;
+
+ hair_dst->totpoint = totpoint;
+ hair_dst->totcurve = totcurve;
+ CustomData_copy(&hair_src->pdata, &hair_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
+ CustomData_copy(&hair_src->cdata, &hair_dst->cdata, CD_MASK_ALL, CD_CALLOC, totcurve);
+ BKE_hair_update_customdata_pointers(hair_dst);
+
+ return hair_dst;
+}
+
+Hair *BKE_hair_copy_for_eval(Hair *hair_src, bool reference)
+{
+ int flags = LIB_ID_COPY_LOCALIZE;
+
+ if (reference) {
+ flags |= LIB_ID_COPY_CD_REFERENCE;
+ }
+
+ Hair *result;
+ BKE_id_copy_ex(NULL, &hair_src->id, (ID **)&result, flags);
+ return result;
+}
+
+static Hair *hair_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
+ struct Scene *UNUSED(scene),
+ Object *UNUSED(object),
+ Hair *hair_input)
+{
+ return hair_input;
+}
+
+void BKE_hair_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
+{
+ /* Free any evaluated data and restore original data. */
+ BKE_object_free_derived_caches(object);
+
+ /* Evaluate modifiers. */
+ Hair *hair = object->data;
+ Hair *hair_eval = hair_evaluate_modifiers(depsgraph, scene, object, hair);
+
+ /* Assign evaluated object. */
+ const bool is_owned = (hair != hair_eval);
+ BKE_object_eval_assign_data(object, &hair_eval->id, is_owned);
+}
+
+/* Draw Cache */
+void (*BKE_hair_batch_cache_dirty_tag_cb)(Hair *hair, int mode) = NULL;
+void (*BKE_hair_batch_cache_free_cb)(Hair *hair) = NULL;
+
+void BKE_hair_batch_cache_dirty_tag(Hair *hair, int mode)
+{
+ if (hair->batch_cache) {
+ BKE_hair_batch_cache_dirty_tag_cb(hair, mode);
+ }
+}
+
+void BKE_hair_batch_cache_free(Hair *hair)
+{
+ if (hair->batch_cache) {
+ BKE_hair_batch_cache_free_cb(hair);
+ }
+}
diff --git a/source/blender/blenkernel/intern/idcode.c b/source/blender/blenkernel/intern/idcode.c
index b9ca77ceb67..a6930a521af 100644
--- a/source/blender/blenkernel/intern/idcode.c
+++ b/source/blender/blenkernel/intern/idcode.c
@@ -61,7 +61,7 @@ static IDType idtypes[] = {
{ID_GR, "Collection", "collections", BLT_I18NCONTEXT_ID_COLLECTION, IDTYPE_FLAGS_ISLINKABLE},
{ID_CU, "Curve", "curves", BLT_I18NCONTEXT_ID_CURVE, IDTYPE_FLAGS_ISLINKABLE},
{ID_GD, "GPencil", "grease_pencils", BLT_I18NCONTEXT_ID_GPENCIL, IDTYPE_FLAGS_ISLINKABLE}, /* rename gpencil */
-
+ {ID_HA, "Hair", "hair", BLT_I18NCONTEXT_ID_HAIR, IDTYPE_FLAGS_ISLINKABLE},
{ID_IM, "Image", "images", BLT_I18NCONTEXT_ID_IMAGE, IDTYPE_FLAGS_ISLINKABLE},
{ID_IP, "Ipo", "ipos", "", IDTYPE_FLAGS_ISLINKABLE}, /* deprecated */
{ID_KE, "Key", "shape_keys", BLT_I18NCONTEXT_ID_SHAPEKEY, 0 },
@@ -80,6 +80,7 @@ static IDType idtypes[] = {
{ID_PAL, "Palettes", "palettes", BLT_I18NCONTEXT_ID_PALETTE, IDTYPE_FLAGS_ISLINKABLE},
{ID_PC, "PaintCurve", "paint_curves", BLT_I18NCONTEXT_ID_PAINTCURVE, IDTYPE_FLAGS_ISLINKABLE},
{ID_LP, "LightProbe", "lightprobes", BLT_I18NCONTEXT_ID_LIGHTPROBE, IDTYPE_FLAGS_ISLINKABLE},
+ {ID_PT, "PointCloud", "pointclouds", BLT_I18NCONTEXT_ID_POINTCLOUD, IDTYPE_FLAGS_ISLINKABLE},
{ID_SCE, "Scene", "scenes", BLT_I18NCONTEXT_ID_SCENE, IDTYPE_FLAGS_ISLINKABLE},
{ID_SCR, "Screen", "screens", BLT_I18NCONTEXT_ID_SCREEN, IDTYPE_FLAGS_ISLINKABLE},
{ID_SEQ, "Sequence", "sequences", BLT_I18NCONTEXT_ID_SEQUENCE, 0 }, /* not actually ID data */
@@ -88,6 +89,7 @@ static IDType idtypes[] = {
{ID_TE, "Texture", "textures", BLT_I18NCONTEXT_ID_TEXTURE, IDTYPE_FLAGS_ISLINKABLE},
{ID_TXT, "Text", "texts", BLT_I18NCONTEXT_ID_TEXT, IDTYPE_FLAGS_ISLINKABLE},
{ID_VF, "VFont", "fonts", BLT_I18NCONTEXT_ID_VFONT, IDTYPE_FLAGS_ISLINKABLE},
+ {ID_VO, "Volume", "volumes", BLT_I18NCONTEXT_ID_VOLUME, IDTYPE_FLAGS_ISLINKABLE},
{ID_WO, "World", "worlds", BLT_I18NCONTEXT_ID_WORLD, IDTYPE_FLAGS_ISLINKABLE},
{ID_WM, "WindowManager", "window_managers", BLT_I18NCONTEXT_ID_WINDOWMANAGER, 0 },
{ID_WS, "WorkSpace", "workspaces", BLT_I18NCONTEXT_ID_WORKSPACE, IDTYPE_FLAGS_ISLINKABLE},
@@ -215,6 +217,9 @@ uint64_t BKE_idcode_to_idfilter(const short idcode)
CASE_IDFILTER(TE);
CASE_IDFILTER(TXT);
CASE_IDFILTER(VF);
+ CASE_IDFILTER(HA);
+ CASE_IDFILTER(PT);
+ CASE_IDFILTER(VO);
CASE_IDFILTER(WO);
CASE_IDFILTER(WS);
default:
@@ -242,8 +247,10 @@ short BKE_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(CU);
CASE_IDFILTER(GD);
CASE_IDFILTER(GR);
+ CASE_IDFILTER(HA);
CASE_IDFILTER(IM);
CASE_IDFILTER(LA);
+ CASE_IDFILTER(LP);
CASE_IDFILTER(LS);
CASE_IDFILTER(LT);
CASE_IDFILTER(MA);
@@ -256,13 +263,14 @@ short BKE_idcode_from_idfilter(const uint64_t idfilter)
CASE_IDFILTER(PA);
CASE_IDFILTER(PAL);
CASE_IDFILTER(PC);
- CASE_IDFILTER(LP);
+ CASE_IDFILTER(PT);
CASE_IDFILTER(SCE);
CASE_IDFILTER(SPK);
CASE_IDFILTER(SO);
CASE_IDFILTER(TE);
CASE_IDFILTER(TXT);
CASE_IDFILTER(VF);
+ CASE_IDFILTER(VO);
CASE_IDFILTER(WO);
default:
return 0;
@@ -289,11 +297,13 @@ int BKE_idcode_to_index(const short idcode)
CASE_IDINDEX(CU);
CASE_IDINDEX(GD);
CASE_IDINDEX(GR);
+ CASE_IDINDEX(HA);
CASE_IDINDEX(IM);
CASE_IDINDEX(KE);
CASE_IDINDEX(IP);
CASE_IDINDEX(LA);
CASE_IDINDEX(LI);
+ CASE_IDINDEX(LP);
CASE_IDINDEX(LS);
CASE_IDINDEX(LT);
CASE_IDINDEX(MA);
@@ -306,7 +316,7 @@ int BKE_idcode_to_index(const short idcode)
CASE_IDINDEX(PA);
CASE_IDINDEX(PAL);
CASE_IDINDEX(PC);
- CASE_IDINDEX(LP);
+ CASE_IDINDEX(PT);
CASE_IDINDEX(SCE);
CASE_IDINDEX(SCR);
CASE_IDINDEX(SPK);
@@ -314,6 +324,7 @@ int BKE_idcode_to_index(const short idcode)
CASE_IDINDEX(TE);
CASE_IDINDEX(TXT);
CASE_IDINDEX(VF);
+ CASE_IDINDEX(VO);
CASE_IDINDEX(WM);
CASE_IDINDEX(WO);
CASE_IDINDEX(WS);
@@ -343,11 +354,13 @@ short BKE_idcode_from_index(const int index)
CASE_IDCODE(CU);
CASE_IDCODE(GD);
CASE_IDCODE(GR);
+ CASE_IDCODE(HA);
CASE_IDCODE(IM);
CASE_IDCODE(KE);
CASE_IDCODE(IP);
CASE_IDCODE(LA);
CASE_IDCODE(LI);
+ CASE_IDCODE(LP);
CASE_IDCODE(LS);
CASE_IDCODE(LT);
CASE_IDCODE(MA);
@@ -360,7 +373,7 @@ short BKE_idcode_from_index(const int index)
CASE_IDCODE(PA);
CASE_IDCODE(PAL);
CASE_IDCODE(PC);
- CASE_IDCODE(LP);
+ CASE_IDCODE(PT);
CASE_IDCODE(SCE);
CASE_IDCODE(SCR);
CASE_IDCODE(SPK);
@@ -368,6 +381,7 @@ short BKE_idcode_from_index(const int index)
CASE_IDCODE(TE);
CASE_IDCODE(TXT);
CASE_IDCODE(VF);
+ CASE_IDCODE(VO);
CASE_IDCODE(WM);
CASE_IDCODE(WO);
CASE_IDCODE(WS);
diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c
index ff4d06cd011..ce2835717a0 100644
--- a/source/blender/blenkernel/intern/idtype.c
+++ b/source/blender/blenkernel/intern/idtype.c
@@ -87,6 +87,9 @@ static void id_type_init(void)
INIT_TYPE(ID_CF);
INIT_TYPE(ID_WS);
INIT_TYPE(ID_LP);
+ INIT_TYPE(ID_HA);
+ INIT_TYPE(ID_PT);
+ INIT_TYPE(ID_VO);
#undef INIT_TYPE
}
diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c
index 1ae53f5a85d..9c4bef04adf 100644
--- a/source/blender/blenkernel/intern/lib_id.c
+++ b/source/blender/blenkernel/intern/lib_id.c
@@ -43,6 +43,7 @@
#include "DNA_camera_types.h"
#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_hair_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
@@ -55,6 +56,7 @@
#include "DNA_mask_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
@@ -62,6 +64,7 @@
#include "DNA_sound_types.h"
#include "DNA_text_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_world_types.h"
#include "DNA_workspace_types.h"
@@ -89,6 +92,7 @@
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_hair.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_idtype.h"
@@ -110,6 +114,7 @@
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "BKE_pointcloud.h"
#include "BKE_lightprobe.h"
#include "BKE_rigidbody.h"
#include "BKE_sound.h"
@@ -117,6 +122,7 @@
#include "BKE_scene.h"
#include "BKE_text.h"
#include "BKE_texture.h"
+#include "BKE_volume.h"
#include "BKE_world.h"
#include "DEG_depsgraph.h"
@@ -645,6 +651,9 @@ static void id_swap(Main *bmain, ID *id_a, ID *id_b, const bool do_full_id)
CASE_SWAP(ID_PAL, Palette);
CASE_SWAP(ID_PC, PaintCurve);
CASE_SWAP(ID_CF, CacheFile);
+ CASE_SWAP(ID_HA, Hair);
+ CASE_SWAP(ID_PT, PointCloud);
+ CASE_SWAP(ID_VO, Volume);
case ID_IP:
break; /* Deprecated. */
}
@@ -1017,6 +1026,9 @@ size_t BKE_libblock_get_alloc_info(short type, const char **name)
CASE_RETURN(ID_PC, PaintCurve);
CASE_RETURN(ID_CF, CacheFile);
CASE_RETURN(ID_WS, WorkSpace);
+ CASE_RETURN(ID_HA, Hair);
+ CASE_RETURN(ID_PT, PointCloud);
+ CASE_RETURN(ID_VO, Volume);
}
return 0;
#undef CASE_RETURN
@@ -1182,8 +1194,8 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || bmain != NULL);
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) != 0 || (flag & LIB_ID_CREATE_NO_ALLOCATE) == 0);
if (!is_private_id_data) {
- /* When we are handling private ID data, we might still want to manage usercounts, even though
- * that ID data-block is actually outside of Main... */
+ /* When we are handling private ID data, we might still want to manage usercounts, even
+ * though that ID data-block is actually outside of Main... */
BLI_assert((flag & LIB_ID_CREATE_NO_MAIN) == 0 ||
(flag & LIB_ID_CREATE_NO_USER_REFCOUNT) != 0);
}
@@ -1394,10 +1406,10 @@ void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
/**
* Helper building final ID name from given base_name and number.
*
- * If everything goes well and we do generate a valid final ID name in given name, we return true.
- * In case the final name would overflow the allowed ID name length, or given number is bigger than
- * maximum allowed value, we truncate further the base_name (and given name, which is assumed to
- * have the same 'base_name' part), and return false.
+ * If everything goes well and we do generate a valid final ID name in given name, we return
+ * true. In case the final name would overflow the allowed ID name length, or given number is
+ * bigger than maximum allowed value, we truncate further the base_name (and given name, which is
+ * assumed to have the same 'base_name' part), and return false.
*/
static bool id_name_final_build(char *name, char *base_name, size_t base_name_len, int number)
{
@@ -1459,10 +1471,10 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
static short prev_id_type = ID_LINK_PLACEHOLDER; /* Should never exist in actual ID list. */
static int prev_number = MIN_NUMBER - 1;
- /* Initial test to check whether we can 'shortcut' the more complex loop of the main code below.
- * Note that we do not do that for low numbers, as that would prevent using actual smallest
- * available number in some cases, and benefits of this special case handling mostly show up with
- * high numbers anyway. */
+ /* Initial test to check whether we can 'shortcut' the more complex loop of the main code
+ * below. Note that we do not do that for low numbers, as that would prevent using actual
+ * smallest available number in some cases, and benefits of this special case handling mostly
+ * show up with high numbers anyway. */
if (id_type == prev_id_type && prev_number >= MAX_NUMBERS_IN_USE &&
prev_number < MAX_NUMBER - 1 && name[0] == prev_final_base_name[0]) {
@@ -1475,8 +1487,8 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
if (base_name_len == prev_orig_base_name_len &&
STREQLEN(base_name, prev_orig_base_name, prev_orig_base_name_len)) {
- /* Once we have ensured given base_name and original previous one are the same, we can check
- * that previously used number is actually used, and that next one is free. */
+ /* Once we have ensured given base_name and original previous one are the same, we can
+ * check that previously used number is actually used, and that next one is free. */
/* Note that from now on, we only used previous final base name, as it might have been
* truncated from original one due to number suffix length. */
char final_name[MAX_ID_NAME - 2];
@@ -1552,8 +1564,8 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
STREQLEN(name, id_test->name + 2, base_name_len) &&
(BLI_split_name_num(base_name_test, &number_test, id_test->name + 2, '.') ==
base_name_len)) {
- /* If we did not yet encounter exact same name as the given one, check the remaining parts
- * of the strings. */
+ /* If we did not yet encounter exact same name as the given one, check the remaining
+ * parts of the strings. */
if (!is_orig_name_used) {
is_orig_name_used = STREQ(name + base_name_len, id_test->name + 2 + base_name_len);
}
@@ -1570,11 +1582,13 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
}
/* If there is no double, we are done.
- * Note however that name might have been changed (truncated) in a previous iteration already.
+ * Note however that name might have been changed (truncated) in a previous iteration
+ * already.
*/
if (!is_orig_name_used) {
/* Don't bother updating prev_ static variables here, this case is not supposed to happen
- * that often, and is not straight-forward here, so just ignore and reset them to default. */
+ * that often, and is not straight-forward here, so just ignore and reset them to default.
+ */
prev_id_type = ID_LINK_PLACEHOLDER;
prev_final_base_name[0] = '\0';
prev_number = MIN_NUMBER - 1;
@@ -1585,8 +1599,8 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
return is_name_changed;
}
- /* Decide which value of number to use, either the smallest unused one if possible, or default
- * to the first largest unused one we got from previous loop. */
+ /* Decide which value of number to use, either the smallest unused one if possible, or
+ * default to the first largest unused one we got from previous loop. */
for (int i = MIN_NUMBER; i < MAX_NUMBERS_IN_USE; i++) {
if (ids_in_use[i] == NULL) {
number = i;
@@ -1605,8 +1619,8 @@ static bool check_for_dupid(ListBase *lb, ID *id, char *name, ID **r_id_sorting_
/* We know for wure that name will be changed. */
is_name_changed = true;
- /* If id_name_final_build helper returns false, it had to truncate further given name, hence we
- * have to go over the whole check again. */
+ /* If id_name_final_build helper returns false, it had to truncate further given name, hence
+ * we have to go over the whole check again. */
if (!id_name_final_build(name, base_name, base_name_len, number)) {
/* We have to clear our list of small used numbers before we do the whole check again. */
memset(ids_in_use, 0, sizeof(ids_in_use));
diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c
index c204c272de1..67065a1d715 100644
--- a/source/blender/blenkernel/intern/lib_query.c
+++ b/source/blender/blenkernel/intern/lib_query.c
@@ -30,6 +30,7 @@
#include "DNA_collection_types.h"
#include "DNA_constraint_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
#include "DNA_lattice_types.h"
@@ -43,6 +44,7 @@
#include "DNA_object_force_types.h"
#include "DNA_outliner_types.h"
#include "DNA_lightprobe_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
@@ -52,6 +54,7 @@
#include "DNA_sound_types.h"
#include "DNA_text_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
@@ -1250,6 +1253,27 @@ static void library_foreach_ID_link(Main *bmain,
break;
}
+ case ID_HA: {
+ Hair *hair = (Hair *)id;
+ for (i = 0; i < hair->totcol; i++) {
+ CALLBACK_INVOKE(hair->mat[i], IDWALK_CB_USER);
+ }
+ break;
+ }
+ case ID_PT: {
+ PointCloud *pointcloud = (PointCloud *)id;
+ for (i = 0; i < pointcloud->totcol; i++) {
+ CALLBACK_INVOKE(pointcloud->mat[i], IDWALK_CB_USER);
+ }
+ break;
+ }
+ case ID_VO: {
+ Volume *volume = (Volume *)id;
+ for (i = 0; i < volume->totcol; i++) {
+ CALLBACK_INVOKE(volume->mat[i], IDWALK_CB_USER);
+ }
+ break;
+ }
case ID_SCR: {
if (data.flag & IDWALK_INCLUDE_UI) {
@@ -1416,6 +1440,12 @@ bool BKE_library_id_can_use_idtype(ID *id_owner, const short id_type_used)
return ELEM(id_type_used, ID_MA);
case ID_WS:
return ELEM(id_type_used, ID_SCR, ID_SCE);
+ case ID_HA:
+ return ELEM(id_type_used, ID_MA);
+ case ID_PT:
+ return ELEM(id_type_used, ID_MA);
+ case ID_VO:
+ return ELEM(id_type_used, ID_MA);
case ID_IM:
case ID_VF:
case ID_TXT:
diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c
index 1a22a6e6f79..578d4be44d3 100644
--- a/source/blender/blenkernel/intern/lib_remap.c
+++ b/source/blender/blenkernel/intern/lib_remap.c
@@ -520,6 +520,9 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const
case ID_ME:
case ID_CU:
case ID_MB:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
if (new_id) { /* Only affects us in case obdata was relinked (changed). */
for (Object *ob = bmain->objects.first; ob; ob = ob->id.next) {
libblock_remap_data_postprocess_obdata_relink(bmain, ob, new_id);
diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c
index 659c3944edb..caa29f7817a 100644
--- a/source/blender/blenkernel/intern/main.c
+++ b/source/blender/blenkernel/intern/main.c
@@ -473,6 +473,12 @@ ListBase *which_libbase(Main *bmain, short type)
return &(bmain->cachefiles);
case ID_WS:
return &(bmain->workspaces);
+ case ID_HA:
+ return &(bmain->hairs);
+ case ID_PT:
+ return &(bmain->pointclouds);
+ case ID_VO:
+ return &(bmain->volumes);
}
return NULL;
}
@@ -521,6 +527,9 @@ int set_listbasepointers(Main *bmain, ListBase **lb)
lb[INDEX_ID_ME] = &(bmain->meshes);
lb[INDEX_ID_CU] = &(bmain->curves);
lb[INDEX_ID_MB] = &(bmain->metaballs);
+ lb[INDEX_ID_HA] = &(bmain->hairs);
+ lb[INDEX_ID_PT] = &(bmain->pointclouds);
+ lb[INDEX_ID_VO] = &(bmain->volumes);
lb[INDEX_ID_LT] = &(bmain->lattices);
lb[INDEX_ID_LA] = &(bmain->lights);
diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c
index 368eb099579..15f18eef7c8 100644
--- a/source/blender/blenkernel/intern/material.c
+++ b/source/blender/blenkernel/intern/material.c
@@ -37,11 +37,14 @@
#include "DNA_meshdata_types.h"
#include "DNA_customdata_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_hair_types.h"
#include "DNA_ID.h"
#include "DNA_meta_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
+#include "DNA_volume_types.h"
#include "DNA_defaults.h"
#include "BLI_math.h"
@@ -243,53 +246,67 @@ Material *BKE_material_localize(Material *ma)
Material ***BKE_object_material_array_p(Object *ob)
{
- Mesh *me;
- Curve *cu;
- MetaBall *mb;
- bGPdata *gpd;
-
if (ob->type == OB_MESH) {
- me = ob->data;
+ Mesh *me = ob->data;
return &(me->mat);
}
else if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) {
- cu = ob->data;
+ Curve *cu = ob->data;
return &(cu->mat);
}
else if (ob->type == OB_MBALL) {
- mb = ob->data;
+ MetaBall *mb = ob->data;
return &(mb->mat);
}
else if (ob->type == OB_GPENCIL) {
- gpd = ob->data;
+ bGPdata *gpd = ob->data;
return &(gpd->mat);
}
+ else if (ob->type == OB_HAIR) {
+ Hair *hair = ob->data;
+ return &(hair->mat);
+ }
+ else if (ob->type == OB_POINTCLOUD) {
+ PointCloud *pointcloud = ob->data;
+ return &(pointcloud->mat);
+ }
+ else if (ob->type == OB_VOLUME) {
+ Volume *volume = ob->data;
+ return &(volume->mat);
+ }
return NULL;
}
short *BKE_object_material_len_p(Object *ob)
{
- Mesh *me;
- Curve *cu;
- MetaBall *mb;
- bGPdata *gpd;
-
if (ob->type == OB_MESH) {
- me = ob->data;
+ Mesh *me = ob->data;
return &(me->totcol);
}
else if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) {
- cu = ob->data;
+ Curve *cu = ob->data;
return &(cu->totcol);
}
else if (ob->type == OB_MBALL) {
- mb = ob->data;
+ MetaBall *mb = ob->data;
return &(mb->totcol);
}
else if (ob->type == OB_GPENCIL) {
- gpd = ob->data;
+ bGPdata *gpd = ob->data;
return &(gpd->totcol);
}
+ else if (ob->type == OB_HAIR) {
+ Hair *hair = ob->data;
+ return &(hair->totcol);
+ }
+ else if (ob->type == OB_POINTCLOUD) {
+ PointCloud *pointcloud = ob->data;
+ return &(pointcloud->totcol);
+ }
+ else if (ob->type == OB_VOLUME) {
+ Volume *volume = ob->data;
+ return &(volume->totcol);
+ }
return NULL;
}
@@ -308,6 +325,12 @@ Material ***BKE_id_material_array_p(ID *id)
return &(((MetaBall *)id)->mat);
case ID_GD:
return &(((bGPdata *)id)->mat);
+ case ID_HA:
+ return &(((Hair *)id)->mat);
+ case ID_PT:
+ return &(((PointCloud *)id)->mat);
+ case ID_VO:
+ return &(((Volume *)id)->mat);
default:
break;
}
@@ -328,6 +351,12 @@ short *BKE_id_material_len_p(ID *id)
return &(((MetaBall *)id)->totcol);
case ID_GD:
return &(((bGPdata *)id)->totcol);
+ case ID_HA:
+ return &(((Hair *)id)->totcol);
+ case ID_PT:
+ return &(((PointCloud *)id)->totcol);
+ case ID_VO:
+ return &(((Volume *)id)->totcol);
default:
break;
}
@@ -347,7 +376,10 @@ static void material_data_index_remove_id(ID *id, short index)
BKE_curve_material_index_remove((Curve *)id, index);
break;
case ID_MB:
- /* meta-elems don't have materials atm */
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
+ /* No material indices for these object data types. */
break;
default:
break;
@@ -387,7 +419,10 @@ static void material_data_index_clear_id(ID *id)
BKE_curve_material_index_clear((Curve *)id);
break;
case ID_MB:
- /* meta-elems don't have materials atm */
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
+ /* No material indices for these object data types. */
break;
default:
break;
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index fa3284c18d6..4e8ae3d57d5 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -118,6 +118,9 @@
#include "BKE_camera.h"
#include "BKE_image.h"
#include "BKE_gpencil.h"
+#include "BKE_hair.h"
+#include "BKE_pointcloud.h"
+#include "BKE_volume.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
@@ -1013,6 +1016,12 @@ static const char *get_obdata_defname(int type)
return DATA_("Armature");
case OB_SPEAKER:
return DATA_("Speaker");
+ case OB_HAIR:
+ return DATA_("Hair");
+ case OB_POINTCLOUD:
+ return DATA_("PointCloud");
+ case OB_VOLUME:
+ return DATA_("Volume");
case OB_EMPTY:
return DATA_("Empty");
case OB_GPENCIL:
@@ -1076,6 +1085,12 @@ void *BKE_object_obdata_add_from_type(Main *bmain, int type, const char *name)
return BKE_lightprobe_add(bmain, name);
case OB_GPENCIL:
return BKE_gpencil_data_addnew(bmain, name);
+ case OB_HAIR:
+ return BKE_hair_add(bmain, name);
+ case OB_POINTCLOUD:
+ return BKE_pointcloud_add(bmain, name);
+ case OB_VOLUME:
+ return BKE_volume_add(bmain, name);
case OB_EMPTY:
return NULL;
default:
@@ -1768,6 +1783,39 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
id_us_min(id);
}
break;
+ case OB_HAIR:
+ if (dupflag & USER_DUP_HAIR) {
+ ID_NEW_REMAP_US2(obn->data)
+ else
+ {
+ obn->data = ID_NEW_SET(obn->data, BKE_hair_copy(bmain, obn->data));
+ didit = 1;
+ }
+ id_us_min(id);
+ }
+ break;
+ case OB_POINTCLOUD:
+ if (dupflag & USER_DUP_POINTCLOUD) {
+ ID_NEW_REMAP_US2(obn->data)
+ else
+ {
+ obn->data = ID_NEW_SET(obn->data, BKE_pointcloud_copy(bmain, obn->data));
+ didit = 1;
+ }
+ id_us_min(id);
+ }
+ break;
+ case OB_VOLUME:
+ if (dupflag & USER_DUP_VOLUME) {
+ ID_NEW_REMAP_US2(obn->data)
+ else
+ {
+ obn->data = ID_NEW_SET(obn->data, BKE_volume_copy(bmain, obn->data));
+ didit = 1;
+ }
+ id_us_min(id);
+ }
+ break;
}
/* Check if obdata is copied. */
@@ -2810,6 +2858,15 @@ BoundBox *BKE_object_boundbox_get(Object *ob)
case OB_GPENCIL:
bb = BKE_gpencil_boundbox_get(ob);
break;
+ case OB_HAIR:
+ bb = BKE_hair_boundbox_get(ob);
+ break;
+ case OB_POINTCLOUD:
+ bb = BKE_pointcloud_boundbox_get(ob);
+ break;
+ case OB_VOLUME:
+ bb = BKE_volume_boundbox_get(ob);
+ break;
default:
break;
}
@@ -2980,6 +3037,25 @@ void BKE_object_minmax(Object *ob, float min_r[3], float max_r[3], const bool us
}
break;
}
+ case OB_HAIR: {
+ bb = *BKE_hair_boundbox_get(ob);
+ BKE_boundbox_minmax(&bb, ob->obmat, min_r, max_r);
+ changed = true;
+ break;
+ }
+
+ case OB_POINTCLOUD: {
+ bb = *BKE_pointcloud_boundbox_get(ob);
+ BKE_boundbox_minmax(&bb, ob->obmat, min_r, max_r);
+ changed = true;
+ break;
+ }
+ case OB_VOLUME: {
+ bb = *BKE_volume_boundbox_get(ob);
+ BKE_boundbox_minmax(&bb, ob->obmat, min_r, max_r);
+ changed = true;
+ break;
+ }
}
if (changed == false) {
@@ -3125,6 +3201,7 @@ void BKE_object_foreach_display_point(Object *ob,
void (*func_cb)(const float[3], void *),
void *user_data)
{
+ /* TODO: pointcloud and hair objects support */
Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob);
float co[3];
diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c
index 20b2ab9409f..66c3b2ea26e 100644
--- a/source/blender/blenkernel/intern/object_update.c
+++ b/source/blender/blenkernel/intern/object_update.c
@@ -45,6 +45,7 @@
#include "BKE_editmesh.h"
#include "BKE_effect.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_hair.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_layer.h"
@@ -56,8 +57,10 @@
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
+#include "BKE_pointcloud.h"
#include "BKE_scene.h"
#include "BKE_gpencil.h"
+#include "BKE_volume.h"
#include "MEM_guardedalloc.h"
@@ -225,6 +228,15 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o
BKE_gpencil_update_layer_parent(depsgraph, ob);
break;
}
+ case OB_HAIR:
+ BKE_hair_data_update(depsgraph, scene, ob);
+ break;
+ case OB_POINTCLOUD:
+ BKE_pointcloud_data_update(depsgraph, scene, ob);
+ break;
+ case OB_VOLUME:
+ BKE_volume_data_update(depsgraph, scene, ob);
+ break;
}
/* particles */
@@ -354,6 +366,15 @@ void BKE_object_batch_cache_dirty_tag(Object *ob)
case OB_GPENCIL:
BKE_gpencil_batch_cache_dirty_tag(ob->data);
break;
+ case OB_HAIR:
+ BKE_hair_batch_cache_dirty_tag(ob->data, BKE_HAIR_BATCH_DIRTY_ALL);
+ break;
+ case OB_POINTCLOUD:
+ BKE_pointcloud_batch_cache_dirty_tag(ob->data, BKE_POINTCLOUD_BATCH_DIRTY_ALL);
+ break;
+ case OB_VOLUME:
+ BKE_volume_batch_cache_dirty_tag(ob->data, BKE_VOLUME_BATCH_DIRTY_ALL);
+ break;
}
}
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index d69527e8626..34e86b29540 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -37,6 +37,7 @@
#include "DNA_ID.h"
#include "DNA_packedFile_types.h"
#include "DNA_sound_types.h"
+#include "DNA_volume_types.h"
#include "DNA_vfont_types.h"
#include "BLI_blenlib.h"
@@ -48,6 +49,7 @@
#include "BKE_packedFile.h"
#include "BKE_report.h"
#include "BKE_sound.h"
+#include "BKE_volume.h"
int BKE_packedfile_seek(PackedFile *pf, int offset, int whence)
{
@@ -114,6 +116,7 @@ int BKE_packedfile_count_all(Main *bmain)
Image *ima;
VFont *vf;
bSound *sound;
+ Volume *volume;
int count = 0;
/* let's check if there are packed files... */
@@ -135,6 +138,12 @@ int BKE_packedfile_count_all(Main *bmain)
}
}
+ for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
+ if (volume->packedfile) {
+ count++;
+ }
+ }
+
return count;
}
@@ -234,6 +243,7 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
Image *ima;
VFont *vfont;
bSound *sound;
+ Volume *volume;
int tot = 0;
for (ima = bmain->images.first; ima; ima = ima->id.next) {
@@ -266,6 +276,14 @@ void BKE_packedfile_pack_all(Main *bmain, ReportList *reports, bool verbose)
}
}
+ for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
+ if (volume->packedfile == NULL && !ID_IS_LINKED(volume)) {
+ volume->packedfile = BKE_packedfile_new(
+ reports, volume->filepath, BKE_main_blendfile_path(bmain));
+ tot++;
+ }
+ }
+
if (tot > 0) {
BKE_reportf(reports, RPT_INFO, "Packed %d file(s)", tot);
}
@@ -524,6 +542,9 @@ static void unpack_generate_paths(const char *name,
case ID_IM:
BLI_snprintf(r_relpath, relpathlen, "//textures/%s", tempname);
break;
+ case ID_VO:
+ BLI_snprintf(r_relpath, relpathlen, "//volumes/%s", tempname);
+ break;
default:
break;
}
@@ -643,6 +664,36 @@ int BKE_packedfile_unpack_image(Main *bmain,
return (ret_value);
}
+int BKE_packedfile_unpack_volume(Main *bmain,
+ ReportList *reports,
+ Volume *volume,
+ enum ePF_FileStatus how)
+{
+ char localname[FILE_MAX], absname[FILE_MAX];
+ char *newfilepath;
+ int ret_value = RET_ERROR;
+
+ if (volume != NULL) {
+ unpack_generate_paths(
+ volume->filepath, (ID *)volume, absname, localname, sizeof(absname), sizeof(localname));
+ newfilepath = BKE_packedfile_unpack_to_file(
+ reports, BKE_main_blendfile_path(bmain), absname, localname, volume->packedfile, how);
+ if (newfilepath != NULL) {
+ BLI_strncpy(volume->filepath, newfilepath, sizeof(volume->filepath));
+ MEM_freeN(newfilepath);
+
+ BKE_packedfile_free(volume->packedfile);
+ volume->packedfile = NULL;
+
+ BKE_volume_unload(volume);
+
+ ret_value = RET_OK;
+ }
+ }
+
+ return (ret_value);
+}
+
int BKE_packedfile_unpack_all_libraries(Main *bmain, ReportList *reports)
{
Library *lib;
@@ -702,6 +753,7 @@ void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileSt
Image *ima;
VFont *vf;
bSound *sound;
+ Volume *volume;
for (ima = bmain->images.first; ima; ima = ima->id.next) {
if (BKE_image_has_packedfile(ima)) {
@@ -720,6 +772,12 @@ void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileSt
BKE_packedfile_unpack_sound(bmain, reports, sound, how);
}
}
+
+ for (volume = bmain->volumes.first; volume; volume = volume->id.next) {
+ if (volume->packedfile) {
+ BKE_packedfile_unpack_volume(bmain, reports, volume, how);
+ }
+ }
}
/* ID should be not NULL, return 1 if there's a packed file */
@@ -738,6 +796,10 @@ bool BKE_packedfile_id_check(ID *id)
bSound *snd = (bSound *)id;
return snd->packedfile != NULL;
}
+ case ID_VO: {
+ Volume *volume = (Volume *)id;
+ return volume->packedfile != NULL;
+ }
case ID_LI: {
Library *li = (Library *)id;
return li->packedfile != NULL;
@@ -773,6 +835,13 @@ void BKE_packedfile_id_unpack(Main *bmain, ID *id, ReportList *reports, enum ePF
}
break;
}
+ case ID_VO: {
+ Volume *volume = (Volume *)id;
+ if (volume->packedfile) {
+ BKE_packedfile_unpack_volume(bmain, reports, volume, how);
+ }
+ break;
+ }
case ID_LI: {
Library *li = (Library *)id;
BKE_reportf(reports, RPT_ERROR, "Cannot unpack individual Library file, '%s'", li->name);
diff --git a/source/blender/blenkernel/intern/pointcloud.c b/source/blender/blenkernel/intern/pointcloud.c
new file mode 100644
index 00000000000..cc68e93a116
--- /dev/null
+++ b/source/blender/blenkernel/intern/pointcloud.c
@@ -0,0 +1,257 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/pointcloud.c
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_defaults.h"
+#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_customdata.h"
+#include "BKE_idtype.h"
+#include "BKE_global.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_lib_remap.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_pointcloud.h"
+
+#include "BLT_translation.h"
+
+#include "DEG_depsgraph_query.h"
+
+/* PointCloud datablock */
+
+static void pointcloud_random(PointCloud *pointcloud)
+{
+ pointcloud->totpoint = 400;
+ CustomData_realloc(&pointcloud->pdata, pointcloud->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+
+ RNG *rng = BLI_rng_new(0);
+
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ pointcloud->co[i][0] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
+ pointcloud->co[i][1] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
+ pointcloud->co[i][2] = 2.0f * BLI_rng_get_float(rng) - 1.0f;
+ pointcloud->radius[i] = 0.05f * BLI_rng_get_float(rng);
+ }
+
+ BLI_rng_free(rng);
+}
+
+static void pointcloud_init_data(ID *id)
+{
+ PointCloud *pointcloud = (PointCloud *)id;
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(pointcloud, id));
+
+ MEMCPY_STRUCT_AFTER(pointcloud, DNA_struct_default_get(PointCloud), id);
+
+ CustomData_reset(&pointcloud->pdata);
+ CustomData_add_layer(&pointcloud->pdata, CD_LOCATION, CD_CALLOC, NULL, pointcloud->totpoint);
+ CustomData_add_layer(&pointcloud->pdata, CD_RADIUS, CD_CALLOC, NULL, pointcloud->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+
+ pointcloud_random(pointcloud);
+}
+
+void *BKE_pointcloud_add(Main *bmain, const char *name)
+{
+ PointCloud *pointcloud = BKE_libblock_alloc(bmain, ID_PT, name, 0);
+
+ pointcloud_init_data(&pointcloud->id);
+
+ return pointcloud;
+}
+
+static void pointcloud_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag)
+{
+ PointCloud *pointcloud_dst = (PointCloud *)id_dst;
+ const PointCloud *pointcloud_src = (const PointCloud *)id_src;
+ pointcloud_dst->mat = MEM_dupallocN(pointcloud_dst->mat);
+
+ const eCDAllocType alloc_type = (flag & LIB_ID_COPY_CD_REFERENCE) ? CD_REFERENCE : CD_DUPLICATE;
+ CustomData_copy(&pointcloud_src->pdata,
+ &pointcloud_dst->pdata,
+ CD_MASK_ALL,
+ alloc_type,
+ pointcloud_dst->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
+}
+
+PointCloud *BKE_pointcloud_copy(Main *bmain, const PointCloud *pointcloud)
+{
+ PointCloud *pointcloud_copy;
+ BKE_id_copy(bmain, &pointcloud->id, (ID **)&pointcloud_copy);
+ return pointcloud_copy;
+}
+
+static void pointcloud_make_local(Main *bmain, ID *id, const int flags)
+{
+ BKE_lib_id_make_local_generic(bmain, id, flags);
+}
+
+static void pointcloud_free_data(ID *id)
+{
+ PointCloud *pointcloud = (PointCloud *)id;
+ BKE_animdata_free(&pointcloud->id, false);
+ BKE_pointcloud_batch_cache_free(pointcloud);
+ CustomData_free(&pointcloud->pdata, pointcloud->totpoint);
+ MEM_SAFE_FREE(pointcloud->mat);
+}
+
+IDTypeInfo IDType_ID_PT = {
+ .id_code = ID_PT,
+ .id_filter = FILTER_ID_PT,
+ .main_listbase_index = INDEX_ID_PT,
+ .struct_size = sizeof(PointCloud),
+ .name = "PointCloud",
+ .name_plural = "pointclouds",
+ .translation_context = BLT_I18NCONTEXT_ID_POINTCLOUD,
+ .flags = 0,
+
+ .init_data = pointcloud_init_data,
+ .copy_data = pointcloud_copy_data,
+ .free_data = pointcloud_free_data,
+ .make_local = pointcloud_make_local,
+};
+
+BoundBox *BKE_pointcloud_boundbox_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_POINTCLOUD);
+ PointCloud *pointcloud = ob->data;
+
+ if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ return ob->runtime.bb;
+ }
+
+ if (ob->runtime.bb == NULL) {
+ ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "pointcloud boundbox");
+
+ float min[3], max[3];
+ INIT_MINMAX(min, max);
+
+ float(*pointcloud_co)[3] = pointcloud->co;
+ float *pointcloud_radius = pointcloud->radius;
+ for (int a = 0; a < pointcloud->totpoint; a++) {
+ float *co = pointcloud_co[a];
+ float radius = (pointcloud_radius) ? pointcloud_radius[a] : 0.0f;
+ float co_min[3] = {co[0] - radius, co[1] - radius, co[2] - radius};
+ float co_max[3] = {co[0] + radius, co[1] + radius, co[2] + radius};
+ DO_MIN(co_min, min);
+ DO_MAX(co_max, max);
+ }
+
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ }
+
+ return ob->runtime.bb;
+}
+
+void BKE_pointcloud_update_customdata_pointers(PointCloud *pointcloud)
+{
+ pointcloud->co = CustomData_get_layer(&pointcloud->pdata, CD_LOCATION);
+ pointcloud->radius = CustomData_get_layer(&pointcloud->pdata, CD_RADIUS);
+}
+
+/* Dependency Graph */
+
+PointCloud *BKE_pointcloud_new_for_eval(const PointCloud *pointcloud_src, int totpoint)
+{
+ PointCloud *pointcloud_dst = BKE_id_new_nomain(ID_HA, NULL);
+
+ STRNCPY(pointcloud_dst->id.name, pointcloud_src->id.name);
+ pointcloud_dst->mat = MEM_dupallocN(pointcloud_src->mat);
+ pointcloud_dst->totcol = pointcloud_src->totcol;
+
+ pointcloud_dst->totpoint = totpoint;
+ CustomData_copy(
+ &pointcloud_src->pdata, &pointcloud_dst->pdata, CD_MASK_ALL, CD_CALLOC, totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud_dst);
+
+ return pointcloud_dst;
+}
+
+PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool reference)
+{
+ int flags = LIB_ID_COPY_LOCALIZE;
+
+ if (reference) {
+ flags |= LIB_ID_COPY_CD_REFERENCE;
+ }
+
+ PointCloud *result;
+ BKE_id_copy_ex(NULL, &pointcloud_src->id, (ID **)&result, flags);
+ return result;
+}
+
+static PointCloud *pointcloud_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
+ struct Scene *UNUSED(scene),
+ Object *UNUSED(object),
+ PointCloud *pointcloud_input)
+{
+ return pointcloud_input;
+}
+
+void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
+{
+ /* Free any evaluated data and restore original data. */
+ BKE_object_free_derived_caches(object);
+
+ /* Evaluate modifiers. */
+ PointCloud *pointcloud = object->data;
+ PointCloud *pointcloud_eval = pointcloud_evaluate_modifiers(
+ depsgraph, scene, object, pointcloud);
+
+ /* Assign evaluated object. */
+ const bool is_owned = (pointcloud != pointcloud_eval);
+ BKE_object_eval_assign_data(object, &pointcloud_eval->id, is_owned);
+}
+
+/* Draw Cache */
+void (*BKE_pointcloud_batch_cache_dirty_tag_cb)(PointCloud *pointcloud, int mode) = NULL;
+void (*BKE_pointcloud_batch_cache_free_cb)(PointCloud *pointcloud) = NULL;
+
+void BKE_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
+{
+ if (pointcloud->batch_cache) {
+ BKE_pointcloud_batch_cache_dirty_tag_cb(pointcloud, mode);
+ }
+}
+
+void BKE_pointcloud_batch_cache_free(PointCloud *pointcloud)
+{
+ if (pointcloud->batch_cache) {
+ BKE_pointcloud_batch_cache_free_cb(pointcloud);
+ }
+}
diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc
new file mode 100644
index 00000000000..6fb44e91388
--- /dev/null
+++ b/source/blender/blenkernel/intern/volume.cc
@@ -0,0 +1,1242 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/volume.cc
+ * \ingroup bke
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_defaults.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_volume_types.h"
+
+#include "BLI_compiler_compat.h"
+#include "BLI_fileops.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "BKE_animsys.h"
+#include "BKE_idtype.h"
+#include "BKE_global.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_lib_remap.h"
+#include "BKE_main.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_packedFile.h"
+#include "BKE_scene.h"
+#include "BKE_volume.h"
+
+#include "BLT_translation.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "CLG_log.h"
+
+static CLG_LogRef LOG = {"bke.volume"};
+
+#define VOLUME_FRAME_NONE INT_MAX
+
+#ifdef WITH_OPENVDB
+# include <atomic>
+# include <list>
+# include <mutex>
+# include <unordered_set>
+
+# include <openvdb/openvdb.h>
+# include <openvdb/points/PointDataGrid.h>
+
+/* Global Volume File Cache
+ *
+ * Global cache of grids read from VDB files. This is used for sharing grids
+ * between multiple volume datablocks with the same filepath, and sharing grids
+ * between original and copy-on-write datablocks created by the depsgraph.
+ *
+ * There are two types of users. Some datablocks only need the grid metadata,
+ * example an original datablock volume showing the list of grids in the
+ * properties editor. Other datablocks also need the tree and voxel data, for
+ * rendering for example. So, depending on the users the grid in the cache may
+ * have a tree or not.
+ *
+ * When the number of users drops to zero, the grid data is immediately deleted.
+ *
+ * TODO: also add a cache for OpenVDB files rather than individual grids,
+ * so getting the list of grids is also cached.
+ * TODO: Further, we could cache openvdb::io::File so that loading a grid
+ * does not re-open it every time. But then we have to take care not to run
+ * out of file descriptors or prevent other applications from writing to it.
+ */
+
+struct VolumeFileCache {
+ /* Cache Entry */
+ struct Entry {
+ Entry(const std::string &filepath, const openvdb::GridBase::Ptr &grid)
+ : filepath(filepath),
+ grid_name(grid->getName()),
+ grid(grid),
+ is_loaded(false),
+ num_metadata_users(0),
+ num_tree_users(0)
+ {
+ }
+
+ Entry(const Entry &other)
+ : filepath(other.filepath),
+ grid_name(other.grid_name),
+ grid(other.grid),
+ is_loaded(other.is_loaded),
+ num_metadata_users(0),
+ num_tree_users(0)
+ {
+ }
+
+ /* Unique key: filename + grid name. */
+ std::string filepath;
+ std::string grid_name;
+
+ /* OpenVDB grid. */
+ openvdb::GridBase::Ptr grid;
+ /* Has the grid tree been loaded? */
+ bool is_loaded;
+ /* Error message if an error occured during loading. */
+ std::string error_msg;
+ /* User counting. */
+ int num_metadata_users;
+ int num_tree_users;
+ /* Mutex for on-demand reading of tree. */
+ std::mutex mutex;
+ };
+
+ struct EntryHasher {
+ std::size_t operator()(const Entry &entry) const
+ {
+ std::hash<std::string> string_hasher;
+ return BLI_ghashutil_combine_hash(string_hasher(entry.filepath),
+ string_hasher(entry.grid_name));
+ }
+ };
+
+ struct EntryEqual {
+ bool operator()(const Entry &a, const Entry &b) const
+ {
+ return a.filepath == b.filepath && a.grid_name == b.grid_name;
+ }
+ };
+
+ /* Cache */
+ VolumeFileCache()
+ {
+ }
+
+ ~VolumeFileCache()
+ {
+ assert(cache.size() == 0);
+ }
+
+ Entry *add_metadata_user(const Entry &template_entry)
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ EntrySet::iterator it = cache.find(template_entry);
+ if (it == cache.end()) {
+ it = cache.emplace(template_entry).first;
+ }
+
+ /* Casting const away is weak, but it's convenient having key and value in one. */
+ Entry &entry = (Entry &)*it;
+ entry.num_metadata_users++;
+
+ /* Note: pointers to unordered_set values are not invalidated when adding
+ * or removing other values. */
+ return &entry;
+ }
+
+ void copy_user(Entry &entry, const bool tree_user)
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ if (tree_user) {
+ entry.num_tree_users++;
+ }
+ else {
+ entry.num_metadata_users++;
+ }
+ }
+
+ void remove_user(Entry &entry, const bool tree_user)
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ if (tree_user) {
+ entry.num_tree_users--;
+ }
+ else {
+ entry.num_metadata_users--;
+ }
+ update_for_remove_user(entry);
+ }
+
+ void change_to_tree_user(Entry &entry)
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ entry.num_tree_users++;
+ entry.num_metadata_users--;
+ update_for_remove_user(entry);
+ }
+
+ void change_to_metadata_user(Entry &entry)
+ {
+ std::lock_guard<std::mutex> lock(mutex);
+ entry.num_metadata_users++;
+ entry.num_tree_users--;
+ update_for_remove_user(entry);
+ }
+
+ protected:
+ void update_for_remove_user(Entry &entry)
+ {
+ if (entry.num_metadata_users + entry.num_tree_users == 0) {
+ cache.erase(entry);
+ }
+ else if (entry.num_tree_users == 0) {
+ entry.grid->clear();
+ entry.is_loaded = false;
+ }
+ }
+
+ /* Cache contents */
+ typedef std::unordered_set<Entry, EntryHasher, EntryEqual> EntrySet;
+ EntrySet cache;
+ /* Mutex for multithreaded access. */
+ std::mutex mutex;
+} GLOBAL_CACHE;
+
+/* VolumeGrid
+ *
+ * Wrapper around OpenVDB grid. Grids loaded from OpenVDB files are always
+ * stored in the global cache. Procedurally generated grids are not. */
+
+struct VolumeGrid {
+ VolumeGrid(const VolumeFileCache::Entry &template_entry) : entry(NULL), is_loaded(false)
+ {
+ entry = GLOBAL_CACHE.add_metadata_user(template_entry);
+ vdb = entry->grid;
+ }
+
+ VolumeGrid(const openvdb::GridBase::Ptr &vdb) : vdb(vdb), entry(NULL), is_loaded(true)
+ {
+ }
+
+ VolumeGrid(const VolumeGrid &other)
+ : vdb(other.vdb), entry(other.entry), is_loaded(other.is_loaded)
+ {
+ if (entry) {
+ GLOBAL_CACHE.copy_user(*entry, is_loaded);
+ }
+ }
+
+ ~VolumeGrid()
+ {
+ if (entry) {
+ GLOBAL_CACHE.remove_user(*entry, is_loaded);
+ }
+ }
+
+ void load(const char *volume_name, const char *filepath)
+ {
+ /* If already loaded or not file-backed, nothing to do. */
+ if (is_loaded || entry == NULL) {
+ return;
+ }
+
+ /* Double-checked lock. */
+ std::lock_guard<std::mutex> lock(entry->mutex);
+ if (is_loaded) {
+ return;
+ }
+
+ /* Change metadata user to tree user. */
+ GLOBAL_CACHE.change_to_tree_user(*entry);
+
+ /* If already loaded by another user, nothing further to do. */
+ if (entry->is_loaded) {
+ is_loaded = true;
+ return;
+ }
+
+ /* Load grid from file. */
+ CLOG_INFO(&LOG, 1, "Volume %s: load grid '%s'", volume_name, name());
+
+ openvdb::io::File file(filepath);
+
+ try {
+ file.setCopyMaxBytes(0);
+ file.open();
+ openvdb::GridBase::Ptr vdb_grid = file.readGrid(name());
+ entry->grid->setTree(vdb_grid->baseTreePtr());
+ }
+ catch (const openvdb::IoError &e) {
+ entry->error_msg = e.what();
+ }
+
+ std::atomic_thread_fence(std::memory_order_release);
+ entry->is_loaded = true;
+ is_loaded = true;
+ }
+
+ void unload(const char *volume_name)
+ {
+ /* Not loaded or not file-backed, nothing to do. */
+ if (!is_loaded || entry == NULL) {
+ return;
+ }
+
+ /* Double-checked lock. */
+ std::lock_guard<std::mutex> lock(entry->mutex);
+ if (!is_loaded) {
+ return;
+ }
+
+ CLOG_INFO(&LOG, 1, "Volume %s: unload grid '%s'", volume_name, name());
+
+ /* Change tree user to metadata user. */
+ GLOBAL_CACHE.change_to_metadata_user(*entry);
+
+ /* Indicate we no longer have a tree. The actual grid may still
+ * have it due to another user. */
+ std::atomic_thread_fence(std::memory_order_release);
+ is_loaded = false;
+ }
+
+ void clear_reference(const char *UNUSED(volume_name))
+ {
+ /* Clear any reference to a grid in the file cache. */
+ vdb = vdb->copyGridWithNewTree();
+ if (entry) {
+ GLOBAL_CACHE.remove_user(*entry, is_loaded);
+ entry = NULL;
+ }
+ is_loaded = true;
+ }
+
+ void duplicate_reference(const char *volume_name, const char *filepath)
+ {
+ /* Make a deep copy of the grid and remove any reference to a grid in the
+ * file cache. Load file grid into memory first if needed. */
+ load(volume_name, filepath);
+ /* TODO: avoid deep copy if we are the only user. */
+ vdb = vdb->deepCopyGrid();
+ if (entry) {
+ GLOBAL_CACHE.remove_user(*entry, is_loaded);
+ entry = NULL;
+ }
+ is_loaded = true;
+ }
+
+ const char *name() const
+ {
+ /* Don't use vdb.getName() since it copies the string, we want a pointer to the
+ * original so it doesn't get freed out of scope. */
+ openvdb::StringMetadata::ConstPtr name_meta = vdb->getMetadata<openvdb::StringMetadata>(
+ openvdb::GridBase::META_GRID_NAME);
+ return (name_meta) ? name_meta->value().c_str() : "";
+ }
+
+ const char *error_message() const
+ {
+ if (is_loaded && entry && !entry->error_msg.empty()) {
+ return entry->error_msg.c_str();
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ /* OpenVDB grid. */
+ openvdb::GridBase::Ptr vdb;
+ /* File cache entry. */
+ VolumeFileCache::Entry *entry;
+ /* Indicates if the tree has been loaded for this grid. Note that vdb.tree()
+ * may actually be loaded by another user while this is false. But only after
+ * calling load() and is_loaded changes to true is it safe to access. */
+ bool is_loaded;
+};
+
+/* Volume Grid Vector
+ *
+ * List of grids contained in a volume datablock. This is runtime-only data,
+ * the actual grids are always saved in a VDB file. */
+
+struct VolumeGridVector : public std::list<VolumeGrid> {
+ VolumeGridVector()
+ {
+ filepath[0] = '\0';
+ }
+
+ VolumeGridVector(const VolumeGridVector &other)
+ : std::list<VolumeGrid>(other), error_msg(other.error_msg), metadata(other.metadata)
+ {
+ memcpy(filepath, other.filepath, sizeof(filepath));
+ }
+
+ bool is_loaded() const
+ {
+ return filepath[0] != '\0';
+ }
+
+ void clear_all()
+ {
+ std::list<VolumeGrid>::clear();
+ filepath[0] = '\0';
+ error_msg.clear();
+ metadata.reset();
+ }
+
+ /* Absolute file path that grids have been loaded from. */
+ char filepath[FILE_MAX];
+ /* File loading error message. */
+ std::string error_msg;
+ /* File Metadata. */
+ openvdb::MetaMap::Ptr metadata;
+ /* Mutex for file loading of grids list. */
+ std::mutex mutex;
+};
+#endif
+
+/* Module */
+
+void BKE_volumes_init()
+{
+#ifdef WITH_OPENVDB
+ openvdb::initialize();
+#endif
+}
+
+/* Volume datablock */
+
+static void volume_init_data(ID *id)
+{
+ Volume *volume = (Volume *)id;
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(volume, id));
+
+ MEMCPY_STRUCT_AFTER(volume, DNA_struct_default_get(Volume), id);
+
+ BKE_volume_init_grids(volume);
+}
+
+void BKE_volume_init_grids(Volume *volume)
+{
+#ifdef WITH_OPENVDB
+ if (volume->runtime.grids == NULL) {
+ volume->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector);
+ }
+#else
+ UNUSED_VARS(volume);
+#endif
+}
+
+void *BKE_volume_add(Main *bmain, const char *name)
+{
+ Volume *volume = (Volume *)BKE_libblock_alloc(bmain, ID_VO, name, 0);
+
+ volume_init_data(&volume->id);
+
+ return volume;
+}
+
+static void volume_copy_data(Main *UNUSED(bmain),
+ ID *id_dst,
+ const ID *id_src,
+ const int UNUSED(flag))
+{
+ Volume *volume_dst = (Volume *)id_dst;
+ const Volume *volume_src = (const Volume *)id_src;
+
+ if (volume_src->packedfile) {
+ volume_dst->packedfile = BKE_packedfile_duplicate(volume_src->packedfile);
+ }
+
+ volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat);
+#ifdef WITH_OPENVDB
+ if (volume_src->runtime.grids) {
+ const VolumeGridVector &grids_src = *(volume_src->runtime.grids);
+ volume_dst->runtime.grids = OBJECT_GUARDED_NEW(VolumeGridVector, grids_src);
+ }
+#endif
+}
+
+Volume *BKE_volume_copy(Main *bmain, const Volume *volume)
+{
+ Volume *volume_copy;
+ BKE_id_copy(bmain, &volume->id, (ID **)&volume_copy);
+ return volume_copy;
+}
+
+static void volume_make_local(Main *bmain, ID *id, const int flags)
+{
+ BKE_lib_id_make_local_generic(bmain, id, flags);
+}
+
+static void volume_free_data(ID *id)
+{
+ Volume *volume = (Volume *)id;
+ BKE_animdata_free(&volume->id, false);
+ BKE_volume_batch_cache_free(volume);
+ MEM_SAFE_FREE(volume->mat);
+#ifdef WITH_OPENVDB
+ OBJECT_GUARDED_SAFE_DELETE(volume->runtime.grids, VolumeGridVector);
+#endif
+}
+
+IDTypeInfo IDType_ID_VO = {
+ /* id_code */ ID_VO,
+ /* id_filter */ FILTER_ID_VO,
+ /* main_listbase_index */ INDEX_ID_VO,
+ /* struct_size */ sizeof(Volume),
+ /* name */ "Volume",
+ /* name_plural */ "volumes",
+ /* translation_context */ BLT_I18NCONTEXT_ID_VOLUME,
+ /* flags */ 0,
+
+ /* init_data */ volume_init_data,
+ /* copy_data */ volume_copy_data,
+ /* free_data */ volume_free_data,
+ /* make_local */ volume_make_local,
+};
+
+/* Sequence */
+
+static int volume_sequence_frame(const Depsgraph *depsgraph, const Volume *volume)
+{
+ if (!volume->is_sequence) {
+ return 0;
+ }
+
+ char filepath[FILE_MAX];
+ STRNCPY(filepath, volume->filepath);
+ int path_frame, path_digits;
+ if (!(volume->is_sequence && BLI_path_frame_get(filepath, &path_frame, &path_digits))) {
+ return 0;
+ }
+
+ const int scene_frame = DEG_get_ctime(depsgraph);
+ const VolumeSequenceMode mode = (VolumeSequenceMode)volume->sequence_mode;
+ const int frame_duration = volume->frame_duration;
+ const int frame_start = volume->frame_start;
+ const int frame_offset = volume->frame_offset;
+
+ if (frame_duration == 0) {
+ return VOLUME_FRAME_NONE;
+ }
+
+ int frame = scene_frame - frame_start + 1;
+
+ switch (mode) {
+ case VOLUME_SEQUENCE_CLIP: {
+ if (frame < 1 || frame > frame_duration) {
+ return VOLUME_FRAME_NONE;
+ }
+ break;
+ }
+ case VOLUME_SEQUENCE_EXTEND: {
+ frame = clamp_i(frame, 1, frame_duration);
+ break;
+ }
+ case VOLUME_SEQUENCE_REPEAT: {
+ frame = frame % frame_duration;
+ if (frame < 0) {
+ frame += frame_duration;
+ }
+ if (frame == 0) {
+ frame = frame_duration;
+ }
+ break;
+ }
+ case VOLUME_SEQUENCE_PING_PONG: {
+ const int pingpong_duration = frame_duration * 2 - 2;
+ frame = frame % pingpong_duration;
+ if (frame < 0) {
+ frame += pingpong_duration;
+ }
+ if (frame == 0) {
+ frame = pingpong_duration;
+ }
+ if (frame > frame_duration) {
+ frame = frame_duration * 2 - frame;
+ }
+ break;
+ }
+ }
+
+ /* Important to apply after, else we cant loop on e.g. frames 100 - 110. */
+ frame += frame_offset;
+
+ return frame;
+}
+
+static void volume_filepath_get(const Main *bmain, const Volume *volume, char r_filepath[FILE_MAX])
+{
+ BLI_strncpy(r_filepath, volume->filepath, FILE_MAX);
+ BLI_path_abs(r_filepath, ID_BLEND_PATH(bmain, &volume->id));
+
+ int path_frame, path_digits;
+ if (volume->is_sequence && BLI_path_frame_get(r_filepath, &path_frame, &path_digits)) {
+ char ext[32];
+ BLI_path_frame_strip(r_filepath, ext);
+ BLI_path_frame(r_filepath, volume->runtime.frame, path_digits);
+ BLI_path_extension_ensure(r_filepath, FILE_MAX, ext);
+ }
+}
+
+/* File Load */
+
+bool BKE_volume_is_loaded(const Volume *volume)
+{
+#ifdef WITH_OPENVDB
+ /* Test if there is a file to load, or if already loaded. */
+ return (volume->filepath[0] == '\0' || volume->runtime.grids->is_loaded());
+#else
+ UNUSED_VARS(volume);
+ return true;
+#endif
+}
+
+bool BKE_volume_load(Volume *volume, Main *bmain)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+
+ if (volume->runtime.frame == VOLUME_FRAME_NONE) {
+ /* Skip loading this frame, outside of sequence range. */
+ return true;
+ }
+
+ if (BKE_volume_is_loaded(volume)) {
+ return grids.error_msg.empty();
+ }
+
+ /* Double-checked lock. */
+ std::lock_guard<std::mutex> lock(grids.mutex);
+ if (BKE_volume_is_loaded(volume)) {
+ return grids.error_msg.empty();
+ }
+
+ /* Get absolute file path at current frame. */
+ const char *volume_name = volume->id.name + 2;
+ volume_filepath_get(bmain, volume, grids.filepath);
+
+ CLOG_INFO(&LOG, 1, "Volume %s: load %s", volume_name, grids.filepath);
+
+ /* Test if file exists. */
+ if (!BLI_exists(grids.filepath)) {
+ char filename[FILE_MAX];
+ BLI_split_file_part(grids.filepath, filename, sizeof(filename));
+ grids.error_msg = filename + std::string(" not found");
+ CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str());
+ return false;
+ }
+
+ /* Test if file exists. */
+ if (!BLI_exists(grids.filepath)) {
+ char filename[FILE_MAX];
+ BLI_split_file_part(grids.filepath, filename, sizeof(filename));
+ grids.error_msg = filename + std::string(" not found");
+ CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str());
+ return false;
+ }
+
+ /* Open OpenVDB file. */
+ openvdb::io::File file(grids.filepath);
+ openvdb::GridPtrVec vdb_grids;
+
+ try {
+ file.setCopyMaxBytes(0);
+ file.open();
+ vdb_grids = *(file.readAllGridMetadata());
+ grids.metadata = file.getMetadata();
+ }
+ catch (const openvdb::IoError &e) {
+ grids.error_msg = e.what();
+ CLOG_INFO(&LOG, 1, "Volume %s: %s", volume_name, grids.error_msg.c_str());
+ }
+
+ /* Add grids read from file to own vector, filtering out any NULL pointers. */
+ for (const openvdb::GridBase::Ptr vdb_grid : vdb_grids) {
+ if (vdb_grid) {
+ VolumeFileCache::Entry template_entry(grids.filepath, vdb_grid);
+ grids.emplace_back(template_entry);
+ }
+ }
+
+ return grids.error_msg.empty();
+#else
+ UNUSED_VARS(bmain, volume);
+ return true;
+#endif
+}
+
+void BKE_volume_unload(Volume *volume)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ if (grids.filepath[0] != '\0') {
+ const char *volume_name = volume->id.name + 2;
+ CLOG_INFO(&LOG, 1, "Volume %s: unload", volume_name);
+ grids.clear_all();
+ }
+#else
+ UNUSED_VARS(volume);
+#endif
+}
+
+BoundBox *BKE_volume_boundbox_get(Object *ob)
+{
+ BLI_assert(ob->type == OB_VOLUME);
+
+ if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) {
+ return ob->runtime.bb;
+ }
+
+ if (ob->runtime.bb == NULL) {
+ Volume *volume = (Volume *)ob->data;
+
+ ob->runtime.bb = (BoundBox *)MEM_callocN(sizeof(BoundBox), "volume boundbox");
+
+ float min[3], max[3];
+ bool have_minmax = false;
+ INIT_MINMAX(min, max);
+
+ /* TODO: if we know the volume is going to be displayed, it may be good to
+ * load it as part of dependency graph evaluation for better threading. We
+ * could also share the bounding box computation in the global volume cache. */
+ if (BKE_volume_load(volume, G.main)) {
+ const int num_grids = BKE_volume_num_grids(volume);
+
+ for (int i = 0; i < num_grids; i++) {
+ VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ float grid_min[3], grid_max[3];
+
+ BKE_volume_grid_load(volume, grid);
+ if (BKE_volume_grid_bounds(grid, grid_min, grid_max)) {
+ DO_MIN(grid_min, min);
+ DO_MAX(grid_max, max);
+ have_minmax = true;
+ }
+ }
+ }
+
+ if (!have_minmax) {
+ min[0] = min[1] = min[2] = -1.0f;
+ max[0] = max[1] = max[2] = 1.0f;
+ }
+
+ BKE_boundbox_init_from_minmax(ob->runtime.bb, min, max);
+ }
+
+ return ob->runtime.bb;
+}
+
+bool BKE_volume_is_y_up(const Volume *volume)
+{
+ /* Simple heuristic for common files to open the right way up. */
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ if (grids.metadata) {
+ openvdb::StringMetadata::ConstPtr creator =
+ grids.metadata->getMetadata<openvdb::StringMetadata>("creator");
+ if (!creator) {
+ creator = grids.metadata->getMetadata<openvdb::StringMetadata>("Creator");
+ }
+ return (creator && creator->str().rfind("Houdini", 0) == 0);
+ }
+#else
+ UNUSED_VARS(volume);
+#endif
+
+ return false;
+}
+
+bool BKE_volume_is_points_only(const Volume *volume)
+{
+ int num_grids = BKE_volume_num_grids(volume);
+ if (num_grids == 0) {
+ return false;
+ }
+
+ for (int i = 0; i < num_grids; i++) {
+ VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ if (BKE_volume_grid_type(grid) != VOLUME_GRID_POINTS) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Dependency Graph */
+
+static Volume *volume_evaluate_modifiers(struct Depsgraph *UNUSED(depsgraph),
+ struct Scene *UNUSED(scene),
+ Object *UNUSED(object),
+ Volume *volume_input)
+{
+ return volume_input;
+}
+
+void BKE_volume_eval_geometry(struct Depsgraph *depsgraph, Volume *volume)
+{
+ /* TODO: can we avoid modifier re-evaluation when frame did not change? */
+ int frame = volume_sequence_frame(depsgraph, volume);
+ if (frame != volume->runtime.frame) {
+ BKE_volume_unload(volume);
+ volume->runtime.frame = frame;
+ }
+
+ /* Flush back to original. */
+ if (DEG_is_active(depsgraph)) {
+ Volume *volume_orig = (Volume *)DEG_get_original_id(&volume->id);
+ volume_orig->runtime.frame = volume->runtime.frame;
+ }
+}
+
+void BKE_volume_data_update(struct Depsgraph *depsgraph, struct Scene *scene, Object *object)
+{
+ /* Free any evaluated data and restore original data. */
+ BKE_object_free_derived_caches(object);
+
+ /* Evaluate modifiers. */
+ Volume *volume = (Volume *)object->data;
+ Volume *volume_eval = volume_evaluate_modifiers(depsgraph, scene, object, volume);
+
+ /* Assign evaluated object. */
+ const bool is_owned = (volume != volume_eval);
+ BKE_object_eval_assign_data(object, &volume_eval->id, is_owned);
+}
+
+void BKE_volume_grids_backup_restore(Volume *volume, VolumeGridVector *grids, const char *filepath)
+{
+#ifdef WITH_OPENVDB
+ /* Restore grids after datablock was re-copied from original by depsgraph,
+ * we don't want to load them again if possible. */
+ BLI_assert(volume->id.tag & LIB_TAG_COPIED_ON_WRITE);
+ BLI_assert(volume->runtime.grids != NULL && grids != NULL);
+
+ if (!grids->is_loaded()) {
+ /* No grids loaded in CoW datablock, nothing lost by discarding. */
+ OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
+ }
+ else if (!STREQ(volume->filepath, filepath)) {
+ /* Filepath changed, discard grids from CoW datablock. */
+ OBJECT_GUARDED_DELETE(grids, VolumeGridVector);
+ }
+ else {
+ /* Keep grids from CoW datablock. We might still unload them a little
+ * later in BKE_volume_eval_geometry if the frame changes. */
+ OBJECT_GUARDED_DELETE(volume->runtime.grids, VolumeGridVector);
+ volume->runtime.grids = grids;
+ }
+#else
+ UNUSED_VARS(volume, grids);
+#endif
+}
+
+/* Draw Cache */
+
+void (*BKE_volume_batch_cache_dirty_tag_cb)(Volume *volume, int mode) = NULL;
+void (*BKE_volume_batch_cache_free_cb)(Volume *volume) = NULL;
+
+void BKE_volume_batch_cache_dirty_tag(Volume *volume, int mode)
+{
+ if (volume->batch_cache) {
+ BKE_volume_batch_cache_dirty_tag_cb(volume, mode);
+ }
+}
+
+void BKE_volume_batch_cache_free(Volume *volume)
+{
+ if (volume->batch_cache) {
+ BKE_volume_batch_cache_free_cb(volume);
+ }
+}
+
+/* Grids */
+
+int BKE_volume_num_grids(const Volume *volume)
+{
+#ifdef WITH_OPENVDB
+ return volume->runtime.grids->size();
+#else
+ UNUSED_VARS(volume);
+ return 0;
+#endif
+}
+
+const char *BKE_volume_grids_error_msg(const Volume *volume)
+{
+#ifdef WITH_OPENVDB
+ return volume->runtime.grids->error_msg.c_str();
+#else
+ UNUSED_VARS(volume);
+ return "";
+#endif
+}
+
+VolumeGrid *BKE_volume_grid_get(const Volume *volume, int grid_index)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ for (VolumeGrid &grid : grids) {
+ if (grid_index-- == 0) {
+ return &grid;
+ }
+ }
+ return NULL;
+#else
+ UNUSED_VARS(volume, grid_index);
+ return NULL;
+#endif
+}
+
+VolumeGrid *BKE_volume_grid_active_get(const Volume *volume)
+{
+ const int num_grids = BKE_volume_num_grids(volume);
+ if (num_grids == 0) {
+ return NULL;
+ }
+
+ const int index = clamp_i(volume->active_grid, 0, num_grids - 1);
+ return BKE_volume_grid_get(volume, index);
+}
+
+VolumeGrid *BKE_volume_grid_find(const Volume *volume, const char *name)
+{
+ int num_grids = BKE_volume_num_grids(volume);
+ for (int i = 0; i < num_grids; i++) {
+ VolumeGrid *grid = BKE_volume_grid_get(volume, i);
+ if (STREQ(BKE_volume_grid_name(grid), name)) {
+ return grid;
+ }
+ }
+
+ return NULL;
+}
+
+/* Grid Loading */
+
+bool BKE_volume_grid_load(const Volume *volume, VolumeGrid *grid)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ const char *volume_name = volume->id.name + 2;
+ grid->load(volume_name, grids.filepath);
+ const char *error_msg = grid->error_message();
+ if (error_msg) {
+ grids.error_msg = error_msg;
+ return false;
+ }
+ return true;
+#else
+ UNUSED_VARS(volume, grid);
+ return true;
+#endif
+}
+
+void BKE_volume_grid_unload(const Volume *volume, VolumeGrid *grid)
+{
+#ifdef WITH_OPENVDB
+ const char *volume_name = volume->id.name + 2;
+ grid->unload(volume_name);
+#else
+ UNUSED_VARS(grid);
+#endif
+}
+
+bool BKE_volume_grid_is_loaded(const VolumeGrid *grid)
+{
+#ifdef WITH_OPENVDB
+ return grid->is_loaded;
+#else
+ UNUSED_VARS(grid);
+ return true;
+#endif
+}
+
+/* Grid Metadata */
+
+const char *BKE_volume_grid_name(const VolumeGrid *volume_grid)
+{
+#ifdef WITH_OPENVDB
+ return volume_grid->name();
+#else
+ UNUSED_VARS(volume_grid);
+ return "density";
+#endif
+}
+
+VolumeGridType BKE_volume_grid_type(const VolumeGrid *volume_grid)
+{
+#ifdef WITH_OPENVDB
+ const openvdb::GridBase::Ptr &grid = volume_grid->vdb;
+
+ if (grid->isType<openvdb::FloatGrid>()) {
+ return VOLUME_GRID_FLOAT;
+ }
+ else if (grid->isType<openvdb::Vec3fGrid>()) {
+ return VOLUME_GRID_VECTOR_FLOAT;
+ }
+ else if (grid->isType<openvdb::BoolGrid>()) {
+ return VOLUME_GRID_BOOLEAN;
+ }
+ else if (grid->isType<openvdb::DoubleGrid>()) {
+ return VOLUME_GRID_DOUBLE;
+ }
+ else if (grid->isType<openvdb::Int32Grid>()) {
+ return VOLUME_GRID_INT;
+ }
+ else if (grid->isType<openvdb::Int64Grid>()) {
+ return VOLUME_GRID_INT64;
+ }
+ else if (grid->isType<openvdb::Vec3IGrid>()) {
+ return VOLUME_GRID_VECTOR_INT;
+ }
+ else if (grid->isType<openvdb::Vec3dGrid>()) {
+ return VOLUME_GRID_VECTOR_DOUBLE;
+ }
+ else if (grid->isType<openvdb::StringGrid>()) {
+ return VOLUME_GRID_STRING;
+ }
+ else if (grid->isType<openvdb::MaskGrid>()) {
+ return VOLUME_GRID_MASK;
+ }
+ else if (grid->isType<openvdb::points::PointDataGrid>()) {
+ return VOLUME_GRID_POINTS;
+ }
+#else
+ UNUSED_VARS(volume_grid);
+#endif
+
+ return VOLUME_GRID_UNKNOWN;
+}
+
+int BKE_volume_grid_channels(const VolumeGrid *grid)
+{
+ switch (BKE_volume_grid_type(grid)) {
+ case VOLUME_GRID_BOOLEAN:
+ case VOLUME_GRID_FLOAT:
+ case VOLUME_GRID_DOUBLE:
+ case VOLUME_GRID_INT:
+ case VOLUME_GRID_INT64:
+ case VOLUME_GRID_MASK:
+ return 1;
+ case VOLUME_GRID_VECTOR_FLOAT:
+ case VOLUME_GRID_VECTOR_DOUBLE:
+ case VOLUME_GRID_VECTOR_INT:
+ return 3;
+ case VOLUME_GRID_STRING:
+ case VOLUME_GRID_POINTS:
+ case VOLUME_GRID_UNKNOWN:
+ return 0;
+ }
+
+ return 0;
+}
+
+/* Transformation from index space to object space. */
+void BKE_volume_grid_transform_matrix(const VolumeGrid *volume_grid, float mat[4][4])
+{
+#ifdef WITH_OPENVDB
+ const openvdb::GridBase::Ptr &grid = volume_grid->vdb;
+ const openvdb::math::Transform &transform = grid->transform();
+
+ /* Perspective not supported for now, getAffineMap() will leave out the
+ * perspective part of the transform. */
+ openvdb::math::Mat4f matrix = transform.baseMap()->getAffineMap()->getMat4();
+ /* Blender column-major and OpenVDB right-multiplication conventions match. */
+ for (int col = 0; col < 4; col++) {
+ for (int row = 0; row < 4; row++) {
+ mat[col][row] = matrix(col, row);
+ }
+ }
+#else
+ unit_m4(mat);
+ UNUSED_VARS(volume_grid);
+#endif
+}
+
+/* Grid Tree and Voxels */
+
+bool BKE_volume_grid_bounds(const VolumeGrid *volume_grid, float min[3], float max[3])
+{
+#ifdef WITH_OPENVDB
+ /* TODO: we can get this from grid metadata in some cases? */
+ const openvdb::GridBase::Ptr &grid = volume_grid->vdb;
+ BLI_assert(BKE_volume_grid_is_loaded(volume_grid));
+
+ openvdb::CoordBBox coordbbox;
+ if (!grid->baseTree().evalLeafBoundingBox(coordbbox)) {
+ INIT_MINMAX(min, max);
+ return false;
+ }
+
+ openvdb::BBoxd bbox = grid->transform().indexToWorld(coordbbox);
+ min[0] = (float)bbox.min().x();
+ min[1] = (float)bbox.min().y();
+ min[2] = (float)bbox.min().z();
+ max[0] = (float)bbox.max().x();
+ max[1] = (float)bbox.max().y();
+ max[2] = (float)bbox.max().z();
+ return true;
+#else
+ UNUSED_VARS(volume_grid);
+ INIT_MINMAX(min, max);
+ return false;
+#endif
+}
+
+/* Volume Editing */
+
+Volume *BKE_volume_new_for_eval(const Volume *volume_src)
+{
+ Volume *volume_dst = (Volume *)BKE_id_new_nomain(ID_VO, NULL);
+
+ STRNCPY(volume_dst->id.name, volume_src->id.name);
+ volume_dst->mat = (Material **)MEM_dupallocN(volume_src->mat);
+ volume_dst->totcol = volume_src->totcol;
+ BKE_volume_init_grids(volume_dst);
+
+ return volume_dst;
+}
+
+Volume *BKE_volume_copy_for_eval(Volume *volume_src, bool reference)
+{
+ int flags = LIB_ID_COPY_LOCALIZE;
+
+ if (reference) {
+ flags |= LIB_ID_COPY_CD_REFERENCE;
+ }
+
+ Volume *result;
+ BKE_id_copy_ex(NULL, &volume_src->id, (ID **)&result, flags);
+ result->filepath[0] = '\0';
+
+ return result;
+}
+
+VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType type)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ BLI_assert(BKE_volume_grid_find(volume, name) == NULL);
+
+ openvdb::GridBase::Ptr vdb_grid;
+ switch (type) {
+ case VOLUME_GRID_FLOAT:
+ vdb_grid = openvdb::FloatGrid::create();
+ break;
+ case VOLUME_GRID_VECTOR_FLOAT:
+ vdb_grid = openvdb::Vec3fGrid::create();
+ break;
+ case VOLUME_GRID_BOOLEAN:
+ vdb_grid = openvdb::BoolGrid::create();
+ break;
+ case VOLUME_GRID_DOUBLE:
+ vdb_grid = openvdb::DoubleGrid::create();
+ break;
+ case VOLUME_GRID_INT:
+ vdb_grid = openvdb::Int32Grid::create();
+ break;
+ case VOLUME_GRID_INT64:
+ vdb_grid = openvdb::Int64Grid::create();
+ break;
+ case VOLUME_GRID_VECTOR_INT:
+ vdb_grid = openvdb::Vec3IGrid::create();
+ break;
+ case VOLUME_GRID_VECTOR_DOUBLE:
+ vdb_grid = openvdb::Vec3dGrid::create();
+ break;
+ case VOLUME_GRID_STRING:
+ vdb_grid = openvdb::StringGrid::create();
+ break;
+ case VOLUME_GRID_MASK:
+ vdb_grid = openvdb::MaskGrid::create();
+ break;
+ case VOLUME_GRID_POINTS:
+ case VOLUME_GRID_UNKNOWN:
+ return NULL;
+ }
+
+ vdb_grid->setName(name);
+ grids.emplace_back(vdb_grid);
+ return &grids.back();
+#else
+ UNUSED_VARS(volume, name, type);
+ return NULL;
+#endif
+}
+
+void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid)
+{
+#ifdef WITH_OPENVDB
+ VolumeGridVector &grids = *volume->runtime.grids;
+ for (VolumeGridVector::iterator it = grids.begin(); it != grids.end(); it++) {
+ if (&*it == grid) {
+ grids.erase(it);
+ break;
+ }
+ }
+#else
+ UNUSED_VARS(volume, grid);
+#endif
+}
+
+/* OpenVDB Grid Access */
+
+#ifdef WITH_OPENVDB
+openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_metadata(const VolumeGrid *grid)
+{
+ return grid->vdb;
+}
+
+openvdb::GridBase::ConstPtr BKE_volume_grid_openvdb_for_read(const Volume *volume,
+ VolumeGrid *grid)
+{
+ BKE_volume_grid_load(volume, grid);
+ return grid->vdb;
+}
+
+openvdb::GridBase::Ptr BKE_volume_grid_openvdb_for_write(const Volume *volume,
+ VolumeGrid *grid,
+ const bool clear)
+{
+ const char *volume_name = volume->id.name + 2;
+ if (clear) {
+ grid->clear_reference(volume_name);
+ }
+ else {
+ VolumeGridVector &grids = *volume->runtime.grids;
+ grid->duplicate_reference(volume_name, grids.filepath);
+ }
+
+ return grid->vdb;
+}
+#endif
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index 085e500f7e5..c12df2bc87d 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -402,6 +402,9 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
/* make lookups of existing sound data in old main */
blo_make_sound_pointer_map(fd, oldmain);
+ /* make lookups of existing volume data in old main */
+ blo_make_volume_pointer_map(fd, oldmain);
+
/* removed packed data from this trick - it's internal data that needs saves */
bfd = blo_read_file_internal(fd, filename);
@@ -418,6 +421,9 @@ BlendFileData *BLO_read_from_memfile(Main *oldmain,
/* ensures relinked sounds are not freed */
blo_end_sound_pointer_map(fd, oldmain);
+ /* ensures relinked volumes are not freed */
+ blo_end_volume_pointer_map(fd, oldmain);
+
/* Still in-use libraries have already been moved from oldmain to new mainlist,
* but oldmain itself shall *never* be 'transferred' to new mainlist! */
BLI_assert(old_mainlist.first == oldmain);
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index f1f274f97d5..7e4d676954a 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -58,6 +58,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_hair_types.h"
#include "DNA_ipo_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
@@ -74,6 +75,7 @@
#include "DNA_object_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_particle_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_rigidbody_types.h"
@@ -88,6 +90,7 @@
#include "DNA_sound_types.h"
#include "DNA_space_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
#include "DNA_movieclip_types.h"
@@ -118,6 +121,7 @@
#include "BKE_fluid.h"
#include "BKE_global.h" // for G
#include "BKE_gpencil_modifier.h"
+#include "BKE_hair.h"
#include "BKE_idcode.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
@@ -136,13 +140,14 @@
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
-#include "BKE_curveprofile.h"
+#include "BKE_pointcloud.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_sequencer.h"
#include "BKE_shader_fx.h"
#include "BKE_sound.h"
+#include "BKE_volume.h"
#include "BKE_workspace.h"
#include "DRW_engine.h"
@@ -1595,6 +1600,9 @@ void blo_filedata_free(FileData *fd)
if (fd->soundmap) {
oldnewmap_free(fd->soundmap);
}
+ if (fd->volumemap) {
+ oldnewmap_free(fd->volumemap);
+ }
if (fd->packedmap) {
oldnewmap_free(fd->packedmap);
}
@@ -1806,6 +1814,15 @@ static void *newsoundadr(FileData *fd, const void *adr)
return NULL;
}
+/* used to restore volume data after undo */
+static void *newvolumeadr(FileData *fd, const void *adr)
+{
+ if (fd->volumemap && adr) {
+ return oldnewmap_lookup_and_inc(fd->volumemap, adr, true);
+ }
+ return NULL;
+}
+
/* used to restore packed data after undo */
static void *newpackedadr(FileData *fd, const void *adr)
{
@@ -2112,6 +2129,37 @@ void blo_end_sound_pointer_map(FileData *fd, Main *oldmain)
}
}
+void blo_make_volume_pointer_map(FileData *fd, Main *oldmain)
+{
+ fd->volumemap = oldnewmap_new();
+
+ Volume *volume = oldmain->volumes.first;
+ for (; volume; volume = volume->id.next) {
+ if (volume->runtime.grids) {
+ oldnewmap_insert(fd->volumemap, volume->runtime.grids, volume->runtime.grids, 0);
+ }
+ }
+}
+
+/* set old main volume caches to zero if it has been restored */
+/* this works because freeing old main only happens after this call */
+void blo_end_volume_pointer_map(FileData *fd, Main *oldmain)
+{
+ OldNew *entry = fd->volumemap->entries;
+ Volume *volume = oldmain->volumes.first;
+ int i;
+
+ /* used entries were restored, so we put them to zero */
+ for (i = 0; i < fd->volumemap->nentries; i++, entry++) {
+ if (entry->nr > 0)
+ entry->newp = NULL;
+ }
+
+ for (; volume; volume = volume->id.next) {
+ volume->runtime.grids = newvolumeadr(fd, volume->runtime.grids);
+ }
+}
+
/* XXX disabled this feature - packed files also belong in temp saves and quit.blend,
* to make restore work. */
@@ -2126,6 +2174,7 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
Image *ima;
VFont *vfont;
bSound *sound;
+ Volume *volume;
Library *lib;
fd->packedmap = oldnewmap_new();
@@ -2156,6 +2205,12 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
}
}
+ for (volume = oldmain->volumes.first; volume; volume = volume->id.next) {
+ if (volume->packedfile) {
+ insert_packedmap(fd, volume->packedfile);
+ }
+ }
+
for (lib = oldmain->libraries.first; lib; lib = lib->id.next) {
if (lib->packedfile) {
insert_packedmap(fd, lib->packedfile);
@@ -2170,6 +2225,7 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
Image *ima;
VFont *vfont;
bSound *sound;
+ Volume *volume;
Library *lib;
OldNew *entry = fd->packedmap->entries;
int i;
@@ -2202,6 +2258,10 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
for (lib = oldmain->libraries.first; lib; lib = lib->id.next) {
lib->packedfile = newpackedadr(fd, lib->packedfile);
}
+
+ for (volume = oldmain->volumes.first; volume; volume = volume->id.next) {
+ volume->packedfile = newpackedadr(fd, volume->packedfile);
+ }
}
/* undo file support: add all library pointers in lookup */
@@ -8962,6 +9022,89 @@ static void direct_link_linestyle(FileData *fd, FreestyleLineStyle *linestyle)
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Read ID: Hair
+ * \{ */
+
+static void lib_link_hair(FileData *fd, Main *UNUSED(main), Hair *hair)
+{
+ for (int a = 0; a < hair->totcol; a++) {
+ hair->mat[a] = newlibadr(fd, hair->id.lib, hair->mat[a]);
+ }
+}
+
+static void direct_link_hair(FileData *fd, Hair *hair)
+{
+ hair->adt = newdataadr(fd, hair->adt);
+ direct_link_animdata(fd, hair->adt);
+
+ /* Geometry */
+ direct_link_customdata(fd, &hair->pdata, hair->totpoint);
+ direct_link_customdata(fd, &hair->cdata, hair->totcurve);
+ BKE_hair_update_customdata_pointers(hair);
+
+ /* Materials */
+ hair->mat = newdataadr(fd, hair->mat);
+ test_pointer_array(fd, (void **)&hair->mat);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Read ID: Point Cloud
+ * \{ */
+
+static void lib_link_pointcloud(FileData *fd, Main *UNUSED(main), PointCloud *pointcloud)
+{
+ for (int a = 0; a < pointcloud->totcol; a++) {
+ pointcloud->mat[a] = newlibadr(fd, pointcloud->id.lib, pointcloud->mat[a]);
+ }
+}
+
+static void direct_link_pointcloud(FileData *fd, PointCloud *pointcloud)
+{
+ pointcloud->adt = newdataadr(fd, pointcloud->adt);
+ direct_link_animdata(fd, pointcloud->adt);
+
+ /* Geometry */
+ direct_link_customdata(fd, &pointcloud->pdata, pointcloud->totpoint);
+ BKE_pointcloud_update_customdata_pointers(pointcloud);
+
+ /* Materials */
+ pointcloud->mat = newdataadr(fd, pointcloud->mat);
+ test_pointer_array(fd, (void **)&pointcloud->mat);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Read ID: Volume
+ * \{ */
+
+static void lib_link_volume(FileData *fd, Main *UNUSED(main), Volume *volume)
+{
+ for (int a = 0; a < volume->totcol; a++) {
+ volume->mat[a] = newlibadr(fd, volume->id.lib, volume->mat[a]);
+ }
+}
+
+static void direct_link_volume(FileData *fd, Volume *volume)
+{
+ volume->adt = newdataadr(fd, volume->adt);
+ direct_link_animdata(fd, volume->adt);
+
+ volume->packedfile = direct_link_packedfile(fd, volume->packedfile);
+ volume->runtime.grids = (fd->volumemap) ? newvolumeadr(fd, volume->runtime.grids) : NULL;
+ volume->runtime.frame = 0;
+ BKE_volume_init_grids(volume);
+
+ /* materials */
+ volume->mat = newdataadr(fd, volume->mat);
+ test_pointer_array(fd, (void **)&volume->mat);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Read Library Data Block
* \{ */
@@ -9073,6 +9216,12 @@ static const char *dataname(short id_code)
return "Data from CF";
case ID_WS:
return "Data from WS";
+ case ID_HA:
+ return "Data from HA";
+ case ID_PT:
+ return "Data from PT";
+ case ID_VO:
+ return "Data from VO";
}
return "Data from Lib Block";
}
@@ -9498,6 +9647,15 @@ static BHead *read_libblock(FileData *fd,
case ID_WS:
direct_link_workspace(fd, (WorkSpace *)id, main);
break;
+ case ID_HA:
+ direct_link_hair(fd, (Hair *)id);
+ break;
+ case ID_PT:
+ direct_link_pointcloud(fd, (PointCloud *)id);
+ break;
+ case ID_VO:
+ direct_link_volume(fd, (Volume *)id);
+ break;
}
oldnewmap_free_unused(fd->datamap);
@@ -9825,6 +9983,15 @@ static void lib_link_all(FileData *fd, Main *bmain)
case ID_VF:
lib_link_vfont(fd, bmain, (VFont *)id);
break;
+ case ID_HA:
+ lib_link_hair(fd, bmain, (Hair *)id);
+ break;
+ case ID_PT:
+ lib_link_pointcloud(fd, bmain, (PointCloud *)id);
+ break;
+ case ID_VO:
+ lib_link_volume(fd, bmain, (Volume *)id);
+ break;
case ID_MA:
lib_link_material(fd, bmain, (Material *)id);
break;
@@ -11236,6 +11403,39 @@ static void expand_workspace(FileData *fd, Main *mainvar, WorkSpace *workspace)
}
}
+static void expand_hair(FileData *fd, Main *mainvar, Hair *hair)
+{
+ for (int a = 0; a < hair->totcol; a++) {
+ expand_doit(fd, mainvar, hair->mat[a]);
+ }
+
+ if (hair->adt) {
+ expand_animdata(fd, mainvar, hair->adt);
+ }
+}
+
+static void expand_pointcloud(FileData *fd, Main *mainvar, PointCloud *pointcloud)
+{
+ for (int a = 0; a < pointcloud->totcol; a++) {
+ expand_doit(fd, mainvar, pointcloud->mat[a]);
+ }
+
+ if (pointcloud->adt) {
+ expand_animdata(fd, mainvar, pointcloud->adt);
+ }
+}
+
+static void expand_volume(FileData *fd, Main *mainvar, Volume *volume)
+{
+ for (int a = 0; a < volume->totcol; a++) {
+ expand_doit(fd, mainvar, volume->mat[a]);
+ }
+
+ if (volume->adt) {
+ expand_animdata(fd, mainvar, volume->adt);
+ }
+}
+
/**
* Set the callback func used over all ID data found by \a BLO_expand_main func.
*
@@ -11356,6 +11556,15 @@ void BLO_expand_main(void *fdhandle, Main *mainvar)
case ID_WS:
expand_workspace(fd, mainvar, (WorkSpace *)id);
break;
+ case ID_HA:
+ expand_hair(fd, mainvar, (Hair *)id);
+ break;
+ case ID_PT:
+ expand_pointcloud(fd, mainvar, (PointCloud *)id);
+ break;
+ case ID_VO:
+ expand_volume(fd, mainvar, (Volume *)id);
+ break;
default:
break;
}
diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h
index 4965845d167..fce6a1d8ff7 100644
--- a/source/blender/blenloader/intern/readfile.h
+++ b/source/blender/blenloader/intern/readfile.h
@@ -124,6 +124,7 @@ typedef struct FileData {
struct OldNewMap *movieclipmap;
struct OldNewMap *scenemap;
struct OldNewMap *soundmap;
+ struct OldNewMap *volumemap;
struct OldNewMap *packedmap;
struct BHeadSort *bheadmap;
@@ -164,6 +165,8 @@ void blo_make_movieclip_pointer_map(FileData *fd, struct Main *oldmain);
void blo_end_movieclip_pointer_map(FileData *fd, struct Main *oldmain);
void blo_make_sound_pointer_map(FileData *fd, struct Main *oldmain);
void blo_end_sound_pointer_map(FileData *fd, struct Main *oldmain);
+void blo_make_volume_pointer_map(FileData *fd, struct Main *oldmain);
+void blo_end_volume_pointer_map(FileData *fd, struct Main *oldmain);
void blo_make_packed_pointer_map(FileData *fd, struct Main *oldmain);
void blo_end_packed_pointer_map(FileData *fd, struct Main *oldmain);
void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd);
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index 837134c0156..c948949c06e 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -106,6 +106,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_gpencil_modifier_types.h"
#include "DNA_shader_fx_types.h"
+#include "DNA_hair_types.h"
#include "DNA_fileglobal_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
@@ -121,6 +122,7 @@
#include "DNA_object_force_types.h"
#include "DNA_packedFile_types.h"
#include "DNA_particle_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
@@ -134,6 +136,7 @@
#include "DNA_text_types.h"
#include "DNA_view3d_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_workspace_types.h"
@@ -3712,6 +3715,98 @@ static void write_workspace(WriteData *wd, WorkSpace *workspace)
}
}
+static void write_hair(WriteData *wd, Hair *hair)
+{
+ if (hair->id.us > 0 || wd->use_memfile) {
+ /* Write a copy of the hair with possibly reduced number of data layers.
+ * Don't edit the original since other threads might be reading it. */
+ Hair *old_hair = hair;
+ Hair copy_hair = *hair;
+ hair = &copy_hair;
+
+ CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
+ CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+ CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff));
+
+ /* Write LibData */
+ writestruct_at_address(wd, ID_HA, Hair, 1, old_hair, hair);
+ write_iddata(wd, &hair->id);
+
+ /* Direct data */
+ write_customdata(wd, &hair->id, hair->totpoint, &hair->pdata, players, CD_MASK_ALL);
+ write_customdata(wd, &hair->id, hair->totcurve, &hair->cdata, clayers, CD_MASK_ALL);
+ writedata(wd, DATA, sizeof(void *) * hair->totcol, hair->mat);
+ if (hair->adt) {
+ write_animdata(wd, hair->adt);
+ }
+
+ /* Remove temporary data. */
+ if (players && players != players_buff) {
+ MEM_freeN(players);
+ }
+ if (clayers && clayers != clayers_buff) {
+ MEM_freeN(clayers);
+ }
+
+ /* restore pointer */
+ hair = old_hair;
+ }
+}
+
+static void write_pointcloud(WriteData *wd, PointCloud *pointcloud)
+{
+ if (pointcloud->id.us > 0 || wd->use_memfile) {
+ /* Write a copy of the pointcloud with possibly reduced number of data layers.
+ * Don't edit the original since other threads might be reading it. */
+ PointCloud *old_pointcloud = pointcloud;
+ PointCloud copy_pointcloud = *pointcloud;
+ pointcloud = &copy_pointcloud;
+
+ CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE];
+ CustomData_file_write_prepare(
+ &pointcloud->pdata, &players, players_buff, ARRAY_SIZE(players_buff));
+
+ /* Write LibData */
+ writestruct_at_address(wd, ID_PT, PointCloud, 1, old_pointcloud, pointcloud);
+ write_iddata(wd, &pointcloud->id);
+
+ /* Direct data */
+ write_customdata(
+ wd, &pointcloud->id, pointcloud->totpoint, &pointcloud->pdata, players, CD_MASK_ALL);
+ writedata(wd, DATA, sizeof(void *) * pointcloud->totcol, pointcloud->mat);
+ if (pointcloud->adt) {
+ write_animdata(wd, pointcloud->adt);
+ }
+
+ /* Remove temporary data. */
+ if (players && players != players_buff) {
+ MEM_freeN(players);
+ }
+ }
+}
+
+static void write_volume(WriteData *wd, Volume *volume)
+{
+ if (volume->id.us > 0 || wd->use_memfile) {
+ /* write LibData */
+ writestruct(wd, ID_VO, Volume, 1, volume);
+ write_iddata(wd, &volume->id);
+
+ /* direct data */
+ writedata(wd, DATA, sizeof(void *) * volume->totcol, volume->mat);
+ if (volume->adt) {
+ write_animdata(wd, volume->adt);
+ }
+
+ if (volume->packedfile) {
+ PackedFile *pf = volume->packedfile;
+ writestruct(wd, DATA, PackedFile, 1, pf);
+ writedata(wd, DATA, pf->size, pf->data);
+ }
+ }
+}
+
/* Keep it last of write_foodata functions. */
static void write_libraries(WriteData *wd, Main *main)
{
@@ -4017,6 +4112,15 @@ static bool write_file_handle(Main *mainvar,
case ID_CF:
write_cachefile(wd, (CacheFile *)id);
break;
+ case ID_HA:
+ write_hair(wd, (Hair *)id);
+ break;
+ case ID_PT:
+ write_pointcloud(wd, (PointCloud *)id);
+ break;
+ case ID_VO:
+ write_volume(wd, (Volume *)id);
+ break;
case ID_LI:
/* Do nothing, handled below - and should never be reached. */
BLI_assert(0);
diff --git a/source/blender/blentranslation/BLT_translation.h b/source/blender/blentranslation/BLT_translation.h
index 55403cc35d3..74c46b1cf3e 100644
--- a/source/blender/blentranslation/BLT_translation.h
+++ b/source/blender/blentranslation/BLT_translation.h
@@ -117,6 +117,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_CURVE "Curve"
#define BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE "FreestyleLineStyle"
#define BLT_I18NCONTEXT_ID_GPENCIL "GPencil"
+#define BLT_I18NCONTEXT_ID_HAIR "Hair"
#define BLT_I18NCONTEXT_ID_ID "ID"
#define BLT_I18NCONTEXT_ID_IMAGE "Image"
/*#define BLT_I18NCONTEXT_ID_IPO "Ipo"*/ /* Deprecated */
@@ -132,6 +133,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_PAINTCURVE "PaintCurve"
#define BLT_I18NCONTEXT_ID_PALETTE "Palette"
#define BLT_I18NCONTEXT_ID_PARTICLESETTINGS "ParticleSettings"
+#define BLT_I18NCONTEXT_ID_POINTCLOUD "PointCloud"
#define BLT_I18NCONTEXT_ID_LIGHTPROBE "LightProbe"
#define BLT_I18NCONTEXT_ID_SCENE "Scene"
#define BLT_I18NCONTEXT_ID_SCREEN "Screen"
@@ -141,6 +143,7 @@ bool BLT_lang_is_ime_supported(void);
#define BLT_I18NCONTEXT_ID_TEXTURE "Texture"
#define BLT_I18NCONTEXT_ID_TEXT "Text"
#define BLT_I18NCONTEXT_ID_VFONT "VFont"
+#define BLT_I18NCONTEXT_ID_VOLUME "Volume"
#define BLT_I18NCONTEXT_ID_WORLD "World"
#define BLT_I18NCONTEXT_ID_WORKSPACE "WorkSpace"
#define BLT_I18NCONTEXT_ID_WINDOWMANAGER "WindowManager"
@@ -175,6 +178,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_CURVE, "id_curve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, "id_fs_linestyle"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_GPENCIL, "id_gpencil"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_HAIR, "id_hair"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_ID, "id_id"), \
BLT_I18NCONTEXTS_ITEM( \
BLT_I18NCONTEXT_ID_IMAGE, \
@@ -193,6 +197,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PAINTCURVE, "id_paintcurve"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PALETTE, "id_palette"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_PARTICLESETTINGS, "id_particlesettings"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_POINTCLOUD, "id_pointcloud"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_LIGHTPROBE, "id_lightprobe"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_SCENE, "id_scene"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_SCREEN, "id_screen"), \
@@ -202,6 +207,7 @@ typedef struct {
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_TEXTURE, "id_texture"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_TEXT, "id_text"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_VFONT, "id_vfont"), \
+ BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_VOLUME, "id_volume"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORLD, "id_world"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WORKSPACE, "id_workspace"), \
BLT_I18NCONTEXTS_ITEM(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "id_windowmanager"), \
diff --git a/source/blender/depsgraph/CMakeLists.txt b/source/blender/depsgraph/CMakeLists.txt
index 169e7f00744..355d2536e1a 100644
--- a/source/blender/depsgraph/CMakeLists.txt
+++ b/source/blender/depsgraph/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sequence.cc
intern/eval/deg_eval_runtime_backup_sequencer.cc
intern/eval/deg_eval_runtime_backup_sound.cc
+ intern/eval/deg_eval_runtime_backup_volume.cc
intern/eval/deg_eval_stats.cc
intern/node/deg_node.cc
intern/node/deg_node_component.cc
@@ -122,6 +123,7 @@ set(SRC
intern/eval/deg_eval_runtime_backup_sequence.h
intern/eval/deg_eval_runtime_backup_sequencer.h
intern/eval/deg_eval_runtime_backup_sound.h
+ intern/eval/deg_eval_runtime_backup_volume.h
intern/eval/deg_eval_stats.h
intern/node/deg_node.h
intern/node/deg_node_component.h
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 442b4aa406a..ea49882fd77 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -97,6 +97,7 @@ extern "C" {
#include "BKE_shader_fx.h"
#include "BKE_sound.h"
#include "BKE_tracking.h"
+#include "BKE_volume.h"
#include "BKE_world.h"
#include "RNA_access.h"
@@ -455,6 +456,9 @@ void DepsgraphNodeBuilder::build_id(ID *id)
case ID_CU:
case ID_MB:
case ID_LT:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
/* TODO(sergey): Get visibility from a "parent" somehow.
*
* NOTE: Similarly to above, we don't want false-positives on
@@ -700,6 +704,9 @@ void DepsgraphNodeBuilder::build_object_data(Object *object, bool is_object_visi
case OB_MBALL:
case OB_LATTICE:
case OB_GPENCIL:
+ case OB_HAIR:
+ case OB_POINTCLOUD:
+ case OB_VOLUME:
build_object_data_geometry(object, is_object_visible);
break;
case OB_ARMATURE:
@@ -1326,6 +1333,26 @@ void DepsgraphNodeBuilder::build_object_data_geometry_datablock(ID *obdata, bool
op_node->set_as_entry();
break;
}
+ case ID_HA: {
+ op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node->set_as_entry();
+ break;
+ }
+ case ID_PT: {
+ op_node = add_operation_node(obdata, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL);
+ op_node->set_as_entry();
+ break;
+ }
+ case ID_VO: {
+ /* Volume frame update. */
+ op_node = add_operation_node(
+ obdata,
+ NodeType::GEOMETRY,
+ OperationCode::GEOMETRY_EVAL,
+ function_bind(BKE_volume_eval_geometry, _1, (Volume *)obdata_cow));
+ op_node->set_as_entry();
+ break;
+ }
default:
BLI_assert(!"Should not happen");
break;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 27d6db6a58f..4647486bafd 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -64,6 +64,7 @@ extern "C" {
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_object_force_types.h"
@@ -537,6 +538,9 @@ void DepsgraphRelationBuilder::build_id(ID *id)
case ID_CU:
case ID_MB:
case ID_LT:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
build_object_data_geometry_datablock(id);
break;
case ID_SPK:
@@ -771,7 +775,10 @@ void DepsgraphRelationBuilder::build_object_data(Object *object)
case OB_SURF:
case OB_MBALL:
case OB_LATTICE:
- case OB_GPENCIL: {
+ case OB_GPENCIL:
+ case OB_HAIR:
+ case OB_POINTCLOUD:
+ case OB_VOLUME: {
build_object_data_geometry(object);
/* TODO(sergey): Only for until we support granular
* update of curves. */
@@ -2145,6 +2152,19 @@ void DepsgraphRelationBuilder::build_object_data_geometry_datablock(ID *obdata)
}
break;
}
+ case ID_HA:
+ break;
+ case ID_PT:
+ break;
+ case ID_VO: {
+ Volume *volume = (Volume *)obdata;
+ if (volume->is_sequence) {
+ TimeSourceKey time_key;
+ ComponentKey geometry_key(obdata, NodeType::GEOMETRY);
+ add_relation(time_key, geometry_key, "Volume sequence time");
+ }
+ break;
+ }
default:
BLI_assert(!"Should not happen");
break;
@@ -2593,7 +2613,7 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node)
continue;
}
int rel_flag = (RELATION_FLAG_NO_FLUSH | RELATION_FLAG_GODMODE);
- if ((id_type == ID_ME && comp_node->type == NodeType::GEOMETRY) ||
+ if ((ELEM(id_type, ID_ME, ID_HA, ID_PT, ID_VO) && comp_node->type == NodeType::GEOMETRY) ||
(id_type == ID_CF && comp_node->type == NodeType::CACHE)) {
rel_flag &= ~RELATION_FLAG_NO_FLUSH;
}
diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc
index 8decb9f6b87..a14f22350a4 100644
--- a/source/blender/depsgraph/intern/depsgraph_tag.cc
+++ b/source/blender/depsgraph/intern/depsgraph_tag.cc
@@ -89,7 +89,7 @@ void depsgraph_geometry_tag_to_component(const ID *id, NodeType *component_type)
bool is_selectable_data_id_type(const ID_Type id_type)
{
- return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD);
+ return ELEM(id_type, ID_ME, ID_CU, ID_MB, ID_LT, ID_GD, ID_HA, ID_PT, ID_VO);
}
void depsgraph_select_tag_to_component_opcode(const ID *id,
@@ -582,6 +582,9 @@ NodeType geometry_tag_to_component(const ID *id)
case OB_LATTICE:
case OB_MBALL:
case OB_GPENCIL:
+ case OB_HAIR:
+ case OB_POINTCLOUD:
+ case OB_VOLUME:
return NodeType::GEOMETRY;
case OB_ARMATURE:
return NodeType::EVAL_POSE;
@@ -593,6 +596,9 @@ NodeType geometry_tag_to_component(const ID *id)
case ID_CU:
case ID_LT:
case ID_MB:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
return NodeType::GEOMETRY;
case ID_PA: /* Particles */
return NodeType::UNDEFINED;
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
index 40a17666880..108f5f04879 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
@@ -37,7 +37,8 @@ RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
sound_backup(depsgraph),
object_backup(depsgraph),
drawdata_ptr(nullptr),
- movieclip_backup(depsgraph)
+ movieclip_backup(depsgraph),
+ volume_backup(depsgraph)
{
drawdata_backup.first = drawdata_backup.last = nullptr;
}
@@ -64,6 +65,9 @@ void RuntimeBackup::init_from_id(ID *id)
case ID_MC:
movieclip_backup.init_from_movieclip(reinterpret_cast<MovieClip *>(id));
break;
+ case ID_VO:
+ volume_backup.init_from_volume(reinterpret_cast<Volume *>(id));
+ break;
default:
break;
}
@@ -95,6 +99,9 @@ void RuntimeBackup::restore_to_id(ID *id)
case ID_MC:
movieclip_backup.restore_to_movieclip(reinterpret_cast<MovieClip *>(id));
break;
+ case ID_VO:
+ volume_backup.restore_to_volume(reinterpret_cast<Volume *>(id));
+ break;
default:
break;
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
index cc8c6ae0d5b..c818c1f7064 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
@@ -30,6 +30,7 @@
#include "intern/eval/deg_eval_runtime_backup_object.h"
#include "intern/eval/deg_eval_runtime_backup_scene.h"
#include "intern/eval/deg_eval_runtime_backup_sound.h"
+#include "intern/eval/deg_eval_runtime_backup_volume.h"
namespace DEG {
@@ -52,6 +53,7 @@ class RuntimeBackup {
DrawDataList drawdata_backup;
DrawDataList *drawdata_ptr;
MovieClipBackup movieclip_backup;
+ VolumeBackup volume_backup;
};
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
index 855dd4821ce..2f45ea45197 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
@@ -129,6 +129,17 @@ void ObjectRuntimeBackup::restore_to_object(Object *object)
}
}
}
+ else if (ELEM(object->type, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) {
+ if (object->id.recalc & ID_RECALC_GEOMETRY) {
+ /* Free evaluated caches. */
+ object->data = data_orig;
+ BKE_object_free_derived_caches(object);
+ }
+ else {
+ object->data = object->runtime.data_eval;
+ }
+ }
+
object->base_flag = base_flag;
object->base_local_view_bits = base_local_view_bits;
/* Restore modifier's runtime data.
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc
new file mode 100644
index 00000000000..09e13ec131d
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc
@@ -0,0 +1,60 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#include "intern/eval/deg_eval_runtime_backup_volume.h"
+
+#include "BLI_assert.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_volume_types.h"
+
+#include "BKE_volume.h"
+
+#include <stdio.h>
+
+namespace DEG {
+
+VolumeBackup::VolumeBackup(const Depsgraph * /*depsgraph*/) : grids(nullptr)
+{
+}
+
+void VolumeBackup::init_from_volume(Volume *volume)
+{
+ STRNCPY(filepath, volume->filepath);
+ BLI_STATIC_ASSERT(sizeof(filepath) == sizeof(volume->filepath),
+ "VolumeBackup filepath length wrong");
+
+ grids = volume->runtime.grids;
+ volume->runtime.grids = nullptr;
+}
+
+void VolumeBackup::restore_to_volume(Volume *volume)
+{
+ if (grids) {
+ BKE_volume_grids_backup_restore(volume, grids, filepath);
+ grids = nullptr;
+ }
+}
+
+} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
new file mode 100644
index 00000000000..cf57c702c8f
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h
@@ -0,0 +1,45 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+struct Volume;
+struct VolumeGridVector;
+
+namespace DEG {
+
+struct Depsgraph;
+
+/* Backup of volume datablocks runtime data. */
+class VolumeBackup {
+ public:
+ VolumeBackup(const Depsgraph *depsgraph);
+
+ void init_from_volume(Volume *volume);
+ void restore_to_volume(Volume *volume);
+
+ VolumeGridVector *grids;
+ char filepath[1024]; /* FILE_MAX */
+};
+
+} // namespace DEG
diff --git a/source/blender/draw/engines/overlay/overlay_pointcloud.c b/source/blender/draw/engines/overlay/overlay_pointcloud.c
new file mode 100644
index 00000000000..a0de7aac1f1
--- /dev/null
+++ b/source/blender/draw/engines/overlay/overlay_pointcloud.c
@@ -0,0 +1,72 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright 2020, Blender Foundation.
+ */
+
+/** \file
+ * \ingroup draw_engine
+ */
+
+#include "DRW_render.h"
+
+#include "DEG_depsgraph_query.h"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_pointcache.h"
+
+#include "overlay_private.h"
+
+/* -------------------------------------------------------------------- */
+/** \name PointCloud
+ * \{ */
+
+void OVERLAY_pointcloud_cache_init(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+ GPUShader *sh;
+ DRWShadingGroup *grp;
+
+ DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL;
+ DRW_PASS_CREATE(psl->pointcloud_ps, state | pd->clipping_state);
+
+ sh = OVERLAY_shader_pointcloud_dot();
+ pd->pointcloud_dots_grp = grp = DRW_shgroup_create(sh, psl->pointcloud_ps);
+ DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo);
+}
+
+void OVERLAY_pointcloud_cache_populate(OVERLAY_Data *vedata, Object *ob)
+{
+ OVERLAY_PrivateData *pd = vedata->stl->pd;
+
+ struct GPUBatch *geom = DRW_cache_pointcloud_get_dots(ob);
+
+ const float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->pointcloud_dots_grp);
+ DRW_shgroup_uniform_vec4_copy(grp, "color", color);
+ DRW_shgroup_call(grp, geom, ob);
+}
+
+void OVERLAY_pointcloud_draw(OVERLAY_Data *vedata)
+{
+ OVERLAY_PassList *psl = vedata->psl;
+
+ DRW_draw_pass(psl->pointcloud_ps);
+}
+
+/** \} */
diff --git a/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl b/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl
new file mode 100644
index 00000000000..36928d0c776
--- /dev/null
+++ b/source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl
@@ -0,0 +1,16 @@
+
+in vec4 finalColor;
+
+out vec4 fragColor;
+
+void main()
+{
+ float dist = length(gl_PointCoord - vec2(0.5));
+
+ if (dist > 0.5) {
+ discard;
+ }
+ /* Nice sphere falloff. */
+ float intensity = sqrt(1.0 - dist * 2.0) * 0.5 + 0.5;
+ fragColor = finalColor * vec4(intensity, intensity, intensity, 1.0);
+}
diff --git a/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl b/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl
new file mode 100644
index 00000000000..d71ccee5159
--- /dev/null
+++ b/source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl
@@ -0,0 +1,27 @@
+
+uniform vec4 color;
+
+/* ---- Per instance Attrs ---- */
+in vec3 pointcloud_pos;
+in vec3 pointcloud_radius;
+
+out vec4 finalColor;
+
+void main()
+{
+ vec3 world_pos = point_object_to_world(pointcloud_pos);
+
+ vec3 world_size = abs(mat3(ModelMatrix) * vec3(pointcloud_radius));
+ float world_radius = (world_size.x + world_size.y + world_size.z) / 3.0;
+
+ gl_Position = point_world_to_ndc(world_pos);
+ /* World sized points. */
+ gl_PointSize = sizePixel * world_radius * ProjectionMatrix[1][1] * sizeViewport.y /
+ gl_Position.w;
+
+ finalColor = color;
+
+#ifdef USE_WORLD_CLIP_PLANES
+ world_clip_planes_calc_clip_distance(world_pos);
+#endif
+}
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
new file mode 100644
index 00000000000..007f6258184
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -0,0 +1,349 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Hair API for render engines
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math_base.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_hair_types.h"
+#include "DNA_object_types.h"
+
+#include "BKE_hair.h"
+
+#include "GPU_batch.h"
+#include "GPU_texture.h"
+
+#include "draw_cache_impl.h" /* own include */
+#include "draw_hair_private.h" /* own include */
+
+static void hair_batch_cache_clear(Hair *hair);
+
+/* ---------------------------------------------------------------------- */
+/* Hair GPUBatch Cache */
+
+typedef struct HairBatchCache {
+ ParticleHairCache hair;
+
+ /* settings to determine if cache is invalid */
+ bool is_dirty;
+} HairBatchCache;
+
+/* GPUBatch cache management. */
+
+static bool hair_batch_cache_valid(Hair *hair)
+{
+ HairBatchCache *cache = hair->batch_cache;
+ return (cache && cache->is_dirty == false);
+}
+
+static void hair_batch_cache_init(Hair *hair)
+{
+ HairBatchCache *cache = hair->batch_cache;
+
+ if (!cache) {
+ cache = hair->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->is_dirty = false;
+}
+
+void DRW_hair_batch_cache_validate(Hair *hair)
+{
+ if (!hair_batch_cache_valid(hair)) {
+ hair_batch_cache_clear(hair);
+ hair_batch_cache_init(hair);
+ }
+}
+
+static HairBatchCache *hair_batch_cache_get(Hair *hair)
+{
+ DRW_hair_batch_cache_validate(hair);
+ return hair->batch_cache;
+}
+
+void DRW_hair_batch_cache_dirty_tag(Hair *hair, int mode)
+{
+ HairBatchCache *cache = hair->batch_cache;
+ if (cache == NULL) {
+ return;
+ }
+ switch (mode) {
+ case BKE_HAIR_BATCH_DIRTY_ALL:
+ cache->is_dirty = true;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
+static void hair_batch_cache_clear(Hair *hair)
+{
+ HairBatchCache *cache = hair->batch_cache;
+ if (!cache) {
+ return;
+ }
+
+ particle_batch_cache_clear_hair(&cache->hair);
+}
+
+void DRW_hair_batch_cache_free(Hair *hair)
+{
+ hair_batch_cache_clear(hair);
+ MEM_SAFE_FREE(hair->batch_cache);
+}
+
+static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
+{
+ if ((hair_cache->pos != NULL && hair_cache->indices != NULL) ||
+ (hair_cache->proc_point_buf != NULL)) {
+ return;
+ }
+
+ hair_cache->strands_len = 0;
+ hair_cache->elems_len = 0;
+ hair_cache->point_len = 0;
+
+ HairCurve *curve = hair->curves;
+ int num_curves = hair->totcurve;
+ for (int i = 0; i < num_curves; i++, curve++) {
+ hair_cache->strands_len++;
+ hair_cache->elems_len += curve->numpoints + 1;
+ hair_cache->point_len += curve->numpoints;
+ }
+}
+
+static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step)
+{
+ /* TODO: use hair radius layer if available. */
+ HairCurve *curve = hair->curves;
+ int num_curves = hair->totcurve;
+ for (int i = 0; i < num_curves; i++, curve++) {
+ float(*curve_co)[3] = hair->co + curve->firstpoint;
+ float total_len = 0.0f;
+ float *co_prev = NULL, *seg_data_first;
+ for (int j = 0; j < curve->numpoints; j++) {
+ float *seg_data = (float *)GPU_vertbuf_raw_step(attr_step);
+ copy_v3_v3(seg_data, curve_co[j]);
+ if (co_prev) {
+ total_len += len_v3v3(co_prev, curve_co[j]);
+ }
+ else {
+ seg_data_first = seg_data;
+ }
+ seg_data[3] = total_len;
+ co_prev = curve_co[j];
+ }
+ if (total_len > 0.0f) {
+ /* Divide by total length to have a [0-1] number. */
+ for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
+ seg_data_first[3] /= total_len;
+ }
+ }
+ }
+}
+
+static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache)
+{
+ if (cache->proc_point_buf != NULL) {
+ return;
+ }
+
+ /* initialize vertex format */
+ GPUVertFormat format = {0};
+ uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+ cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
+
+ GPUVertBufRaw pos_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
+
+ hair_batch_cache_fill_segments_proc_pos(hair, &pos_step);
+
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_point_buf);
+
+ cache->point_tex = GPU_texture_create_from_vertbuf(cache->proc_point_buf);
+}
+
+static void hair_batch_cache_fill_strands_data(Hair *hair,
+ GPUVertBufRaw *data_step,
+ GPUVertBufRaw *seg_step)
+{
+ HairCurve *curve = hair->curves;
+ int num_curves = hair->totcurve;
+ for (int i = 0; i < num_curves; i++, curve++) {
+ *(uint *)GPU_vertbuf_raw_step(data_step) = curve->firstpoint;
+ *(ushort *)GPU_vertbuf_raw_step(seg_step) = curve->numpoints - 1;
+ }
+}
+
+static void hair_batch_cache_ensure_procedural_strand_data(Hair *hair, ParticleHairCache *cache)
+{
+ GPUVertBufRaw data_step, seg_step;
+
+ GPUVertFormat format_data = {0};
+ uint data_id = GPU_vertformat_attr_add(&format_data, "data", GPU_COMP_U32, 1, GPU_FETCH_INT);
+
+ GPUVertFormat format_seg = {0};
+ uint seg_id = GPU_vertformat_attr_add(&format_seg, "data", GPU_COMP_U16, 1, GPU_FETCH_INT);
+
+ /* Strand Data */
+ cache->proc_strand_buf = GPU_vertbuf_create_with_format(&format_data);
+ GPU_vertbuf_data_alloc(cache->proc_strand_buf, cache->strands_len);
+ GPU_vertbuf_attr_get_raw_data(cache->proc_strand_buf, data_id, &data_step);
+
+ cache->proc_strand_seg_buf = GPU_vertbuf_create_with_format(&format_seg);
+ GPU_vertbuf_data_alloc(cache->proc_strand_seg_buf, cache->strands_len);
+ GPU_vertbuf_attr_get_raw_data(cache->proc_strand_seg_buf, seg_id, &seg_step);
+
+ hair_batch_cache_fill_strands_data(hair, &data_step, &seg_step);
+
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_strand_buf);
+ cache->strand_tex = GPU_texture_create_from_vertbuf(cache->proc_strand_buf);
+
+ GPU_vertbuf_use(cache->proc_strand_seg_buf);
+ cache->strand_seg_tex = GPU_texture_create_from_vertbuf(cache->proc_strand_seg_buf);
+}
+
+static void hair_batch_cache_ensure_procedural_final_points(ParticleHairCache *cache, int subdiv)
+{
+ /* Same format as point_tex. */
+ GPUVertFormat format = {0};
+ GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+
+ cache->final[subdiv].proc_buf = GPU_vertbuf_create_with_format(&format);
+
+ /* Create a destination buffer for the transform feedback. Sized appropriately */
+ /* Those are points! not line segments. */
+ GPU_vertbuf_data_alloc(cache->final[subdiv].proc_buf,
+ cache->final[subdiv].strands_res * cache->strands_len);
+
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->final[subdiv].proc_buf);
+
+ cache->final[subdiv].proc_tex = GPU_texture_create_from_vertbuf(cache->final[subdiv].proc_buf);
+}
+
+static void hair_batch_cache_fill_segments_indices(Hair *hair,
+ const int res,
+ GPUIndexBufBuilder *elb)
+{
+ HairCurve *curve = hair->curves;
+ int num_curves = hair->totcurve;
+ uint curr_point = 0;
+ for (int i = 0; i < num_curves; i++, curve++) {
+ for (int k = 0; k < res; k++) {
+ GPU_indexbuf_add_generic_vert(elb, curr_point++);
+ }
+ GPU_indexbuf_add_primitive_restart(elb);
+ }
+}
+
+static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
+ ParticleHairCache *cache,
+ int thickness_res,
+ int subdiv)
+{
+ BLI_assert(thickness_res <= MAX_THICKRES); /* Cylinder strip not currently supported. */
+
+ if (cache->final[subdiv].proc_hairs[thickness_res - 1] != NULL) {
+ return;
+ }
+
+ int verts_per_hair = cache->final[subdiv].strands_res * thickness_res;
+ /* +1 for primitive restart */
+ int element_count = (verts_per_hair + 1) * cache->strands_len;
+ GPUPrimType prim_type = (thickness_res == 1) ? GPU_PRIM_LINE_STRIP : GPU_PRIM_TRI_STRIP;
+
+ static GPUVertFormat format = {0};
+ GPU_vertformat_clear(&format);
+
+ /* initialize vertex format */
+ GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_U8, 1, GPU_FETCH_INT_TO_FLOAT_UNIT);
+
+ GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(vbo, 1);
+
+ GPUIndexBufBuilder elb;
+ GPU_indexbuf_init_ex(&elb, prim_type, element_count, element_count);
+
+ hair_batch_cache_fill_segments_indices(hair, verts_per_hair, &elb);
+
+ cache->final[subdiv].proc_hairs[thickness_res - 1] = GPU_batch_create_ex(
+ prim_type, vbo, GPU_indexbuf_build(&elb), GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX);
+}
+
+/* Ensure all textures and buffers needed for GPU accelerated drawing. */
+bool hair_ensure_procedural_data(Object *object,
+ ParticleHairCache **r_hair_cache,
+ int subdiv,
+ int thickness_res)
+{
+ bool need_ft_update = false;
+ Hair *hair = object->data;
+
+ HairBatchCache *cache = hair_batch_cache_get(hair);
+ *r_hair_cache = &cache->hair;
+
+ const int steps = 2; /* TODO: don't hardcode? */
+ (*r_hair_cache)->final[subdiv].strands_res = 1 << (steps + subdiv);
+
+ /* Refreshed on combing and simulation. */
+ if ((*r_hair_cache)->proc_point_buf == NULL) {
+ ensure_seg_pt_count(hair, &cache->hair);
+ hair_batch_cache_ensure_procedural_pos(hair, &cache->hair);
+ need_ft_update = true;
+ }
+
+ /* Refreshed if active layer or custom data changes. */
+ if ((*r_hair_cache)->strand_tex == NULL) {
+ hair_batch_cache_ensure_procedural_strand_data(hair, &cache->hair);
+ }
+
+ /* Refreshed only on subdiv count change. */
+ if ((*r_hair_cache)->final[subdiv].proc_buf == NULL) {
+ hair_batch_cache_ensure_procedural_final_points(&cache->hair, subdiv);
+ need_ft_update = true;
+ }
+ if ((*r_hair_cache)->final[subdiv].proc_hairs[thickness_res - 1] == NULL) {
+ hair_batch_cache_ensure_procedural_indices(hair, &cache->hair, thickness_res, subdiv);
+ }
+
+ return need_ft_update;
+}
+
+int DRW_hair_material_count_get(Hair *hair)
+{
+ return max_ii(1, hair->totcol);
+}
diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
new file mode 100644
index 00000000000..83757cb714a
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.c
@@ -0,0 +1,176 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief PointCloud API for render engines
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math_base.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_pointcloud.h"
+
+#include "GPU_batch.h"
+
+#include "draw_cache_impl.h" /* own include */
+
+static void pointcloud_batch_cache_clear(PointCloud *pointcloud);
+
+/* ---------------------------------------------------------------------- */
+/* PointCloud GPUBatch Cache */
+
+typedef struct PointCloudBatchCache {
+ GPUVertBuf *pos;
+ GPUBatch *batch;
+
+ /* settings to determine if cache is invalid */
+ bool is_dirty;
+} PointCloudBatchCache;
+
+/* GPUBatch cache management. */
+
+static bool pointcloud_batch_cache_valid(PointCloud *pointcloud)
+{
+ PointCloudBatchCache *cache = pointcloud->batch_cache;
+ return (cache && cache->is_dirty == false);
+}
+
+static void pointcloud_batch_cache_init(PointCloud *pointcloud)
+{
+ PointCloudBatchCache *cache = pointcloud->batch_cache;
+
+ if (!cache) {
+ cache = pointcloud->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->is_dirty = false;
+}
+
+void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud)
+{
+ if (!pointcloud_batch_cache_valid(pointcloud)) {
+ pointcloud_batch_cache_clear(pointcloud);
+ pointcloud_batch_cache_init(pointcloud);
+ }
+}
+
+static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud *pointcloud)
+{
+ return pointcloud->batch_cache;
+}
+
+void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode)
+{
+ PointCloudBatchCache *cache = pointcloud->batch_cache;
+ if (cache == NULL) {
+ return;
+ }
+ switch (mode) {
+ case BKE_POINTCLOUD_BATCH_DIRTY_ALL:
+ cache->is_dirty = true;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
+static void pointcloud_batch_cache_clear(PointCloud *pointcloud)
+{
+ PointCloudBatchCache *cache = pointcloud->batch_cache;
+ if (!cache) {
+ return;
+ }
+
+ GPU_BATCH_DISCARD_SAFE(cache->batch);
+ GPU_VERTBUF_DISCARD_SAFE(cache->pos);
+}
+
+void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud)
+{
+ pointcloud_batch_cache_clear(pointcloud);
+ MEM_SAFE_FREE(pointcloud->batch_cache);
+}
+
+static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *cache)
+{
+ if (cache->pos != NULL) {
+ return;
+ }
+
+ PointCloud *pointcloud = ob->data;
+
+ static GPUVertFormat format = {0};
+ static uint pos_id;
+ static uint radius_id;
+ if (format.attr_len == 0) {
+ /* initialize vertex format */
+ pos_id = GPU_vertformat_attr_add(&format, "pointcloud_pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ radius_id = GPU_vertformat_attr_add(
+ &format, "pointcloud_radius", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ }
+
+ GPU_VERTBUF_DISCARD_SAFE(cache->pos);
+ cache->pos = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint);
+ GPU_vertbuf_attr_fill(cache->pos, pos_id, pointcloud->co);
+
+ if (pointcloud->radius) {
+ GPU_vertbuf_attr_fill(cache->pos, radius_id, pointcloud->radius);
+ }
+ else if (pointcloud->totpoint) {
+ /* TODO: optimize for constant radius by not including in vertex buffer at all? */
+ float *radius = MEM_malloc_arrayN(pointcloud->totpoint, sizeof(float), __func__);
+ for (int i = 0; i < pointcloud->totpoint; i++) {
+ /* TODO: add default radius to PointCloud data structure. */
+ radius[i] = 0.01f;
+ }
+ GPU_vertbuf_attr_fill(cache->pos, radius_id, radius);
+ MEM_freeN(radius);
+ }
+}
+
+GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob)
+{
+ PointCloud *pointcloud = ob->data;
+ PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud);
+
+ if (cache->batch == NULL) {
+ pointcloud_batch_cache_ensure_pos(ob, cache);
+ cache->batch = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL);
+ }
+
+ return cache->batch;
+}
+
+int DRW_pointcloud_material_count_get(PointCloud *pointcloud)
+{
+ return max_ii(1, pointcloud->totcol);
+}
diff --git a/source/blender/draw/intern/draw_cache_impl_volume.c b/source/blender/draw/intern/draw_cache_impl_volume.c
new file mode 100644
index 00000000000..cdac8b33fba
--- /dev/null
+++ b/source/blender/draw/intern/draw_cache_impl_volume.c
@@ -0,0 +1,297 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 by Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup draw
+ *
+ * \brief Volume API for render engines
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_math_base.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_object_types.h"
+#include "DNA_volume_types.h"
+
+#include "BKE_global.h"
+#include "BKE_volume.h"
+#include "BKE_volume_render.h"
+
+#include "GPU_batch.h"
+#include "GPU_texture.h"
+
+#include "DRW_render.h"
+
+#include "draw_cache.h" /* own include */
+#include "draw_cache_impl.h" /* own include */
+
+static void volume_batch_cache_clear(Volume *volume);
+
+/* ---------------------------------------------------------------------- */
+/* Volume GPUBatch Cache */
+
+typedef struct VolumeBatchCache {
+ /* 3D textures */
+ ListBase grids;
+
+ /* Wireframe */
+ struct {
+ GPUVertBuf *pos_nor_in_order;
+ GPUBatch *batch;
+ } face_wire;
+
+ /* settings to determine if cache is invalid */
+ bool is_dirty;
+} VolumeBatchCache;
+
+/* GPUBatch cache management. */
+
+static bool volume_batch_cache_valid(Volume *volume)
+{
+ VolumeBatchCache *cache = volume->batch_cache;
+ return (cache && cache->is_dirty == false);
+}
+
+static void volume_batch_cache_init(Volume *volume)
+{
+ VolumeBatchCache *cache = volume->batch_cache;
+
+ if (!cache) {
+ cache = volume->batch_cache = MEM_callocN(sizeof(*cache), __func__);
+ }
+ else {
+ memset(cache, 0, sizeof(*cache));
+ }
+
+ cache->is_dirty = false;
+}
+
+void DRW_volume_batch_cache_validate(Volume *volume)
+{
+ if (!volume_batch_cache_valid(volume)) {
+ volume_batch_cache_clear(volume);
+ volume_batch_cache_init(volume);
+ }
+}
+
+static VolumeBatchCache *volume_batch_cache_get(Volume *volume)
+{
+ DRW_volume_batch_cache_validate(volume);
+ return volume->batch_cache;
+}
+
+void DRW_volume_batch_cache_dirty_tag(Volume *volume, int mode)
+{
+ VolumeBatchCache *cache = volume->batch_cache;
+ if (cache == NULL) {
+ return;
+ }
+ switch (mode) {
+ case BKE_VOLUME_BATCH_DIRTY_ALL:
+ cache->is_dirty = true;
+ break;
+ default:
+ BLI_assert(0);
+ }
+}
+
+static void volume_batch_cache_clear(Volume *volume)
+{
+ VolumeBatchCache *cache = volume->batch_cache;
+ if (!cache) {
+ return;
+ }
+
+ for (DRWVolumeGrid *grid = cache->grids.first; grid; grid = grid->next) {
+ MEM_SAFE_FREE(grid->name);
+ DRW_TEXTURE_FREE_SAFE(grid->texture);
+ }
+ BLI_freelistN(&cache->grids);
+
+ GPU_VERTBUF_DISCARD_SAFE(cache->face_wire.pos_nor_in_order);
+ GPU_BATCH_DISCARD_SAFE(cache->face_wire.batch);
+}
+
+void DRW_volume_batch_cache_free(Volume *volume)
+{
+ volume_batch_cache_clear(volume);
+ MEM_SAFE_FREE(volume->batch_cache);
+}
+
+static void drw_volume_wireframe_cb(
+ void *userdata, float (*verts)[3], int (*edges)[2], int totvert, int totedge)
+{
+ Volume *volume = userdata;
+ VolumeBatchCache *cache = volume->batch_cache;
+
+ /* Create vertex buffer. */
+ static GPUVertFormat format = {0};
+ static uint pos_id, nor_id;
+ if (format.attr_len == 0) {
+ pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+ nor_id = GPU_vertformat_attr_add(&format, "nor", GPU_COMP_I10, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
+ }
+
+ static float normal[3] = {1.0f, 0.0f, 0.0f};
+ GPUPackedNormal packed_normal = GPU_normal_convert_i10_v3(normal);
+
+ cache->face_wire.pos_nor_in_order = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->face_wire.pos_nor_in_order, totvert);
+ GPU_vertbuf_attr_fill(cache->face_wire.pos_nor_in_order, pos_id, verts);
+ GPU_vertbuf_attr_fill_stride(cache->face_wire.pos_nor_in_order, nor_id, 0, &packed_normal);
+
+ /* Create wiredata. */
+ GPUVertBuf *vbo_wiredata = MEM_callocN(sizeof(GPUVertBuf), __func__);
+ DRW_vertbuf_create_wiredata(vbo_wiredata, totvert);
+
+ if (volume->display.wireframe_type == VOLUME_WIREFRAME_POINTS) {
+ /* Create batch. */
+ cache->face_wire.batch = GPU_batch_create(
+ GPU_PRIM_POINTS, cache->face_wire.pos_nor_in_order, NULL);
+ }
+ else {
+ /* Create edge index buffer. */
+ GPUIndexBufBuilder elb;
+ GPU_indexbuf_init(&elb, GPU_PRIM_LINES, totedge, totvert);
+ for (int i = 0; i < totedge; i++) {
+ GPU_indexbuf_add_line_verts(&elb, edges[i][0], edges[i][1]);
+ }
+ GPUIndexBuf *ibo = GPU_indexbuf_build(&elb);
+
+ /* Create batch. */
+ cache->face_wire.batch = GPU_batch_create_ex(
+ GPU_PRIM_LINES, cache->face_wire.pos_nor_in_order, ibo, GPU_BATCH_OWNS_INDEX);
+ }
+
+ GPU_batch_vertbuf_add_ex(cache->face_wire.batch, vbo_wiredata, true);
+}
+
+GPUBatch *DRW_volume_batch_cache_get_wireframes_face(Volume *volume)
+{
+ if (volume->display.wireframe_type == VOLUME_WIREFRAME_NONE) {
+ return NULL;
+ }
+
+ VolumeBatchCache *cache = volume_batch_cache_get(volume);
+
+ if (cache->face_wire.batch == NULL) {
+ VolumeGrid *volume_grid = BKE_volume_grid_active_get(volume);
+ if (volume_grid == NULL) {
+ return NULL;
+ }
+
+ /* Create wireframe from OpenVDB tree. */
+ BKE_volume_grid_wireframe(volume, volume_grid, drw_volume_wireframe_cb, volume);
+ }
+
+ return cache->face_wire.batch;
+}
+
+static DRWVolumeGrid *volume_grid_cache_get(Volume *volume,
+ VolumeGrid *grid,
+ VolumeBatchCache *cache)
+{
+ const char *name = BKE_volume_grid_name(grid);
+
+ /* Return cached grid. */
+ DRWVolumeGrid *cache_grid;
+ for (cache_grid = cache->grids.first; cache_grid; cache_grid = cache_grid->next) {
+ if (STREQ(cache_grid->name, name)) {
+ return cache_grid;
+ }
+ }
+
+ /* Allocate new grid. */
+ cache_grid = MEM_callocN(sizeof(DRWVolumeGrid), __func__);
+ cache_grid->name = BLI_strdup(name);
+ BLI_addtail(&cache->grids, cache_grid);
+
+ /* TODO: can we load this earlier, avoid accessing the global and take
+ * advantage of dependency graph multithreading? */
+ BKE_volume_load(volume, G.main);
+
+ /* Test if we support textures with the number of channels. */
+ size_t channels = BKE_volume_grid_channels(grid);
+ if (!ELEM(channels, 1, 3)) {
+ return cache_grid;
+ }
+
+ /* Load grid tree into memory, if not loaded already. */
+ const bool was_loaded = BKE_volume_grid_is_loaded(grid);
+ BKE_volume_grid_load(volume, grid);
+
+ /* Compute dense voxel grid size. */
+ int64_t dense_min[3], dense_max[3], resolution[3] = {0};
+ if (BKE_volume_grid_dense_bounds(volume, grid, dense_min, dense_max)) {
+ resolution[0] = dense_max[0] - dense_min[0];
+ resolution[1] = dense_max[1] - dense_min[1];
+ resolution[2] = dense_max[2] - dense_min[2];
+ }
+ size_t num_voxels = resolution[0] * resolution[1] * resolution[2];
+ size_t elem_size = sizeof(float) * channels;
+
+ /* Allocate and load voxels. */
+ float *voxels = (num_voxels > 0) ? MEM_malloc_arrayN(num_voxels, elem_size, __func__) : NULL;
+ if (voxels != NULL) {
+ BKE_volume_grid_dense_voxels(volume, grid, dense_min, dense_max, voxels);
+
+ /* Create GPU texture. */
+ cache_grid->texture = GPU_texture_create_3d(resolution[0],
+ resolution[1],
+ resolution[2],
+ (channels == 3) ? GPU_RGB16F : GPU_R16F,
+ voxels,
+ NULL);
+
+ GPU_texture_bind(cache_grid->texture, 0);
+ GPU_texture_swizzle_channel_auto(cache_grid->texture, channels);
+ GPU_texture_unbind(cache_grid->texture);
+
+ MEM_freeN(voxels);
+
+ /* Compute transform matrices. */
+ BKE_volume_grid_dense_transform_matrix(
+ grid, dense_min, dense_max, cache_grid->texture_to_object);
+ invert_m4_m4(cache_grid->object_to_texture, cache_grid->texture_to_object);
+ }
+
+ /* Free grid from memory if it wasn't previously loaded. */
+ if (!was_loaded) {
+ BKE_volume_grid_unload(volume, grid);
+ }
+
+ return cache_grid;
+}
+
+DRWVolumeGrid *DRW_volume_batch_cache_get_grid(Volume *volume, VolumeGrid *volume_grid)
+{
+ VolumeBatchCache *cache = volume_batch_cache_get(volume);
+ DRWVolumeGrid *grid = volume_grid_cache_get(volume, volume_grid, cache);
+ return (grid->texture != NULL) ? grid : NULL;
+}
+
+int DRW_volume_material_count_get(Volume *volume)
+{
+ return max_ii(1, volume->totcol);
+}
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index df114bab367..07743f3b4bd 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -35,6 +35,7 @@
#include "DNA_armature_types.h"
#include "DNA_cachefile_types.h"
#include "DNA_camera_types.h"
+#include "DNA_hair_types.h"
#include "DNA_object_types.h"
#include "DNA_particle_types.h"
#include "DNA_screen_types.h"
@@ -48,6 +49,8 @@
#include "DNA_material_types.h"
#include "DNA_meta_types.h"
#include "DNA_node_types.h"
+#include "DNA_pointcloud_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_speaker_types.h"
@@ -699,6 +702,12 @@ static int acf_object_icon(bAnimListElem *ale)
return ICON_OUTLINER_OB_FONT;
case OB_SURF:
return ICON_OUTLINER_OB_SURFACE;
+ case OB_HAIR:
+ return ICON_OUTLINER_OB_HAIR;
+ case OB_POINTCLOUD:
+ return ICON_OUTLINER_OB_POINTCLOUD;
+ case OB_VOLUME:
+ return ICON_OUTLINER_OB_VOLUME;
case OB_EMPTY:
return ICON_OUTLINER_OB_EMPTY;
case OB_GPENCIL:
@@ -2785,6 +2794,244 @@ static bAnimChannelType ACF_DSSPK = {
acf_dsspk_setting_ptr, /* pointer for setting */
};
+/* Hair Expander ------------------------------------------- */
+
+// TODO: just get this from RNA?
+static int acf_dshair_icon(bAnimListElem *UNUSED(ale))
+{
+ return ICON_HAIR_DATA;
+}
+
+/* get the appropriate flag(s) for the setting when it is valid */
+static int acf_dshair_setting_flag(bAnimContext *UNUSED(ac),
+ eAnimChannel_Settings setting,
+ bool *neg)
+{
+ /* clear extra return data first */
+ *neg = false;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return VO_DS_EXPAND;
+
+ case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
+ return ADT_NLA_EVAL_OFF;
+
+ case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
+ *neg = true;
+ return ADT_CURVES_NOT_VISIBLE;
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return ADT_UI_SELECTED;
+
+ default: /* unsupported */
+ return 0;
+ }
+}
+
+/* get pointer to the setting */
+static void *acf_dshair_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings setting, short *type)
+{
+ Hair *hair = (Hair *)ale->data;
+
+ /* clear extra return data first */
+ *type = 0;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return GET_ACF_FLAG_PTR(hair->flag, type);
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
+ case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
+ if (hair->adt)
+ return GET_ACF_FLAG_PTR(hair->adt->flag, type);
+ return NULL;
+
+ default: /* unsupported */
+ return NULL;
+ }
+}
+
+/* hair expander type define */
+static bAnimChannelType ACF_DSHAIR = {
+ "Hair Expander", /* type name */
+ ACHANNEL_ROLE_EXPANDER, /* role */
+
+ acf_generic_dataexpand_color, /* backdrop color */
+ acf_generic_dataexpand_backdrop, /* backdrop */
+ acf_generic_indention_1, /* indent level */
+ acf_generic_basic_offset, /* offset */
+
+ acf_generic_idblock_name, /* name */
+ acf_generic_idblock_name_prop, /* name prop */
+ acf_dshair_icon, /* icon */
+
+ acf_generic_dataexpand_setting_valid, /* has setting */
+ acf_dshair_setting_flag, /* flag for setting */
+ acf_dshair_setting_ptr /* pointer for setting */
+};
+
+/* PointCloud Expander ------------------------------------------- */
+
+// TODO: just get this from RNA?
+static int acf_dspointcloud_icon(bAnimListElem *UNUSED(ale))
+{
+ return ICON_POINTCLOUD_DATA;
+}
+
+/* get the appropriate flag(s) for the setting when it is valid */
+static int acf_dspointcloud_setting_flag(bAnimContext *UNUSED(ac),
+ eAnimChannel_Settings setting,
+ bool *neg)
+{
+ /* clear extra return data first */
+ *neg = false;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return VO_DS_EXPAND;
+
+ case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
+ return ADT_NLA_EVAL_OFF;
+
+ case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
+ *neg = true;
+ return ADT_CURVES_NOT_VISIBLE;
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return ADT_UI_SELECTED;
+
+ default: /* unsupported */
+ return 0;
+ }
+}
+
+/* get pointer to the setting */
+static void *acf_dspointcloud_setting_ptr(bAnimListElem *ale,
+ eAnimChannel_Settings setting,
+ short *type)
+{
+ PointCloud *pointcloud = (PointCloud *)ale->data;
+
+ /* clear extra return data first */
+ *type = 0;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return GET_ACF_FLAG_PTR(pointcloud->flag, type);
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
+ case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
+ if (pointcloud->adt)
+ return GET_ACF_FLAG_PTR(pointcloud->adt->flag, type);
+ return NULL;
+
+ default: /* unsupported */
+ return NULL;
+ }
+}
+
+/* pointcloud expander type define */
+static bAnimChannelType ACF_DSPOINTCLOUD = {
+ "PointCloud Expander", /* type name */
+ ACHANNEL_ROLE_EXPANDER, /* role */
+
+ acf_generic_dataexpand_color, /* backdrop color */
+ acf_generic_dataexpand_backdrop, /* backdrop */
+ acf_generic_indention_1, /* indent level */
+ acf_generic_basic_offset, /* offset */
+
+ acf_generic_idblock_name, /* name */
+ acf_generic_idblock_name_prop, /* name prop */
+ acf_dspointcloud_icon, /* icon */
+
+ acf_generic_dataexpand_setting_valid, /* has setting */
+ acf_dspointcloud_setting_flag, /* flag for setting */
+ acf_dspointcloud_setting_ptr /* pointer for setting */
+};
+
+/* Volume Expander ------------------------------------------- */
+
+// TODO: just get this from RNA?
+static int acf_dsvolume_icon(bAnimListElem *UNUSED(ale))
+{
+ return ICON_VOLUME_DATA;
+}
+
+/* get the appropriate flag(s) for the setting when it is valid */
+static int acf_dsvolume_setting_flag(bAnimContext *UNUSED(ac),
+ eAnimChannel_Settings setting,
+ bool *neg)
+{
+ /* clear extra return data first */
+ *neg = false;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return VO_DS_EXPAND;
+
+ case ACHANNEL_SETTING_MUTE: /* mute (only in NLA) */
+ return ADT_NLA_EVAL_OFF;
+
+ case ACHANNEL_SETTING_VISIBLE: /* visible (only in Graph Editor) */
+ *neg = true;
+ return ADT_CURVES_NOT_VISIBLE;
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ return ADT_UI_SELECTED;
+
+ default: /* unsupported */
+ return 0;
+ }
+}
+
+/* get pointer to the setting */
+static void *acf_dsvolume_setting_ptr(bAnimListElem *ale,
+ eAnimChannel_Settings setting,
+ short *type)
+{
+ Volume *volume = (Volume *)ale->data;
+
+ /* clear extra return data first */
+ *type = 0;
+
+ switch (setting) {
+ case ACHANNEL_SETTING_EXPAND: /* expanded */
+ return GET_ACF_FLAG_PTR(volume->flag, type);
+
+ case ACHANNEL_SETTING_SELECT: /* selected */
+ case ACHANNEL_SETTING_MUTE: /* muted (for NLA only) */
+ case ACHANNEL_SETTING_VISIBLE: /* visible (for Graph Editor only) */
+ if (volume->adt)
+ return GET_ACF_FLAG_PTR(volume->adt->flag, type);
+ return NULL;
+
+ default: /* unsupported */
+ return NULL;
+ }
+}
+
+/* volume expander type define */
+static bAnimChannelType ACF_DSVOLUME = {
+ "Volume Expander", /* type name */
+ ACHANNEL_ROLE_EXPANDER, /* role */
+
+ acf_generic_dataexpand_color, /* backdrop color */
+ acf_generic_dataexpand_backdrop, /* backdrop */
+ acf_generic_indention_1, /* indent level */
+ acf_generic_basic_offset, /* offset */
+
+ acf_generic_idblock_name, /* name */
+ acf_generic_idblock_name_prop, /* name prop */
+ acf_dsvolume_icon, /* icon */
+
+ acf_generic_dataexpand_setting_valid, /* has setting */
+ acf_dsvolume_setting_flag, /* flag for setting */
+ acf_dsvolume_setting_ptr /* pointer for setting */
+};
+
/* GPencil Expander ------------------------------------------- */
// TODO: just get this from RNA?
@@ -3780,24 +4027,27 @@ static void ANIM_init_channel_typeinfo_data(void)
animchannelTypeInfo[type++] = &ACF_FILLACTD; /* Object Action Expander */
animchannelTypeInfo[type++] = &ACF_FILLDRIVERS; /* Drivers Expander */
- animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */
- animchannelTypeInfo[type++] = &ACF_DSLIGHT; /* Light Channel */
- animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */
- animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */
- animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */
- animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */
- animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */
- animchannelTypeInfo[type++] = &ACF_DSNTREE; /* NodeTree Channel */
- animchannelTypeInfo[type++] = &ACF_DSPART; /* Particle Channel */
- animchannelTypeInfo[type++] = &ACF_DSMBALL; /* MetaBall Channel */
- animchannelTypeInfo[type++] = &ACF_DSARM; /* Armature Channel */
- animchannelTypeInfo[type++] = &ACF_DSMESH; /* Mesh Channel */
- animchannelTypeInfo[type++] = &ACF_DSTEX; /* Texture Channel */
- animchannelTypeInfo[type++] = &ACF_DSLAT; /* Lattice Channel */
- animchannelTypeInfo[type++] = &ACF_DSLINESTYLE; /* LineStyle Channel */
- animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */
- animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */
- animchannelTypeInfo[type++] = &ACF_DSMCLIP; /* MovieClip Channel */
+ animchannelTypeInfo[type++] = &ACF_DSMAT; /* Material Channel */
+ animchannelTypeInfo[type++] = &ACF_DSLIGHT; /* Light Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCAM; /* Camera Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCACHEFILE; /* CacheFile Channel */
+ animchannelTypeInfo[type++] = &ACF_DSCUR; /* Curve Channel */
+ animchannelTypeInfo[type++] = &ACF_DSSKEY; /* ShapeKey Channel */
+ animchannelTypeInfo[type++] = &ACF_DSWOR; /* World Channel */
+ animchannelTypeInfo[type++] = &ACF_DSNTREE; /* NodeTree Channel */
+ animchannelTypeInfo[type++] = &ACF_DSPART; /* Particle Channel */
+ animchannelTypeInfo[type++] = &ACF_DSMBALL; /* MetaBall Channel */
+ animchannelTypeInfo[type++] = &ACF_DSARM; /* Armature Channel */
+ animchannelTypeInfo[type++] = &ACF_DSMESH; /* Mesh Channel */
+ animchannelTypeInfo[type++] = &ACF_DSTEX; /* Texture Channel */
+ animchannelTypeInfo[type++] = &ACF_DSLAT; /* Lattice Channel */
+ animchannelTypeInfo[type++] = &ACF_DSLINESTYLE; /* LineStyle Channel */
+ animchannelTypeInfo[type++] = &ACF_DSSPK; /* Speaker Channel */
+ animchannelTypeInfo[type++] = &ACF_DSGPENCIL; /* GreasePencil Channel */
+ animchannelTypeInfo[type++] = &ACF_DSMCLIP; /* MovieClip Channel */
+ animchannelTypeInfo[type++] = &ACF_DSHAIR; /* Hair Channel */
+ animchannelTypeInfo[type++] = &ACF_DSPOINTCLOUD; /* PointCloud Channel */
+ animchannelTypeInfo[type++] = &ACF_DSVOLUME; /* Volume Channel */
animchannelTypeInfo[type++] = &ACF_SHAPEKEY; /* ShapeKey */
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index 8b5e22db61a..791545f216a 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -135,7 +135,10 @@ void ANIM_set_active_channel(bAnimContext *ac,
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_DSMCLIP: {
+ case ANIMTYPE_DSMCLIP:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* need to verify that this data is valid for now */
if (ale->adt) {
ACHANNEL_SET_FLAG(ale->adt, ACHANNEL_SETFLAG_CLEAR, ADT_UI_ACTIVE);
@@ -188,7 +191,10 @@ void ANIM_set_active_channel(bAnimContext *ac,
case ANIMTYPE_DSNTREE:
case ANIMTYPE_DSTEX:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_DSMCLIP: {
+ case ANIMTYPE_DSMCLIP:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* need to verify that this data is valid for now */
if (ale && ale->adt) {
ale->adt->flag |= ADT_UI_ACTIVE;
@@ -323,7 +329,10 @@ void ANIM_deselect_anim_channels(
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_DSMCLIP: {
+ case ANIMTYPE_DSMCLIP:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
if ((ale->adt) && (ale->adt->flag & ADT_UI_SELECTED)) {
sel = ACHANNEL_SETFLAG_CLEAR;
}
@@ -416,7 +425,10 @@ void ANIM_deselect_anim_channels(
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_DSMCLIP: {
+ case ANIMTYPE_DSMCLIP:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* need to verify that this data is valid for now */
if (ale->adt) {
ACHANNEL_SET_FLAG(ale->adt, sel, ADT_UI_SELECTED);
@@ -2949,7 +2961,10 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_DSMCLIP: {
+ case ANIMTYPE_DSMCLIP:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* sanity checking... */
if (ale->adt) {
/* select/deselect */
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 8dfc9cd8d1a..0895bd4f8a2 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -46,6 +46,7 @@
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_cachefile_types.h"
+#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_lattice_types.h"
#include "DNA_linestyle_types.h"
@@ -58,11 +59,13 @@
#include "DNA_node_types.h"
#include "DNA_object_force_types.h"
#include "DNA_particle_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_space_types.h"
#include "DNA_sequence_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_speaker_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_brush_types.h"
@@ -787,6 +790,42 @@ static bAnimListElem *make_new_animlistelem(void *data,
ale->adt = BKE_animdata_from_id(data);
break;
}
+ case ANIMTYPE_DSHAIR: {
+ Hair *hair = (Hair *)data;
+ AnimData *adt = hair->adt;
+
+ ale->flag = FILTER_HAIR_OBJD(hair);
+
+ ale->key_data = (adt) ? adt->action : NULL;
+ ale->datatype = ALE_ACT;
+
+ ale->adt = BKE_animdata_from_id(data);
+ break;
+ }
+ case ANIMTYPE_DSPOINTCLOUD: {
+ PointCloud *pointcloud = (PointCloud *)data;
+ AnimData *adt = pointcloud->adt;
+
+ ale->flag = FILTER_POINTS_OBJD(pointcloud);
+
+ ale->key_data = (adt) ? adt->action : NULL;
+ ale->datatype = ALE_ACT;
+
+ ale->adt = BKE_animdata_from_id(data);
+ break;
+ }
+ case ANIMTYPE_DSVOLUME: {
+ Volume *volume = (Volume *)data;
+ AnimData *adt = volume->adt;
+
+ ale->flag = FILTER_VOLUME_OBJD(volume);
+
+ ale->key_data = (adt) ? adt->action : NULL;
+ ale->datatype = ALE_ACT;
+
+ ale->adt = BKE_animdata_from_id(data);
+ break;
+ }
case ANIMTYPE_DSSKEY: {
Key *key = (Key *)data;
AnimData *adt = key->adt;
@@ -2544,6 +2583,39 @@ static size_t animdata_filter_ds_obdata(
expanded = FILTER_SPK_OBJD(spk);
break;
}
+ case OB_HAIR: /* ---------- Hair ----------- */
+ {
+ Hair *hair = (Hair *)ob->data;
+
+ if (ads->filterflag2 & ADS_FILTER_NOHAIR)
+ return 0;
+
+ type = ANIMTYPE_DSHAIR;
+ expanded = FILTER_HAIR_OBJD(hair);
+ break;
+ }
+ case OB_POINTCLOUD: /* ---------- PointCloud ----------- */
+ {
+ PointCloud *pointcloud = (PointCloud *)ob->data;
+
+ if (ads->filterflag2 & ADS_FILTER_NOPOINTCLOUD)
+ return 0;
+
+ type = ANIMTYPE_DSPOINTCLOUD;
+ expanded = FILTER_POINTS_OBJD(pointcloud);
+ break;
+ }
+ case OB_VOLUME: /* ---------- Volume ----------- */
+ {
+ Volume *volume = (Volume *)ob->data;
+
+ if (ads->filterflag2 & ADS_FILTER_NOVOLUME)
+ return 0;
+
+ type = ANIMTYPE_DSVOLUME;
+ expanded = FILTER_VOLUME_OBJD(volume);
+ break;
+ }
}
/* add object data animation channels */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 2e810297138..7f4c3470020 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -226,6 +226,9 @@ typedef enum eAnim_ChannelType {
ANIMTYPE_DSSPK,
ANIMTYPE_DSGPENCIL,
ANIMTYPE_DSMCLIP,
+ ANIMTYPE_DSHAIR,
+ ANIMTYPE_DSPOINTCLOUD,
+ ANIMTYPE_DSVOLUME,
ANIMTYPE_SHAPEKEY,
@@ -350,6 +353,9 @@ typedef enum eAnimFilter_Flags {
#define FILTER_MESH_OBJD(me) (CHECK_TYPE_INLINE(me, Mesh *), ((me->flag & ME_DS_EXPAND)))
#define FILTER_LATTICE_OBJD(lt) (CHECK_TYPE_INLINE(lt, Lattice *), ((lt->flag & LT_DS_EXPAND)))
#define FILTER_SPK_OBJD(spk) (CHECK_TYPE_INLINE(spk, Speaker *), ((spk->flag & SPK_DS_EXPAND)))
+#define FILTER_HAIR_OBJD(ha) (CHECK_TYPE_INLINE(ha, Hair *), ((ha->flag & HA_DS_EXPAND)))
+#define FILTER_POINTS_OBJD(pt) (CHECK_TYPE_INLINE(pt, PointCloud *), ((pt->flag & PT_DS_EXPAND)))
+#define FILTER_VOLUME_OBJD(vo) (CHECK_TYPE_INLINE(vo, Volume *), ((vo->flag & VO_DS_EXPAND)))
/* Variable use expanders */
#define FILTER_NTREE_DATA(ntree) \
(CHECK_TYPE_INLINE(ntree, bNodeTree *), ((ntree->flag & NTREE_DS_EXPAND)))
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 6739f7cb12c..6fdef4a06e0 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -745,21 +745,22 @@ DEF_ICON_BLANK(252b)
DEF_ICON_BLANK(252c)
DEF_ICON(LAYER_USED)
DEF_ICON(LAYER_ACTIVE)
-/* available */
DEF_ICON_BLANK(254)
DEF_ICON_BLANK(255)
DEF_ICON_BLANK(256)
DEF_ICON_BLANK(257)
DEF_ICON_BLANK(257b)
-DEF_ICON_BLANK(258)
-DEF_ICON_BLANK(259)
-DEF_ICON_BLANK(260)
-DEF_ICON_BLANK(261)
-DEF_ICON_BLANK(262)
-DEF_ICON_BLANK(263)
-DEF_ICON_BLANK(264)
-DEF_ICON_BLANK(265)
-DEF_ICON_BLANK(266)
+
+/* ADDITIONAL OBJECT TYPES */
+DEF_ICON_OBJECT(OUTLINER_OB_HAIR)
+DEF_ICON_OBJECT_DATA(OUTLINER_DATA_HAIR)
+DEF_ICON_OBJECT_DATA(HAIR_DATA)
+DEF_ICON_OBJECT(OUTLINER_OB_POINTCLOUD)
+DEF_ICON_OBJECT_DATA(OUTLINER_DATA_POINTCLOUD)
+DEF_ICON_OBJECT_DATA(POINTCLOUD_DATA)
+DEF_ICON_OBJECT(OUTLINER_OB_VOLUME)
+DEF_ICON_OBJECT_DATA(OUTLINER_DATA_VOLUME)
+DEF_ICON_OBJECT_DATA(VOLUME_DATA)
DEF_ICON_BLANK(267)
DEF_ICON_BLANK(268)
DEF_ICON_BLANK(269)
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index a37b49f5b6e..ed4131440d5 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -2340,6 +2340,12 @@ int UI_idcode_icon_get(const int idcode)
return ICON_TEXT;
case ID_VF:
return ICON_FONT_DATA;
+ case ID_HA:
+ return ICON_HAIR_DATA;
+ case ID_PT:
+ return ICON_POINTCLOUD_DATA;
+ case ID_VO:
+ return ICON_VOLUME_DATA;
case ID_WO:
return ICON_WORLD_DATA;
case ID_WS:
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index f11e3a1f5f0..cb55c639d91 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -668,6 +668,12 @@ static const char *template_id_browse_tip(const StructRNA *type)
return N_("Browse Workspace to be linked");
case ID_LP:
return N_("Browse LightProbe to be linked");
+ case ID_HA:
+ return N_("Browse Hair Data to be linked");
+ case ID_PT:
+ return N_("Browse Point Cloud Data to be linked");
+ case ID_VO:
+ return N_("Browse Volume Data to be linked");
}
}
return N_("Browse ID data to be linked");
@@ -730,7 +736,13 @@ static uiBut *template_id_def_new_but(uiBlock *block,
BLT_I18NCONTEXT_ID_GPENCIL,
BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE,
BLT_I18NCONTEXT_ID_WORKSPACE,
- BLT_I18NCONTEXT_ID_LIGHTPROBE, );
+ BLT_I18NCONTEXT_ID_LIGHTPROBE,
+ BLT_I18NCONTEXT_ID_HAIR,
+ BLT_I18NCONTEXT_ID_POINTCLOUD,
+ BLT_I18NCONTEXT_ID_VOLUME, );
+ /* Note: BLT_I18N_MSGID_MULTI_CTXT takes a maximum number of parameters,
+ * check the definition to see if a new call must be added when the limit
+ * is exceeded. */
if (newop) {
but = uiDefIconTextButO(block,
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index 69abe475fed..a5b6fa55aa9 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -65,6 +65,7 @@ set(SRC
object_transform.c
object_utils.c
object_vgroup.c
+ object_volume.c
object_warp.c
object_intern.h
@@ -88,4 +89,8 @@ if(WITH_INTERNATIONAL)
add_definitions(-DWITH_INTERNATIONAL)
endif()
+if(WITH_NEW_OBJECT_TYPES)
+ add_definitions(-DWITH_NEW_OBJECT_TYPES)
+endif()
+
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 17b6bfdb956..832854ec4cc 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -65,6 +65,7 @@
#include "BKE_effect.h"
#include "BKE_font.h"
#include "BKE_gpencil.h"
+#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_light.h"
#include "BKE_lattice.h"
@@ -81,9 +82,11 @@
#include "BKE_nla.h"
#include "BKE_object.h"
#include "BKE_particle.h"
+#include "BKE_pointcloud.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_speaker.h"
+#include "BKE_volume.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -1459,9 +1462,82 @@ void OBJECT_OT_speaker_add(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
-/** \name Delete Object Operator
+/** \name Add Hair Operator
+ * \{ */
+
+static int object_hair_add_exec(bContext *C, wmOperator *op)
+{
+ ushort local_view_bits;
+ float loc[3], rot[3];
+
+ if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &local_view_bits, NULL)) {
+ return OPERATOR_CANCELLED;
+ }
+ Object *object = ED_object_add_type(C, OB_HAIR, NULL, loc, rot, false, local_view_bits);
+ object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_hair_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Hair";
+ ot->description = "Add a hair object to the scene";
+ ot->idname = "OBJECT_OT_hair_add";
+
+ /* api callbacks */
+ ot->exec = object_hair_add_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ED_object_add_generic_props(ot, false);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Add Point Cloud Operator
* \{ */
+static int object_pointcloud_add_exec(bContext *C, wmOperator *op)
+{
+ ushort local_view_bits;
+ float loc[3], rot[3];
+
+ if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &local_view_bits, NULL)) {
+ return OPERATOR_CANCELLED;
+ }
+ Object *object = ED_object_add_type(C, OB_POINTCLOUD, NULL, loc, rot, false, local_view_bits);
+ object->dtx |= OB_DRAWBOUNDOX; /* TODO: remove once there is actual drawing. */
+
+ return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_pointcloud_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Point Cloud";
+ ot->description = "Add a point cloud object to the scene";
+ ot->idname = "OBJECT_OT_pointcloud_add";
+
+ /* api callbacks */
+ ot->exec = object_pointcloud_add_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ED_object_add_generic_props(ot, false);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Delete Object Operator
+ * \{ */
/* remove base from a specific scene */
/* note: now unlinks constraints as well */
void ED_object_base_free_and_unlink(Main *bmain, Scene *scene, Object *ob)
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 352ba744d92..c1cb0d6cef0 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -111,6 +111,8 @@ void OBJECT_OT_light_add(struct wmOperatorType *ot);
void OBJECT_OT_effector_add(struct wmOperatorType *ot);
void OBJECT_OT_camera_add(struct wmOperatorType *ot);
void OBJECT_OT_speaker_add(struct wmOperatorType *ot);
+void OBJECT_OT_hair_add(struct wmOperatorType *ot);
+void OBJECT_OT_pointcloud_add(struct wmOperatorType *ot);
void OBJECT_OT_collection_instance_add(struct wmOperatorType *ot);
void OBJECT_OT_duplicates_make_real(struct wmOperatorType *ot);
@@ -120,6 +122,10 @@ void OBJECT_OT_join(struct wmOperatorType *ot);
void OBJECT_OT_join_shapes(struct wmOperatorType *ot);
void OBJECT_OT_convert(struct wmOperatorType *ot);
+/* object_volume.c */
+void OBJECT_OT_volume_add(struct wmOperatorType *ot);
+void OBJECT_OT_volume_import(struct wmOperatorType *ot);
+
/* object_hook.c */
void OBJECT_OT_hook_add_selob(struct wmOperatorType *ot);
void OBJECT_OT_hook_add_newob(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 31c4f96693c..7c74213608c 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -53,6 +53,7 @@
#include "BKE_effect.h"
#include "BKE_global.h"
#include "BKE_gpencil_modifier.h"
+#include "BKE_hair.h"
#include "BKE_key.h"
#include "BKE_lattice.h"
#include "BKE_lib_id.h"
@@ -67,9 +68,11 @@
#include "BKE_ocean.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
+#include "BKE_pointcloud.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_softbody.h"
+#include "BKE_volume.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -114,6 +117,15 @@ static void object_force_modifier_update_for_bind(Depsgraph *depsgraph, Object *
else if (ob->type == OB_GPENCIL) {
BKE_gpencil_modifiers_calc(depsgraph, scene_eval, ob_eval);
}
+ else if (ob->type == OB_HAIR) {
+ BKE_hair_data_update(depsgraph, scene_eval, ob);
+ }
+ else if (ob->type == OB_POINTCLOUD) {
+ BKE_pointcloud_data_update(depsgraph, scene_eval, ob);
+ }
+ else if (ob->type == OB_VOLUME) {
+ BKE_volume_data_update(depsgraph, scene_eval, ob);
+ }
}
static void object_force_modifier_bind_simple_options(Depsgraph *depsgraph,
@@ -654,6 +666,7 @@ static int modifier_apply_shape(Main *bmain,
BKE_id_free(NULL, mesh_applied);
}
else {
+ /* TODO: implement for hair, pointclouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return 0;
}
@@ -732,6 +745,7 @@ static int modifier_apply_obdata(
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
else {
+ /* TODO: implement for hair, pointclouds and volumes. */
BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type");
return 0;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index f6b08b953a4..52273b887dd 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -109,6 +109,12 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_light_add);
WM_operatortype_append(OBJECT_OT_camera_add);
WM_operatortype_append(OBJECT_OT_speaker_add);
+#ifdef WITH_NEW_OBJECT_TYPES
+ WM_operatortype_append(OBJECT_OT_hair_add);
+ WM_operatortype_append(OBJECT_OT_pointcloud_add);
+#endif
+ WM_operatortype_append(OBJECT_OT_volume_add);
+ WM_operatortype_append(OBJECT_OT_volume_import);
WM_operatortype_append(OBJECT_OT_add);
WM_operatortype_append(OBJECT_OT_add_named);
WM_operatortype_append(OBJECT_OT_effector_add);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 29bf9e88853..836e3bf7a44 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -66,6 +66,7 @@
#include "BKE_editmesh.h"
#include "BKE_gpencil.h"
#include "BKE_fcurve.h"
+#include "BKE_hair.h"
#include "BKE_idprop.h"
#include "BKE_light.h"
#include "BKE_lattice.h"
@@ -82,10 +83,12 @@
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_object.h"
+#include "BKE_pointcloud.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_speaker.h"
#include "BKE_texture.h"
+#include "BKE_volume.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
@@ -1900,6 +1903,15 @@ static void single_obdata_users(
case OB_GPENCIL:
ob->data = ID_NEW_SET(ob->data, BKE_gpencil_copy(bmain, ob->data));
break;
+ case OB_HAIR:
+ ob->data = ID_NEW_SET(ob->data, BKE_hair_copy(bmain, ob->data));
+ break;
+ case OB_POINTCLOUD:
+ ob->data = ID_NEW_SET(ob->data, BKE_pointcloud_copy(bmain, ob->data));
+ break;
+ case OB_VOLUME:
+ ob->data = ID_NEW_SET(ob->data, BKE_volume_copy(bmain, ob->data));
+ break;
default:
printf("ERROR %s: can't copy %s\n", __func__, id->name);
BLI_assert(!"This should never happen.");
diff --git a/source/blender/editors/object/object_volume.c b/source/blender/editors/object/object_volume.c
new file mode 100644
index 00000000000..64482a0bcf6
--- /dev/null
+++ b/source/blender/editors/object/object_volume.c
@@ -0,0 +1,193 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2008 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edobj
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_fileops.h"
+#include "BLI_listbase.h"
+#include "BLI_math_base.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+
+#include "DNA_object_types.h"
+#include "DNA_volume_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "BKE_context.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_report.h"
+#include "BKE_volume.h"
+
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "ED_image.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+
+#include "object_intern.h"
+
+/* Volume Add */
+
+static Object *object_volume_add(bContext *C, wmOperator *op, const char *name)
+{
+ ushort local_view_bits;
+ float loc[3], rot[3];
+
+ if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &local_view_bits, NULL)) {
+ return false;
+ }
+ return ED_object_add_type(C, OB_VOLUME, name, loc, rot, false, local_view_bits);
+}
+
+static int object_volume_add_exec(bContext *C, wmOperator *op)
+{
+ return (object_volume_add(C, op, NULL) != NULL) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
+
+void OBJECT_OT_volume_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Volume";
+ ot->description = "Add a volume object to the scene";
+ ot->idname = "OBJECT_OT_volume_add";
+
+ /* api callbacks */
+ ot->exec = object_volume_add_exec;
+ ot->poll = ED_operator_objectmode;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ED_object_add_generic_props(ot, false);
+}
+
+/* Volume Import */
+
+static int volume_import_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
+ bool imported = false;
+
+ ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, false);
+ for (ImageFrameRange *range = ranges.first; range; range = range->next) {
+ char filename[FILE_MAX];
+ BLI_split_file_part(range->filepath, filename, sizeof(filename));
+ BLI_path_extension_replace(filename, sizeof(filename), "");
+
+ Object *object = object_volume_add(C, op, filename);
+ Volume *volume = (Volume *)object->data;
+
+ STRNCPY(volume->filepath, range->filepath);
+ if (is_relative_path) {
+ BLI_path_rel(volume->filepath, BKE_main_blendfile_path(bmain));
+ }
+
+ volume->is_sequence = (range->length > 1);
+ volume->frame_duration = (volume->is_sequence) ? range->length : 0;
+ volume->frame_start = 1;
+ volume->frame_offset = (volume->is_sequence) ? range->offset - 1 : 0;
+
+ if (!BKE_volume_load(volume, bmain)) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Volume \"%s\" failed to load: %s",
+ filename,
+ BKE_volume_grids_error_msg(volume));
+ BKE_id_delete(bmain, &object->id);
+ BKE_id_delete(bmain, &volume->id);
+ continue;
+ }
+ else if (BKE_volume_is_points_only(volume)) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Volume \"%s\" contains points, only voxel grids are supported",
+ filename);
+ BKE_id_delete(bmain, &object->id);
+ BKE_id_delete(bmain, &volume->id);
+ continue;
+ }
+
+ if (BKE_volume_is_y_up(volume)) {
+ object->rot[0] += M_PI_2;
+ }
+
+ imported = true;
+ }
+ BLI_freelistN(&ranges);
+
+ return (imported) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
+}
+
+static int volume_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ if (RNA_struct_property_is_set(op->ptr, "filepath")) {
+ return volume_import_exec(C, op);
+ }
+
+ RNA_string_set(op->ptr, "filepath", U.textudir);
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+/* called by other space types too */
+void OBJECT_OT_volume_import(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Import OpenVDB Volume";
+ ot->description = "Import OpenVDB volume file";
+ ot->idname = "OBJECT_OT_volume_import";
+
+ /* api callbacks */
+ ot->exec = volume_import_exec;
+ ot->invoke = volume_import_invoke;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER | FILE_TYPE_VOLUME,
+ FILE_SPECIAL,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES |
+ WM_FILESEL_RELPATH,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_ALPHA);
+
+ RNA_def_boolean(
+ ot->srna,
+ "use_sequence_detection",
+ true,
+ "Detect Sequences",
+ "Automatically detect animated sequences in selected volume files (based on file names)");
+
+ ED_object_add_generic_props(ot, false);
+}
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 4fa1903758f..c48d5917ec2 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -628,6 +628,9 @@ static int gather_frames_to_render_for_id(LibraryIDLinkCallbackData *cb_data)
case ID_MC: /* MovieClip */
case ID_MSK: /* Mask */
case ID_LP: /* LightProbe */
+ case ID_HA: /* Hair */
+ case ID_PT: /* PointCloud */
+ case ID_VO: /* Volume */
break;
/* Blacklist: */
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index 25ff6bbd098..c3b7d65689f 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -54,4 +54,8 @@ if(WITH_FREESTYLE)
add_definitions(-DWITH_FREESTYLE)
endif()
+if(WITH_NEW_OBJECT_TYPES)
+ add_definitions(-DWITH_NEW_OBJECT_TYPES)
+endif()
+
blender_add_lib(bf_editor_space_buttons "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 035239cc7d0..21dfb3df771 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -251,6 +251,17 @@ static int buttons_context_path_data(ButsContextPath *path, int type)
else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) {
return 1;
}
+#ifdef WITH_NEW_OBJECT_TYPES
+ else if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) {
+ return 1;
+ }
+ else if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
+ return 1;
+ }
+#endif
+ else if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
+ return 1;
+ }
/* try to get an object in the path, no pinning supported here */
else if (buttons_context_path_object(path)) {
ob = path->ptr[path->len - 1].data;
@@ -274,7 +285,16 @@ static int buttons_context_path_modifier(ButsContextPath *path)
if (buttons_context_path_object(path)) {
ob = path->ptr[path->len - 1].data;
- if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE, OB_GPENCIL)) {
+ if (ob && ELEM(ob->type,
+ OB_MESH,
+ OB_CURVE,
+ OB_FONT,
+ OB_SURF,
+ OB_LATTICE,
+ OB_GPENCIL,
+ OB_HAIR,
+ OB_POINTCLOUD,
+ OB_VOLUME)) {
return 1;
}
}
@@ -776,6 +796,11 @@ const char *buttons_context_dir[] = {
"line_style",
"collection",
"gpencil",
+#ifdef WITH_NEW_OBJECT_TYPES
+ "hair",
+ "pointcloud",
+#endif
+ "volume",
NULL,
};
@@ -853,6 +878,20 @@ int buttons_context(const bContext *C, const char *member, bContextDataResult *r
set_pointer_type(path, result, &RNA_LightProbe);
return 1;
}
+#ifdef WITH_NEW_OBJECT_TYPES
+ else if (CTX_data_equals(member, "hair")) {
+ set_pointer_type(path, result, &RNA_Hair);
+ return 1;
+ }
+ else if (CTX_data_equals(member, "pointcloud")) {
+ set_pointer_type(path, result, &RNA_PointCloud);
+ return 1;
+ }
+#endif
+ else if (CTX_data_equals(member, "volume")) {
+ set_pointer_type(path, result, &RNA_Volume);
+ return 1;
+ }
else if (CTX_data_equals(member, "material")) {
set_pointer_type(path, result, &RNA_Material);
return 1;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 188f3417ddc..1fc0866bd9f 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -1080,6 +1080,9 @@ static int filelist_geticon_ex(FileDirEntry *file,
else if (typeflag & FILE_TYPE_USD) {
return ICON_FILE_3D;
}
+ else if (typeflag & FILE_TYPE_VOLUME) {
+ return ICON_FILE_VOLUME;
+ }
else if (typeflag & FILE_TYPE_OBJECT_IO) {
return ICON_FILE_3D;
}
@@ -2236,6 +2239,9 @@ int ED_path_extension_type(const char *path)
else if (BLI_path_extension_check_n(path, ".usd", ".usda", ".usdc", NULL)) {
return FILE_TYPE_USD;
}
+ else if (BLI_path_extension_check(path, ".vdb")) {
+ return FILE_TYPE_VOLUME;
+ }
else if (BLI_path_extension_check(path, ".zip")) {
return FILE_TYPE_ARCHIVE;
}
@@ -2298,6 +2304,8 @@ int ED_file_extension_icon(const char *path)
return ICON_FILE_TEXT;
case FILE_TYPE_ARCHIVE:
return ICON_FILE_ARCHIVE;
+ case FILE_TYPE_VOLUME:
+ return ICON_FILE_VOLUME;
default:
return ICON_FILE_BLANK;
}
@@ -2625,9 +2633,9 @@ static void filelist_readjob_main_rec(Main *bmain, FileList *filelist)
if (filelist->dir[0] == 0) {
/* make directories */
# ifdef WITH_FREESTYLE
- filelist->filelist.nbr_entries = 24;
+ filelist->filelist.nbr_entries = 27;
# else
- filelist->filelist.nbr_entries = 23;
+ filelist->filelist.nbr_entries = 26;
# endif
filelist_resize(filelist, filelist->filelist.nbr_entries);
@@ -2658,8 +2666,11 @@ static void filelist_readjob_main_rec(Main *bmain, FileList *filelist)
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
+ filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
+ filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
# ifdef WITH_FREESTYLE
- filelist->filelist.entries[23].entry->relpath = BLI_strdup("FreestyleLineStyle");
+ filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
# endif
}
else {
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index d07db12eeac..6c0f79f0f5c 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -218,6 +218,9 @@ short ED_fileselect_set_params(SpaceFile *sfile)
if ((prop = RNA_struct_find_property(op->ptr, "filter_usd"))) {
params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_USD : 0;
}
+ if ((prop = RNA_struct_find_property(op->ptr, "filter_volume"))) {
+ params->filter |= RNA_property_boolean_get(op->ptr, prop) ? FILE_TYPE_VOLUME : 0;
+ }
if ((prop = RNA_struct_find_property(op->ptr, "filter_glob"))) {
/* Protection against pyscripts not setting proper size limit... */
char *tmp = RNA_property_string_get_alloc(
diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c
index d00083d343b..dd74e1a8ccc 100644
--- a/source/blender/editors/space_info/info_stats.c
+++ b/source/blender/editors/space_info/info_stats.c
@@ -204,6 +204,11 @@ static void stats_object(Object *ob, SceneStats *stats, GSet *objects_gset)
}
break;
}
+ case OB_HAIR:
+ case OB_POINTCLOUD:
+ case OB_VOLUME: {
+ break;
+ }
}
}
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index 4277e579274..315fa1d12aa 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -140,7 +140,10 @@ bool nla_panel_context(const bContext *C,
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_PALETTE: {
+ case ANIMTYPE_PALETTE:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* for these channels, we only do AnimData */
if (ale->adt && adt_ptr) {
ID *id;
diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c
index cf3e889b26c..b97267dc2a8 100644
--- a/source/blender/editors/space_nla/nla_channels.c
+++ b/source/blender/editors/space_nla/nla_channels.c
@@ -190,7 +190,10 @@ static int mouse_nla_channels(
case ANIMTYPE_DSLINESTYLE:
case ANIMTYPE_DSSPK:
case ANIMTYPE_DSGPENCIL:
- case ANIMTYPE_PALETTE: {
+ case ANIMTYPE_PALETTE:
+ case ANIMTYPE_DSHAIR:
+ case ANIMTYPE_DSPOINTCLOUD:
+ case ANIMTYPE_DSVOLUME: {
/* sanity checking... */
if (ale->adt) {
/* select/deselect */
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index f679bcc4e15..677a7cac745 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -446,8 +446,17 @@ void ED_node_shader_default(const bContext *C, ID *id)
if (GS(id->name) == ID_MA) {
/* Materials */
+ Object *ob = CTX_data_active_object(C);
Material *ma = (Material *)id;
- Material *ma_default = BKE_material_default_surface();
+ Material *ma_default;
+
+ if (ob && ob->type == OB_VOLUME) {
+ ma_default = BKE_material_default_volume();
+ }
+ else {
+ ma_default = BKE_material_default_surface();
+ }
+
ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree);
ntreeUpdateTree(bmain, ma->nodetree);
}
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 1b34f85f800..83ddbe63e9c 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -2418,6 +2418,15 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case OB_LIGHTPROBE:
data.icon = ICON_OUTLINER_OB_LIGHTPROBE;
break;
+ case OB_HAIR:
+ data.icon = ICON_OUTLINER_OB_HAIR;
+ break;
+ case OB_POINTCLOUD:
+ data.icon = ICON_OUTLINER_OB_POINTCLOUD;
+ break;
+ case OB_VOLUME:
+ data.icon = ICON_OUTLINER_OB_VOLUME;
+ break;
case OB_EMPTY:
if (ob->instance_collection && (ob->transflag & OB_DUPLICOLLECTION)) {
data.icon = ICON_OUTLINER_OB_GROUP_INSTANCE;
@@ -2515,6 +2524,15 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case ID_GR:
data.icon = ICON_GROUP;
break;
+ case ID_HA:
+ data.icon = ICON_OUTLINER_DATA_HAIR;
+ break;
+ case ID_PT:
+ data.icon = ICON_OUTLINER_DATA_POINTCLOUD;
+ break;
+ case ID_VO:
+ data.icon = ICON_OUTLINER_DATA_VOLUME;
+ break;
case ID_LI:
if (tselem->id->tag & LIB_TAG_MISSING) {
data.icon = ICON_LIBRARY_DATA_BROKEN;
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 1d39dc156b2..fb40ae195ef 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -105,7 +105,10 @@ typedef struct TreeElementIcon {
ID_PA, \
ID_GD, \
ID_LS, \
- ID_LP) || /* Only in 'blendfile' mode ... :/ */ \
+ ID_LP, \
+ ID_HA, \
+ ID_PT, \
+ ID_VO) || /* Only in 'blendfile' mode ... :/ */ \
ELEM(GS((_id)->name), \
ID_SCR, \
ID_WM, \
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index a99e1b63119..5bb0d626c2f 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -27,13 +27,16 @@
#include "DNA_armature_types.h"
#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_hair_types.h"
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_object_types.h"
#include "DNA_constraint_types.h"
@@ -153,6 +156,9 @@ static void set_operation_types(SpaceOutliner *soops,
case ID_CF:
case ID_WS:
case ID_LP:
+ case ID_HA:
+ case ID_PT:
+ case ID_VO:
is_standard_id = true;
break;
case ID_WM:
@@ -230,6 +236,21 @@ static void unlink_material_cb(bContext *UNUSED(C),
totcol = mb->totcol;
matar = mb->mat;
}
+ else if (GS(tsep->id->name) == ID_HA) {
+ Hair *hair = (Hair *)tsep->id;
+ totcol = hair->totcol;
+ matar = hair->mat;
+ }
+ else if (GS(tsep->id->name) == ID_PT) {
+ PointCloud *pointcloud = (PointCloud *)tsep->id;
+ totcol = pointcloud->totcol;
+ matar = pointcloud->mat;
+ }
+ else if (GS(tsep->id->name) == ID_VO) {
+ Volume *volume = (Volume *)tsep->id;
+ totcol = volume->totcol;
+ matar = volume->mat;
+ }
else {
BLI_assert(0);
}
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index b8cbb6bb0e3..b942021ca33 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -33,6 +33,7 @@
#include "DNA_cachefile_types.h"
#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_light_types.h"
#include "DNA_material_types.h"
@@ -40,7 +41,9 @@
#include "DNA_meta_types.h"
#include "DNA_lightprobe_types.h"
#include "DNA_particle_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_sequence_types.h"
#include "DNA_speaker_types.h"
@@ -750,6 +753,25 @@ static void outliner_add_id_contents(SpaceOutliner *soops,
Collection *collection = (Collection *)id;
outliner_add_collection_recursive(soops, collection, te);
}
+ break;
+ }
+ case ID_HA: {
+ Hair *hair = (Hair *)id;
+ if (outliner_animdata_test(hair->adt))
+ outliner_add_element(soops, &te->subtree, hair, te, TSE_ANIM_DATA, 0);
+ break;
+ }
+ case ID_PT: {
+ PointCloud *pointcloud = (PointCloud *)id;
+ if (outliner_animdata_test(pointcloud->adt))
+ outliner_add_element(soops, &te->subtree, pointcloud, te, TSE_ANIM_DATA, 0);
+ break;
+ }
+ case ID_VO: {
+ Volume *volume = (Volume *)id;
+ if (outliner_animdata_test(volume->adt))
+ outliner_add_element(soops, &te->subtree, volume, te, TSE_ANIM_DATA, 0);
+ break;
}
default:
break;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index f16e19c598e..0f5607bc8cd 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -559,6 +559,14 @@ static bool view3d_ima_empty_drop_poll(bContext *C,
return false;
}
+static bool view3d_volume_drop_poll(bContext *UNUSED(C),
+ wmDrag *drag,
+ const wmEvent *UNUSED(event),
+ const char **UNUSED(tooltip))
+{
+ return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME);
+}
+
static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
{
ID *id = WM_drag_ID(drag, ID_OB);
@@ -626,6 +634,7 @@ static void view3d_dropboxes(void)
lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy);
WM_dropbox_add(
lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy);
+ WM_dropbox_add(lb, "OBJECT_OT_volume_import", view3d_volume_drop_poll, view3d_id_path_drop_copy);
WM_dropbox_add(lb,
"OBJECT_OT_collection_instance_add",
view3d_collection_drop_poll,
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index 9b02ea7238c..54f00b67898 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1591,8 +1591,8 @@ static void view3d_panel_transform(const bContext *C, Panel *pa)
RNA_id_pointer_create(&ob->id, &obptr);
v3d_transform_butsR(col, &obptr);
- /* dimensions and editmode just happen to be the same checks */
- if (OB_TYPE_SUPPORT_EDITMODE(ob->type)) {
+ /* Dimensions and editmode are mostly the same check. */
+ if (OB_TYPE_SUPPORT_EDITMODE(ob->type) || ELEM(ob->type, OB_VOLUME, OB_HAIR, OB_POINTCLOUD)) {
View3D *v3d = CTX_wm_view3d(C);
v3d_object_dimension_buts(NULL, col, v3d, ob);
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 063ea04bdba..1e894d44f87 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -405,6 +405,9 @@ typedef enum ID_Type {
ID_CF = MAKE_ID2('C', 'F'), /* CacheFile */
ID_WS = MAKE_ID2('W', 'S'), /* WorkSpace */
ID_LP = MAKE_ID2('L', 'P'), /* LightProbe */
+ ID_HA = MAKE_ID2('H', 'A'), /* Hair */
+ ID_PT = MAKE_ID2('P', 'T'), /* PointCloud */
+ ID_VO = MAKE_ID2('V', 'O'), /* Volume */
} ID_Type;
/* Only used as 'placeholder' in .blend files for directly linked data-blocks. */
@@ -670,39 +673,40 @@ typedef enum IDRecalcFlag {
} IDRecalcFlag;
/* To filter ID types (filter_id). 64 bit to fit all types. */
-enum {
- FILTER_ID_AC = (1ULL << 0),
- FILTER_ID_AR = (1ULL << 1),
- FILTER_ID_BR = (1ULL << 2),
- FILTER_ID_CA = (1ULL << 3),
- FILTER_ID_CU = (1ULL << 4),
- FILTER_ID_GD = (1ULL << 5),
- FILTER_ID_GR = (1ULL << 6),
- FILTER_ID_IM = (1ULL << 7),
- FILTER_ID_LA = (1ULL << 8),
- FILTER_ID_LS = (1ULL << 9),
- FILTER_ID_LT = (1ULL << 10),
- FILTER_ID_MA = (1ULL << 11),
- FILTER_ID_MB = (1ULL << 12),
- FILTER_ID_MC = (1ULL << 13),
- FILTER_ID_ME = (1ULL << 14),
- FILTER_ID_MSK = (1ULL << 15),
- FILTER_ID_NT = (1ULL << 16),
- FILTER_ID_OB = (1ULL << 17),
- FILTER_ID_PAL = (1ULL << 18),
- FILTER_ID_PC = (1ULL << 19),
- FILTER_ID_SCE = (1ULL << 20),
- FILTER_ID_SPK = (1ULL << 21),
- FILTER_ID_SO = (1ULL << 22),
- FILTER_ID_TE = (1ULL << 23),
- FILTER_ID_TXT = (1ULL << 24),
- FILTER_ID_VF = (1ULL << 25),
- FILTER_ID_WO = (1ULL << 26),
- FILTER_ID_PA = (1ULL << 27),
- FILTER_ID_CF = (1ULL << 28),
- FILTER_ID_WS = (1ULL << 29),
- FILTER_ID_LP = (1ULL << 31),
-};
+#define FILTER_ID_AC (1ULL << 0)
+#define FILTER_ID_AR (1ULL << 1)
+#define FILTER_ID_BR (1ULL << 2)
+#define FILTER_ID_CA (1ULL << 3)
+#define FILTER_ID_CU (1ULL << 4)
+#define FILTER_ID_GD (1ULL << 5)
+#define FILTER_ID_GR (1ULL << 6)
+#define FILTER_ID_IM (1ULL << 7)
+#define FILTER_ID_LA (1ULL << 8)
+#define FILTER_ID_LS (1ULL << 9)
+#define FILTER_ID_LT (1ULL << 10)
+#define FILTER_ID_MA (1ULL << 11)
+#define FILTER_ID_MB (1ULL << 12)
+#define FILTER_ID_MC (1ULL << 13)
+#define FILTER_ID_ME (1ULL << 14)
+#define FILTER_ID_MSK (1ULL << 15)
+#define FILTER_ID_NT (1ULL << 16)
+#define FILTER_ID_OB (1ULL << 17)
+#define FILTER_ID_PAL (1ULL << 18)
+#define FILTER_ID_PC (1ULL << 19)
+#define FILTER_ID_SCE (1ULL << 20)
+#define FILTER_ID_SPK (1ULL << 21)
+#define FILTER_ID_SO (1ULL << 22)
+#define FILTER_ID_TE (1ULL << 23)
+#define FILTER_ID_TXT (1ULL << 24)
+#define FILTER_ID_VF (1ULL << 25)
+#define FILTER_ID_WO (1ULL << 26)
+#define FILTER_ID_PA (1ULL << 27)
+#define FILTER_ID_CF (1ULL << 28)
+#define FILTER_ID_WS (1ULL << 29)
+#define FILTER_ID_LP (1ULL << 31)
+#define FILTER_ID_HA (1ULL << 32)
+#define FILTER_ID_PT (1ULL << 33)
+#define FILTER_ID_VO (1ULL << 34)
#define FILTER_ID_ALL \
(FILTER_ID_AC | FILTER_ID_AR | FILTER_ID_BR | FILTER_ID_CA | FILTER_ID_CU | FILTER_ID_GD | \
@@ -710,7 +714,7 @@ enum {
FILTER_ID_MB | FILTER_ID_MC | FILTER_ID_ME | FILTER_ID_MSK | FILTER_ID_NT | FILTER_ID_OB | \
FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_SCE | FILTER_ID_SPK | FILTER_ID_SO | \
FILTER_ID_TE | FILTER_ID_TXT | FILTER_ID_VF | FILTER_ID_WO | FILTER_ID_CF | FILTER_ID_WS | \
- FILTER_ID_LP)
+ FILTER_ID_LP | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO)
/* IMPORTANT: this enum matches the order currently use in set_listbasepointers,
* keep them in sync! */
@@ -731,6 +735,9 @@ enum {
INDEX_ID_ME,
INDEX_ID_CU,
INDEX_ID_MB,
+ INDEX_ID_HA,
+ INDEX_ID_PT,
+ INDEX_ID_VO,
INDEX_ID_LT,
INDEX_ID_LA,
INDEX_ID_CA,
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 5b9340ab3d9..be9097cf5bd 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -776,6 +776,9 @@ typedef enum eDopeSheet_FilterFlag {
typedef enum eDopeSheet_FilterFlag2 {
ADS_FILTER_NOCACHEFILES = (1 << 1),
ADS_FILTER_NOMOVIECLIPS = (1 << 2),
+ ADS_FILTER_NOHAIR = (1 << 3),
+ ADS_FILTER_NOPOINTCLOUD = (1 << 4),
+ ADS_FILTER_NOVOLUME = (1 << 5),
} eDopeSheet_FilterFlag2;
/* DopeSheet general flags */
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 4d0d66f29ff..5ee16c2631d 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -76,7 +76,7 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we cant use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
- int typemap[43];
+ int typemap[47];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -147,7 +147,13 @@ typedef enum CustomDataType {
CD_CUSTOMLOOPNORMAL = 41,
CD_SCULPT_FACE_SETS = 42,
- CD_NUMTYPES = 43,
+ /* Hair and PointCloud */
+ CD_LOCATION = 43,
+ CD_RADIUS = 44,
+ CD_HAIRCURVE = 45,
+ CD_HAIRMAPPING = 46,
+
+ CD_NUMTYPES = 47,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -203,6 +209,9 @@ typedef enum CustomDataType {
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
+/* All data layers. */
+#define CD_MASK_ALL (~0LL)
+
typedef struct CustomData_MeshMasks {
uint64_t vmask;
uint64_t emask;
diff --git a/source/blender/makesdna/DNA_defaults.h b/source/blender/makesdna/DNA_defaults.h
index c20ccc05637..9b4a05034df 100644
--- a/source/blender/makesdna/DNA_defaults.h
+++ b/source/blender/makesdna/DNA_defaults.h
@@ -29,6 +29,10 @@
#include "BLI_utildefines.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
#include "dna_type_offsets.h"
extern const void *DNA_default_table[SDNA_TYPE_MAX];
@@ -45,4 +49,8 @@ char *_DNA_struct_default_alloc_impl(const char *data_src, size_t size, const ch
(struct_name *)_DNA_struct_default_alloc_impl( \
DNA_default_table[SDNA_TYPE_FROM_STRUCT(struct_name)], sizeof(struct_name), __func__)
+#ifdef __cplusplus
+}
+#endif
+
#endif /* __DNA_DEFAULTS_H__ */
diff --git a/source/blender/makesdna/DNA_hair_defaults.h b/source/blender/makesdna/DNA_hair_defaults.h
new file mode 100644
index 00000000000..de7a830885d
--- /dev/null
+++ b/source/blender/makesdna/DNA_hair_defaults.h
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_HAIR_DEFAULTS_H__
+#define __DNA_HAIR_DEFAULTS_H__
+
+/* Struct members on own line. */
+/* clang-format off */
+
+/* -------------------------------------------------------------------- */
+/** \name Hair Struct
+ * \{ */
+
+#define _DNA_DEFAULT_Hair \
+ { \
+ .flag = 0, \
+ }
+
+/** \} */
+
+/* clang-format on */
+
+#endif /* __DNA_HAIR_DEFAULTS_H__ */
diff --git a/source/blender/makesdna/DNA_hair_types.h b/source/blender/makesdna/DNA_hair_types.h
new file mode 100644
index 00000000000..c7f145dda48
--- /dev/null
+++ b/source/blender/makesdna/DNA_hair_types.h
@@ -0,0 +1,82 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_hair_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_HAIR_TYPES_H__
+#define __DNA_HAIR_TYPES_H__
+
+#include "DNA_ID.h"
+#include "DNA_customdata_types.h"
+
+typedef struct HairCurve {
+ /* Index of first point of hair curve. */
+ int firstpoint;
+ /* Number of points in hair curve, must be 2 or higher. */
+ int numpoints;
+} HairCurve;
+
+/* Hair attachment to a mesh.
+ * TODO: attach to tesselated triangles or polygons?
+ * TODO: what type of interpolation to use for uv? */
+typedef struct HairMapping {
+ float uv[2];
+ int poly;
+} HairMapping;
+
+typedef struct Hair {
+ ID id;
+ struct AnimData *adt; /* animation data (must be immediately after id) */
+
+ int flag;
+ int _pad1[1];
+
+ /* Geometry */
+ float (*co)[3];
+ float *radius;
+ struct HairCurve *curves;
+ struct HairMaping *mapping;
+ int totpoint;
+ int totcurve;
+
+ /* Custom Data */
+ struct CustomData pdata;
+ struct CustomData cdata;
+
+ /* Material */
+ struct Material **mat;
+ short totcol;
+ short _pad2[3];
+
+ /* Draw Cache */
+ void *batch_cache;
+} Hair;
+
+/* Hair.flag */
+enum {
+ HA_DS_EXPAND = (1 << 0),
+};
+
+/* Only one material supported currently. */
+#define HAIR_MATERIAL_NR 1
+
+#endif /* __DNA_HAIR_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h
index fca74c29909..645611144e9 100644
--- a/source/blender/makesdna/DNA_object_types.h
+++ b/source/blender/makesdna/DNA_object_types.h
@@ -456,12 +456,18 @@ enum {
/** Grease Pencil object used in 3D view but not used for annotation in 2D. */
OB_GPENCIL = 26,
+ OB_HAIR = 27,
+
+ OB_POINTCLOUD = 28,
+
+ OB_VOLUME = 29,
+
OB_TYPE_MAX,
};
/* check if the object type supports materials */
#define OB_TYPE_SUPPORT_MATERIAL(_type) \
- (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) == OB_GPENCIL))
+ (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME))
#define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL))
#define OB_TYPE_SUPPORT_EDITMODE(_type) \
(ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE))
@@ -472,7 +478,20 @@ enum {
/* is this ID type used as object data */
#define OB_DATA_SUPPORT_ID(_id_type) \
- (ELEM(_id_type, ID_ME, ID_CU, ID_MB, ID_LA, ID_SPK, ID_LP, ID_CA, ID_LT, ID_GD, ID_AR))
+ (ELEM(_id_type, \
+ ID_ME, \
+ ID_CU, \
+ ID_MB, \
+ ID_LA, \
+ ID_SPK, \
+ ID_LP, \
+ ID_CA, \
+ ID_LT, \
+ ID_GD, \
+ ID_AR, \
+ ID_HA, \
+ ID_PT, \
+ ID_VO))
#define OB_DATA_SUPPORT_ID_CASE \
ID_ME: \
@@ -484,7 +503,10 @@ case ID_LP: \
case ID_CA: \
case ID_LT: \
case ID_GD: \
-case ID_AR
+case ID_AR: \
+case ID_HA: \
+case ID_PT: \
+case ID_VO
/* partype: first 4 bits: type */
enum {
diff --git a/source/blender/makesdna/DNA_pointcloud_defaults.h b/source/blender/makesdna/DNA_pointcloud_defaults.h
new file mode 100644
index 00000000000..89df2d3c4be
--- /dev/null
+++ b/source/blender/makesdna/DNA_pointcloud_defaults.h
@@ -0,0 +1,40 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_POINTCLOUD_DEFAULTS_H__
+#define __DNA_POINTCLOUD_DEFAULTS_H__
+
+/* Struct members on own line. */
+/* clang-format off */
+
+/* -------------------------------------------------------------------- */
+/** \name PointCloud Struct
+ * \{ */
+
+#define _DNA_DEFAULT_PointCloud \
+ { \
+ .flag = 0, \
+ }
+
+/** \} */
+
+/* clang-format on */
+
+#endif /* __DNA_POINTCLOUD_DEFAULTS_H__ */
diff --git a/source/blender/makesdna/DNA_pointcloud_types.h b/source/blender/makesdna/DNA_pointcloud_types.h
new file mode 100644
index 00000000000..5d6a11bfb48
--- /dev/null
+++ b/source/blender/makesdna/DNA_pointcloud_types.h
@@ -0,0 +1,64 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_pointcloud_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_POINTCLOUD_TYPES_H__
+#define __DNA_POINTCLOUD_TYPES_H__
+
+#include "DNA_ID.h"
+#include "DNA_customdata_types.h"
+
+typedef struct PointCloud {
+ ID id;
+ struct AnimData *adt; /* animation data (must be immediately after id) */
+
+ int flag;
+ int _pad1[1];
+
+ /* Geometry */
+ float (*co)[3];
+ float *radius;
+ int totpoint;
+ int _pad2[1];
+
+ /* Custom Data */
+ struct CustomData pdata;
+
+ /* Material */
+ struct Material **mat;
+ short totcol;
+ short _pad3[3];
+
+ /* Draw Cache */
+ void *batch_cache;
+} PointCloud;
+
+/* PointCloud.flag */
+enum {
+ PT_DS_EXPAND = (1 << 0),
+};
+
+/* Only one material supported currently. */
+#define POINTCLOUD_MATERIAL_NR 1
+
+#endif /* __DNA_POINTCLOUD_TYPES_H__ */
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index 548212a2b0d..f12ed0553ce 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -858,6 +858,7 @@ typedef enum eFileSel_File_Types {
/** For all kinds of recognized import/export formats. No need for specialized types. */
FILE_TYPE_OBJECT_IO = (1 << 17),
FILE_TYPE_USD = (1 << 18),
+ FILE_TYPE_VOLUME = (1 << 19),
/** An FS directory (i.e. S_ISDIR on its path is true). */
FILE_TYPE_DIR = (1 << 30),
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 904d7b8a52e..4a3c5695827 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -1133,6 +1133,9 @@ typedef enum eDupli_ID_Flags {
USER_DUP_PSYS = (1 << 11),
USER_DUP_LIGHTPROBE = (1 << 12),
USER_DUP_GPENCIL = (1 << 13),
+ USER_DUP_HAIR = (1 << 14),
+ USER_DUP_POINTCLOUD = (1 << 15),
+ USER_DUP_VOLUME = (1 << 16),
} eDupli_ID_Flags;
/**
diff --git a/source/blender/makesdna/DNA_volume_defaults.h b/source/blender/makesdna/DNA_volume_defaults.h
new file mode 100644
index 00000000000..3a0373851da
--- /dev/null
+++ b/source/blender/makesdna/DNA_volume_defaults.h
@@ -0,0 +1,59 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_VOLUME_DEFAULTS_H__
+#define __DNA_VOLUME_DEFAULTS_H__
+
+/* Struct members on own line. */
+/* clang-format off */
+
+/* -------------------------------------------------------------------- */
+/** \name Volume Struct
+ * \{ */
+
+#define _DNA_DEFAULT_VolumeDisplay \
+ { \
+ .density = 1.0f, \
+ .wireframe_type = VOLUME_WIREFRAME_BOXES, \
+ .wireframe_detail = VOLUME_WIREFRAME_COARSE, \
+ }
+
+#define _DNA_DEFAULT_VolumeRender \
+ { \
+ .space = VOLUME_SPACE_OBJECT, \
+ .step_size = 0.0f, \
+ .clipping = 0.001f, \
+ }
+
+#define _DNA_DEFAULT_Volume \
+ { \
+ .filepath[0] = '\0', \
+ .frame_start = 1, \
+ .frame_offset = 0, \
+ .frame_duration = 0, \
+ .display = _DNA_DEFAULT_VolumeDisplay, \
+ .render = _DNA_DEFAULT_VolumeRender, \
+ }
+
+/** \} */
+
+/* clang-format on */
+
+#endif /* __DNA_VOLUME_DEFAULTS_H__ */
diff --git a/source/blender/makesdna/DNA_volume_types.h b/source/blender/makesdna/DNA_volume_types.h
new file mode 100644
index 00000000000..af419ea1d55
--- /dev/null
+++ b/source/blender/makesdna/DNA_volume_types.h
@@ -0,0 +1,130 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file DNA_volume_types.h
+ * \ingroup DNA
+ */
+
+#ifndef __DNA_VOLUME_TYPES_H__
+#define __DNA_VOLUME_TYPES_H__
+
+#include "DNA_ID.h"
+
+struct PackedFile;
+struct VolumeGridVector;
+
+typedef struct Volume_Runtime {
+ /* OpenVDB Grids */
+ struct VolumeGridVector *grids;
+
+ /* Current frame in sequence for evaluated volume */
+ int frame;
+ int _pad;
+} Volume_Runtime;
+
+typedef struct VolumeDisplay {
+ float density;
+ int wireframe_type;
+ int wireframe_detail;
+ int _pad[1];
+} VolumeDisplay;
+
+typedef struct VolumeRender {
+ int precision;
+ int space;
+ float step_size;
+ float clipping;
+} VolumeRender;
+
+typedef struct Volume {
+ ID id;
+ struct AnimData *adt; /* animation data (must be immediately after id) */
+
+ /* File */
+ char filepath[1024]; /* FILE_MAX */
+ struct PackedFile *packedfile;
+
+ /* Sequence */
+ char is_sequence;
+ char sequence_mode;
+ char _pad1[2];
+ int frame_start;
+ int frame_duration;
+ int frame_offset;
+
+ /* Flag */
+ int flag;
+
+ /* Grids */
+ int active_grid;
+
+ /* Material */
+ struct Material **mat;
+ short totcol;
+ short _pad2[3];
+
+ /* Render & Display Settings */
+ VolumeRender render;
+ VolumeDisplay display;
+
+ /* Draw Cache */
+ void *batch_cache;
+
+ /* Runtime Data */
+ Volume_Runtime runtime;
+} Volume;
+
+/* Volume.flag */
+enum {
+ VO_DS_EXPAND = (1 << 0),
+};
+
+/* Volume.sequence_mode */
+typedef enum VolumeSequenceMode {
+ VOLUME_SEQUENCE_CLIP = 0,
+ VOLUME_SEQUENCE_EXTEND,
+ VOLUME_SEQUENCE_REPEAT,
+ VOLUME_SEQUENCE_PING_PONG,
+} VolumeSequenceMode;
+
+/* VolumeDisplay.wireframe_type */
+typedef enum VolumeWireframeType {
+ VOLUME_WIREFRAME_NONE = 0,
+ VOLUME_WIREFRAME_BOUNDS = 1,
+ VOLUME_WIREFRAME_BOXES = 2,
+ VOLUME_WIREFRAME_POINTS = 3,
+} VolumeWireframeType;
+
+/* VolumeDisplay.wireframe_detail */
+typedef enum VolumeWireframeDetail {
+ VOLUME_WIREFRAME_COARSE = 0,
+ VOLUME_WIREFRAME_FINE = 1,
+} VolumeWireframeDetail;
+
+/* VolumeRender.space */
+typedef enum VolumeRenderSpace {
+ VOLUME_SPACE_OBJECT = 0,
+ VOLUME_SPACE_WORLD = 1,
+} VolumeRenderSpace;
+
+/* Only one material supported currently. */
+#define VOLUME_MATERIAL_NR 1
+
+#endif /* __DNA_VOLUME_TYPES_H__ */
diff --git a/source/blender/makesdna/intern/CMakeLists.txt b/source/blender/makesdna/intern/CMakeLists.txt
index 799ec931d57..f8f765c6ece 100644
--- a/source/blender/makesdna/intern/CMakeLists.txt
+++ b/source/blender/makesdna/intern/CMakeLists.txt
@@ -135,6 +135,7 @@ set(SRC
../DNA_curve_defaults.h
../DNA_defaults.h
../DNA_image_defaults.h
+ ../DNA_hair_defaults.h
../DNA_lattice_defaults.h
../DNA_light_defaults.h
../DNA_lightprobe_defaults.h
@@ -143,11 +144,13 @@ set(SRC
../DNA_mesh_defaults.h
../DNA_meta_defaults.h
../DNA_object_defaults.h
+ ../DNA_pointcloud_defaults.h
../DNA_scene_defaults.h
../DNA_speaker_defaults.h
../DNA_texture_defaults.h
../DNA_vec_defaults.h
../DNA_view3d_defaults.h
+ ../DNA_volume_defaults.h
../DNA_world_defaults.h
)
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 260f1cd20f6..a8be435b902 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -59,6 +59,7 @@
#include "DNA_camera_types.h"
#include "DNA_curve_types.h"
#include "DNA_image_types.h"
+#include "DNA_hair_types.h"
#include "DNA_key_types.h"
#include "DNA_lattice_types.h"
#include "DNA_light_types.h"
@@ -68,10 +69,12 @@
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
+#include "DNA_pointcloud_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_volume_types.h"
#include "DNA_world_types.h"
#include "DNA_brush_defaults.h"
@@ -79,6 +82,7 @@
#include "DNA_camera_defaults.h"
#include "DNA_curve_defaults.h"
#include "DNA_image_defaults.h"
+#include "DNA_hair_defaults.h"
#include "DNA_lattice_defaults.h"
#include "DNA_light_defaults.h"
#include "DNA_lightprobe_defaults.h"
@@ -87,9 +91,11 @@
#include "DNA_mesh_defaults.h"
#include "DNA_meta_defaults.h"
#include "DNA_object_defaults.h"
+#include "DNA_pointcloud_defaults.h"
#include "DNA_scene_defaults.h"
#include "DNA_speaker_defaults.h"
#include "DNA_texture_defaults.h"
+#include "DNA_volume_defaults.h"
#include "DNA_world_defaults.h"
#define SDNA_DEFAULT_DECL_STRUCT(struct_name) \
@@ -110,6 +116,9 @@ SDNA_DEFAULT_DECL_STRUCT(Curve);
/* DNA_image_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Image);
+/* DNA_hair_defaults.h */
+SDNA_DEFAULT_DECL_STRUCT(Hair);
+
/* DNA_lattice_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Lattice);
@@ -134,6 +143,9 @@ SDNA_DEFAULT_DECL_STRUCT(MetaBall);
/* DNA_object_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Object);
+/* DNA_pointcloud_defaults.h */
+SDNA_DEFAULT_DECL_STRUCT(PointCloud);
+
/* DNA_scene_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(Scene);
SDNA_DEFAULT_DECL_STRUCT(ToolSettings);
@@ -147,6 +159,9 @@ SDNA_DEFAULT_DECL_STRUCT(Tex);
/* DNA_view3d_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(View3D);
+/* DNA_volume_defaults.h */
+SDNA_DEFAULT_DECL_STRUCT(Volume);
+
/* DNA_world_defaults.h */
SDNA_DEFAULT_DECL_STRUCT(World);
@@ -194,6 +209,9 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* DNA_image_defaults.h */
SDNA_DEFAULT_DECL(Image),
+ /* DNA_hair_defaults.h */
+ SDNA_DEFAULT_DECL(Hair),
+
/* DNA_lattice_defaults.h */
SDNA_DEFAULT_DECL(Lattice),
@@ -218,6 +236,9 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
/* DNA_object_defaults.h */
SDNA_DEFAULT_DECL(Object),
+ /* DNA_pointcloud_defaults.h */
+ SDNA_DEFAULT_DECL(PointCloud),
+
/* DNA_scene_defaults.h */
SDNA_DEFAULT_DECL(Scene),
SDNA_DEFAULT_DECL_EX(RenderData, Scene.r),
@@ -259,6 +280,9 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = {
SDNA_DEFAULT_DECL_EX(View3DShading, View3D.shading),
SDNA_DEFAULT_DECL_EX(View3DCursor, Scene.cursor),
+ /* DNA_volume_defaults.h */
+ SDNA_DEFAULT_DECL(Volume),
+
/* DNA_world_defaults.h */
SDNA_DEFAULT_DECL(World),
};
diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c
index a33f13be09a..3d82df79e4c 100644
--- a/source/blender/makesdna/intern/makesdna.c
+++ b/source/blender/makesdna/intern/makesdna.c
@@ -133,6 +133,9 @@ static const char *includefiles[] = {
"DNA_lightprobe_types.h",
"DNA_curveprofile_types.h",
"DNA_xr_types.h",
+ "DNA_hair_types.h",
+ "DNA_pointcloud_types.h",
+ "DNA_volume_types.h",
/* see comment above before editing! */
@@ -1600,6 +1603,9 @@ int main(int argc, char **argv)
#include "DNA_lightprobe_types.h"
#include "DNA_curveprofile_types.h"
#include "DNA_xr_types.h"
+#include "DNA_hair_types.h"
+#include "DNA_pointcloud_types.h"
+#include "DNA_volume_types.h"
/* end of list */
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 1e07da23429..31d1ed54fa1 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -274,6 +274,7 @@ extern StructRNA RNA_GizmoProperties;
extern StructRNA RNA_GlowSequence;
extern StructRNA RNA_GpencilModifier;
extern StructRNA RNA_GreasePencil;
+extern StructRNA RNA_Hair;
extern StructRNA RNA_Header;
extern StructRNA RNA_Histogram;
extern StructRNA RNA_HookGpencilModifier;
@@ -459,6 +460,7 @@ extern StructRNA RNA_ParticleSystemModifier;
extern StructRNA RNA_ParticleTarget;
extern StructRNA RNA_PivotConstraint;
extern StructRNA RNA_PointCache;
+extern StructRNA RNA_PointCloud;
extern StructRNA RNA_PointLight;
extern StructRNA RNA_PointerProperty;
extern StructRNA RNA_Pose;
@@ -679,6 +681,7 @@ extern StructRNA RNA_View3DOverlay;
extern StructRNA RNA_View3DShading;
extern StructRNA RNA_ViewLayer;
extern StructRNA RNA_ViewLayerEEVEE;
+extern StructRNA RNA_Volume;
extern StructRNA RNA_VoronoiTexture;
extern StructRNA RNA_WalkNavigation;
extern StructRNA RNA_WarpModifier;
diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h
index 66a5df001de..abbe284e97a 100644
--- a/source/blender/makesrna/RNA_types.h
+++ b/source/blender/makesrna/RNA_types.h
@@ -362,6 +362,11 @@ typedef struct ArrayIterator {
IteratorSkipFunc skip;
} ArrayIterator;
+typedef struct CountIterator {
+ void *ptr;
+ int item;
+} CountIterator;
+
typedef struct CollectionPropertyIterator {
/* internal */
PointerRNA parent;
@@ -370,6 +375,7 @@ typedef struct CollectionPropertyIterator {
union {
ArrayIterator array;
ListBaseIterator listbase;
+ CountIterator count;
void *custom;
} internal;
int idprop;
diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt
index 30989a62603..64b7a3e70c1 100644
--- a/source/blender/makesrna/intern/CMakeLists.txt
+++ b/source/blender/makesrna/intern/CMakeLists.txt
@@ -88,6 +88,7 @@ set(DEFSRC
rna_ui.c
rna_userdef.c
rna_vfont.c
+ rna_volume.c
rna_wm.c
rna_wm_gizmo.c
rna_workspace.c
@@ -95,6 +96,13 @@ set(DEFSRC
rna_xr.c
)
+if(WITH_NEW_OBJECT_TYPES)
+ list(APPEND DEFSRC
+ rna_hair.c
+ rna_pointcloud.c
+ )
+endif()
+
set(APISRC
rna_action_api.c
rna_animation_api.c
@@ -330,6 +338,10 @@ if(WITH_XR_OPENXR)
add_definitions(-DWITH_XR_OPENXR)
endif()
+if(WITH_NEW_OBJECT_TYPES)
+ add_definitions(-DWITH_NEW_OBJECT_TYPES)
+endif()
+
# Build makesrna executable
blender_include_dirs(
.
diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c
index 7ec8f6167d0..3eaee4a1ef4 100644
--- a/source/blender/makesrna/intern/makesrna.c
+++ b/source/blender/makesrna/intern/makesrna.c
@@ -4265,6 +4265,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_dynamicpaint.c", NULL, RNA_def_dynamic_paint},
{"rna_fcurve.c", "rna_fcurve_api.c", RNA_def_fcurve},
{"rna_gpencil.c", NULL, RNA_def_gpencil},
+#ifdef WITH_NEW_OBJECT_TYPES
+ {"rna_hair.c", NULL, RNA_def_hair},
+#endif
{"rna_image.c", "rna_image_api.c", RNA_def_image},
{"rna_key.c", NULL, RNA_def_key},
{"rna_light.c", NULL, RNA_def_light},
@@ -4287,6 +4290,9 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_packedfile.c", NULL, RNA_def_packedfile},
{"rna_palette.c", NULL, RNA_def_palette},
{"rna_particle.c", NULL, RNA_def_particle},
+#ifdef WITH_NEW_OBJECT_TYPES
+ {"rna_pointcloud.c", NULL, RNA_def_pointcloud},
+#endif
{"rna_pose.c", "rna_pose_api.c", RNA_def_pose},
{"rna_curveprofile.c", NULL, RNA_def_profile},
{"rna_lightprobe.c", NULL, RNA_def_lightprobe},
@@ -4305,6 +4311,7 @@ static RNAProcessItem PROCESS_ITEMS[] = {
{"rna_ui.c", "rna_ui_api.c", RNA_def_ui},
{"rna_userdef.c", NULL, RNA_def_userdef},
{"rna_vfont.c", "rna_vfont_api.c", RNA_def_vfont},
+ {"rna_volume.c", NULL, RNA_def_volume},
{"rna_wm.c", "rna_wm_api.c", RNA_def_wm},
{"rna_wm_gizmo.c", "rna_wm_gizmo_api.c", RNA_def_wm_gizmo},
{"rna_workspace.c", "rna_workspace_api.c", RNA_def_workspace},
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index bd5c7e755d6..9a660153a3b 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -75,6 +75,11 @@ const EnumPropertyItem rna_enum_id_type_items[] = {
{ID_SPK, "SPEAKER", ICON_SPEAKER, "Speaker", ""},
{ID_TXT, "TEXT", ICON_TEXT, "Text", ""},
{ID_TE, "TEXTURE", ICON_TEXTURE_DATA, "Texture", ""},
+#ifdef WITH_NEW_OBJECT_TYPES
+ {ID_HA, "HAIR", ICON_HAIR_DATA, "Hair", ""},
+ {ID_PT, "POINTCLOUD", ICON_POINTCLOUD_DATA, "PointCloud", ""},
+#endif
+ {ID_VO, "VOLUME", ICON_VOLUME_DATA, "Volume", ""},
{ID_WM, "WINDOWMANAGER", ICON_WINDOW, "Window Manager", ""},
{ID_WO, "WORLD", ICON_WORLD_DATA, "World", ""},
{ID_WS, "WORKSPACE", ICON_WORKSPACE, "Workspace", ""},
@@ -246,6 +251,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_FreestyleLineStyle) {
return ID_LS;
}
+# ifdef WITH_NEW_OBJECT_TYPES
+ if (base_type == &RNA_Hair) {
+ return ID_HA;
+ }
+# endif
if (base_type == &RNA_Lattice) {
return ID_LT;
}
@@ -279,6 +289,11 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_PaintCurve) {
return ID_PC;
}
+# ifdef WITH_NEW_OBJECT_TYPES
+ if (base_type == &RNA_PointCloud) {
+ return ID_PT;
+ }
+# endif
if (base_type == &RNA_LightProbe) {
return ID_LP;
}
@@ -303,6 +318,9 @@ short RNA_type_to_ID_code(const StructRNA *type)
if (base_type == &RNA_VectorFont) {
return ID_VF;
}
+ if (base_type == &RNA_Volume) {
+ return ID_VO;
+ }
if (base_type == &RNA_WorkSpace) {
return ID_WS;
}
@@ -337,6 +355,12 @@ StructRNA *ID_code_to_RNA_type(short idcode)
return &RNA_GreasePencil;
case ID_GR:
return &RNA_Collection;
+ case ID_HA:
+# ifdef WITH_NEW_OBJECT_TYPES
+ return &RNA_Hair;
+# else
+ return &RNA_ID;
+# endif
case ID_IM:
return &RNA_Image;
case ID_KE:
@@ -369,6 +393,12 @@ StructRNA *ID_code_to_RNA_type(short idcode)
return &RNA_Palette;
case ID_PC:
return &RNA_PaintCurve;
+ case ID_PT:
+# ifdef WITH_NEW_OBJECT_TYPES
+ return &RNA_PointCloud;
+# else
+ return &RNA_ID;
+# endif
case ID_LP:
return &RNA_LightProbe;
case ID_SCE:
@@ -385,6 +415,8 @@ StructRNA *ID_code_to_RNA_type(short idcode)
return &RNA_Text;
case ID_VF:
return &RNA_VectorFont;
+ case ID_VO:
+ return &RNA_Volume;
case ID_WM:
return &RNA_WindowManager;
case ID_WO:
diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c
index c85a94d9fc2..a9dfa8b529e 100644
--- a/source/blender/makesrna/intern/rna_action.c
+++ b/source/blender/makesrna/intern/rna_action.c
@@ -568,6 +568,29 @@ static void rna_def_dopesheet(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_FILE, 0);
RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+# ifdef WITH_NEW_OBJECT_TYPES
+ prop = RNA_def_property(srna, "show_hairs", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOHAIR);
+ RNA_def_property_ui_text(
+ prop, "Display Hair", "Include visualization of hair related animation data");
+ RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_HAIR, 0);
+ RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+
+ prop = RNA_def_property(srna, "show_pointclouds", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOPOINTCLOUD);
+ RNA_def_property_ui_text(
+ prop, "Display Point Cloud", "Include visualization of point cloud related animation data");
+ RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_POINTCLOUD, 0);
+ RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+# endif
+
+ prop = RNA_def_property(srna, "show_volumes", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag2", ADS_FILTER_NOVOLUME);
+ RNA_def_property_ui_text(
+ prop, "Display Volume", "Include visualization of volume related animation data");
+ RNA_def_property_ui_icon(prop, ICON_OUTLINER_OB_VOLUME, 0);
+ RNA_def_property_update(prop, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL);
+
prop = RNA_def_property(srna, "show_gpencil", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_negative_sdna(prop, NULL, "filterflag", ADS_FILTER_NOGPENCIL);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_hair.c b/source/blender/makesrna/intern/rna_hair.c
new file mode 100644
index 00000000000..13f3132b7b8
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_hair.c
@@ -0,0 +1,244 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Jörg Müller.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_hair.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_hair_types.h"
+
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+
+#ifdef RNA_RUNTIME
+
+# include "BLI_math_vector.h"
+
+# include "BKE_hair.h"
+
+# include "DEG_depsgraph.h"
+
+# include "WM_api.h"
+# include "WM_types.h"
+
+static Hair *rna_hair(PointerRNA *ptr)
+{
+ return (Hair *)ptr->owner_id;
+}
+
+static int rna_HairPoint_index_get(PointerRNA *ptr)
+{
+ const Hair *hair = rna_hair(ptr);
+ const float(*co)[3] = ptr->data;
+ return (int)(co - hair->co);
+}
+
+static void rna_HairPoint_location_get(PointerRNA *ptr, float value[3])
+{
+ copy_v3_v3(value, (const float *)ptr->data);
+}
+
+static void rna_HairPoint_location_set(PointerRNA *ptr, const float value[3])
+{
+ copy_v3_v3((float *)ptr->data, value);
+}
+
+static float rna_HairPoint_radius_get(PointerRNA *ptr)
+{
+ const Hair *hair = rna_hair(ptr);
+ if (hair->radius == NULL) {
+ return 0.0f;
+ }
+ const float(*co)[3] = ptr->data;
+ return hair->radius[co - hair->co];
+}
+
+static void rna_HairPoint_radius_set(PointerRNA *ptr, float value)
+{
+ const Hair *hair = rna_hair(ptr);
+ if (hair->radius == NULL) {
+ return;
+ }
+ const float(*co)[3] = ptr->data;
+ hair->radius[co - hair->co] = value;
+}
+
+static char *rna_HairPoint_path(PointerRNA *ptr)
+{
+ return BLI_sprintfN("points[%d]", rna_HairPoint_index_get(ptr));
+}
+
+static int rna_HairCurve_index_get(PointerRNA *ptr)
+{
+ Hair *hair = rna_hair(ptr);
+ return (int)((HairCurve *)ptr->data - hair->curves);
+}
+
+static char *rna_HairCurve_path(PointerRNA *ptr)
+{
+ return BLI_sprintfN("curves[%d]", rna_HairCurve_index_get(ptr));
+}
+
+static void rna_HairCurve_points_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Hair *hair = rna_hair(ptr);
+ HairCurve *curve = ptr->data;
+ float(*co)[3] = hair->co + curve->firstpoint;
+ rna_iterator_array_begin(iter, co, sizeof(float[3]), curve->numpoints, 0, NULL);
+}
+
+static int rna_HairCurve_points_length(PointerRNA *ptr)
+{
+ HairCurve *curve = ptr->data;
+ return curve->numpoints;
+}
+
+static void rna_Hair_update_data(struct Main *UNUSED(bmain),
+ struct Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ ID *id = ptr->owner_id;
+
+ /* cheating way for importers to avoid slow updates */
+ if (id->us > 0) {
+ DEG_id_tag_update(id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, id);
+ }
+}
+
+#else
+
+static void rna_def_hair_point(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "HairPoint", NULL);
+ RNA_def_struct_ui_text(srna, "Hair Point", "Hair curve control point");
+ RNA_def_struct_path_func(srna, "rna_HairPoint_path");
+
+ prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(
+ prop, "rna_HairPoint_location_get", "rna_HairPoint_location_set", NULL);
+ RNA_def_property_ui_text(prop, "Location", "");
+ RNA_def_property_update(prop, 0, "rna_Hair_update_data");
+
+ prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_funcs(prop, "rna_HairPoint_radius_get", "rna_HairPoint_radius_set", NULL);
+ RNA_def_property_ui_text(prop, "Radius", "");
+ RNA_def_property_update(prop, 0, "rna_Hair_update_data");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_HairPoint_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index of this points");
+}
+
+static void rna_def_hair_curve(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "HairCurve", NULL);
+ RNA_def_struct_ui_text(srna, "Hair Curve", "Hair curve");
+ RNA_def_struct_path_func(srna, "rna_HairCurve_path");
+
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "HairPoint");
+ RNA_def_property_ui_text(prop, "Points", "Control points of the curve");
+ RNA_def_property_collection_funcs(prop,
+ "rna_HairCurve_points_begin",
+ "rna_iterator_array_next",
+ "rna_iterator_array_end",
+ "rna_iterator_array_get",
+ "rna_HairCurve_points_length",
+ NULL,
+ NULL,
+ NULL);
+
+ /* TODO: naming consistency, editable? */
+ prop = RNA_def_property(srna, "first_point_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "firstpoint");
+ RNA_def_property_ui_text(prop, "First Point Index", "Index of the first loop of this polygon");
+
+ prop = RNA_def_property(srna, "num_points", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "numpoints");
+ RNA_def_property_ui_text(prop, "Number of Points", "Number of loops used by this polygon");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_HairCurve_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index of this curve");
+}
+
+static void rna_def_hair(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Hair", "ID");
+ RNA_def_struct_ui_text(srna, "Hair", "Hair data-block for hair curves");
+ RNA_def_struct_ui_icon(srna, ICON_HAIR_DATA);
+
+ /* geometry */
+ prop = RNA_def_property(srna, "curves", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "curves", "totcurve");
+ RNA_def_property_struct_type(prop, "HairCurve");
+ RNA_def_property_ui_text(prop, "Curves", "All hair curves");
+
+ /* TODO: better solution for (*co)[3] parsing issue. */
+ RNA_define_verify_sdna(0);
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint");
+ RNA_def_property_struct_type(prop, "HairPoint");
+ RNA_def_property_ui_text(prop, "Points", "Control points of all hair curves");
+ RNA_define_verify_sdna(1);
+
+ /* materials */
+ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_ui_text(prop, "Materials", "");
+ RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */
+ RNA_def_property_collection_funcs(
+ prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
+
+ /* common */
+ rna_def_animdata_common(srna);
+}
+
+void RNA_def_hair(BlenderRNA *brna)
+{
+ rna_def_hair_point(brna);
+ rna_def_hair_curve(brna);
+ rna_def_hair(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 3e18e882e2b..27097261930 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -159,6 +159,7 @@ void RNA_def_fcurve(struct BlenderRNA *brna);
void RNA_def_gpencil(struct BlenderRNA *brna);
void RNA_def_greasepencil_modifier(struct BlenderRNA *brna);
void RNA_def_shader_fx(struct BlenderRNA *brna);
+void RNA_def_hair(struct BlenderRNA *brna);
void RNA_def_image(struct BlenderRNA *brna);
void RNA_def_key(struct BlenderRNA *brna);
void RNA_def_light(struct BlenderRNA *brna);
@@ -176,6 +177,7 @@ void RNA_def_object_force(struct BlenderRNA *brna);
void RNA_def_packedfile(struct BlenderRNA *brna);
void RNA_def_palette(struct BlenderRNA *brna);
void RNA_def_particle(struct BlenderRNA *brna);
+void RNA_def_pointcloud(struct BlenderRNA *brna);
void RNA_def_pose(struct BlenderRNA *brna);
void RNA_def_profile(struct BlenderRNA *brna);
void RNA_def_lightprobe(struct BlenderRNA *brna);
@@ -198,6 +200,7 @@ void RNA_def_sound(struct BlenderRNA *brna);
void RNA_def_ui(struct BlenderRNA *brna);
void RNA_def_userdef(struct BlenderRNA *brna);
void RNA_def_vfont(struct BlenderRNA *brna);
+void RNA_def_volume(struct BlenderRNA *brna);
void RNA_def_wm(struct BlenderRNA *brna);
void RNA_def_wm_gizmo(struct BlenderRNA *brna);
void RNA_def_workspace(struct BlenderRNA *brna);
@@ -443,6 +446,9 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop);
+void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop);
/* ID Properties */
diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c
index 41de02a738f..949a7e44feb 100644
--- a/source/blender/makesrna/intern/rna_main.c
+++ b/source/blender/makesrna/intern/rna_main.c
@@ -109,6 +109,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(collections)
RNA_MAIN_LISTBASE_FUNCS_DEF(curves)
RNA_MAIN_LISTBASE_FUNCS_DEF(fonts)
RNA_MAIN_LISTBASE_FUNCS_DEF(gpencils)
+# ifdef WITH_NEW_OBJECT_TYPES
+RNA_MAIN_LISTBASE_FUNCS_DEF(hairs)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(images)
RNA_MAIN_LISTBASE_FUNCS_DEF(lattices)
RNA_MAIN_LISTBASE_FUNCS_DEF(libraries)
@@ -125,6 +128,9 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(objects)
RNA_MAIN_LISTBASE_FUNCS_DEF(paintcurves)
RNA_MAIN_LISTBASE_FUNCS_DEF(palettes)
RNA_MAIN_LISTBASE_FUNCS_DEF(particles)
+# ifdef WITH_NEW_OBJECT_TYPES
+RNA_MAIN_LISTBASE_FUNCS_DEF(pointclouds)
+# endif
RNA_MAIN_LISTBASE_FUNCS_DEF(scenes)
RNA_MAIN_LISTBASE_FUNCS_DEF(screens)
RNA_MAIN_LISTBASE_FUNCS_DEF(shapekeys)
@@ -132,6 +138,7 @@ RNA_MAIN_LISTBASE_FUNCS_DEF(sounds)
RNA_MAIN_LISTBASE_FUNCS_DEF(speakers)
RNA_MAIN_LISTBASE_FUNCS_DEF(texts)
RNA_MAIN_LISTBASE_FUNCS_DEF(textures)
+RNA_MAIN_LISTBASE_FUNCS_DEF(volumes)
RNA_MAIN_LISTBASE_FUNCS_DEF(wm)
RNA_MAIN_LISTBASE_FUNCS_DEF(workspaces)
RNA_MAIN_LISTBASE_FUNCS_DEF(worlds)
@@ -380,6 +387,21 @@ void RNA_def_main(BlenderRNA *brna)
"LightProbes",
"LightProbe data-blocks",
RNA_def_main_lightprobes},
+# ifdef WITH_NEW_OBJECT_TYPES
+ {"hairs", "Hair", "rna_Main_hairs_begin", "Hairs", "Hair data-blocks", RNA_def_main_hairs},
+ {"pointclouds",
+ "PointCloud",
+ "rna_Main_pointclouds_begin",
+ "Point Clouds",
+ "Point cloud data-blocks",
+ RNA_def_main_pointclouds},
+# endif
+ {"volumes",
+ "Volume",
+ "rna_Main_volumes_begin",
+ "Volumes",
+ "Volume data-blocks",
+ RNA_def_main_volumes},
{NULL, NULL, NULL, NULL, NULL, NULL},
};
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index bb851365997..dfd94ccc927 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -49,6 +49,7 @@
# include "BKE_displist.h"
# include "BKE_font.h"
# include "BKE_gpencil.h"
+# include "BKE_hair.h"
# include "BKE_icons.h"
# include "BKE_idcode.h"
# include "BKE_image.h"
@@ -66,11 +67,13 @@
# include "BKE_object.h"
# include "BKE_paint.h"
# include "BKE_particle.h"
+# include "BKE_pointcloud.h"
# include "BKE_scene.h"
# include "BKE_sound.h"
# include "BKE_speaker.h"
# include "BKE_text.h"
# include "BKE_texture.h"
+# include "BKE_volume.h"
# include "BKE_workspace.h"
# include "BKE_world.h"
@@ -80,6 +83,7 @@
# include "DNA_armature_types.h"
# include "DNA_camera_types.h"
# include "DNA_curve_types.h"
+# include "DNA_hair_types.h"
# include "DNA_light_types.h"
# include "DNA_material_types.h"
# include "DNA_mesh_types.h"
@@ -94,7 +98,9 @@
# include "DNA_meta_types.h"
# include "DNA_world_types.h"
# include "DNA_particle_types.h"
+# include "DNA_pointcloud_types.h"
# include "DNA_vfont_types.h"
+# include "DNA_volume_types.h"
# include "DNA_node_types.h"
# include "DNA_movieclip_types.h"
# include "DNA_mask_types.h"
@@ -253,6 +259,15 @@ static Object *rna_Main_objects_new(Main *bmain, ReportList *reports, const char
case ID_LP:
type = OB_LIGHTPROBE;
break;
+ case ID_HA:
+ type = OB_HAIR;
+ break;
+ case ID_PT:
+ type = OB_POINTCLOUD;
+ break;
+ case ID_VO:
+ type = OB_VOLUME;
+ break;
default: {
const char *idname;
if (RNA_enum_id_from_value(rna_enum_id_type_items, GS(data->name), &idname) == 0) {
@@ -691,6 +706,38 @@ static bGPdata *rna_Main_gpencils_new(Main *bmain, const char *name)
return gpd;
}
+# ifdef WITH_NEW_OBJECT_TYPES
+static Hair *rna_Main_hairs_new(Main *bmain, const char *name)
+{
+ char safe_name[MAX_ID_NAME - 2];
+ rna_idname_validate(name, safe_name);
+
+ Hair *hair = BKE_hair_add(bmain, safe_name);
+ id_us_min(&hair->id);
+ return hair;
+}
+
+static PointCloud *rna_Main_pointclouds_new(Main *bmain, const char *name)
+{
+ char safe_name[MAX_ID_NAME - 2];
+ rna_idname_validate(name, safe_name);
+
+ PointCloud *pointcloud = BKE_pointcloud_add(bmain, safe_name);
+ id_us_min(&pointcloud->id);
+ return pointcloud;
+}
+# endif
+
+static Volume *rna_Main_volumes_new(Main *bmain, const char *name)
+{
+ char safe_name[MAX_ID_NAME - 2];
+ rna_idname_validate(name, safe_name);
+
+ Volume *volume = BKE_volume_add(bmain, safe_name);
+ id_us_min(&volume->id);
+ return volume;
+}
+
/* tag functions, all the same */
# define RNA_MAIN_ID_TAG_FUNCS_DEF(_func_name, _listbase_name, _id_type) \
static void rna_Main_##_func_name##_tag(Main *bmain, bool value) \
@@ -733,6 +780,11 @@ RNA_MAIN_ID_TAG_FUNCS_DEF(cachefiles, cachefiles, ID_CF)
RNA_MAIN_ID_TAG_FUNCS_DEF(paintcurves, paintcurves, ID_PC)
RNA_MAIN_ID_TAG_FUNCS_DEF(workspaces, workspaces, ID_WS)
RNA_MAIN_ID_TAG_FUNCS_DEF(lightprobes, lightprobes, ID_LP)
+# ifdef WITH_NEW_OBJECT_TYPES
+RNA_MAIN_ID_TAG_FUNCS_DEF(hairs, hairs, ID_HA)
+RNA_MAIN_ID_TAG_FUNCS_DEF(pointclouds, pointclouds, ID_PT)
+# endif
+RNA_MAIN_ID_TAG_FUNCS_DEF(volumes, volumes, ID_VO)
# undef RNA_MAIN_ID_TAG_FUNCS_DEF
@@ -2117,4 +2169,139 @@ void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
+void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "BlendDataHairs");
+ srna = RNA_def_struct(brna, "BlendDataHairs", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Hairs", "Collection of hairs");
+
+ func = RNA_def_function(srna, "new", "rna_Main_hairs_new");
+ RNA_def_function_ui_description(func, "Add a new hair to the main database");
+ parm = RNA_def_string(func, "name", "Hair", 0, "", "New name for the data-block");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "hair", "Hair", "", "New hair data-block");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a hair from the current blendfile");
+ parm = RNA_def_pointer(func, "hair", "Hair", "", "Hair to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_boolean(func,
+ "do_unlink",
+ true,
+ "",
+ "Unlink all usages of this hair before deleting it "
+ "(WARNING: will also delete objects instancing that hair data)");
+ RNA_def_boolean(func,
+ "do_id_user",
+ true,
+ "",
+ "Decrement user counter of all datablocks used by this hair data");
+ RNA_def_boolean(
+ func, "do_ui_user", true, "", "Make sure interface does not reference this hair data");
+
+ func = RNA_def_function(srna, "tag", "rna_Main_hairs_tag");
+ parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
+void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "BlendDataPointClouds");
+ srna = RNA_def_struct(brna, "BlendDataPointClouds", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Point Clouds", "Collection of point clouds");
+
+ func = RNA_def_function(srna, "new", "rna_Main_pointclouds_new");
+ RNA_def_function_ui_description(func, "Add a new point cloud to the main database");
+ parm = RNA_def_string(func, "name", "PointCloud", 0, "", "New name for the data-block");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "pointcloud", "PointCloud", "", "New point cloud data-block");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a point cloud from the current blendfile");
+ parm = RNA_def_pointer(func, "pointcloud", "PointCloud", "", "Point cloud to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_boolean(func,
+ "do_unlink",
+ true,
+ "",
+ "Unlink all usages of this point cloud before deleting it "
+ "(WARNING: will also delete objects instancing that point cloud data)");
+ RNA_def_boolean(func,
+ "do_id_user",
+ true,
+ "",
+ "Decrement user counter of all datablocks used by this point cloud data");
+ RNA_def_boolean(func,
+ "do_ui_user",
+ true,
+ "",
+ "Make sure interface does not reference this point cloud data");
+
+ func = RNA_def_function(srna, "tag", "rna_Main_pointclouds_tag");
+ parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
+void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "BlendDataVolumes");
+ srna = RNA_def_struct(brna, "BlendDataVolumes", NULL);
+ RNA_def_struct_sdna(srna, "Main");
+ RNA_def_struct_ui_text(srna, "Main Volumes", "Collection of volumes");
+
+ func = RNA_def_function(srna, "new", "rna_Main_volumes_new");
+ RNA_def_function_ui_description(func, "Add a new volume to the main database");
+ parm = RNA_def_string(func, "name", "Volume", 0, "", "New name for the data-block");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+ /* return type */
+ parm = RNA_def_pointer(func, "volume", "Volume", "", "New volume data-block");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_Main_ID_remove");
+ RNA_def_function_flag(func, FUNC_USE_REPORTS);
+ RNA_def_function_ui_description(func, "Remove a volume from the current blendfile");
+ parm = RNA_def_pointer(func, "volume", "Volume", "", "Volume to remove");
+ RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
+ RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, 0);
+ RNA_def_boolean(func,
+ "do_unlink",
+ true,
+ "",
+ "Unlink all usages of this volume before deleting it "
+ "(WARNING: will also delete objects instancing that volume data)");
+ RNA_def_boolean(func,
+ "do_id_user",
+ true,
+ "",
+ "Decrement user counter of all datablocks used by this volume data");
+ RNA_def_boolean(
+ func, "do_ui_user", true, "", "Make sure interface does not reference this volume data");
+
+ func = RNA_def_function(srna, "tag", "rna_Main_volumes_tag");
+ parm = RNA_def_boolean(func, "value", 0, "Value", "");
+ RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
+}
+
#endif
diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c
index 78f5cfb60b2..269dd08dd36 100644
--- a/source/blender/makesrna/intern/rna_object.c
+++ b/source/blender/makesrna/intern/rna_object.c
@@ -234,6 +234,11 @@ const EnumPropertyItem rna_enum_object_type_items[] = {
OBTYPE_CU_SURF,
{OB_MBALL, "META", 0, "Meta", ""},
OBTYPE_CU_FONT,
+#ifdef WITH_NEW_OBJECT_TYPES
+ {OB_HAIR, "HAIR", 0, "Hair", ""},
+ {OB_POINTCLOUD, "POINTCLOUD", 0, "PointCloud", ""},
+#endif
+ {OB_VOLUME, "VOLUME", 0, "Volume", ""},
{0, "", 0, NULL, NULL},
{OB_ARMATURE, "ARMATURE", 0, "Armature", ""},
{OB_LATTICE, "LATTICE", 0, "Lattice", ""},
@@ -552,6 +557,14 @@ static StructRNA *rna_Object_data_typef(PointerRNA *ptr)
return &RNA_LightProbe;
case OB_GPENCIL:
return &RNA_GreasePencil;
+# ifdef WITH_NEW_OBJECT_TYPES
+ case OB_HAIR:
+ return &RNA_Hair;
+ case OB_POINTCLOUD:
+ return &RNA_PointCloud;
+# endif
+ case OB_VOLUME:
+ return &RNA_Volume;
default:
return &RNA_ID;
}
diff --git a/source/blender/makesrna/intern/rna_pointcloud.c b/source/blender/makesrna/intern/rna_pointcloud.c
new file mode 100644
index 00000000000..d62fa6dc3fc
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_pointcloud.c
@@ -0,0 +1,175 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Jörg Müller.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_pointcloud.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_pointcloud_types.h"
+
+#include "BLI_math_base.h"
+#include "BLI_string.h"
+
+#ifdef RNA_RUNTIME
+
+# include "BLI_math_vector.h"
+
+# include "BKE_pointcloud.h"
+
+# include "DEG_depsgraph.h"
+
+# include "WM_api.h"
+# include "WM_types.h"
+
+static PointCloud *rna_pointcloud(PointerRNA *ptr)
+{
+ return (PointCloud *)ptr->owner_id;
+}
+
+static int rna_Point_index_get(PointerRNA *ptr)
+{
+ const PointCloud *pointcloud = rna_pointcloud(ptr);
+ const float(*co)[3] = ptr->data;
+ return (int)(co - pointcloud->co);
+}
+
+static void rna_Point_location_get(PointerRNA *ptr, float value[3])
+{
+ copy_v3_v3(value, (const float *)ptr->data);
+}
+
+static void rna_Point_location_set(PointerRNA *ptr, const float value[3])
+{
+ copy_v3_v3((float *)ptr->data, value);
+}
+
+static float rna_Point_radius_get(PointerRNA *ptr)
+{
+ const PointCloud *pointcloud = rna_pointcloud(ptr);
+ if (pointcloud->radius == NULL) {
+ return 0.0f;
+ }
+ const float(*co)[3] = ptr->data;
+ return pointcloud->radius[co - pointcloud->co];
+}
+
+static void rna_Point_radius_set(PointerRNA *ptr, float value)
+{
+ const PointCloud *pointcloud = rna_pointcloud(ptr);
+ if (pointcloud->radius == NULL) {
+ return;
+ }
+ const float(*co)[3] = ptr->data;
+ pointcloud->radius[co - pointcloud->co] = value;
+}
+
+static char *rna_Point_path(PointerRNA *ptr)
+{
+ return BLI_sprintfN("points[%d]", rna_Point_index_get(ptr));
+}
+
+static void rna_PointCloud_update_data(struct Main *UNUSED(bmain),
+ struct Scene *UNUSED(scene),
+ PointerRNA *ptr)
+{
+ ID *id = ptr->owner_id;
+
+ /* cheating way for importers to avoid slow updates */
+ if (id->us > 0) {
+ DEG_id_tag_update(id, 0);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, id);
+ }
+}
+
+#else
+
+static void rna_def_point(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Point", NULL);
+ RNA_def_struct_ui_text(srna, "Point", "Point in a point cloud");
+ RNA_def_struct_path_func(srna, "rna_Point_path");
+
+ prop = RNA_def_property(srna, "co", PROP_FLOAT, PROP_TRANSLATION);
+ RNA_def_property_array(prop, 3);
+ RNA_def_property_float_funcs(prop, "rna_Point_location_get", "rna_Point_location_set", NULL);
+ RNA_def_property_ui_text(prop, "Location", "");
+ RNA_def_property_update(prop, 0, "rna_PointCloud_update_data");
+
+ prop = RNA_def_property(srna, "radius", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_funcs(prop, "rna_Point_radius_get", "rna_Point_radius_set", NULL);
+ RNA_def_property_ui_text(prop, "Radius", "");
+ RNA_def_property_update(prop, 0, "rna_PointCloud_update_data");
+
+ prop = RNA_def_property(srna, "index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_Point_index_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Index", "Index of this points");
+}
+
+static void rna_def_pointcloud(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "PointCloud", "ID");
+ RNA_def_struct_ui_text(srna, "PointCloud", "Point cloud data-block");
+ RNA_def_struct_ui_icon(srna, ICON_POINTCLOUD_DATA);
+
+ /* geometry */
+ /* TODO: better solution for (*co)[3] parsing issue. */
+ RNA_define_verify_sdna(0);
+ prop = RNA_def_property(srna, "points", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "co", "totpoint");
+ RNA_def_property_struct_type(prop, "Point");
+ RNA_def_property_ui_text(prop, "Points", "");
+ RNA_define_verify_sdna(1);
+
+ /* materials */
+ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_ui_text(prop, "Materials", "");
+ RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */
+ RNA_def_property_collection_funcs(
+ prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
+
+ /* common */
+ rna_def_animdata_common(srna);
+}
+
+void RNA_def_pointcloud(BlenderRNA *brna)
+{
+ rna_def_point(brna);
+ rna_def_pointcloud(brna);
+}
+
+#endif
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index b4bb141ba7a..5d4ca44f53a 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -4230,6 +4230,13 @@ static void rna_def_space_view3d(BlenderRNA *brna)
{"Surface", (1 << OB_SURF), {"show_object_viewport_surf", "show_object_select_surf"}},
{"Meta", (1 << OB_MBALL), {"show_object_viewport_meta", "show_object_select_meta"}},
{"Font", (1 << OB_FONT), {"show_object_viewport_font", "show_object_select_font"}},
+# ifdef WITH_NEW_OBJECT_TYPES
+ {"Hair", (1 << OB_HAIR), {"show_object_viewport_hair", "show_object_select_hair"}},
+ {"Point Cloud",
+ (1 << OB_POINTCLOUD),
+ {"show_object_viewport_pointcloud", "show_object_select_pointcloud"}},
+# endif
+ {"Volume", (1 << OB_VOLUME), {"show_object_viewport_volume", "show_object_select_volume"}},
{"Armature",
(1 << OB_ARMATURE),
{"show_object_viewport_armature", "show_object_select_armature"}},
@@ -5432,8 +5439,16 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
"Grease Pencil",
"Show Grease pencil data-blocks"},
{FILTER_ID_GR, "filter_group", ICON_GROUP, "Collections", "Show Collection data-blocks"},
+# ifdef WITH_NEW_OBJECT_TYPES
+ {FILTER_ID_HA, "filter_hair", ICON_HAIR_DATA, "Hairs", "Show/hide Hair data-blocks"},
+# endif
{FILTER_ID_IM, "filter_image", ICON_IMAGE_DATA, "Images", "Show Image data-blocks"},
{FILTER_ID_LA, "filter_light", ICON_LIGHT_DATA, "Lights", "Show Light data-blocks"},
+ {FILTER_ID_LP,
+ "filter_light_probe",
+ ICON_OUTLINER_DATA_LIGHTPROBE,
+ "Light Probes",
+ "Show Light Probe data-blocks"},
{FILTER_ID_LS,
"filter_linestyle",
ICON_LINE_DATA,
@@ -5470,17 +5485,20 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
ICON_CURVE_BEZCURVE,
"Paint Curves",
"Show Paint Curve data-blocks"},
- {FILTER_ID_LP,
- "filter_light_probe",
- ICON_OUTLINER_DATA_LIGHTPROBE,
- "Light Probes",
- "Show Light Probe data-blocks"},
+# ifdef WITH_NEW_OBJECT_TYPES
+ {FILTER_ID_PT,
+ "filter_pointcloud",
+ ICON_POINTCLOUD_DATA,
+ "Point Clouds",
+ "Show/hide Point Cloud data-blocks"},
+# endif
{FILTER_ID_SCE, "filter_scene", ICON_SCENE_DATA, "Scenes", "Show Scene data-blocks"},
{FILTER_ID_SPK, "filter_speaker", ICON_SPEAKER, "Speakers", "Show Speaker data-blocks"},
{FILTER_ID_SO, "filter_sound", ICON_SOUND, "Sounds", "Show Sound data-blocks"},
{FILTER_ID_TE, "filter_texture", ICON_TEXTURE_DATA, "Textures", "Show Texture data-blocks"},
{FILTER_ID_TXT, "filter_text", ICON_TEXT, "Texts", "Show Text data-blocks"},
{FILTER_ID_VF, "filter_font", ICON_FONT_DATA, "Fonts", "Show Font data-blocks"},
+ {FILTER_ID_VO, "filter_volume", ICON_VOLUME_DATA, "Volumes", "Show/hide Volume data-blocks"},
{FILTER_ID_WO, "filter_world", ICON_WORLD_DATA, "Worlds", "Show World data-blocks"},
{FILTER_ID_WS,
"filter_work_space",
@@ -5495,8 +5513,9 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna)
"category_object",
ICON_GROUP,
"Objects & Collections",
- "Show objects and groups"},
- {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME,
+ "Show objects and collections"},
+ {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME | FILTER_ID_HA |
+ FILTER_ID_PT | FILTER_ID_VO,
"category_geometry",
ICON_MESH_DATA,
"Geometry",
@@ -5693,6 +5712,12 @@ static void rna_def_fileselect_params(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_FILE_TEXT, 0);
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+ prop = RNA_def_property(srna, "use_filter_volume", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "filter", FILE_TYPE_VOLUME);
+ RNA_def_property_ui_text(prop, "Filter Volume", "Show 3D volume files");
+ RNA_def_property_ui_icon(prop, ICON_FILE_VOLUME, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL);
+
prop = RNA_def_property(srna, "use_filter_folder", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "filter", FILE_TYPE_FOLDER);
RNA_def_property_ui_text(prop, "Filter Folder", "Show folders");
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index 195a80a1101..fdefbc9f499 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -4985,6 +4985,23 @@ static void rna_def_userdef_edit(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Duplicate GPencil", "Causes grease pencil data to be duplicated with the object");
+# ifdef WITH_NEW_OBJECT_TYPES
+ prop = RNA_def_property(srna, "use_duplicate_hair", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_HAIR);
+ RNA_def_property_ui_text(
+ prop, "Duplicate Hair", "Causes hair data to be duplicated with the object");
+
+ prop = RNA_def_property(srna, "use_duplicate_pointcloud", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_POINTCLOUD);
+ RNA_def_property_ui_text(
+ prop, "Duplicate Point Cloud", "Causes point cloud data to be duplicated with the object");
+# endif
+
+ prop = RNA_def_property(srna, "use_duplicate_volume", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_VOLUME);
+ RNA_def_property_ui_text(
+ prop, "Duplicate Volume", "Causes volume data to be duplicated with the object");
+
/* Currently only used for insert offset (aka auto-offset),
* maybe also be useful for later stuff though. */
prop = RNA_def_property(srna, "node_margin", PROP_INT, PROP_PIXEL);
diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c
new file mode 100644
index 00000000000..755d8922140
--- /dev/null
+++ b/source/blender/makesrna/intern/rna_volume.c
@@ -0,0 +1,557 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor(s): Jörg Müller.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/makesrna/intern/rna_volume.c
+ * \ingroup RNA
+ */
+
+#include <stdlib.h>
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
+#include "rna_internal.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_volume_types.h"
+
+#include "BKE_volume.h"
+
+#include "BLI_math_base.h"
+
+#ifdef RNA_RUNTIME
+
+# include "DEG_depsgraph.h"
+# include "DEG_depsgraph_build.h"
+
+# include "WM_types.h"
+# include "WM_api.h"
+
+/* Updates */
+
+static void rna_Volume_update_display(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Volume *volume = (Volume *)ptr->owner_id;
+ WM_main_add_notifier(NC_GEOM | ND_DATA, volume);
+}
+
+static void rna_Volume_update_filepath(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ Volume *volume = (Volume *)ptr->owner_id;
+ BKE_volume_unload(volume);
+ DEG_id_tag_update(&volume->id, ID_RECALC_COPY_ON_WRITE);
+ WM_main_add_notifier(NC_GEOM | ND_DATA, volume);
+}
+
+static void rna_Volume_update_is_sequence(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_Volume_update_filepath(bmain, scene, ptr);
+ DEG_relations_tag_update(bmain);
+}
+
+/* Grid */
+
+static void rna_VolumeGrid_name_get(PointerRNA *ptr, char *value)
+{
+ VolumeGrid *grid = ptr->data;
+ strcpy(value, BKE_volume_grid_name(grid));
+}
+
+static int rna_VolumeGrid_name_length(PointerRNA *ptr)
+{
+ VolumeGrid *grid = ptr->data;
+ return strlen(BKE_volume_grid_name(grid));
+}
+
+static int rna_VolumeGrid_data_type_get(PointerRNA *ptr)
+{
+ const VolumeGrid *grid = ptr->data;
+ return BKE_volume_grid_type(grid);
+}
+
+static int rna_VolumeGrid_channels_get(PointerRNA *ptr)
+{
+ const VolumeGrid *grid = ptr->data;
+ return BKE_volume_grid_channels(grid);
+}
+
+static void rna_VolumeGrid_matrix_object_get(PointerRNA *ptr, float *value)
+{
+ VolumeGrid *grid = ptr->data;
+ BKE_volume_grid_transform_matrix(grid, (float(*)[4])value);
+}
+
+static bool rna_VolumeGrid_is_loaded_get(PointerRNA *ptr)
+{
+ VolumeGrid *grid = ptr->data;
+ return BKE_volume_grid_is_loaded(grid);
+}
+
+static bool rna_VolumeGrid_load(ID *id, VolumeGrid *grid)
+{
+ return BKE_volume_grid_load((Volume *)id, grid);
+}
+
+static void rna_VolumeGrid_unload(ID *id, VolumeGrid *grid)
+{
+ BKE_volume_grid_unload((Volume *)id, grid);
+}
+
+/* Grids Iterator */
+
+static void rna_Volume_grids_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ Volume *volume = ptr->data;
+ int num_grids = BKE_volume_num_grids(volume);
+ iter->internal.count.ptr = volume;
+ iter->internal.count.item = 0;
+ iter->valid = (iter->internal.count.item < num_grids);
+}
+
+static void rna_Volume_grids_next(CollectionPropertyIterator *iter)
+{
+ Volume *volume = iter->internal.count.ptr;
+ int num_grids = BKE_volume_num_grids(volume);
+ iter->internal.count.item++;
+ iter->valid = (iter->internal.count.item < num_grids);
+}
+
+static void rna_Volume_grids_end(CollectionPropertyIterator *UNUSED(iter))
+{
+}
+
+static PointerRNA rna_Volume_grids_get(CollectionPropertyIterator *iter)
+{
+ Volume *volume = iter->internal.count.ptr;
+ const VolumeGrid *grid = BKE_volume_grid_get(volume, iter->internal.count.item);
+ return rna_pointer_inherit_refine(&iter->parent, &RNA_VolumeGrid, (void *)grid);
+}
+
+static int rna_Volume_grids_length(PointerRNA *ptr)
+{
+ Volume *volume = ptr->data;
+ return BKE_volume_num_grids(volume);
+}
+
+/* Active Grid */
+
+static void rna_VolumeGrids_active_index_range(
+ PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ Volume *volume = (Volume *)ptr->data;
+ int num_grids = BKE_volume_num_grids(volume);
+
+ *min = 0;
+ *max = max_ii(0, num_grids - 1);
+}
+
+static int rna_VolumeGrids_active_index_get(PointerRNA *ptr)
+{
+ Volume *volume = (Volume *)ptr->data;
+ int num_grids = BKE_volume_num_grids(volume);
+ return clamp_i(volume->active_grid, 0, max_ii(num_grids - 1, 0));
+}
+
+static void rna_VolumeGrids_active_index_set(PointerRNA *ptr, int value)
+{
+ Volume *volume = (Volume *)ptr->data;
+ volume->active_grid = value;
+}
+
+/* Loading */
+
+static bool rna_VolumeGrids_is_loaded_get(PointerRNA *ptr)
+{
+ Volume *volume = (Volume *)ptr->data;
+ return BKE_volume_is_loaded(volume);
+}
+
+/* Error Message */
+
+static void rna_VolumeGrids_error_message_get(PointerRNA *ptr, char *value)
+{
+ Volume *volume = (Volume *)ptr->data;
+ strcpy(value, BKE_volume_grids_error_msg(volume));
+}
+
+static int rna_VolumeGrids_error_message_length(PointerRNA *ptr)
+{
+ Volume *volume = (Volume *)ptr->data;
+ return strlen(BKE_volume_grids_error_msg(volume));
+}
+
+#else
+
+static void rna_def_volume_grid(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "VolumeGrid", NULL);
+ RNA_def_struct_ui_text(srna, "Volume Grid", "3D volume grid");
+ RNA_def_struct_ui_icon(srna, ICON_VOLUME_DATA);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL);
+ RNA_def_property_ui_text(prop, "Name", "Volume grid name");
+
+ static const EnumPropertyItem data_type_items[] = {
+ {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"},
+ {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"},
+ {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"},
+ {VOLUME_GRID_INT, "INT", 0, "Integer", "32 bit integer"},
+ {VOLUME_GRID_INT64, "INT64", 0, "Integer 64 bit", "64 bit integer"},
+ {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"},
+ {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"},
+ {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"},
+ {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"},
+ {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"},
+ {VOLUME_GRID_POINTS,
+ "POINTS",
+ 0,
+ "Points (Unsupported)",
+ "Points grid, currently unsupported by volume objects"},
+ {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_enum_funcs(prop, "rna_VolumeGrid_data_type_get", NULL, NULL);
+ RNA_def_property_enum_items(prop, data_type_items);
+ RNA_def_property_ui_text(prop, "Data Type", "Data type of voxel values");
+
+ prop = RNA_def_property(srna, "channels", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_int_funcs(prop, "rna_VolumeGrid_channels_get", NULL, NULL);
+ RNA_def_property_ui_text(prop, "Channels", "Number of dimensions of the grid data type");
+
+ prop = RNA_def_property(srna, "matrix_object", PROP_FLOAT, PROP_MATRIX);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
+ RNA_def_property_float_funcs(prop, "rna_VolumeGrid_matrix_object_get", NULL, NULL);
+ RNA_def_property_ui_text(
+ prop, "Matrix Object", "Transformation matrix from voxel index to object space");
+
+ prop = RNA_def_property(srna, "is_loaded", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_VolumeGrid_is_loaded_get", NULL);
+ RNA_def_property_ui_text(prop, "Is Loaded", "Grid tree is loaded in memory");
+
+ /* API */
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ func = RNA_def_function(srna, "load", "rna_VolumeGrid_load");
+ RNA_def_function_ui_description(func, "Load grid tree from file");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+ parm = RNA_def_boolean(func, "success", 0, "", "True if grid tree was successfully loaded");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "unload", "rna_VolumeGrid_unload");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+ RNA_def_function_ui_description(
+ func, "Unload grid tree and voxel data from memory, leaving only metadata");
+}
+
+static void rna_def_volume_grids(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ RNA_def_property_srna(cprop, "VolumeGrids");
+ srna = RNA_def_struct(brna, "VolumeGrids", NULL);
+ RNA_def_struct_sdna(srna, "Volume");
+ RNA_def_struct_ui_text(srna, "Volume Grids", "3D volume grids");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_funcs(prop,
+ "rna_VolumeGrids_active_index_get",
+ "rna_VolumeGrids_active_index_set",
+ "rna_VolumeGrids_active_index_range");
+ RNA_def_property_ui_text(prop, "Active Grid Index", "Index of active volume grid");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+
+ prop = RNA_def_property(srna, "error_message", PROP_STRING, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_string_funcs(
+ prop, "rna_VolumeGrids_error_message_get", "rna_VolumeGrids_error_message_length", NULL);
+ RNA_def_property_ui_text(
+ prop, "Error Message", "If loading grids failed, error message with details");
+
+ prop = RNA_def_property(srna, "is_loaded", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_boolean_funcs(prop, "rna_VolumeGrids_is_loaded_get", NULL);
+ RNA_def_property_ui_text(prop, "Is Loaded", "List of grids and metadata are loaded in memory");
+
+ prop = RNA_def_property(srna, "frame", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "runtime.frame");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop,
+ "Frame",
+ "Frame number that volume grids will be loaded at, based on scene time "
+ "and volume parameters");
+
+ /* API */
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ func = RNA_def_function(srna, "load", "BKE_volume_load");
+ RNA_def_function_ui_description(func, "Load list of grids and metadata from file");
+ RNA_def_function_flag(func, FUNC_USE_MAIN);
+ parm = RNA_def_boolean(func, "success", 0, "", "True if grid list was successfully loaded");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "unload", "BKE_volume_unload");
+ RNA_def_function_ui_description(func, "Unload all grid and voxel data from memory");
+}
+
+static void rna_def_volume_display(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "VolumeDisplay", NULL);
+ RNA_def_struct_ui_text(srna, "Volume Display", "Volume object display settings for 3d viewport");
+ RNA_def_struct_sdna(srna, "VolumeDisplay");
+
+ prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 0.00001, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.1, 100.0, 1, 3);
+ RNA_def_property_ui_text(prop, "Density", "Thickness of volume drawing in the viewport");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+
+ static const EnumPropertyItem wireframe_type_items[] = {
+ {VOLUME_WIREFRAME_NONE, "NONE", 0, "None", "Don't display volume in wireframe mode"},
+ {VOLUME_WIREFRAME_BOUNDS,
+ "BOUNDS",
+ 0,
+ "Bounds",
+ "Display single bounding box for the entire grid"},
+ {VOLUME_WIREFRAME_BOXES,
+ "BOXES",
+ 0,
+ "Boxes",
+ "Display bounding boxes for nodes in the volume tree"},
+ {VOLUME_WIREFRAME_POINTS,
+ "POINTS",
+ 0,
+ "Points",
+ "Display points for nodes in the volume tree"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem wireframe_detail_items[] = {
+ {VOLUME_WIREFRAME_COARSE,
+ "COARSE",
+ 0,
+ "Coarse",
+ "Display one box or point for each intermediate tree node"},
+ {VOLUME_WIREFRAME_FINE,
+ "FINE",
+ 0,
+ "Fine",
+ "Display box for each leaf node containing 8x8 voxels"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "wireframe_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, wireframe_type_items);
+ RNA_def_property_ui_text(prop, "Wireframe", "Type of wireframe display");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+
+ prop = RNA_def_property(srna, "wireframe_detail", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, wireframe_detail_items);
+ RNA_def_property_ui_text(prop, "Wireframe Detail", "Amount of detail for wireframe display");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+}
+
+static void rna_def_volume_render(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "VolumeRender", NULL);
+ RNA_def_struct_ui_text(srna, "Volume Render", "Volume object render settings");
+ RNA_def_struct_sdna(srna, "VolumeRender");
+
+ static const EnumPropertyItem space_items[] = {
+ {VOLUME_SPACE_OBJECT,
+ "OBJECT",
+ 0,
+ "Object",
+ "Keep volume opacity and detail the same regardless of object scale"},
+ {VOLUME_SPACE_WORLD,
+ "WORLD",
+ 0,
+ "World",
+ "Specify volume step size and density in world space"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, space_items);
+ RNA_def_property_ui_text(
+ prop, "Space", "Specify volume density and step size in object or world space");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+
+ prop = RNA_def_property(srna, "step_size", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 0.00001, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.001, 100.0, 1, 3);
+ RNA_def_property_ui_text(prop,
+ "Step Size",
+ "Distance between volume samples. Higher values render more detail at "
+ "the cost of performance. If set to zero, the step size is "
+ "automatically determined based on voxel size");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+
+ prop = RNA_def_property(srna, "clipping", PROP_FLOAT, PROP_NONE);
+ RNA_def_property_float_sdna(prop, NULL, "clipping");
+ RNA_def_property_range(prop, 0.0, 1.0);
+ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 3);
+ RNA_def_property_ui_text(
+ prop,
+ "Clipping",
+ "Value under which voxels are considered empty space to optimize rendering");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_display");
+}
+
+static void rna_def_volume(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Volume", "ID");
+ RNA_def_struct_ui_text(srna, "Volume", "Volume data-block for 3D volume grids");
+ RNA_def_struct_ui_icon(srna, ICON_VOLUME_DATA);
+
+ /* File */
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "File Path", "Volume sample file used by this Volume data-block");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_filepath");
+
+ prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "packedfile");
+ RNA_def_property_ui_text(prop, "Packed File", "");
+
+ /* Sequence */
+ prop = RNA_def_property(srna, "is_sequence", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(
+ prop, "Sequence", "Whether the cache is separated in a series of files");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_is_sequence");
+
+ prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, MINAFRAMEF, MAXFRAMEF);
+ RNA_def_property_ui_text(
+ prop, "Start Frame", "Global starting frame of the sequence, assuming first has a #1");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_filepath");
+
+ prop = RNA_def_property(srna, "frame_duration", PROP_INT, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_range(prop, 0, MAXFRAMEF);
+ RNA_def_property_ui_text(prop, "Frames", "Number of frames of the sequence to use");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_filepath");
+
+ prop = RNA_def_property(srna, "frame_offset", PROP_INT, PROP_NONE);
+ RNA_def_property_ui_text(
+ prop, "Offset", "Offset the number of the frame to use in the animation");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_filepath");
+
+ static const EnumPropertyItem sequence_mode_items[] = {
+ {VOLUME_SEQUENCE_CLIP, "CLIP", 0, "Clip", "Hide frames outside the specified frame range"},
+ {VOLUME_SEQUENCE_EXTEND,
+ "EXTEND",
+ 0,
+ "Extend",
+ "Repeat the start frame before, and the end frame after the frame range"},
+ {VOLUME_SEQUENCE_REPEAT, "REPEAT", 0, "Repeat", "Cycle the frames in the sequence"},
+ {VOLUME_SEQUENCE_PING_PONG,
+ "PING_PONG",
+ 0,
+ "Ping-Pong",
+ "Repeat the frames, reversing the playback direction every other cycle"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "sequence_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_enum_items(prop, sequence_mode_items);
+ RNA_def_property_ui_text(prop, "Sequence Mode", "Sequence playback mode");
+ RNA_def_property_update(prop, 0, "rna_Volume_update_filepath");
+
+ /* Grids */
+ prop = RNA_def_property(srna, "grids", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "VolumeGrid");
+ RNA_def_property_ui_text(prop, "Grids", "3D volume grids");
+ RNA_def_property_collection_funcs(prop,
+ "rna_Volume_grids_begin",
+ "rna_Volume_grids_next",
+ "rna_Volume_grids_end",
+ "rna_Volume_grids_get",
+ "rna_Volume_grids_length",
+ NULL,
+ NULL,
+ NULL);
+ rna_def_volume_grids(brna, prop);
+
+ /* Materials */
+ prop = RNA_def_property(srna, "materials", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "mat", "totcol");
+ RNA_def_property_struct_type(prop, "Material");
+ RNA_def_property_ui_text(prop, "Materials", "");
+ RNA_def_property_srna(prop, "IDMaterials"); /* see rna_ID.c */
+ RNA_def_property_collection_funcs(
+ prop, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "rna_IDMaterials_assign_int");
+
+ /* Display */
+ prop = RNA_def_property(srna, "display", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "display");
+ RNA_def_property_struct_type(prop, "VolumeDisplay");
+ RNA_def_property_ui_text(prop, "Display", "Volume display settings for 3d viewport");
+
+ /* Render */
+ prop = RNA_def_property(srna, "render", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "render");
+ RNA_def_property_struct_type(prop, "VolumeRender");
+ RNA_def_property_ui_text(prop, "Render", "Volume render settings for 3d viewport");
+
+ /* Common */
+ rna_def_animdata_common(srna);
+}
+
+void RNA_def_volume(BlenderRNA *brna)
+{
+ rna_def_volume_grid(brna);
+ rna_def_volume_display(brna);
+ rna_def_volume_render(brna);
+ rna_def_volume(brna);
+}
+
+#endif
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index 676ce655a42..e11d636ffc9 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -161,6 +161,12 @@ void WM_operator_properties_filesel(wmOperatorType *ot,
prop = RNA_def_boolean(
ot->srna, "filter_usd", (filter & FILE_TYPE_USD) != 0, "Filter USD files", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna,
+ "filter_volume",
+ (filter & FILE_TYPE_VOLUME) != 0,
+ "Filter OpenVDB volume files",
+ "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_boolean(
ot->srna, "filter_folder", (filter & FILE_TYPE_FOLDER) != 0, "Filter folders", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
diff --git a/source/creator/creator.c b/source/creator/creator.c
index 6c2f78efdb4..76b632ba060 100644
--- a/source/creator/creator.c
+++ b/source/creator/creator.c
@@ -64,6 +64,7 @@
#include "BKE_sound.h"
#include "BKE_image.h"
#include "BKE_particle.h"
+#include "BKE_volume.h"
#include "DEG_depsgraph.h"
@@ -374,6 +375,7 @@ int main(int argc,
BKE_modifier_init();
BKE_gpencil_modifier_init();
BKE_shaderfx_init();
+ BKE_volumes_init();
DEG_register_node_types();
BKE_brush_system_init();