diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index a8184b1d84b..f6ba5e22bcf 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -717,6 +717,9 @@ geometry_node_categories = [ NodeItem("GeometryNodeVolumeCube"), NodeItem("GeometryNodeVolumeToMesh"), ]), + GeometryNodeCategory("GEO_PARTICLES", "Particles", items=[ + NodeItem("ParticleNodeSetShape"), + ]), GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items), GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[ NodeItem("NodeFrame"), diff --git a/source/blender/blenkernel/BKE_geometry_cache.h b/source/blender/blenkernel/BKE_geometry_cache.h new file mode 100644 index 00000000000..9ee6826c058 --- /dev/null +++ b/source/blender/blenkernel/BKE_geometry_cache.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup bke + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct GeometryCache *BKE_geometry_cache_new(); +void BKE_geometry_cache_free(struct GeometryCache *cache); + +void BKE_geometry_cache_insert_and_continue_from(struct GeometryCache *cache, + int cfra, + const struct GeometrySet *geometry_set); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_geometry_cache.hh b/source/blender/blenkernel/BKE_geometry_cache.hh new file mode 100644 index 00000000000..daee2b76664 --- /dev/null +++ b/source/blender/blenkernel/BKE_geometry_cache.hh @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +/** \file + * \ingroup bke + */ + +struct GeometrySet; + +struct GeometryCache { + public: + using Timestamp = int; + + static const int TimestampInvalid = INT_MIN; + + GeometryCache(); + ~GeometryCache(); + + GeometryCache(GeometryCache &other) = default; + GeometryCache(GeometryCache &&other) = default; + + GeometryCache &operator=(GeometryCache &other) = default; + GeometryCache &operator=(GeometryCache &&other) = default; + + void clear(); + void insert_and_continue_from(Timestamp timestamp, const GeometrySet &geometry_set); + GeometrySet *get_exact(Timestamp timestamp) const; + GeometrySet *get_before(Timestamp timestamp) const; + + private: + struct KeyValuePair { + Timestamp timestamp = TimestampInvalid; + GeometrySet *geometry_set = nullptr; + + bool is_valid() const + { + return timestamp != TimestampInvalid; + } + + operator bool() const + { + return is_valid(); + } + }; + /* Placeholder, just two frames cached for now */ + KeyValuePair first_ = {TimestampInvalid, nullptr}; + KeyValuePair last_ = {TimestampInvalid, nullptr}; +}; diff --git a/source/blender/blenkernel/BKE_modifier.h b/source/blender/blenkernel/BKE_modifier.h index 866b0353d07..bced0228ae5 100644 --- a/source/blender/blenkernel/BKE_modifier.h +++ b/source/blender/blenkernel/BKE_modifier.h @@ -101,6 +101,9 @@ typedef enum { /** Accepts #BMesh input (without conversion). */ eModifierTypeFlag_AcceptsBMesh = (1 << 11), + + /** Modifier output needs to be cached. */ + eModifierTypeFlag_NeedCaching = (1 << 12), } ModifierTypeFlag; ENUM_OPERATORS(ModifierTypeFlag, eModifierTypeFlag_AcceptsBMesh) diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 59a1599e7ae..da9e5638795 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1525,6 +1525,14 @@ struct TexResult; /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Particle Nodes + * \{ */ + +#define PARTICLE_NODE_SET_SHAPE 1300 + +/** \} */ + void BKE_node_system_init(void); void BKE_node_system_exit(void); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index faf878dfc2a..1314647f809 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -433,7 +433,10 @@ void BKE_object_eval_uber_transform(struct Depsgraph *depsgraph, struct Object * void BKE_object_eval_uber_data(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); -/** +void BKE_object_write_geometry_cache(struct Depsgraph *depsgraph, + struct Scene *scene, + struct Object *ob); + /** * Assign #Object.data after modifier stack evaluation. */ void BKE_object_eval_assign_data(struct Object *object, struct ID *data, bool is_owned); @@ -568,6 +571,8 @@ struct MovieClip *BKE_object_movieclip_get(struct Scene *scene, struct Object *ob, bool use_default); +void BKE_object_runtime_ensure_geometry_cache(struct Object *ob, bool enable); +void BKE_object_runtime_ensure_rigid_body_map(struct RigidBodyWorld *rbw, struct Object *ob, bool enable); void BKE_object_runtime_reset(struct Object *object); /** * Reset all pointers which we don't want to be shared when copying the object. diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index 9c4bb01c22a..3eb4d61be34 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -157,6 +157,15 @@ void BKE_rigidbody_remove_constraint(struct Main *bmain, struct Object *ob, bool free_us); +bool BKE_rigidbody_add_nodes(struct Main *bmain, + struct Scene *scene, + struct Object *ob, + struct ReportList *reports); +void BKE_rigidbody_remove_nodes(struct Main *bmain, + struct Scene *scene, + struct Object *ob, + const bool free_us); + /** \} */ /* -------------------------------------------------------------------- */ @@ -230,6 +239,26 @@ void BKE_rigidbody_object_sync_transforms(struct Depsgraph *depsgraph, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Internal + * \{ */ + +struct rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(struct Object *ob, + float margin, + bool *can_embed); + +struct rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(struct Object *ob); + +void BKE_rigidbody_update_simulation_nodes(struct RigidBodyWorld *rbw, + struct Object *object, + struct NodesModifierData *nmd); + +void BKE_rigidbody_update_simulation_nodes_post_step(struct RigidBodyWorld *rbw, + struct Object *object, + struct NodesModifierData *nmd); + +/** \} */ + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_rigidbody.hh b/source/blender/blenkernel/BKE_rigidbody.hh new file mode 100644 index 00000000000..22a162f6f16 --- /dev/null +++ b/source/blender/blenkernel/BKE_rigidbody.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2013 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + * \brief API for Blender-side Rigid Body stuff + */ + +#pragma once + +namespace blender::particles { + +static const char *id_attribute_name = "id"; +static const char *shape_index_attribute_name = "shape_index"; +static const char *pos_attribute_name = "position"; +static const char *rot_attribute_name = "rotation"; + +} // namespace blender::particles + +struct RigidBodyMap { + using UID = int; + + enum BodyFlag { + /* Body is used by data in Blender, used for removing bodies that are no longer needed */ + Used = (1 << 0), + }; + + struct BodyPointer { + int flag; + struct rbRigidBody *body; + }; + + struct ShapePointer { + struct ID *id_key; + struct rbCollisionShape *shape; + int users; + }; + + blender::Map body_map_; + blender::Vector shape_list_; + + void clear(struct RigidBodyWorld *rbw); + + void add_shape(struct ID *id_key, struct rbCollisionShape *shape); + void clear_shapes(); +}; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 8dc6f711fae..fca86ee8f5d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -136,6 +136,7 @@ set(SRC intern/fluid.c intern/fmodifier.c intern/freestyle.c + intern/geometry_cache.cc intern/geometry_component_curve.cc intern/geometry_component_curves.cc intern/geometry_component_instances.cc @@ -252,6 +253,7 @@ set(SRC intern/preferences.c intern/report.c intern/rigidbody.c + intern/rigidbody.cc intern/scene.cc intern/screen.c intern/shader_fx.c @@ -375,6 +377,8 @@ set(SRC BKE_fcurve_driver.h BKE_fluid.h BKE_freestyle.h + BKE_geometry_cache.h + BKE_geometry_cache.hh BKE_geometry_fields.hh BKE_geometry_set.h BKE_geometry_set.hh @@ -447,6 +451,7 @@ set(SRC BKE_preferences.h BKE_report.h BKE_rigidbody.h + BKE_rigidbody.hh BKE_scene.h BKE_screen.h BKE_sequencer_offscreen.h diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index b3a9d894944..71114918ab7 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -734,7 +734,8 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* return args */ Mesh **r_deform, Mesh **r_final, - GeometrySet **r_geometry_set) + GeometrySet **r_geometry_set, + bool *r_need_caching) { /* Input and final mesh. Final mesh is only created the moment the first * constructive modifier is executed, or a deform modifier needs normals @@ -875,6 +876,10 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, continue; } + if (r_need_caching && (mti->flags & eModifierTypeFlag_NeedCaching)) { + *r_need_caching = true; + } + if (sculpt_mode && (!has_multires || multires_applied || sculpt_dyntopo)) { bool unsupported = false; @@ -1617,6 +1622,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, Mesh *mesh_eval = nullptr, *mesh_deform_eval = nullptr; GeometrySet *geometry_set_eval = nullptr; + bool need_caching = false; mesh_calc_modifiers(depsgraph, scene, ob, @@ -1627,7 +1633,8 @@ static void mesh_build_data(struct Depsgraph *depsgraph, true, &mesh_deform_eval, &mesh_eval, - &geometry_set_eval); + &geometry_set_eval, + &need_caching); /* The modifier stack evaluation is storing result in mesh->runtime.mesh_eval, but this result * is not guaranteed to be owned by object. @@ -1647,6 +1654,7 @@ static void mesh_build_data(struct Depsgraph *depsgraph, ob->runtime.mesh_deform_eval = mesh_deform_eval; ob->runtime.last_data_mask = *dataMask; ob->runtime.last_need_mapping = need_mapping; + BKE_object_runtime_ensure_geometry_cache(ob, need_caching); BKE_object_boundbox_calc_from_mesh(ob, mesh_eval); @@ -1876,7 +1884,7 @@ Mesh *mesh_create_eval_final(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, true, false, dataMask, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, true, false, dataMask, false, false, nullptr, &result, nullptr, nullptr); return result; } @@ -1887,7 +1895,7 @@ Mesh *mesh_create_eval_no_deform(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr, nullptr); return result; } @@ -1898,7 +1906,7 @@ Mesh *mesh_create_eval_no_deform_render(Depsgraph *depsgraph, { Mesh *result; mesh_calc_modifiers( - depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr); + depsgraph, scene, ob, false, false, dataMask, false, false, nullptr, &result, nullptr, nullptr); return result; } diff --git a/source/blender/blenkernel/intern/geometry_cache.cc b/source/blender/blenkernel/intern/geometry_cache.cc new file mode 100644 index 00000000000..ee4c2c6aef4 --- /dev/null +++ b/source/blender/blenkernel/intern/geometry_cache.cc @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_geometry_cache.h" +#include "BKE_geometry_cache.hh" +#include "BKE_geometry_set.hh" + +/* -------------------------------------------------------------------- */ +/** \name Geometry Cache + * \{ */ + +GeometryCache::GeometryCache() +{ +} + +GeometryCache::~GeometryCache() +{ + clear(); +} + +void GeometryCache::clear() +{ + MEM_delete(first_.geometry_set); + first_ = KeyValuePair{}; + MEM_delete(last_.geometry_set); + last_ = KeyValuePair{}; +} + +void GeometryCache::insert_and_continue_from(Timestamp timestamp, const GeometrySet &geometry_set) +{ + BLI_assert(timestamp != TimestampInvalid); + + if (!first_ || timestamp <= first_.timestamp) { + MEM_delete(first_.geometry_set); + first_ = KeyValuePair{timestamp, MEM_new("geometry set", geometry_set)}; + first_.geometry_set->ensure_owns_direct_data(); + MEM_delete(last_.geometry_set); + last_ = KeyValuePair{}; + } + else if (!last_ || timestamp <= last_.timestamp) { + MEM_delete(last_.geometry_set); + last_ = KeyValuePair{timestamp, MEM_new("geometry set", geometry_set)}; + last_.geometry_set->ensure_owns_direct_data(); + } + else { + MEM_delete(first_.geometry_set); + first_ = last_; + last_ = KeyValuePair{timestamp, MEM_new("geometry set", geometry_set)}; + last_.geometry_set->ensure_owns_direct_data(); + } +} + +GeometrySet *GeometryCache::get_exact(Timestamp timestamp) const +{ + BLI_assert(timestamp != TimestampInvalid); + + if (last_ && last_.timestamp == timestamp) { + return last_.geometry_set; + } + if (first_ && first_.timestamp == timestamp) { + return first_.geometry_set; + } + return nullptr; +} + +GeometrySet *GeometryCache::get_before(Timestamp timestamp) const +{ + BLI_assert(timestamp != TimestampInvalid); + + if (last_ && last_.timestamp < timestamp) { + return last_.geometry_set; + } + if (first_ && first_.timestamp < timestamp) { + return first_.geometry_set; + } + return nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C API + * \{ */ + +GeometryCache *BKE_geometry_cache_new() +{ + return MEM_new("geometry cache"); +} + +void BKE_geometry_cache_free(GeometryCache *cache) +{ + MEM_delete(cache); +} + +void BKE_geometry_cache_insert_and_continue_from(GeometryCache *cache, + int cfra, + const GeometrySet *geometry_set) +{ + cache->insert_and_continue_from(GeometryCache::Timestamp{cfra}, *geometry_set); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index e02d3b6486c..b8cae4c33bf 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -73,6 +73,7 @@ #include "NOD_geometry.h" #include "NOD_node_declaration.hh" #include "NOD_node_tree_ref.hh" +#include "NOD_particles.h" #include "NOD_shader.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -538,7 +539,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) } if (node->storage) { - if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && + if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY, NTREE_PARTICLES) && ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } @@ -4840,6 +4841,11 @@ static void registerGeometryNodes() register_node_type_geo_volume_to_mesh(); } +static void registerParticleNodes() +{ + register_node_type_particles_add_shape(); +} + static void registerFunctionNodes() { register_node_type_fn_align_euler_to_vector(); @@ -4876,6 +4882,7 @@ void BKE_node_system_init() register_node_tree_type_sh(); register_node_tree_type_tex(); register_node_tree_type_geo(); + register_node_tree_type_particles(); register_node_type_frame(); register_node_type_reroute(); @@ -4886,6 +4893,7 @@ void BKE_node_system_init() registerShaderNodes(); registerTextureNodes(); registerGeometryNodes(); + registerParticleNodes(); registerFunctionNodes(); } diff --git a/source/blender/blenkernel/intern/node_tree_update.cc b/source/blender/blenkernel/intern/node_tree_update.cc index 019ab114b83..c44c2bf7c86 100644 --- a/source/blender/blenkernel/intern/node_tree_update.cc +++ b/source/blender/blenkernel/intern/node_tree_update.cc @@ -860,7 +860,7 @@ class NodeTreeMainUpdater { this->reset_changed_flags(*ntree); if (result.interface_changed) { - if (ntree->type == NTREE_GEOMETRY) { + if (ELEM(ntree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { relations_.ensure_modifier_users(); for (const ObjectModifierPair &pair : relations_.get_modifier_users(ntree)) { Object *object = pair.first; @@ -987,7 +987,7 @@ class NodeTreeMainUpdater { this->ensure_tree_ref(ntree, tree_ref); this->propagate_runtime_flags(*tree_ref); - if (ntree.type == NTREE_GEOMETRY) { + if (ELEM(ntree.type, NTREE_GEOMETRY, NTREE_PARTICLES)) { if (node_field_inferencing::update_field_inferencing(*tree_ref)) { result.interface_changed = true; } diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index c8b87c27697..15bc77046aa 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -83,6 +83,7 @@ #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" +#include "BKE_geometry_cache.hh" #include "BKE_geometry_set.h" #include "BKE_geometry_set.hh" #include "BKE_global.h" @@ -118,6 +119,7 @@ #include "BKE_pointcache.h" #include "BKE_pointcloud.h" #include "BKE_rigidbody.h" +#include "BKE_rigidbody.hh" #include "BKE_scene.h" #include "BKE_shader_fx.h" #include "BKE_softbody.h" @@ -1825,6 +1827,16 @@ void BKE_object_free_derived_caches(Object *ob) ob->runtime.geometry_set_eval = nullptr; } + if (ob->runtime.geometry_cache) { + MEM_delete(ob->runtime.geometry_cache); + ob->runtime.geometry_cache = nullptr; + } + + if (ob->runtime.rigid_body_map) { + MEM_delete(ob->runtime.rigid_body_map); + ob->runtime.rigid_body_map = nullptr; + } + MEM_SAFE_FREE(ob->runtime.editmesh_bb_cage); } @@ -5000,6 +5012,39 @@ bool BKE_object_supports_material_slots(struct Object *ob) /** \name Object Runtime * \{ */ +void BKE_object_runtime_ensure_geometry_cache(Object *ob, bool enable) +{ + BLI_assert(ob->id.orig_id); + Object *orig_ob = (Object *)ob->id.orig_id; + + if (enable) { + if (!orig_ob->runtime.geometry_cache) { + orig_ob->runtime.geometry_cache = MEM_new("geometry cache"); + } + } + else { + MEM_delete(orig_ob->runtime.geometry_cache); + orig_ob->runtime.geometry_cache = nullptr; + } +} + +void BKE_object_runtime_ensure_rigid_body_map(RigidBodyWorld *rbw, Object *ob, bool enable) +{ + BLI_assert(ob->id.orig_id); + Object *orig_ob = (Object *)ob->id.orig_id; + + if (enable) { + if (!orig_ob->runtime.rigid_body_map) { + orig_ob->runtime.rigid_body_map = MEM_new("rigid body map"); + } + } + else { + orig_ob->runtime.rigid_body_map->clear(rbw); + MEM_delete(orig_ob->runtime.rigid_body_map); + orig_ob->runtime.rigid_body_map = nullptr; + } +} + void BKE_object_runtime_reset(Object *object) { memset(&object->runtime, 0, sizeof(object->runtime)); @@ -5015,6 +5060,7 @@ void BKE_object_runtime_reset_on_copy(Object *object, const int UNUSED(flag)) runtime->object_as_temp_mesh = nullptr; runtime->object_as_temp_curve = nullptr; runtime->geometry_set_eval = nullptr; + runtime->geometry_cache = nullptr; runtime->crazyspace_deform_imats = nullptr; runtime->crazyspace_deform_cos = nullptr; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 8ff02c7e698..b0aef31c805 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -29,6 +29,7 @@ #include "BKE_displist.h" #include "BKE_editmesh.h" #include "BKE_effect.h" +#include "BKE_geometry_cache.h" #include "BKE_gpencil.h" #include "BKE_gpencil_modifier.h" #include "BKE_image.h" @@ -332,6 +333,17 @@ void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) BKE_object_batch_cache_dirty_tag(ob); } +void BKE_object_write_geometry_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) +{ + DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); + BLI_assert(ob->id.orig_id); + Object *orig_ob = (Object *)ob->id.orig_id; + if (orig_ob->runtime.geometry_cache && ob->runtime.geometry_set_eval) { + BKE_geometry_cache_insert_and_continue_from( + orig_ob->runtime.geometry_cache, scene->r.cfra, ob->runtime.geometry_set_eval); + } +} + void BKE_object_eval_ptcache_reset(Depsgraph *depsgraph, Scene *scene, Object *object) { DEG_debug_print_eval(depsgraph, __func__, object->id.name, object); diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index e38c20d8eb7..5d8a543488d 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -354,7 +354,8 @@ PointCloud *BKE_pointcloud_copy_for_eval(struct PointCloud *pointcloud_src, bool static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, struct Scene *scene, Object *object, - GeometrySet &geometry_set) + GeometrySet &geometry_set, + bool *r_need_caching) { /* Modifier evaluation modes. */ const bool use_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); @@ -375,6 +376,10 @@ static void pointcloud_evaluate_modifiers(struct Depsgraph *depsgraph, continue; } + if (r_need_caching && (mti->flags & eModifierTypeFlag_NeedCaching)) { + *r_need_caching = true; + } + if (mti->modifyGeometrySet) { mti->modifyGeometrySet(md, &mectx, &geometry_set); } @@ -409,7 +414,8 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene PointCloud *pointcloud = static_cast(object->data); GeometrySet geometry_set = GeometrySet::create_with_pointcloud(pointcloud, GeometryOwnershipType::ReadOnly); - pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set); + bool need_caching = false; + pointcloud_evaluate_modifiers(depsgraph, scene, object, geometry_set, &need_caching); PointCloud *pointcloud_eval = take_pointcloud_ownership_from_geometry_set(geometry_set); @@ -422,6 +428,7 @@ void BKE_pointcloud_data_update(struct Depsgraph *depsgraph, struct Scene *scene const bool eval_is_owned = pointcloud_eval != pointcloud; BKE_object_eval_assign_data(object, &pointcloud_eval->id, eval_is_owned); object->runtime.geometry_set_eval = new GeometrySet(std::move(geometry_set)); + BKE_object_runtime_ensure_geometry_cache(object, need_caching); } /* Draw Cache */ diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 60cc4ce83af..6dd33641f15 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -28,6 +28,7 @@ #include "DNA_collection_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" #include "DNA_rigidbody_types.h" @@ -53,6 +54,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "MOD_nodes.h" + #ifdef WITH_BULLET static CLG_LogRef LOG = {"bke.rigidbody"}; #endif @@ -334,17 +337,22 @@ static Mesh *rigidbody_get_mesh(Object *ob) { BLI_assert(ob->type == OB_MESH); - switch (ob->rigidbody_object->mesh_source) { - case RBO_MESH_DEFORM: - return ob->runtime.mesh_deform_eval; - case RBO_MESH_FINAL: - return BKE_object_get_evaluated_mesh(ob); - case RBO_MESH_BASE: - /* This mesh may be used for computing looptris, which should be done - * on the original; otherwise every time the CoW is recreated it will - * have to be recomputed. */ - BLI_assert(ob->rigidbody_object->mesh_source == RBO_MESH_BASE); - return (Mesh *)ob->runtime.data_orig; + if (ob->rigidbody_object) { + switch (ob->rigidbody_object->mesh_source) { + case RBO_MESH_DEFORM: + return ob->runtime.mesh_deform_eval; + case RBO_MESH_FINAL: + return BKE_object_get_evaluated_mesh(ob); + case RBO_MESH_BASE: + /* This mesh may be used for computing looptris, which should be done + * on the original; otherwise every time the CoW is recreated it will + * have to be recomputed. */ + BLI_assert(ob->rigidbody_object->mesh_source == RBO_MESH_BASE); + return (Mesh *)ob->runtime.data_orig; + } + } + else { + return (Mesh *)ob->data; } /* Just return something sensible so that at least Blender won't crash. */ @@ -353,9 +361,9 @@ static Mesh *rigidbody_get_mesh(Object *ob) } /* create collision shape of mesh - convex hull */ -static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob, - float margin, - bool *can_embed) +rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob, + float margin, + bool *can_embed) { rbCollisionShape *shape = NULL; Mesh *mesh = NULL; @@ -384,7 +392,7 @@ static rbCollisionShape *rigidbody_get_shape_convexhull_from_mesh(Object *ob, /* create collision shape of mesh - triangulated mesh * returns NULL if creation fails. */ -static rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob) +rbCollisionShape *rigidbody_get_shape_trimesh_from_mesh(Object *ob) { rbCollisionShape *shape = NULL; @@ -1408,7 +1416,7 @@ RigidBodyWorld *BKE_rigidbody_get_world(Scene *scene) return scene->rigidbody_world; } -static bool rigidbody_add_object_to_scene(Main *bmain, Scene *scene, Object *ob) +static bool rigidbody_add_object_to_world(Main *bmain, Scene *scene, Object *ob) { /* Add rigid body world and group if they don't exist for convenience */ RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -1437,7 +1445,63 @@ static bool rigidbody_add_object_to_scene(Main *bmain, Scene *scene, Object *ob) return true; } -static bool rigidbody_add_constraint_to_scene(Main *bmain, Scene *scene, Object *ob) +static void rigidbody_remove_object_from_world(Main *bmain, + Scene *scene, + Object *ob, + const bool free_us) +{ + RigidBodyWorld *rbw = scene->rigidbody_world; + if (rbw == NULL) { + return; + } + + /* remove object from array */ + if (rbw->objects) { + for (int i = 0; i < rbw->numbodies; i++) { + if (rbw->objects[i] == ob) { + rbw->objects[i] = NULL; + break; + } + } + } + + /* remove object from rigid body constraints */ + if (rbw->constraints) { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, obt) { + if (obt && obt->rigidbody_constraint) { + RigidBodyCon *rbc = obt->rigidbody_constraint; + if (rbc->ob1 == ob) { + rbc->ob1 = NULL; + DEG_id_tag_update(&obt->id, ID_RECALC_COPY_ON_WRITE); + } + if (rbc->ob2 == ob) { + rbc->ob2 = NULL; + DEG_id_tag_update(&obt->id, ID_RECALC_COPY_ON_WRITE); + } + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } + + /* Relying on usercount of the object should be OK, and it is much cheaper than looping in all + * collections to check whether the object is already in another one... */ + if (ID_REAL_USERS(&ob->id) == 1) { + /* Some users seems to find it funny to use a view-layer instancing collection + * as RBW collection... Despite this being a bad (ab)use of the system, avoid losing objects + * when we remove them from RB simulation. */ + BKE_collection_object_add(bmain, scene->master_collection, ob); + } + BKE_collection_object_remove(bmain, rbw->group, ob, free_us); + + /* flag cache as outdated */ + BKE_rigidbody_cache_reset(rbw); + /* Reset cache as the object order probably changed after freeing the object. */ + PTCacheID pid; + BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw); + BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); +} + +static bool rigidbody_add_constraint_to_world(Main *bmain, Scene *scene, Object *ob) { /* Add rigid body world and group if they don't exist for convenience */ RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); @@ -1472,7 +1536,7 @@ void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) /* Add newly local object to scene. */ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { if (BKE_scene_object_find(scene, ob)) { - rigidbody_add_object_to_scene(bmain, scene, ob); + rigidbody_add_object_to_world(bmain, scene, ob); } } } @@ -1480,7 +1544,7 @@ void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) /* Add newly local object to scene. */ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { if (BKE_scene_object_find(scene, ob)) { - rigidbody_add_constraint_to_scene(bmain, scene, ob); + rigidbody_add_constraint_to_world(bmain, scene, ob); } } } @@ -1489,13 +1553,17 @@ void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) bool BKE_rigidbody_add_object(Main *bmain, Scene *scene, Object *ob, int type, ReportList *reports) { if (ob->type != OB_MESH) { - BKE_report(reports, RPT_ERROR, "Can't add Rigid Body to non mesh object"); + if (reports) { + BKE_report(reports, RPT_ERROR, "Can't add Rigid Body to non mesh object"); + } return false; } /* Add object to rigid body world in scene. */ - if (!rigidbody_add_object_to_scene(bmain, scene, ob)) { - BKE_report(reports, RPT_ERROR, "Can't create Rigid Body world"); + if (!rigidbody_add_object_to_world(bmain, scene, ob)) { + if (reports) { + BKE_report(reports, RPT_ERROR, "Can't create Rigid Body world"); + } return false; } @@ -1514,64 +1582,36 @@ bool BKE_rigidbody_add_object(Main *bmain, Scene *scene, Object *ob, int type, R void BKE_rigidbody_remove_object(Main *bmain, Scene *scene, Object *ob, const bool free_us) { - RigidBodyWorld *rbw = scene->rigidbody_world; - RigidBodyCon *rbc; - int i; + rigidbody_remove_object_from_world(bmain, scene, ob, free_us); - if (rbw) { - - /* remove object from array */ - if (rbw->objects) { - for (i = 0; i < rbw->numbodies; i++) { - if (rbw->objects[i] == ob) { - rbw->objects[i] = NULL; - break; - } - } - } + RigidBodyWorld *rbw = scene->rigidbody_world; + /* remove object's settings */ + BKE_rigidbody_free_object(ob, rbw); - /* remove object from rigid body constraints */ - if (rbw->constraints) { - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, obt) { - if (obt && obt->rigidbody_constraint) { - rbc = obt->rigidbody_constraint; - if (rbc->ob1 == ob) { - rbc->ob1 = NULL; - DEG_id_tag_update(&obt->id, ID_RECALC_COPY_ON_WRITE); - } - if (rbc->ob2 == ob) { - rbc->ob2 = NULL; - DEG_id_tag_update(&obt->id, ID_RECALC_COPY_ON_WRITE); - } - } - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - } + /* Dependency graph update */ + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); +} - /* Relying on usercount of the object should be OK, and it is much cheaper than looping in all - * collections to check whether the object is already in another one... */ - if (ID_REAL_USERS(&ob->id) == 1) { - /* Some users seems to find it funny to use a view-layer instancing collection - * as RBW collection... Despite this being a bad (ab)use of the system, avoid losing objects - * when we remove them from RB simulation. */ - BKE_collection_object_add(bmain, scene->master_collection, ob); +bool BKE_rigidbody_add_nodes(Main *bmain, Scene *scene, Object *ob, ReportList *reports) +{ + /* Add object to rigid body world in scene. */ + if (!rigidbody_add_object_to_world(bmain, scene, ob)) { + if (reports) { + BKE_report(reports, RPT_ERROR, "Can't create Rigid Body world"); } - BKE_collection_object_remove(bmain, rbw->group, ob, free_us); - - /* flag cache as outdated */ - BKE_rigidbody_cache_reset(rbw); - /* Reset cache as the object order probably changed after freeing the object. */ - PTCacheID pid; - BKE_ptcache_id_from_rigidbody(&pid, NULL, rbw); - BKE_ptcache_id_reset(scene, &pid, PTCACHE_RESET_OUTDATED); + return false; } - /* remove object's settings */ - BKE_rigidbody_free_object(ob, rbw); - - /* Dependency graph update */ DEG_relations_tag_update(bmain); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + + return true; +} + +void BKE_rigidbody_remove_nodes(Main *bmain, Scene *scene, Object *ob, const bool free_us) +{ + rigidbody_remove_object_from_world(bmain, scene, ob, free_us); } void BKE_rigidbody_remove_constraint(Main *bmain, Scene *scene, Object *ob, const bool free_us) @@ -1856,6 +1896,15 @@ static void rigidbody_update_simulation(Depsgraph *depsgraph, /* update simulation object... */ rigidbody_update_sim_ob(depsgraph, scene, rbw, ob, rbo); } + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (MOD_nodes_needs_rigid_body_sim(ob, nmd)) { + BKE_rigidbody_update_simulation_nodes(rbw, ob, nmd); + } + } + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; @@ -2013,6 +2062,15 @@ static void rigidbody_update_simulation_post_step(Depsgraph *depsgraph, RigidBod RB_body_deactivate(rbo->shared->physics_object); } } + + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (MOD_nodes_needs_rigid_body_sim(ob, nmd)) { + BKE_rigidbody_update_simulation_nodes_post_step(rbw, ob, nmd); + } + } + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -2201,11 +2259,11 @@ void BKE_rigidbody_do_simulation(Depsgraph *depsgraph, Scene *scene, float ctime /* RB_TODO deal with interpolated, old and baked results */ bool can_simulate = (ctime == rbw->ltime + 1) && !(cache->flag & PTCACHE_BAKED); - if (BKE_ptcache_read(&pid, ctime, can_simulate) == PTCACHE_READ_EXACT) { - BKE_ptcache_validate(cache, (int)ctime); - rbw->ltime = ctime; - return; - } + //if (BKE_ptcache_read(&pid, ctime, can_simulate) == PTCACHE_READ_EXACT) { + // BKE_ptcache_validate(cache, (int)ctime); + // rbw->ltime = ctime; + // return; + //} if (!DEG_is_active(depsgraph)) { /* When the depsgraph is inactive we should neither write to the cache diff --git a/source/blender/blenkernel/intern/rigidbody.cc b/source/blender/blenkernel/intern/rigidbody.cc new file mode 100644 index 00000000000..a34d19bfdb3 --- /dev/null +++ b/source/blender/blenkernel/intern/rigidbody.cc @@ -0,0 +1,362 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2013 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup bke + * \brief Blender-side interface and methods for dealing with Rigid Body simulations + */ + +#include "BLI_generic_virtual_array.hh" +#include "BLI_map.hh" +#include "BLI_math_rotation.h" + +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_pointcloud_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_scene_types.h" + +#include "BKE_geometry_set.hh" +#include "BKE_global.h" +#include "BKE_lib_id.h" +#include "BKE_object.h" +#include "BKE_pointcloud.h" +#include "BKE_rigidbody.h" +#include "BKE_rigidbody.hh" + +#include "MOD_nodes.h" + +#ifdef WITH_BULLET +# include "RBI_api.h" +#endif + +void RigidBodyMap::clear(RigidBodyWorld *rbw) +{ + rbDynamicsWorld *physics_world = (rbDynamicsWorld *)rbw->shared->physics_world; + + for (const RigidBodyMap::BodyPointer &body_ptr : body_map_.values()) { + RB_dworld_remove_body(physics_world, body_ptr.body); + RB_body_delete(body_ptr.body); + } + body_map_.clear(); + + clear_shapes(); +} + +void RigidBodyMap::add_shape(ID *id_key, rbCollisionShape *shape) +{ + shape_list_.append(ShapePointer{id_key, shape, 0}); +} + +void RigidBodyMap::clear_shapes() +{ + for (const RigidBodyMap::ShapePointer &shape_ptr : shape_list_) { + RB_shape_delete(shape_ptr.shape); + } + shape_list_.clear(); +} + + +namespace blender::particles { + +static rbCollisionShape *get_collision_shape_from_object(Object *object, + ParticleNodeShapeType shape_type) +{ + rbCollisionShape *new_shape = NULL; + float size[3] = {1.0f, 1.0f, 1.0f}; + float radius = 1.0f; + float height = 1.0f; + float capsule_height; + float hull_margin = 0.0f; + float margin = 0.0f; + bool can_embed = true; + bool has_volume; + + const bool use_margin = false; + + /* if automatically determining dimensions, use the Object's boundbox + * - assume that all quadrics are standing upright on local z-axis + * - assume even distribution of mass around the Object's pivot + * (i.e. Object pivot is centralized in boundbox) + */ + /* XXX: all dimensions are auto-determined now... later can add stored settings for this */ + /* get object dimensions without scaling */ + const BoundBox *bb = BKE_object_boundbox_get(object); + if (bb) { + size[0] = (bb->vec[4][0] - bb->vec[0][0]); + size[1] = (bb->vec[2][1] - bb->vec[0][1]); + size[2] = (bb->vec[1][2] - bb->vec[0][2]); + } + mul_v3_fl(size, 0.5f); + + if (ELEM(shape_type, PARTICLE_SHAPE_CAPSULE, PARTICLE_SHAPE_CYLINDER, PARTICLE_SHAPE_CONE)) { + /* take radius as largest x/y dimension, and height as z-dimension */ + radius = MAX2(size[0], size[1]); + height = size[2]; + } + else if (shape_type == PARTICLE_SHAPE_SPHERE) { + /* take radius to the largest dimension to try and encompass everything */ + radius = MAX3(size[0], size[1], size[2]); + } + + /* create new shape */ + switch (shape_type) { + case PARTICLE_SHAPE_BOX: + new_shape = RB_shape_new_box(size[0], size[1], size[2]); + break; + + case PARTICLE_SHAPE_SPHERE: + new_shape = RB_shape_new_sphere(radius); + break; + + case PARTICLE_SHAPE_CAPSULE: + capsule_height = (height - radius) * 2.0f; + new_shape = RB_shape_new_capsule(radius, (capsule_height > 0.0f) ? capsule_height : 0.0f); + break; + case PARTICLE_SHAPE_CYLINDER: + new_shape = RB_shape_new_cylinder(radius, height); + break; + case PARTICLE_SHAPE_CONE: + new_shape = RB_shape_new_cone(radius, height * 2.0f); + break; + + case PARTICLE_SHAPE_CONVEX_HULL: + /* try to embed collision margin */ + has_volume = (MIN3(size[0], size[1], size[2]) > 0.0f); + + if (!use_margin && has_volume) { + hull_margin = 0.04f; + } + new_shape = rigidbody_get_shape_convexhull_from_mesh(object, hull_margin, &can_embed); + if (!use_margin) { + margin = (can_embed && has_volume) ? 0.04f : 0.0f; + } + break; + case PARTICLE_SHAPE_TRIMESH: + new_shape = rigidbody_get_shape_trimesh_from_mesh(object); + break; + //case PARTICLE_SHAPE_COMPOUND: + // new_shape = RB_shape_new_compound(); + // rbCollisionShape *childShape = NULL; + // float loc[3], rot[4]; + // float mat[4][4]; + // /* Add children to the compound shape */ + // FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, childObject) { + // if (childObject->parent == ob) { + // childShape = rigidbody_validate_sim_shape_helper(rbw, childObject); + // if (childShape) { + // BKE_object_matrix_local_get(childObject, mat); + // mat4_to_loc_quat(loc, rot, mat); + // RB_compound_add_child_shape(new_shape, childShape, loc, rot); + // } + // } + // } + // FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + // break; + } + /* use box shape if it failed to create new shape */ + if (new_shape == NULL) { + new_shape = RB_shape_new_box(size[0], size[1], size[2]); + } + if (new_shape) { + RB_shape_set_margin(new_shape, margin); + } + + return new_shape; +} + +static void update_simulation_nodes_component(struct RigidBodyWorld *rbw, + Object *object, + NodesModifierData *nmd, + const GeometryComponent *component, + int &num_used_bodies) +{ + static rbCollisionShape *generic_shape = RB_shape_new_sphere(0.05f); + static int generic_collision_groups = 0xFFFFFFFF; + static RigidBodyMap::ShapePointer fallback_shape_ptr{nullptr, generic_shape, 0}; + + rbDynamicsWorld *physics_world = (rbDynamicsWorld *)rbw->shared->physics_world; + Object *orig_ob = (Object *)object->id.orig_id; + BLI_assert(orig_ob->runtime.rigid_body_map); + RigidBodyMap &rb_map = *orig_ob->runtime.rigid_body_map; + + if (component == nullptr) { + return; + } + + bke::ReadAttributeLookup id_attribute = + component->attribute_try_get_for_read(id_attribute_name, CD_PROP_INT32); + bke::ReadAttributeLookup shape_index_attribute = component->attribute_try_get_for_read( + shape_index_attribute_name, CD_PROP_INT32); + bke::ReadAttributeLookup pos_attribute = component->attribute_try_get_for_read( + pos_attribute_name, CD_PROP_FLOAT3); + bke::ReadAttributeLookup rot_attribute = component->attribute_try_get_for_read( + rot_attribute_name, CD_PROP_FLOAT3); + + if (id_attribute && pos_attribute) { + /* XXX should have some kind of point group feature to make bodies for relevant points only */ + VArray id_data = id_attribute.varray.typed(); + VArray shape_index_data = shape_index_attribute.varray.typed(); + VArray pos_data = pos_attribute.varray.typed(); + VArray rot_data = rot_attribute.varray.typed(); + BLI_assert(id_data.size() == component->attribute_domain_num(id_attribute.domain)); + BLI_assert(pos_data.size() == component->attribute_domain_num(id_attribute.domain)); + for (int i : id_data.index_range()) { + int uid = id_data[i]; + int shape_index = shape_index_data ? shape_index_data[i] : -1; + RigidBodyMap::ShapePointer &shape_ptr = rb_map.shape_list_.index_range().contains( + shape_index) ? + rb_map.shape_list_[shape_index] : + fallback_shape_ptr; + ++shape_ptr.users; + + /* Add new bodies */ + RigidBodyMap::BodyPointer &body_ptr = rb_map.body_map_.lookup_or_add_cb( + uid, [=]() { + /* Callback to add a new body */ + const float3 &pos = pos_data[i]; + const float3 &rot_eul = rot_data ? rot_data[i] : float3(0, 0, 0); + float rot_qt[4]; + eul_to_quat(rot_qt, rot_eul); + RigidBodyMap::BodyFlag flag = RigidBodyMap::Used; + rbRigidBody *body = RB_body_new(shape_ptr.shape, pos, rot_qt); + /* This also computes local moment of inertia, which is needed for rotations! */ + RB_body_set_friction(body, 0.5f); + RB_body_set_restitution(body, 0.05f); + RB_body_set_mass(body, 1.0f); + RB_dworld_add_body(physics_world, body, generic_collision_groups); + return RigidBodyMap::BodyPointer{flag, body}; + }); + /* Flag existing bodies as used */ + body_ptr.flag |= RigidBodyMap::Used; + ++num_used_bodies; + } + } +} + +} // namespace blender + +void BKE_rigidbody_update_simulation_nodes(RigidBodyWorld *rbw, + Object *object, + NodesModifierData *nmd) +{ + using namespace blender; + + rbDynamicsWorld *physics_world = (rbDynamicsWorld *)rbw->shared->physics_world; + + BKE_object_runtime_ensure_rigid_body_map( + rbw, object, MOD_nodes_needs_rigid_body_sim(object, nmd)); + Object *orig_ob = (Object *)object->id.orig_id; + RigidBodyMap &rb_map = *orig_ob->runtime.rigid_body_map; + + /* XXX Placeholder */ + if (rb_map.shape_list_.size() < 1) { + if (Object *suzanne = (Object *)BKE_libblock_find_name(G_MAIN, ID_OB, "Suzanne")) { + if (rbCollisionShape *shape = particles::get_collision_shape_from_object( + suzanne, PARTICLE_SHAPE_CONVEX_HULL)) { + rb_map.add_shape(&suzanne->id, shape); + } + } + } + + /* Update flags for used bodies */ + const int num_existing_bodies = rb_map.body_map_.size(); + for (RigidBodyMap::BodyPointer &body_ptr : rb_map.body_map_.values()) { + body_ptr.flag &= ~RigidBodyMap::BodyFlag::Used; + } + for (RigidBodyMap::ShapePointer &shape_ptr : rb_map.shape_list_) { + shape_ptr.users = 0; + } + + int num_used_bodies = 0; + if (GeometrySet *geometry_set = object->runtime.geometry_set_eval) { + for (const GeometryComponent *component : geometry_set->get_components_for_read()) { + particles::update_simulation_nodes_component(rbw, object, nmd, component, num_used_bodies); + } + } + + /* Remove unused bodies */ + Vector bodies_to_remove; + bodies_to_remove.reserve(num_existing_bodies - num_used_bodies); + for (const auto &item : rb_map.body_map_.items()) { + if (!(item.value.flag & RigidBodyMap::BodyFlag::Used)) { + RB_dworld_remove_body(physics_world, item.value.body); + RB_body_delete(item.value.body); + bodies_to_remove.append(item.key); + } + } + for (RigidBodyMap::UID uid : bodies_to_remove) { + rb_map.body_map_.remove(uid); + } +} + +namespace blender::particles { + +static void update_simulation_nodes_component_post_step(RigidBodyWorld *rbw, + Object *object, + NodesModifierData *nmd, + GeometryComponent *component) +{ + if (component == nullptr) { + return; + } + + Object *orig_ob = (Object *)object->id.orig_id; + if (orig_ob->runtime.rigid_body_map == nullptr) { + return; + } + const RigidBodyMap &rb_map = *orig_ob->runtime.rigid_body_map; + + bke::ReadAttributeLookup id_attribute = component->attribute_try_get_for_read(id_attribute_name, + CD_PROP_INT32); + bke::WriteAttributeLookup pos_attribute = component->attribute_try_get_for_write( + pos_attribute_name); + bke::WriteAttributeLookup rot_attribute = component->attribute_try_get_for_write( + rot_attribute_name); + + if (id_attribute) { + const int num_points = component->attribute_domain_num(id_attribute.domain); + + VArray id_data = id_attribute.varray.typed(); + Array pos_data(num_points); + Array rot_data(num_points); + BLI_assert(id_data.size() == num_points); + + for (int i : id_data.index_range()) { + int uid = id_data[i]; + + const RigidBodyMap::BodyPointer *body_ptr = rb_map.body_map_.lookup_ptr(uid); + if (body_ptr) { + RB_body_get_position(body_ptr->body, pos_data[i]); + float rot_qt[4]; + RB_body_get_orientation(body_ptr->body, rot_qt); + quat_to_eul(rot_data[i], rot_qt); + } + } + + if (pos_attribute) { + pos_attribute.varray.set_all(pos_data.data()); + } + if (rot_attribute) { + rot_attribute.varray.set_all(rot_data.data()); + } + } +} + +} // namespace blender + +void BKE_rigidbody_update_simulation_nodes_post_step(RigidBodyWorld *rbw, + Object *object, + NodesModifierData *nmd) +{ + using namespace blender; + + if (GeometrySet *geometry_set = object->runtime.geometry_set_eval) { + particles::update_simulation_nodes_component_post_step( + rbw, object, nmd, &geometry_set->get_component_for_write()); + particles::update_simulation_nodes_component_post_step( + rbw, object, nmd, &geometry_set->get_component_for_write()); + particles::update_simulation_nodes_component_post_step( + rbw, object, nmd, &geometry_set->get_component_for_write()); + } +} diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 763d2d29035..ef15f08dd4a 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -164,6 +164,9 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, const char *description); +void DEG_add_modifier_to_rigid_body_sim_relation(struct DepsNodeHandle *node_handle, + const char *description); + /** * Adds relations from the given component of a given object to the given node * handle AND the component to the point cache component of the node's ID. diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 657bc3eb25c..1c1fc96c705 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -98,6 +98,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "MOD_nodes.h" + #include "SEQ_iterator.h" #include "SEQ_sequencer.h" @@ -1278,27 +1280,34 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) if (rbw->group != nullptr) { build_collection(nullptr, rbw->group); FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->group, object) { - if (object->type != OB_MESH) { - continue; - } - if (object->rigidbody_object == nullptr) { - continue; + bool needs_transform_copy = false; + if (object->rigidbody_object && object->type == OB_MESH && + object->rigidbody_object->type != RBO_TYPE_PASSIVE) { + needs_transform_copy = true; } - if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { - continue; + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (MOD_nodes_needs_rigid_body_sim(object, nmd)) { + needs_transform_copy = true; + } + } } - /* Create operation for flushing results. */ - /* Object's transform component - where the rigidbody operation - * lives. */ - Object *object_cow = get_cow_datablock(object); - add_operation_node(&object->id, - NodeType::TRANSFORM, - OperationCode::RIGIDBODY_TRANSFORM_COPY, - [scene_cow, object_cow](::Depsgraph *depsgraph) { - BKE_rigidbody_object_sync_transforms(depsgraph, scene_cow, object_cow); - }); + if (needs_transform_copy) { + /* Create operation for flushing results. */ + /* Object's transform component - where the rigidbody operation + * lives. */ + Object *object_cow = get_cow_datablock(object); + add_operation_node(&object->id, + NodeType::TRANSFORM, + OperationCode::RIGIDBODY_TRANSFORM_COPY, + [scene_cow, object_cow](::Depsgraph *depsgraph) { + BKE_rigidbody_object_sync_transforms( + depsgraph, scene_cow, object_cow); + }); + } } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -1453,6 +1462,13 @@ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object) BKE_object_eval_uber_data(depsgraph, scene_cow, object_cow); }); op_node->set_as_exit(); + /* Geometry caching. */ + op_node = add_operation_node(&object->id, + NodeType::GEOMETRY, + OperationCode::GEOMETRY_WRITE_CACHE, + [scene_cow, object_cow](::Depsgraph *depsgraph) { + BKE_object_write_geometry_cache(depsgraph, scene_cow, object_cow); + }); /* Materials. */ build_materials(object->mat, object->totcol); /* Point caches. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c13c6d2f870..c0869fafb08 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -307,6 +307,24 @@ void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNode add_depends_on_transform_relation(id, geometry_key, description); } +void DepsgraphRelationBuilder::add_modifier_to_rigid_body_sim_relation(const DepsNodeHandle *handle, + const char *description) +{ + IDNode *id_node = handle->node->owner->owner; + ID *id = id_node->id_orig; + ComponentKey geometry_key(id, NodeType::GEOMETRY); + OperationKey write_cache_key(id, NodeType::GEOMETRY, OperationCode::GEOMETRY_WRITE_CACHE); + OperationKey rb_simulate_key(&scene_->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_SIM); + OperationKey rb_transform_copy_key( + id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); + /* Simulation only after evaluating nodes. */ + add_relation(geometry_key, rb_simulate_key, description); + /* Rigid body synchronization depends on the actual simulation. */ + add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync"); + /* Wait for simulation results to be copied before writing to cache. */ + add_relation(rb_transform_copy_key, write_cache_key, description); +} + void DepsgraphRelationBuilder::add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks) { @@ -2214,6 +2232,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) } } } + /* Geometry Cache. */ + OperationKey write_cache_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_WRITE_CACHE); + add_relation(geom_key, write_cache_key, "Object Geometry Write Cache"); /* Shader FX. */ if (object->shader_fx.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 64bdd2334d8..d14f90318bd 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -178,6 +178,8 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { * Takes care of checking for possible physics solvers modifying position * of this object. */ void add_modifier_to_transform_relation(const DepsNodeHandle *handle, const char *description); + void add_modifier_to_rigid_body_sim_relation(const DepsNodeHandle *handle, + const char *description); void add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks); void add_special_eval_flag(ID *id, uint32_t flag); diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index c64b7bc1eb7..1abab040a14 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -206,6 +206,13 @@ void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, deg_node_handle->builder->add_modifier_to_transform_relation(deg_node_handle, description); } +void DEG_add_modifier_to_rigid_body_sim_relation(struct DepsNodeHandle *node_handle, + const char *description) +{ + deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); + deg_node_handle->builder->add_modifier_to_rigid_body_sim_relation(deg_node_handle, description); +} + void DEG_add_special_eval_flag(struct DepsNodeHandle *node_handle, ID *id, uint32_t flag) { deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index c29aeefd9b2..99be3b3310b 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -88,6 +88,8 @@ const char *operationCodeAsString(OperationCode opcode) return "GEOMETRY_EVAL_DONE"; case OperationCode::GEOMETRY_SHAPEKEY: return "GEOMETRY_SHAPEKEY"; + case OperationCode::GEOMETRY_WRITE_CACHE: + return "GEOMETRY_WRITE_CACHE"; /* Object data. */ case OperationCode::LIGHT_PROBE_EVAL: return "LIGHT_PROBE_EVAL"; diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index d4916be1113..258215c0aa6 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -91,6 +91,8 @@ enum class OperationCode { /* Evaluation of a shape key. * NOTE: Currently only for object data data-blocks. */ GEOMETRY_SHAPEKEY, + /* Write geometry output into the runtime cache. */ + GEOMETRY_WRITE_CACHE, /* Object data. --------------------------------------------------------- */ LIGHT_PROBE_EVAL, diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 6806d715004..f4a5cc5f247 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1353,7 +1353,7 @@ static void std_node_socket_draw( } case SOCK_IMAGE: { const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; - if (node_tree->type == NTREE_GEOMETRY) { + if (ELEM(node_tree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { if (text[0] == '\0') { uiTemplateID(layout, C, @@ -1996,7 +1996,7 @@ void node_draw_link_bezier(const bContext &C, bTheme *btheme = UI_GetTheme(); const float dash_alpha = btheme->space_node.dash_alpha; - if (snode.edittree->type == NTREE_GEOMETRY) { + if (ELEM(snode.edittree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { if (link.fromsock && link.fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) { /* Make field links a bit thinner. */ thickness = 1.0f; @@ -2158,7 +2158,7 @@ void node_draw_link(const bContext &C, } } /* Links from field to non-field sockets are not allowed. */ - if (snode.edittree->type == NTREE_GEOMETRY && !(link.flag & NODE_LINK_DRAGGED)) { + if (ELEM(snode.edittree->type, NTREE_GEOMETRY, NTREE_PARTICLES) && !(link.flag & NODE_LINK_DRAGGED)) { if ((link.fromsock && link.fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) && (link.tosock && link.tosock->display_shape == SOCK_DISPLAY_SHAPE_CIRCLE)) { th_col1 = th_col2 = th_col3 = TH_REDALERT; diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 975d4eda7e3..8f190e8bf97 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -490,7 +490,7 @@ static int node_add_object_invoke(bContext *C, wmOperator *op, const wmEvent *ev static bool node_add_object_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && + return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY, NTREE_PARTICLES) && !UI_but_active_drop_name(C); } @@ -578,7 +578,7 @@ static int node_add_collection_invoke(bContext *C, wmOperator *op, const wmEvent static bool node_add_collection_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); - return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY) && + return ED_operator_node_editable(C) && ELEM(snode->nodetree->type, NTREE_GEOMETRY, NTREE_PARTICLES) && !UI_but_active_drop_name(C); } @@ -610,7 +610,7 @@ static bool node_add_file_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); return ED_operator_node_editable(C) && - ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY); + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY, NTREE_PARTICLES); } static int node_add_file_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc index b9bee3ed15e..bbdd9bc9e40 100644 --- a/source/blender/editors/space_node/node_context_path.cc +++ b/source/blender/editors/space_node/node_context_path.cc @@ -151,7 +151,7 @@ Vector context_path_for_space_node(const bContext &C) Vector context_path; - if (snode->edittree->type == NTREE_GEOMETRY) { + if (ELEM(snode->edittree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { get_context_path_node_geometry(C, *snode, context_path); } else if (snode->edittree->type == NTREE_SHADER) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 7003d51b2b6..563c5082af8 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -148,6 +148,9 @@ void ED_node_tag_update_id(ID *id) else if (ntree->type == NTREE_GEOMETRY) { WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); } + else if (ntree->type == NTREE_PARTICLES) { + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); + } else if (id == &ntree->id) { /* Node groups. */ DEG_id_tag_update(id, 0); @@ -1022,7 +1025,7 @@ static std::optional create_socket_inspection_string(bContext *C, static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) { - if (ntree->type == NTREE_GEOMETRY) { + if (ELEM(ntree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { return true; } @@ -1048,7 +1051,7 @@ static char *node_socket_get_tooltip(bContext *C, } } - if (ntree->type == NTREE_GEOMETRY) { + if (ELEM(ntree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { if (!output.str().empty()) { output << ".\n\n"; } @@ -1873,7 +1876,7 @@ static Vector node_get_extra_info(const SpaceNode &snode, cons } if (snode.overlay.flag & SN_OVERLAY_SHOW_NAMED_ATTRIBUTES && - snode.edittree->type == NTREE_GEOMETRY) { + ELEM(snode.edittree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { if (std::optional row = node_get_accessed_attributes_row(snode, node)) { rows.append(std::move(*row)); } diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index ffc9befc81c..e1ecd8d619c 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -422,6 +422,9 @@ static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree) else if (ntree->type == NTREE_GEOMETRY) { WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); } + else if (ntree->type == NTREE_PARTICLES) { + WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id); + } } /** \} */ @@ -813,6 +816,9 @@ void ED_node_set_active( } } } + else if (ntree->type == NTREE_PARTICLES) { + /* TODO particle viewer node takes point cloud from context and does not need geometry input */ + } } } diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index 160a379d3c6..fc450a867d2 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -65,7 +65,8 @@ static bool node_group_operator_active_poll(bContext *C) "ShaderNodeTree", "CompositorNodeTree", "TextureNodeTree", - "GeometryNodeTree")) { + "GeometryNodeTree", + "ParticleNodeTree")) { return true; } } diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index e10bedb18f4..8e5356ee18a 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -670,7 +670,7 @@ static int link_socket_to_viewer(const bContext &C, remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode); - if (btree.type == NTREE_GEOMETRY) { + if (ELEM(btree.type == NTREE_GEOMETRY, NTREE_PARTICLES)) { ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode); } diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 58a313c328e..7cc354318f7 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -877,7 +877,7 @@ static void ui_node_draw_input( case SOCK_STRING: { const bNodeTree *node_tree = (const bNodeTree *)nodeptr.owner_id; SpaceNode *snode = CTX_wm_space_node(C); - if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) { + if (ELEM(node_tree->type, NTREE_GEOMETRY, NTREE_PARTICLES) && snode != nullptr) { /* Only add the attribute search in the node editor, in other places there is not * enough context. */ node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub); diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 15afd024766..aef479816b5 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -62,7 +62,7 @@ void ED_node_tree_start(SpaceNode *snode, bNodeTree *ntree, ID *id, ID *from) BLI_addtail(&snode->treepath, path); - if (ntree->type != NTREE_GEOMETRY) { + if (!ELEM(ntree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { /* This can probably be removed for all node tree types. It mainly exists because it was not * possible to store id references in custom properties. Also see T36024. I don't want to * remove it for all tree types in bcon3 though. */ diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index ec9fa72edb1..6f396182352 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -392,7 +392,7 @@ void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspr if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; if (snode->edittree != nullptr) { - if (snode->edittree->type == NTREE_GEOMETRY) { + if (ELEM(snode->edittree->type, NTREE_GEOMETRY, NTREE_PARTICLES)) { LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { if (node->type == GEO_NODE_VIEWER) { if (node->flag & NODE_DO_OUTPUT) { diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 25c8a1f1514..c8d702d0e95 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -545,6 +545,7 @@ typedef struct bNodeTree { #define NTREE_COMPOSIT 1 #define NTREE_TEXTURE 2 #define NTREE_GEOMETRY 3 +#define NTREE_PARTICLES 4 /** #NodeTree.flag */ #define NTREE_DS_EXPAND (1 << 0) /* for animation editors */ @@ -1450,6 +1451,10 @@ typedef struct NodeGeometryViewer { int8_t data_type; } NodeGeometryViewer; +typedef struct NodeParticlesSetShape { + int shape_type; +} NodeParticlesSetShape; + typedef struct NodeFunctionCompare { /* NodeCompareOperation */ int8_t operation; @@ -2119,6 +2124,17 @@ typedef enum GeometryNodeScaleElementsMode { GEO_NODE_SCALE_ELEMENTS_SINGLE_AXIS = 1, } GeometryNodeScaleElementsMode; +typedef enum ParticleNodeShapeType { + PARTICLE_SHAPE_BOX, + PARTICLE_SHAPE_SPHERE, + PARTICLE_SHAPE_CAPSULE, + PARTICLE_SHAPE_CYLINDER, + PARTICLE_SHAPE_CONE, + PARTICLE_SHAPE_CONVEX_HULL, + PARTICLE_SHAPE_TRIMESH, + // PARTICLE_SHAPE_COMPOUND, +} ParticleNodeShapeType; + typedef enum NodeCombSepColorMode { NODE_COMBSEP_COLOR_RGB = 0, NODE_COMBSEP_COLOR_HSV = 1, diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 698fbe8ee8f..6cca68c6e75 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -166,6 +166,12 @@ typedef struct Object_Runtime { */ struct Mesh *mesh_deform_eval; + /* Runtime cache for iterative geometry. */ + struct GeometryCache *geometry_cache; + + /* Maps identifier values to rigid bodies in the simulation world. */ + struct RigidBodyMap *rigid_body_map; + /* Evaluated mesh cage in edit mode. */ struct Mesh *editmesh_eval_cage; @@ -206,7 +212,7 @@ typedef struct Object_Runtime { float (*crazyspace_deform_cos)[3]; int crazyspace_verts_num; - int _pad3[3]; + int _pad4[3]; } Object_Runtime; typedef struct ObjectLineArt { diff --git a/source/blender/makesdna/DNA_rigidbody_types.h b/source/blender/makesdna/DNA_rigidbody_types.h index c00900bb44c..493f13a6961 100644 --- a/source/blender/makesdna/DNA_rigidbody_types.h +++ b/source/blender/makesdna/DNA_rigidbody_types.h @@ -44,7 +44,7 @@ typedef struct RigidBodyWorld { /** Group containing objects to use for Rigid Bodies. */ struct Collection *group; - /** Array to access group objects by index, only used at runtime. */ + /** Array for the point cache to access group objects by index, only used at runtime. */ struct Object **objects; /** Group containing objects to use for Rigid Body Constraints. */ diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 8591d4abd63..d958ff74505 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -1662,7 +1662,7 @@ static bool rna_Modifier_show_expanded_get(PointerRNA *ptr) static bool rna_NodesModifier_node_group_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { bNodeTree *ntree = value.data; - return ntree->type == NTREE_GEOMETRY; + return ELEM(ntree->type, NTREE_GEOMETRY, NTREE_PARTICLES); } static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -1671,6 +1671,7 @@ static void rna_NodesModifier_node_group_update(Main *bmain, Scene *scene, Point NodesModifierData *nmd = ptr->data; rna_Modifier_dependency_update(bmain, scene, ptr); MOD_nodes_update_interface(object, nmd); + MOD_nodes_update_world(bmain, scene, object, nmd); } static IDProperty **rna_NodesModifier_properties(PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 65a8d8bf24a..3d9761ab84a 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2318,6 +2318,28 @@ static StructRNA *rna_FunctionNode_register(Main *bmain, return nt->rna_ext.srna; } +static StructRNA *rna_ParticleNode_register(Main *bmain, + ReportList *reports, + void *data, + const char *identifier, + StructValidateFunc validate, + StructCallbackFunc call, + StructFreeFunc free) +{ + bNodeType *nt = rna_Node_register_base( + bmain, reports, &RNA_ParticleNode, data, identifier, validate, call, free); + if (!nt) { + return NULL; + } + + nodeRegisterType(nt); + + /* update while blender is running */ + WM_main_add_notifier(NC_NODE | NA_EDITED, NULL); + + return nt->rna_ext.srna; +} + static IDProperty **rna_Node_idprops(PointerRNA *ptr) { bNode *node = ptr->data; @@ -10749,6 +10771,50 @@ static void def_geo_scale_elements(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); } +static void def_particles_set_shape(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem shape_type_items[] = { + {PARTICLE_SHAPE_BOX, + "BOX", + ICON_MESH_CUBE, + "Box", + "Box-like shapes (i.e. cubes), including planes (i.e. ground planes)"}, + {PARTICLE_SHAPE_SPHERE, "SPHERE", ICON_MESH_UVSPHERE, "Sphere", ""}, + {PARTICLE_SHAPE_CAPSULE, "CAPSULE", ICON_MESH_CAPSULE, "Capsule", ""}, + {PARTICLE_SHAPE_CYLINDER, "CYLINDER", ICON_MESH_CYLINDER, "Cylinder", ""}, + {PARTICLE_SHAPE_CONE, "CONE", ICON_MESH_CONE, "Cone", ""}, + {PARTICLE_SHAPE_CONVEX_HULL, + "CONVEX_HULL", + ICON_MESH_ICOSPHERE, + "Convex Hull", + "A mesh-like surface encompassing (i.e. shrinkwrap over) all vertices (best results with " + "fewer vertices)"}, + {PARTICLE_SHAPE_TRIMESH, + "MESH", + ICON_MESH_MONKEY, + "Mesh", + "Mesh consisting of triangles only, allowing for more detailed interactions than convex " + "hulls"}, + //{PARTICLE_SHAPE_COMPOUND, + // "COMPOUND", + // ICON_MESH_DATA, + // "Compound Parent", + // "Combines all of its direct rigid body children into one rigid object"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeParticlesSetShape", "storage"); + + prop = RNA_def_property(srna, "shape_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "shape_type"); + RNA_def_property_enum_items(prop, shape_type_items); + RNA_def_property_enum_default(prop, PARTICLE_SHAPE_SPHERE); + RNA_def_property_ui_text(prop, "Shape", "Collision shape of the particle"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -10808,6 +10874,16 @@ static void rna_def_function_node(BlenderRNA *brna) RNA_def_struct_register_funcs(srna, "rna_FunctionNode_register", "rna_Node_unregister", NULL); } +static void rna_def_particle_node(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "ParticleNode", "GeometryNode"); + RNA_def_struct_ui_text(srna, "Particle Node", ""); + RNA_def_struct_sdna(srna, "bNode"); + RNA_def_struct_register_funcs(srna, "rna_ParticleNode_register", "rna_Node_unregister", NULL); +} + /* -------------------------------------------------------------------------- */ static void rna_def_node_socket(BlenderRNA *brna) @@ -12428,6 +12504,7 @@ static void rna_def_nodetree(BlenderRNA *brna) {NTREE_TEXTURE, "TEXTURE", ICON_TEXTURE, "Texture", "Texture nodes"}, {NTREE_COMPOSIT, "COMPOSITING", ICON_RENDERLAYERS, "Compositing", "Compositing nodes"}, {NTREE_GEOMETRY, "GEOMETRY", ICON_GEOMETRY_NODES, "Geometry", "Geometry nodes"}, + {NTREE_PARTICLES, "PARTICLES", ICON_PARTICLE_DATA, "Particles", "Particle nodes"}, {0, NULL, 0, NULL, NULL}, }; @@ -12681,6 +12758,17 @@ static void rna_def_geometry_nodetree(BlenderRNA *brna) RNA_def_struct_ui_icon(srna, ICON_NODETREE); } +static void rna_def_particle_nodetree(BlenderRNA *brna) +{ + StructRNA *srna; + + srna = RNA_def_struct(brna, "ParticleNodeTree", "NodeTree"); + RNA_def_struct_ui_text( + srna, "Particle Node Tree", "Node tree consisting of linked nodes used for particles"); + RNA_def_struct_sdna(srna, "bNodeTree"); + RNA_def_struct_ui_icon(srna, ICON_NODETREE); +} + static StructRNA *define_specific_node(BlenderRNA *brna, const char *struct_name, const char *base_name, @@ -12769,6 +12857,7 @@ void RNA_def_nodetree(BlenderRNA *brna) rna_def_texture_node(brna); rna_def_geometry_node(brna); rna_def_function_node(brna); + rna_def_particle_node(brna); rna_def_nodetree(brna); @@ -12778,6 +12867,7 @@ void RNA_def_nodetree(BlenderRNA *brna) rna_def_shader_nodetree(brna); rna_def_texture_nodetree(brna); rna_def_geometry_nodetree(brna); + rna_def_particle_nodetree(brna); # define DefNode(Category, ID, DefFunc, EnumName, StructName, UIName, UIDesc) \ { \ diff --git a/source/blender/modifiers/MOD_nodes.h b/source/blender/modifiers/MOD_nodes.h index 4a3ccd8ecd1..7fb8d387f90 100644 --- a/source/blender/modifiers/MOD_nodes.h +++ b/source/blender/modifiers/MOD_nodes.h @@ -5,6 +5,7 @@ struct Main; struct NodesModifierData; struct Object; +struct RigidBodyMap; #ifdef __cplusplus extern "C" { @@ -17,6 +18,15 @@ extern "C" { */ void MOD_nodes_update_interface(struct Object *object, struct NodesModifierData *nmd); +/* Update simulation dependencies. */ +void MOD_nodes_update_world(struct Main *bmain, + struct Scene *scene, + struct Object *object, + struct NodesModifierData *nmd); + +/* Modifier needs rigid body simulation depsgraph nodes. */ +bool MOD_nodes_needs_rigid_body_sim(struct Object *object, struct NodesModifierData *nmd); + #ifdef __cplusplus } #endif diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 73db56186de..8785303b954 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -29,13 +29,16 @@ #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" +#include "DNA_rigidbody_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_windowmanager_types.h" #include "BKE_attribute_math.hh" +#include "BKE_collection.h" #include "BKE_customdata.h" +#include "BKE_geometry_cache.hh" #include "BKE_geometry_fields.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_global.h" @@ -49,6 +52,7 @@ #include "BKE_node_tree_update.h" #include "BKE_object.h" #include "BKE_pointcloud.h" +#include "BKE_rigidbody.h" #include "BKE_screen.h" #include "BKE_simulation.h" #include "BKE_workspace.h" @@ -122,6 +126,22 @@ using namespace blender::nodes::derived_node_tree_types; using geo_log::eNamedAttrUsage; using geo_log::GeometryAttributeInfo; +static bool nodes_need_cache(const bNodeTree &tree) +{ + return tree.type == NTREE_PARTICLES; +} + +static bool nodes_need_rigid_body_sim(const bNodeTree &tree) +{ + /* XXX Dummy, should be based on existence of certain nodes */ + return tree.type == NTREE_PARTICLES; +} + +bool MOD_nodes_needs_rigid_body_sim(Object *object, NodesModifierData *nmd) +{ + return nmd->node_group && nodes_need_rigid_body_sim(*nmd->node_group); +} + static void initData(ModifierData *md) { NodesModifierData *nmd = (NodesModifierData *)md; @@ -195,7 +215,8 @@ static bool node_needs_own_transform_relation(const bNode &node) static void process_nodes_for_depsgraph(const bNodeTree &tree, Set &ids, - bool &needs_own_transform_relation) + bool &needs_own_transform_relation, + bool &needs_rigid_body_sim) { Set handled_groups; @@ -206,11 +227,15 @@ static void process_nodes_for_depsgraph(const bNodeTree &tree, if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { const bNodeTree *group = (bNodeTree *)node->id; if (group != nullptr && handled_groups.add(group)) { - process_nodes_for_depsgraph(*group, ids, needs_own_transform_relation); + process_nodes_for_depsgraph( + *group, ids, needs_own_transform_relation, needs_rigid_body_sim); } } needs_own_transform_relation |= node_needs_own_transform_relation(*node); } + + /* XXX dummy */ + needs_rigid_body_sim = nodes_need_rigid_body_sim(tree); } static void find_used_ids_from_settings(const NodesModifierSettings &settings, Set &ids) @@ -266,9 +291,11 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_node_tree_output_relation(ctx->node, nmd->node_group, "Nodes Modifier"); bool needs_own_transform_relation = false; + bool needs_rigid_body_sim = false; Set used_ids; find_used_ids_from_settings(nmd->settings, used_ids); - process_nodes_for_depsgraph(*nmd->node_group, used_ids, needs_own_transform_relation); + process_nodes_for_depsgraph( + *nmd->node_group, used_ids, needs_own_transform_relation, needs_rigid_body_sim); for (ID *id : used_ids) { switch ((ID_Type)GS(id->name)) { case ID_OB: { @@ -296,6 +323,10 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte if (needs_own_transform_relation) { DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); } + + if (needs_rigid_body_sim) { + DEG_add_modifier_to_rigid_body_sim_relation(ctx->node, "Nodes Modifier"); + } } static bool check_tree_for_time_node(const bNodeTree &tree, @@ -325,8 +356,13 @@ static bool dependsOnTime(struct Scene *UNUSED(scene), ModifierData *md) if (tree == nullptr) { return false; } - Set checked_trees; - return check_tree_for_time_node(*tree, checked_trees); + if (tree->type == NTREE_PARTICLES) { + return true; + } + else { + Set checked_trees; + return check_tree_for_time_node(*tree, checked_trees); + } } static void foreachIDLink(ModifierData *md, Object *ob, IDWalkFunc walk, void *userData) @@ -749,6 +785,16 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); } +void MOD_nodes_update_world(Main *bmain, Scene *scene, Object *object, NodesModifierData *nmd) +{ + if (nmd->node_group && nodes_need_rigid_body_sim(*nmd->node_group)) { + BKE_rigidbody_add_nodes(bmain, scene, object, nullptr); + } + else { + BKE_rigidbody_remove_nodes(bmain, scene, object, false); + } +} + static void initialize_group_input(NodesModifierData &nmd, const OutputSocketRef &socket, void *r_value) @@ -1295,6 +1341,21 @@ static void modifyGeometrySet(ModifierData *md, const ModifierEvalContext *ctx, GeometrySet *geometry_set) { + const Scene *scene = DEG_get_input_scene(ctx->depsgraph); + GeometryCache::Timestamp timestamp{scene->r.cfra}; + + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group && nodes_need_cache(*nmd->node_group)) { + BLI_assert(ctx->object->id.orig_id); + Object *orig_ob = (Object *)ctx->object->id.orig_id; + if (GeometryCache *cache = orig_ob->runtime.geometry_cache) { + GeometrySet *cached_geometry_set = cache->get_before(timestamp); + if (cached_geometry_set) { + *geometry_set = *cached_geometry_set; + } + } + } + modifyGeometry(md, ctx, *geometry_set); } @@ -1813,10 +1874,10 @@ ModifierTypeInfo modifierType_Nodes = { /* srna */ &RNA_NodesModifier, /* type */ eModifierTypeType_Constructive, /* flags */ - static_cast(eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | - eModifierTypeFlag_SupportsEditmode | - eModifierTypeFlag_EnableInEditmode | - eModifierTypeFlag_SupportsMapping), + static_cast( + eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_AcceptsCVs | + eModifierTypeFlag_SupportsEditmode | eModifierTypeFlag_EnableInEditmode | + eModifierTypeFlag_SupportsMapping | eModifierTypeFlag_NeedCaching), /* icon */ ICON_GEOMETRY_NODES, /* copyData */ copyData, diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 386e5fe14c9..d87500d2f6f 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(composite) add_subdirectory(function) add_subdirectory(geometry) +add_subdirectory(particles) add_subdirectory(shader) add_subdirectory(texture) @@ -13,6 +14,7 @@ set(INC function geometry intern + particles shader texture ../editors/include @@ -65,6 +67,8 @@ set(SRC NOD_multi_function.hh NOD_node_declaration.hh NOD_node_tree_ref.hh + NOD_particles.h + NOD_particles_exec.hh NOD_shader.h NOD_socket.h NOD_socket_declarations.hh @@ -83,6 +87,7 @@ set(LIB bf_nodes_composite bf_nodes_function bf_nodes_geometry + bf_nodes_particles bf_nodes_shader bf_nodes_texture ) diff --git a/source/blender/nodes/NOD_particles.h b/source/blender/nodes/NOD_particles.h new file mode 100644 index 00000000000..1f54dd14f22 --- /dev/null +++ b/source/blender/nodes/NOD_particles.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BKE_node.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct bNodeTreeType *ntreeType_Particles; + +void register_node_tree_type_particles(void); + +void register_node_type_particles_add_shape(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/nodes/NOD_particles_exec.hh b/source/blender/nodes/NOD_particles_exec.hh new file mode 100644 index 00000000000..ecda6ed87c3 --- /dev/null +++ b/source/blender/nodes/NOD_particles_exec.hh @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field.hh" +#include "FN_multi_function_builder.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +struct Depsgraph; +struct ModifierData; + +namespace blender::nodes { + +} // namespace blender::nodes diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 9793f133dd6..b88d735c18f 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -400,6 +400,8 @@ DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") +DefNode(ParticleNode, PARTICLE_NODE_SET_SHAPE, def_particles_set_shape, "SET_SHAPE", SetShape, "Set Shape", "") + /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/function/node_function_util.cc b/source/blender/nodes/function/node_function_util.cc index d956f91a91d..5f26422bb30 100644 --- a/source/blender/nodes/function/node_function_util.cc +++ b/source/blender/nodes/function/node_function_util.cc @@ -10,7 +10,7 @@ static bool fn_node_poll_default(bNodeType *UNUSED(ntype), const char **r_disabled_hint) { /* Function nodes are only supported in simulation node trees so far. */ - if (!STREQ(ntree->idname, "GeometryNodeTree")) { + if (!STR_ELEM(ntree->idname, "GeometryNodeTree", "ParticleNodeTree")) { *r_disabled_hint = TIP_("Not a geometry node tree"); return false; } diff --git a/source/blender/nodes/geometry/node_geometry_util.cc b/source/blender/nodes/geometry/node_geometry_util.cc index 8f673d2264e..8fc2f37f6ca 100644 --- a/source/blender/nodes/geometry/node_geometry_util.cc +++ b/source/blender/nodes/geometry/node_geometry_util.cc @@ -45,7 +45,7 @@ bool geo_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { - if (!STREQ(ntree->idname, "GeometryNodeTree")) { + if (!STR_ELEM(ntree->idname, "GeometryNodeTree", "ParticleNodeTree")) { *r_disabled_hint = TIP_("Not a geometry node tree"); return false; } diff --git a/source/blender/nodes/particles/CMakeLists.txt b/source/blender/nodes/particles/CMakeLists.txt new file mode 100644 index 00000000000..406c7dcde67 --- /dev/null +++ b/source/blender/nodes/particles/CMakeLists.txt @@ -0,0 +1,120 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + .. + ../intern + ../../editors/include + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../functions + ../../geometry + ../../gpu + ../../imbuf + ../../makesdna + ../../makesrna + ../../render + ../../windowmanager + ../../../../intern/guardedalloc + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna +) + + +set(SRC + nodes/node_particles_add_shape.cc + + node_particles_exec.cc + node_particles_tree.cc + node_particles_util.cc + + node_particles_util.hh +) + +set(LIB + bf_functions + bf_geometry + bf_nodes +) + +if(WITH_BULLET) + list(APPEND INC_SYS + ${BULLET_INCLUDE_DIRS} + ../../../../intern/rigidbody + ) + if(NOT WITH_SYSTEM_BULLET) + list(APPEND LIB + extern_bullet + ) + endif() + + list(APPEND LIB + ${BULLET_LIBRARIES} + ) + add_definitions(-DWITH_BULLET) +endif() + +if(WITH_PYTHON) + list(APPEND INC + ../../python + ) + list(APPEND INC_SYS + ${PYTHON_INCLUDE_DIRS} + ) + list(APPEND LIB + ${PYTHON_LINKFLAGS} + ${PYTHON_LIBRARIES} + ) + add_definitions(-DWITH_PYTHON) +endif() + +if(WITH_TBB) + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_TBB) + if(WIN32) + # TBB includes Windows.h which will define min/max macros + # that will collide with the stl versions. + add_definitions(-DNOMINMAX) + endif() +endif() + +if(WITH_IMAGE_OPENEXR) + add_definitions(-DWITH_OPENEXR) +endif() + +if(WITH_OPENSUBDIV) + add_definitions(-DWITH_OPENSUBDIV) +endif() + +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + +blender_add_lib(bf_nodes_particles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_UNITY_BUILD) + set_target_properties(bf_nodes_particles PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_nodes_particles PROPERTIES UNITY_BUILD_BATCH_SIZE 10) +endif() + +# RNA_prototypes.h +add_dependencies(bf_nodes_particles bf_rna) diff --git a/source/blender/nodes/particles/node_particles_exec.cc b/source/blender/nodes/particles/node_particles_exec.cc new file mode 100644 index 00000000000..dd8fff2c0ef --- /dev/null +++ b/source/blender/nodes/particles/node_particles_exec.cc @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "NOD_particles_exec.hh" diff --git a/source/blender/nodes/particles/node_particles_tree.cc b/source/blender/nodes/particles/node_particles_tree.cc new file mode 100644 index 00000000000..c36cc41e7f0 --- /dev/null +++ b/source/blender/nodes/particles/node_particles_tree.cc @@ -0,0 +1,122 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include + +#include "MEM_guardedalloc.h" + +#include "NOD_particles.h" + +#include "BKE_context.h" +#include "BKE_node.h" +#include "BKE_object.h" + +#include "BLT_translation.h" + +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" +#include "DNA_space_types.h" + +#include "RNA_access.h" +#include "RNA_prototypes.h" + +#include "UI_resources.h" + +#include "node_common.h" + +bNodeTreeType *ntreeType_Particles; + +static void particle_node_tree_get_from_context(const bContext *C, + bNodeTreeType *UNUSED(treetype), + bNodeTree **r_ntree, + ID **r_id, + ID **r_from) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + + if (ob == nullptr) { + return; + } + + const ModifierData *md = BKE_object_active_modifier(ob); + + if (md == nullptr) { + return; + } + + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group != nullptr) { + *r_from = &ob->id; + *r_id = &ob->id; + *r_ntree = nmd->node_group; + } + } +} + +static void particle_node_tree_update(bNodeTree *ntree) +{ + ntreeSetOutput(ntree); + + /* Needed to give correct types to reroutes. */ + ntree_update_reroute_nodes(ntree); +} + +static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCallback func) +{ + func(calldata, NODE_CLASS_INPUT, N_("Input")); + func(calldata, NODE_CLASS_ATTRIBUTE, N_("Attribute")); + func(calldata, NODE_CLASS_OP_COLOR, N_("Color")); + func(calldata, NODE_CLASS_OP_VECTOR, N_("Vector")); + func(calldata, NODE_CLASS_CONVERTER, N_("Converter")); + func(calldata, NODE_CLASS_LAYOUT, N_("Layout")); +} + +static bool particle_node_tree_validate_link(eNodeSocketDatatype type_a, + eNodeSocketDatatype type_b) +{ + /* Geometry, string, object, material, texture and collection sockets can only be connected to + * themselves. The other types can be converted between each other. */ + if (ELEM(type_a, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT) && + ELEM(type_b, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA, SOCK_BOOLEAN, SOCK_INT)) { + return true; + } + return type_a == type_b; +} + +static bool particle_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype), + bNodeSocketType *socket_type) +{ + return nodeIsStaticSocketType(socket_type) && ELEM(socket_type->type, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_BOOLEAN, + SOCK_INT, + SOCK_STRING, + SOCK_OBJECT, + SOCK_GEOMETRY, + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_IMAGE, + SOCK_MATERIAL); +} + +void register_node_tree_type_particles() +{ + bNodeTreeType *tt = ntreeType_Particles = static_cast( + MEM_callocN(sizeof(bNodeTreeType), "particles node tree type")); + tt->type = NTREE_PARTICLES; + strcpy(tt->idname, "ParticleNodeTree"); + strcpy(tt->ui_name, N_("Particle Node Editor")); + tt->ui_icon = ICON_PARTICLE_DATA; + strcpy(tt->ui_description, N_("Particle nodes")); + tt->rna_ext.srna = &RNA_ParticleNodeTree; + tt->update = particle_node_tree_update; + tt->get_from_context = particle_node_tree_get_from_context; + tt->foreach_nodeclass = foreach_nodeclass; + tt->valid_socket_type = particle_node_tree_socket_type_valid; + tt->validate_link = particle_node_tree_validate_link; + + ntreeTypeAdd(tt); +} diff --git a/source/blender/nodes/particles/node_particles_util.cc b/source/blender/nodes/particles/node_particles_util.cc new file mode 100644 index 00000000000..16875d21e99 --- /dev/null +++ b/source/blender/nodes/particles/node_particles_util.cc @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "node_particles_util.hh" +#include "node_util.h" + +#include "NOD_socket_search_link.hh" + +bool particle_node_poll_default(bNodeType *UNUSED(ntype), + bNodeTree *ntree, + const char **r_disabled_hint) +{ + if (!STR_ELEM(ntree->idname, "ParticleNodeTree")) { + *r_disabled_hint = TIP_("Not a particle node tree"); + return false; + } + return true; +} + +void particle_node_type_base(bNodeType *ntype, int type, const char *name, short nclass) +{ + node_type_base(ntype, type, name, nclass); + ntype->poll = particle_node_poll_default; + ntype->insert_link = node_insert_link_default; + ntype->gather_link_search_ops = blender::nodes::search_link_ops_for_basic_node; +} diff --git a/source/blender/nodes/particles/node_particles_util.hh b/source/blender/nodes/particles/node_particles_util.hh new file mode 100644 index 00000000000..4e9a02fd580 --- /dev/null +++ b/source/blender/nodes/particles/node_particles_util.hh @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include + +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#include "MEM_guardedalloc.h" + +#include "DNA_node_types.h" + +#include "BKE_node.h" + +#include "BLT_translation.h" + +#include "NOD_particles.h" +#include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" + +#include "node_util.h" + +void particle_node_type_base(struct bNodeType *ntype, int type, const char *name, short nclass); +bool particle_node_poll_default(struct bNodeType *ntype, + struct bNodeTree *ntree, + const char **r_disabled_hint); diff --git a/source/blender/nodes/particles/nodes/node_particles_add_shape.cc b/source/blender/nodes/particles/nodes/node_particles_add_shape.cc new file mode 100644 index 00000000000..eaa46748bd5 --- /dev/null +++ b/source/blender/nodes/particles/nodes/node_particles_add_shape.cc @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_mesh_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" +#include "BKE_rigidbody.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "NOD_particles.h" +#include "NOD_geometry_exec.hh" +#include "NOD_socket_search_link.hh" + +#include "node_particles_util.hh" + +namespace blender::nodes::node_particles_set_shape_cc { + +NODE_STORAGE_FUNCS(NodeParticlesSetShape) + +static void node_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Particles")); + b.add_input(N_("Object")); + + b.add_output(N_("Particles")); +} + +static void node_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "shape_type", 0, "", ICON_NONE); +} + +static void node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeParticlesSetShape *data = MEM_cnew(__func__); + data->shape_type = PARTICLE_SHAPE_SPHERE; + node->storage = data; +} + +static void try_capture_field_on_geometry(GeometryComponent &component, + const StringRef name, + const eAttrDomain domain, + const GField &field) +{ + GeometryComponentFieldContext field_context{component, domain}; + const int domain_num = component.attribute_domain_num(domain); + const IndexMask mask{IndexMask(domain_num)}; + + const CPPType &type = field.cpp_type(); + const eCustomDataType data_type = bke::cpp_type_to_custom_data_type(type); + + /* Could avoid allocating a new buffer if: + * - We are writing to an attribute that exists already. + * - The field does not depend on that attribute (we can't easily check for that yet). */ + void *buffer = MEM_mallocN(type.size() * domain_num, __func__); + + fn::FieldEvaluator evaluator{field_context, &mask}; + evaluator.add_with_destination(field, GMutableSpan{type, buffer, domain_num}); + evaluator.evaluate(); + + component.attribute_try_delete(name); + if (component.attribute_exists(name)) { + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(name); + if (write_attribute && write_attribute.domain == domain && + write_attribute.varray.type() == type) { + write_attribute.varray.set_all(buffer); + write_attribute.tag_modified_fn(); + } + else { + /* Cannot change type of built-in attribute. */ + } + type.destruct_n(buffer, domain_num); + MEM_freeN(buffer); + } + else { + component.attribute_try_create(name, domain, data_type, AttributeInitMove{buffer}); + } +} + +static void node_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Particles"); + + params.used_named_attribute(particles::shape_index_attribute_name, eNamedAttrUsage::Write); + + const NodeParticlesSetShape &storage = node_storage(params.node()); + const ParticleNodeShapeType shape_type = static_cast(storage.shape_type); + + const int shape_index = 0; + Field shape_index_field{std::make_shared(CPPType::get(), &shape_index)}; + + /* Run on the instances component separately to only affect the top level of instances. */ + if (geometry_set.has_instances()) { + GeometryComponent &component = geometry_set.get_component_for_write( + GEO_COMPONENT_TYPE_INSTANCES); + try_capture_field_on_geometry( + component, particles::shape_index_attribute_name, ATTR_DOMAIN_INSTANCE, shape_index_field); + } + else { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + for (const GeometryComponentType type : + {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, + particles::shape_index_attribute_name, + ATTR_DOMAIN_POINT, + shape_index_field); + } + } + }); + } + + params.set_output("Particles", std::move(geometry_set)); +} + +} // namespace blender::nodes::node_particles_set_shape_cc + +void register_node_type_particles_add_shape() +{ + namespace file_ns = blender::nodes::node_particles_set_shape_cc; + + static bNodeType ntype; + + particle_node_type_base(&ntype, PARTICLE_NODE_SET_SHAPE, "Rigid Body Physics", NODE_CLASS_GEOMETRY); + node_type_init(&ntype, file_ns::node_init); + node_type_storage( + &ntype, "NodeParticlesSetShape", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = file_ns::node_declare; + ntype.geometry_node_execute = file_ns::node_exec; + ntype.draw_buttons = file_ns::node_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/node_shader_util.cc b/source/blender/nodes/shader/node_shader_util.cc index 059d7800fc5..99558a0a1f5 100644 --- a/source/blender/nodes/shader/node_shader_util.cc +++ b/source/blender/nodes/shader/node_shader_util.cc @@ -26,7 +26,7 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { - if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree")) { + if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree", "ParticleNodeTree")) { *r_disabled_hint = TIP_("Not a shader or geometry node tree"); return false; } diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 8a2b18d7d76..efc4e34af7b 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -50,7 +50,7 @@ static void sh_node_math_gather_link_searches(GatherLinkSearchOpParams ¶ms) return; } - const bool is_geometry_node_tree = params.node_tree().type == NTREE_GEOMETRY; + const bool is_geometry_node_tree = ELEM(params.node_tree().type, NTREE_GEOMETRY, NTREE_PARTICLES); const int weight = ELEM(params.other_socket().type, SOCK_FLOAT, SOCK_BOOLEAN, SOCK_INT) ? 0 : -1; for (const EnumPropertyItem *item = rna_enum_node_math_items; item->identifier != nullptr;