Objects: add Volume object type, and prototypes for Hair and PointCloud
authorBrecht Van Lommel <brechtvanlommel@gmail.com>
Tue, 17 Mar 2020 13:41:48 +0000 (14:41 +0100)
committerBrecht Van Lommel <brechtvanlommel@gmail.com>
Wed, 18 Mar 2020 10:23:05 +0000 (11:23 +0100)
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

118 files changed:
CMakeLists.txt
release/scripts/startup/bl_ui/__init__.py
release/scripts/startup/bl_ui/properties_data_hair.py [new file with mode: 0644]
release/scripts/startup/bl_ui/properties_data_pointcloud.py [new file with mode: 0644]
release/scripts/startup/bl_ui/properties_data_volume.py [new file with mode: 0644]
release/scripts/startup/bl_ui/properties_object.py
release/scripts/startup/bl_ui/space_dopesheet.py
release/scripts/startup/bl_ui/space_filebrowser.py
release/scripts/startup/bl_ui/space_node.py
release/scripts/startup/bl_ui/space_outliner.py
release/scripts/startup/bl_ui/space_userpref.py
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_hair.h [new file with mode: 0644]
source/blender/blenkernel/BKE_idtype.h
source/blender/blenkernel/BKE_main.h
source/blender/blenkernel/BKE_packedFile.h
source/blender/blenkernel/BKE_pointcloud.h [new file with mode: 0644]
source/blender/blenkernel/BKE_volume.h [new file with mode: 0644]
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/bpath.c
source/blender/blenkernel/intern/customdata.c
source/blender/blenkernel/intern/effect.c
source/blender/blenkernel/intern/hair.c [new file with mode: 0644]
source/blender/blenkernel/intern/idcode.c
source/blender/blenkernel/intern/idtype.c
source/blender/blenkernel/intern/lib_id.c
source/blender/blenkernel/intern/lib_query.c
source/blender/blenkernel/intern/lib_remap.c
source/blender/blenkernel/intern/main.c
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_update.c
source/blender/blenkernel/intern/packedFile.c
source/blender/blenkernel/intern/pointcloud.c [new file with mode: 0644]
source/blender/blenkernel/intern/volume.cc [new file with mode: 0644]
source/blender/blenloader/intern/readblenentry.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/readfile.h
source/blender/blenloader/intern/writefile.c
source/blender/blentranslation/BLT_translation.h
source/blender/depsgraph/CMakeLists.txt
source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
source/blender/depsgraph/intern/builder/deg_builder_relations.cc
source/blender/depsgraph/intern/depsgraph_tag.cc
source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc
source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h
source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_object.cc
source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.cc [new file with mode: 0644]
source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_volume.h [new file with mode: 0644]
source/blender/draw/engines/overlay/overlay_pointcloud.c [new file with mode: 0644]
source/blender/draw/engines/overlay/shaders/pointcloud_frag.glsl [new file with mode: 0644]
source/blender/draw/engines/overlay/shaders/pointcloud_vert.glsl [new file with mode: 0644]
source/blender/draw/intern/draw_cache_impl_hair.c [new file with mode: 0644]
source/blender/draw/intern/draw_cache_impl_pointcloud.c [new file with mode: 0644]
source/blender/draw/intern/draw_cache_impl_volume.c [new file with mode: 0644]
source/blender/editors/animation/anim_channels_defines.c
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_filter.c
source/blender/editors/include/ED_anim_api.h
source/blender/editors/include/UI_icons.h
source/blender/editors/interface/interface_icons.c
source/blender/editors/interface/interface_templates.c
source/blender/editors/object/CMakeLists.txt
source/blender/editors/object/object_add.c
source/blender/editors/object/object_intern.h
source/blender/editors/object/object_modifier.c
source/blender/editors/object/object_ops.c
source/blender/editors/object/object_relations.c
source/blender/editors/object/object_volume.c [new file with mode: 0644]
source/blender/editors/render/render_opengl.c
source/blender/editors/space_buttons/CMakeLists.txt
source/blender/editors/space_buttons/buttons_context.c
source/blender/editors/space_file/filelist.c
source/blender/editors/space_file/filesel.c
source/blender/editors/space_info/info_stats.c
source/blender/editors/space_nla/nla_buttons.c
source/blender/editors/space_nla/nla_channels.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_outliner/outliner_draw.c
source/blender/editors/space_outliner/outliner_intern.h
source/blender/editors/space_outliner/outliner_tools.c
source/blender/editors/space_outliner/outliner_tree.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_buttons.c
source/blender/makesdna/DNA_ID.h
source/blender/makesdna/DNA_action_types.h
source/blender/makesdna/DNA_customdata_types.h
source/blender/makesdna/DNA_defaults.h
source/blender/makesdna/DNA_hair_defaults.h [new file with mode: 0644]
source/blender/makesdna/DNA_hair_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_object_types.h
source/blender/makesdna/DNA_pointcloud_defaults.h [new file with mode: 0644]
source/blender/makesdna/DNA_pointcloud_types.h [new file with mode: 0644]
source/blender/makesdna/DNA_space_types.h
source/blender/makesdna/DNA_userdef_types.h
source/blender/makesdna/DNA_volume_defaults.h [new file with mode: 0644]
source/blender/makesdna/DNA_volume_types.h [new file with mode: 0644]
source/blender/makesdna/intern/CMakeLists.txt
source/blender/makesdna/intern/dna_defaults.c
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_types.h
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/makesrna.c
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_action.c
source/blender/makesrna/intern/rna_hair.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_internal.h
source/blender/makesrna/intern/rna_main.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_object.c
source/blender/makesrna/intern/rna_pointcloud.c [new file with mode: 0644]
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_volume.c [new file with mode: 0644]
source/blender/windowmanager/intern/wm_operator_props.c
source/creator/creator.c

