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