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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2020-03-17 16:41:48 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2020-03-18 13:23:05 +0300
commitb0a1cf2c9ae696b07f7a236bc855a5ab4a493dcb (patch)
tree92295af11db5e984da42bfac7ca60190b8549a3f /source/blender/blenkernel
parent8dcfd392e4e62f193b666304425bc5ae635ecffe (diff)
Objects: add Volume object type, and prototypes for Hair and PointCloud
Only the volume object is exposed in the user interface. It is based on OpenVDB internally. Drawing and rendering code will follow in another commit. https://wiki.blender.org/wiki/Source/Objects/Volume https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Volumes Hair and PointCloud object types are hidden behind a WITH_NEW_OBJECT_TYPES build option. These are unfinished, and included only to make it easier to cooperate on development in the future and avoid tricky merges. https://wiki.blender.org/wiki/Source/Objects/New_Object_Types Ref T73201, T68981 Differential Revision: https://developer.blender.org/D6945
Diffstat (limited to 'source/blender/blenkernel')
-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
24 files changed, 2504 insertions, 55 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