index 9f4d0c208860b5d58617da87b1b0efa6bb5dceb1..f71646f4aba3f428f36e647960ef0a9c55d4db63 100644 (file)
@@ -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)
index c2bcb7d5ea584ebf43746ad0a454cb9681a27d21..7d3ecceca41ba0492f0ff0c67397d22ae0398f67 100644 (file)
@@ -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 (file)
index 0000000..6017765
--- /dev/null
@@ -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 (file)
index 0000000..10ebdea
--- /dev/null
@@ -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 (file)
index 0000000..29e28aa
--- /dev/null
@@ -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)
index 33ba981e235836cc4491087d0ed6d2bf1da27418..91bd055741c407f277ea63ad2ad4111c3941d4b6 100644 (file)
@@ -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')
index 76ba4dfb78e53f4d02c22f5bc961f098ae8d7b76..3f8c41e4f21958c59c5b0491b982317536c87e01 100644 (file)
@@ -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")
index 95046678b2701bc9e74e66b06a5838ccb92b4189..3bf5bbf7b465af60c88409f5ea405235da1ed556 100644 (file)
@@ -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()
 
index 8fe758b8bf61bebe353f6c561a6e61afc7a5b6f7..bdda0ebbe9a8ac146d6d9c80b58e9b17dd8111b7 100644 (file)
@@ -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
index 6ac31aeb3d03f5e8579f35de04a49dda01e11e97..cc773300d9ef64e834bd241b8f532cd9f2588d47 100644 (file)
@@ -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
index 2ca0e73bd7410fce4853b413890cf3fe5fdd7478..4cccf429179b449adf0d4cef5e42364572c37109 100644 (file)
@@ -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):
index f8b291c0c5fcb62c37479fbcd64adde9cef03267..a2bfd711c04361afa064360dfc390fca551440b9 100644 (file)
@@ -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 (file)
index 0000000..0cb2658
--- /dev/null
@@ -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
index e6e82900f6d99e9828f7a44afc12be22c5cfdc11..60eee2035f55d25b0f8b1ebc526a42aa022be9ff 100644 (file)
@@ -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. ********** */
 
index 8aac09d873817c2f338245caa55635f30e0cc586..306d889fba4f87727f7aff78bf4bd545474d0e10 100644 (file)
@@ -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) \
index 2b491a1919e8b1fb5e94383d984f5240270a7f8f..16d9b5b2c8bdfd80f3a1f16900396ab39c3cdb74 100644 (file)
@@ -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 (file)
index 0000000..972795e
--- /dev/null
@@ -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 (file)
index 0000000..b984539
--- /dev/null
@@ -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
index 047901b4c8132fd01d69a8a250875415a9f0f77f..d3dfa422ebdb88929e6c97c559dc45d397f5f6d6 100644 (file)
@@ -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)
index 2f4d58a1992cf529f2f0ae20b45bdd4b3cba27d6..ada104a7b5a771950a14bda1efb87e157a88a474 100644 (file)
@@ -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
index ef049afc0f3c7beab0469b4e45cc0d89dddeeec1..7e3e629d328c6dac2a5d75c766f678eecce15459 100644 (file)
@@ -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);
index 33707d3f18d8fd573390767b0c236b3375ded51c..117c96e29326345933a6646d0cd4f1f3f5476f52 100644 (file)
@@ -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 = {
index b12201df8099f95b3117df80bbf1a570ce23d681..2cb9167889336f77271636df71e64cf0e28dccc5 100644 (file)
@@ -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 (file)
index 0000000..1553fb4
--- /dev/null
@@ -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);
+  }
+}
index b9ca77ceb675a023d3e2764260ed998d6c8c92ed..a6930a521af6b5beecba739caa156da083f57380 100644 (file)
@@ -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);
index ff4d06cd01119d69c93da503211ce0ffb800247b..ce2835717a0fea3b134b32a05f30a3d30e736fef 100644 (file)
@@ -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
 }
