diff options
Diffstat (limited to 'source/blender/blenkernel')
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 |