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

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