index 1ae53f5a85d4203b831ef47ce2db33bf783b933b..9c4bef04adfb0c82c25a4597566d93dc2dfab893 100644 (file)
@@ -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"
 #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"
 #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));
index c204c272de12f35c45a8b115f4dc88f482e12cb0..67065a1d71583576d9b9666f36775242da56efc3 100644 (file)
@@ -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:
index 1a22a6e6f792fc126a0fab88e88008c8e1c5157f..578d4be44d35088786847b4102348decbce667aa 100644 (file)
@@ -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);
index 659c3944edb4849a39eeebf66b57b8cda3da2e98..caa29f7817aba52942e1b7df6e21cd705f31f2ca 100644 (file)
@@ -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);
index 368eb0995799a560af41ea29661781a0ea5df62b..15f18eef7c8b22af92f9dbb5789c3da91014c202 100644 (file)
 #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;
index fa3284c18d64b675f152c7c23cb92282bbda9715..4e8ae3d57d50d5ea1e49d58af40037342a0e8f06 100644 (file)
 #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];
 
index 20b2ab9409f6df2d2305e510f3b73fa48539f0b7..66c3b2ea26e0f2276b84ffceeb00c6d05da59f32 100644 (file)
@@ -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"
 #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;
   }
 }
 
index d69527e86260cb617473d957e766ddf225f7c5ce..34e86b29540df382a6e6f82a1ebce6e0b5b28bf0 100644 (file)
@@ -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 (file)
index 0000000..cc68e93
--- /dev/null
@@ -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 (file)
index 0000000..6fb44e9
--- /dev/null
@@ -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
index 085e500f7e52fe3939efb0b6090831367b172084..c12df2bc87d01a50bf73f145fdcb1680b8c69d94 100644 (file)
@@ -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);
index f1f274f97d5f7d8e615b6acbeb14ccc8885f9e53..7e4d676954a2a62e8fe4f4da6d83964cdea716ee 100644 (file)
@@ -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"
 #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"
 #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 */
@@ -8961,6 +9021,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;
           }
index 4965845d1671a5e80d30d5a3a2fc35127e548338..fce6a1d8ff76916bc9b337b61430cabd290a1eca 100644 (file)
@@ -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);
index 837134c0156f29b0755cdee867ec890ff98f24bd..c948949c06ebb2e40850e3aae1a6b79c67318eb0 100644 (file)
 #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"
 #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"
 #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);
index 55403cc35d39dc318db34d05b6610751f6c8182d..74c46b1cf3e6ce7128eb7d2a403d5c902d89d67d 100644 (file)
@@ -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"), \
index 169e7f0074402f26cc967abde0bd52375ee6d8dd..355d2536e1a803ad83dfbf17c8af9febc2eba528 100644 (file)
@@ -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
index 442b4aa406a57eca56a7f4ec597d7a3981ec94e2..ea49882fd77fdf9a3b9d25735bc5f63ef4f3df23 100644 (file)
@@ -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;
index 27d6db6a58f372b56e036a8fb5fd467ee2531b94..4647486bafd1ec8648185084548ffa220a568e5d 100644 (file)
@@ -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;
     }
index 8decb9f6b877b17fdef62f0a0157035b028fc419..a14f22350a4c2e7ac58f910bc6b5afaa5d270317 100644 (file)
@@ -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;
index 40a17666880dadaa59241042f2c1d51964a84a72..108f5f04879441418af5dc5be46021f992952001 100644 (file)
@@ -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;
   }
index cc8c6ae0d5b7e9f4b038c869302d2665eca250bf..c818c1f7064c4cfec465b5ce0e1d2f24425f59ae 100644 (file)
@@ -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
index 855dd4821ce6cb0deccfa459afa4c623a127d146..2f45ea45197e7773ab5123d85eedc75ea14d9965 100644 (file)
@@ -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 (file)
index 0000000..09e13ec
--- /dev/null
@@ -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 (file)
index 0000000..cf57c70
--- /dev/null
@@ -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 (file)
index 0000000..a0de7aa
--- /dev/null
@@ -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 (file)
index 0000000..36928d0
--- /dev/null
@@ -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 (file)
index 0000000..d71ccee
--- /dev/null
@@ -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 (file)
index 0000000..007f625
--- /dev/null
@@ -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 (file)
index 0000000..83757cb
--- /dev/null
@@ -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 (file)
index 0000000..cdac8b3
--- /dev/null
@@ -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);
+}
index df114bab367d2468877474de3a753428f3b9b6b0..07743f3b4bd118fad153017b9e3872b06d0fd751 100644 (file)
@@ -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 */
 
index 8b5e22db61ab311907c5267f8c0cb229fb1df769..791545f216a19e22f1d820b595853eeb63723f8a 100644 (file)
@@ -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 */
index 8dfc9cd8d1a5fece27ba6888ff5bd0a2bb7b3819..0895bd4f8a26073f58ffd208d9176c7cad531be2 100644 (file)
@@ -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"
 #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 */
index 2e8102971381dbcb3e14c65d1996c3ae0e3601b7..7f4c3470020c1297cbc8d63e2eaef2e8b84e7a53 100644 (file)
@@ -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)))
index 6739f7cb12cbbca4a6c9af4c8623e606fadca4a0..6fdef4a06e0f8d0499fa069076c93764ad9e44e4 100644 (file)
@@ -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)
index a37b49f5b6ec7aece2dac343fc28028c2ba0a472..ed4131440d5fcf2e80a036f270a6d4d82228e287 100644 (file)
@@ -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:
index f11e3a1f5f0192db5000ae23dc8ee0fb44791145..cb55c639d91d28e5f5e6f0638c35833dbc58b27b 100644 (file)
@@ -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,
index 69abe475fed25535e4408e0b56716622cf41fe13..a5b6fa55aa9f3bb736e336f7a5a792db1701513c 100644 (file)
@@ -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}")
index 17b6bfdb9563833577d8f498e80641e302ad4b08..832854ec4cc1fa824636de1d2e767be013a123f4 100644 (file)
@@ -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"
 #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)
index 352ba744d929f321b850878833b8bde603cd6de6..c1cb0d6cef0eaa8e37bc140365e9ba84c4c842d7 100644 (file)
@@ -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);
index 31c4f96693c2557928dd66fb263353ffbc5defca..7c74213608c65a89e1e80633defb2da367b110e9 100644 (file)
@@ -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"
 #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;
   }
index f6b08b953a4343424373618a98d3157c3b417b1f..52273b887dd82f367212239bcb77202f50f51bff 100644 (file)
@@ -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);
index 29bf9e888533fc2abe1e8b7a7f19b72b2a53da19..836e3bf7a44e8e37b431eff06acc997ba969f02c 100644 (file)
@@ -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"
 #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 (file)
index 0000000..64482a0
--- /dev/null
@@ -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);
+}
index 4fa1903758fb97f18fcae7749f2a0d3aad0cd894..c48d5917ec2ea62f3bb0b7c754d5327df741846d 100644 (file)
@@ -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: */
index 25ff6bbd098155d85bd6a712d65201602192e2be..c3b7d65689f1f931d064f3ef6cdbb2acde45b319 100644 (file)
@@ -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}")
index 035239cc7d08cbf398bf2d899a4159c5b02a641c..21dfb3df7717eed561beda2a97767f067ec878c1 100644 (file)
@@ -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;
index 188f3417ddc24c7bf91ee6800a60b1ebbf36eca3..1fc0866bd9fea195bec948a15adca7b96ab80418 100644 (file)
@@ -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 {
index d07db12eeacb92213e5496b64152ed325d9fd457..6c0f79f0f5c72db406b0d4aa71b1bf295adb04e5 100644 (file)
@@ -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(
index d00083d343b2648beef673a265f7391c764715af..dd74e1a8ccc8849e4752507ed32dda09f11fe846 100644 (file)
@@ -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;
+    }
   }
 }
 
index 4277e5792747ea53c15cb3a33f6e9e9c3112cf3a..315fa1d12aa6aca2163e0114be379218e0440d32 100644 (file)
@@ -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;
index cf3e889b26c616172e07b9dc182c99a17babebe6..b97267dc2a8934e3e5840fe0ec4064a6f4a6f3db 100644 (file)
@@ -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 */
index f679bcc4e158836d41bee716bc1aabcca10b922f..677a7cac7458d00cf4f56a529ccbce18a9be04d0 100644 (file)
@@ -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);
   }
index 1b34f85f8005605c4fdb68d43a404becf9cbe6f7..83ddbe63e9cfbfe28a45cbd65efe64f4c6a84726 100644 (file)
@@ -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;
index 1d39dc156b21b7d87edae2ae37fca62defd8dc75..fb40ae195ef1e6d51277070d72294ca0e43ad630 100644 (file)
@@ -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, \
index a99e1b631196d901cce54ac3df3d0578060304da..5bb0d626c2fb6fa33c8f6fb578833f74a88de4aa 100644 (file)
 #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);
   }
index b8cbb6bb0e3244ad5d17baf95ab9146fda2255b1..b942021ca33ca38971cf01d154607297eec905ca 100644 (file)
@@ -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;
index f16e19c598eb8ebfd145322f4d160a13240ca08a..0f5607bc8cd582ef79b9cec2525b5be5324377c3 100644 (file)
@@ -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,
index 9b02ea7238cee1ac2b7c5cff6397f4bb3b34f03e..54f00b6789874a222194babe538259e98eee00d2 100644 (file)
@@ -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);
     }
index 063ea04bdbab74cf3aab3bfa21bc462722903a8d..1e894d44f874cbcb1e8d197597e96f310d3b368e 100644 (file)
@@ -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,
index 5b9340ab3d90ae494836cc9b0b9f28ea39764100..be9097cf5bda556b322cb389b586eb0b986a2cdf 100644 (file)
@@ -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 */
index 4d0d66f29fff68ac80c41968ba280a539b3bac61..5ee16c2631d97c537b77b95c3d84a9fb01342e4c 100644 (file)
@@ -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;
index c20ccc056373fff3d6327b7775118c4714e5a91f..9b4a05034df5dde379b3c241884c415babd37c4e 100644 (file)
 
 #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 (file)
index 0000000..de7a830
--- /dev/null
@@ -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 (file)
index 0000000..c7f145d
--- /dev/null
@@ -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__ */
index fca74c299097b88f003fb61c20a17a5cd7e6b476..645611144e922d5c7952bdee7117fea0956b51fa 100644 (file)
@@ -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 (file)
index 0000000..89df2d3
--- /dev/null
@@ -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 (file)
index 0000000..5d6a11b
--- /dev/null
@@ -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__ */
index 548212a2b0dbd67b359d779179ac638ec5b1afe2..f12ed0553ce7ebb7dbb23f315b3c9d7dff9edcbf 100644 (file)
@@ -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),
index 904d7b8a52ed128a4d9e1d6c7edd348988b7a229..4a3c5695827a4e31a5278f4274dd20d1149fa9ff 100644 (file)
@@ -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 (file)
index 0000000..3a03738
--- /dev/null
@@ -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 (file)
index 0000000..af419ea
--- /dev/null
@@ -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__ */
index 799ec931d57409453cb1c2ab3d43f15d83d54f63..f8f765c6ece098c5ba2af910101a721fb736ba3f 100644 (file)
@@ -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
 )
 
index 260f1cd20f62416d900ffde9700009639e332c01..a8be435b902b1bd94e2ebaf52146b42f5ab3ece1 100644 (file)
@@ -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"
 #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"
 #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),
 };
index a33f13be09aa0eddb13e0ec204cea2fd23f9093e..3d82df79e4c8c70c6ccdb768b6a3591a505b7f7c 100644 (file)
@@ -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 */
 
index 1e07da23429b1f5ff57920afe7d5ded97014cd9b..31d1ed54fa1dfdf7484947f713aba112d470ed4f 100644 (file)
@@ -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;
index 66a5df001deec7ca3ffed4e999e77edaf87dae24..abbe284e97a446707d20bdafdf4b6446ff6d39d5 100644 (file)
@@ -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;
index 30989a62603ef7ae9625a5c9b5c729885d36f46a..64b7a3e70c1dab5a0afcded62364b243480f5d01 100644 (file)
@@ -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(
   .
index 7ec8f6167d0541ad8c9a0ff0fe2437858404c6b1..3eaee4a1ef4257d79b41f75a27e38c47340a877b 100644 (file)
@@ -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},
index bd5c7e755d6ec6a7ed964ac97333495f4bf72c95..9a660153a3b69c30d20f341dbbe9bcd9aaaacf83 100644 (file)
@@ -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:
index c85a94d9fc2d48f5becc7201f7981c737db3c4ae..a9dfa8b529ea649f613101c55c7f256f9b14facb 100644 (file)
@@ -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 (file)
index 0000000..13f3132
--- /dev/null
@@ -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&nb