diff options
author | Hans Goudey <h.goudey@me.com> | 2021-02-03 20:59:12 +0300 |
---|---|---|
committer | Hans Goudey <h.goudey@me.com> | 2021-02-03 20:59:12 +0300 |
commit | d726aaec133480f977d044493af60125aaec4c9e (patch) | |
tree | 64a1e0dff83b1df7f3dafd4360de7fc1aed1359a | |
parent | c4076474695d096656fbc4a5b9c5df45a1620fc4 (diff) | |
parent | c5514d3a2a03242ddc43f83be4bb72df7f85469f (diff) |
Merge branch 'master' into temp-geometry-nodes-instances-api
45 files changed, 716 insertions, 336 deletions
diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 671417214d8..78e23ed75a6 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -385,25 +385,25 @@ USE_CXX11=true CLANG_FORMAT_VERSION_MIN="6.0" CLANG_FORMAT_VERSION_MAX="10.0" -PYTHON_VERSION="3.7.7" -PYTHON_VERSION_SHORT="3.7" +PYTHON_VERSION="3.9.1" +PYTHON_VERSION_SHORT="3.9" PYTHON_VERSION_MIN="3.7" -PYTHON_VERSION_MAX="3.9" +PYTHON_VERSION_MAX="3.10" PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_MIN PYTHON_FORCE_BUILD=false PYTHON_FORCE_REBUILD=false PYTHON_SKIP=false -NUMPY_VERSION="1.17.5" -NUMPY_VERSION_SHORT="1.17" +NUMPY_VERSION="1.19.5" +NUMPY_VERSION_SHORT="1.19" NUMPY_VERSION_MIN="1.8" NUMPY_VERSION_MAX="2.0" NUMPY_FORCE_BUILD=false NUMPY_FORCE_REBUILD=false NUMPY_SKIP=false -BOOST_VERSION="1.70.0" -BOOST_VERSION_SHORT="1.70" +BOOST_VERSION="1.73.0" +BOOST_VERSION_SHORT="1.73" BOOST_VERSION_MIN="1.49" BOOST_VERSION_MAX="2.0" BOOST_FORCE_BUILD=false @@ -439,7 +439,7 @@ _with_built_openexr=false OIIO_VERSION="2.1.15.0" OIIO_VERSION_SHORT="2.1" OIIO_VERSION_MIN="2.1.12" -OIIO_VERSION_MAX="3.0" +OIIO_VERSION_MAX="2.2.10" OIIO_FORCE_BUILD=false OIIO_FORCE_REBUILD=false OIIO_SKIP=false @@ -483,7 +483,7 @@ OPENVDB_FORCE_REBUILD=false OPENVDB_SKIP=false # Alembic needs to be compiled for now -ALEMBIC_VERSION="1.7.12" +ALEMBIC_VERSION="1.7.16" ALEMBIC_VERSION_SHORT="1.7" ALEMBIC_VERSION_MIN="1.7" ALEMBIC_VERSION_MAX="2.0" @@ -2064,7 +2064,6 @@ compile_OIIO() { cmake_d="$cmake_d -D CMAKE_PREFIX_PATH=$_inst" cmake_d="$cmake_d -D CMAKE_INSTALL_PREFIX=$_inst" cmake_d="$cmake_d -D STOP_ON_WARNING=OFF" - cmake_d="$cmake_d -D BUILDSTATIC=OFF" cmake_d="$cmake_d -D LINKSTATIC=OFF" cmake_d="$cmake_d -D USE_SIMD=sse2" @@ -2099,9 +2098,6 @@ compile_OIIO() { # if [ -d $INST/ocio ]; then # cmake_d="$cmake_d -D OCIO_PATH=$INST/ocio" # fi - cmake_d="$cmake_d -D USE_OCIO=OFF" - - cmake_d="$cmake_d -D OIIO_BUILD_CPP11=ON" if file /bin/cp | grep -q '32-bit'; then cflags="-fPIC -m32 -march=i686" diff --git a/release/darwin/Blender.app/Contents/Info.plist b/release/darwin/Blender.app/Contents/Info.plist index 2408cf363b1..67b786bd933 100644 --- a/release/darwin/Blender.app/Contents/Info.plist +++ b/release/darwin/Blender.app/Contents/Info.plist @@ -11,8 +11,6 @@ <array> <string>blend</string> </array> - <key>CFBundleTypeIconFile</key> - <string>blender file icon.icns</string> <key>CFBundleTypeName</key> <string>Blender File</string> <key>CFBundleTypeOSTypes</key> @@ -23,6 +21,12 @@ <string>Editor</string> <key>LSIsAppleDefaultForType</key> <true/> + <key>LSItemContentTypes</key> + <array> + <string>org.blenderfoundation.blender.file</string> + </array> + <key>CFBundleTypeIconSystemGenerated</key> + <true/> </dict> </array> <key>CFBundleExecutable</key> @@ -49,5 +53,30 @@ <string>NSApplication</string> <key>NSHighResolutionCapable</key> <true/> + <key>UTExportedTypeDeclarations</key> + <array> + <dict> + <key>UTTypeConformsTo</key> + <array> + <string>public.data</string> + </array> + <key>UTTypeIdentifier</key> + <string>org.blenderfoundation.blender.file</string> + <key>UTTypeIcons</key> + <dict> + <key>UTTypeIconName</key> + <string>Blender File</string> + <key>UTTypeIconText</key> + <string>blend</string> + </dict> + <key>UTTypeTagSpecification</key> + <dict> + <key>public.filename-extension</key> + <array> + <string>blend</string> + </array> + </dict> + </dict> + </array> </dict> </plist> diff --git a/release/darwin/Blender.app/Contents/Resources/blender file icon.icns b/release/darwin/Blender.app/Contents/Resources/blender file icon.icns Binary files differdeleted file mode 100644 index 2bf78d3eb0d..00000000000 --- a/release/darwin/Blender.app/Contents/Resources/blender file icon.icns +++ /dev/null diff --git a/release/scripts/startup/bl_ui/properties_physics_field.py b/release/scripts/startup/bl_ui/properties_physics_field.py index c8c49ee02b0..7e017b121b3 100644 --- a/release/scripts/startup/bl_ui/properties_physics_field.py +++ b/release/scripts/startup/bl_ui/properties_physics_field.py @@ -118,6 +118,9 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col.prop(field, "strength") + sub = col.column(heading="Affect") + sub.prop(field, "apply_to_location", text="Location") + col = flow.column() col.prop(field, "texture_nabla") col.prop(field, "use_object_coords") @@ -128,6 +131,10 @@ class PHYSICS_PT_field_settings(PhysicButtonsPanel, Panel): col.prop(field, "strength") col.prop(field, "flow") + sub = col.column(heading="Affect") + sub.prop(field, "apply_to_location", text="Location") + sub.prop(field, "apply_to_rotation", text="Rotation") + col = flow.column() col.prop(field, "source_object") col.prop(field, "use_smoke_density") diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 56f0b5c0ba4..f04a3f6eaf6 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -502,6 +502,7 @@ geometry_node_categories = [ ]), GeometryNodeCategory("GEO_INPUT", "Input", items=[ NodeItem("GeometryNodeObjectInfo"), + NodeItem("GeometryNodeCollectionInfo"), NodeItem("FunctionNodeRandomFloat"), NodeItem("ShaderNodeValue"), NodeItem("FunctionNodeInputVector"), diff --git a/source/blender/blenkernel/BKE_effect.h b/source/blender/blenkernel/BKE_effect.h index 3cba47afc46..231a4563630 100644 --- a/source/blender/blenkernel/BKE_effect.h +++ b/source/blender/blenkernel/BKE_effect.h @@ -121,7 +121,8 @@ void BKE_effector_relations_free(struct ListBase *lb); struct ListBase *BKE_effectors_create(struct Depsgraph *depsgraph, struct Object *ob_src, struct ParticleSystem *psys_src, - struct EffectorWeights *weights); + struct EffectorWeights *weights, + bool use_rotation); void BKE_effectors_apply(struct ListBase *effectors, struct ListBase *colliders, struct EffectorWeights *weights, diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index bfcfe84cbe7..17f116912fa 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -1364,6 +1364,7 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_POINT_SCALE 1020 #define GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE 1021 #define GEO_NODE_POINTS_TO_VOLUME 1022 +#define GEO_NODE_COLLECTION_INFO 1023 /** \} */ diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 0603ef85cc1..4c3b21482ea 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -203,23 +203,32 @@ UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, UndoStep *BKE_undosys_step_find_by_type(UndoStack *ustack, const UndoType *ut); UndoStep *BKE_undosys_step_find_by_name(UndoStack *ustack, const char *name); +int BKE_undosys_step_calc_direction(const UndoStack *ustack, + const UndoStep *us_target, + const UndoStep *us_reference); + +bool BKE_undosys_step_load_data_ex(UndoStack *ustack, + struct bContext *C, + UndoStep *us_target, + UndoStep *us_reference, + const bool use_skip); +bool BKE_undosys_step_load_data(UndoStack *ustack, struct bContext *C, UndoStep *us_target); +void BKE_undosys_step_load_from_index(UndoStack *ustack, struct bContext *C, const int index); + bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip); -bool BKE_undosys_step_undo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us); +bool BKE_undosys_step_undo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us_target); bool BKE_undosys_step_undo(UndoStack *ustack, struct bContext *C); bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, struct bContext *C, UndoStep *us, bool use_skip); -bool BKE_undosys_step_redo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us); +bool BKE_undosys_step_redo_with_data(UndoStack *ustack, struct bContext *C, UndoStep *us_target); bool BKE_undosys_step_redo(UndoStack *ustack, struct bContext *C); -bool BKE_undosys_step_load_data(UndoStack *ustack, struct bContext *C, UndoStep *us); - -void BKE_undosys_step_undo_from_index(UndoStack *ustack, struct bContext *C, int index); UndoStep *BKE_undosys_step_same_type_next(UndoStep *us); UndoStep *BKE_undosys_step_same_type_prev(UndoStep *us); diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c index c2d6d5c7594..09bd397cc78 100644 --- a/source/blender/blenkernel/intern/cloth.c +++ b/source/blender/blenkernel/intern/cloth.c @@ -285,7 +285,7 @@ static int do_step_cloth( mul_m4_v3(ob->obmat, verts->xconst); } - effectors = BKE_effectors_create(depsgraph, ob, NULL, clmd->sim_parms->effector_weights); + effectors = BKE_effectors_create(depsgraph, ob, NULL, clmd->sim_parms->effector_weights, false); if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_DYNAMIC_BASEMESH) { cloth_update_verts(ob, clmd, result); diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 811a27c9f3f..31860bbe0b4 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -5156,7 +5156,8 @@ static int dynamicPaint_prepareEffectStep(struct Depsgraph *depsgraph, /* Init force data if required */ if (surface->effect & MOD_DPAINT_EFFECT_DO_DRIP) { - ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, surface->effector_weights); + ListBase *effectors = BKE_effectors_create( + depsgraph, ob, NULL, surface->effector_weights, false); /* allocate memory for force data (dir vector + strength) */ *force = MEM_mallocN(sizeof(float[4]) * sData->total_points, "PaintEffectForces"); diff --git a/source/blender/blenkernel/intern/effect.c b/source/blender/blenkernel/intern/effect.c index 13e9bb1bf24..4104b6080c5 100644 --- a/source/blender/blenkernel/intern/effect.c +++ b/source/blender/blenkernel/intern/effect.c @@ -270,11 +270,71 @@ void BKE_effector_relations_free(ListBase *lb) } } +/* Check that the force field isn't disabled via its flags. */ +static bool is_effector_enabled(PartDeflect *pd, bool use_rotation) +{ + switch (pd->forcefield) { + case PFIELD_BOID: + case PFIELD_GUIDE: + return true; + + case PFIELD_TEXTURE: + return (pd->flag & PFIELD_DO_LOCATION) != 0 && pd->tex != NULL; + + default: + if (use_rotation) { + return (pd->flag & (PFIELD_DO_LOCATION | PFIELD_DO_ROTATION)) != 0; + } + else { + return (pd->flag & PFIELD_DO_LOCATION) != 0; + } + } +} + +/* Check that the force field won't have zero effect due to strength settings. */ +static bool is_effector_nonzero_strength(PartDeflect *pd) +{ + if (pd->f_strength != 0.0f) { + return true; + } + + if (pd->forcefield == PFIELD_TEXTURE) { + return false; + } + + if (pd->f_noise > 0.0f || pd->f_flow != 0.0f) { + return true; + } + + switch (pd->forcefield) { + case PFIELD_BOID: + case PFIELD_GUIDE: + return true; + + case PFIELD_VORTEX: + return pd->shape != PFIELD_SHAPE_POINT; + + case PFIELD_DRAG: + return pd->f_damp != 0.0f; + + default: + return false; + } +} + +/* Check if the force field will affect its user. */ +static bool is_effector_relevant(PartDeflect *pd, EffectorWeights *weights, bool use_rotation) +{ + return (weights->weight[pd->forcefield] != 0.0f) && is_effector_enabled(pd, use_rotation) && + is_effector_nonzero_strength(pd); +} + /* Create effective list of effectors from relations built beforehand. */ ListBase *BKE_effectors_create(Depsgraph *depsgraph, Object *ob_src, ParticleSystem *psys_src, - EffectorWeights *weights) + EffectorWeights *weights, + bool use_rotation) { Scene *scene = DEG_get_evaluated_scene(depsgraph); ListBase *relations = DEG_get_effector_relations(depsgraph, weights->group); @@ -299,7 +359,8 @@ ListBase *BKE_effectors_create(Depsgraph *depsgraph, } PartDeflect *pd = (relation->pd == relation->psys->part->pd) ? part->pd : part->pd2; - if (weights->weight[pd->forcefield] == 0.0f) { + + if (!is_effector_relevant(pd, weights, use_rotation)) { continue; } @@ -310,7 +371,7 @@ ListBase *BKE_effectors_create(Depsgraph *depsgraph, if (ob == ob_src) { continue; } - if (weights->weight[ob->pd->forcefield] == 0.0f) { + if (!is_effector_relevant(ob->pd, weights, use_rotation)) { continue; } if (ob->pd->shape == PFIELD_SHAPE_POINTS && BKE_object_get_evaluated_mesh(ob) == NULL) { @@ -903,7 +964,9 @@ static void do_texture_effector(EffectorCache *eff, madd_v3_v3fl(force, efd->nor, fac); } - add_v3_v3(total_force, force); + if (eff->pd->flag & PFIELD_DO_LOCATION) { + add_v3_v3(total_force, force); + } } static void do_physical_effector(EffectorCache *eff, EffectorData *efd, @@ -918,6 +981,7 @@ static void do_physical_effector(EffectorCache *eff, float strength = pd->f_strength; float damp = pd->f_damp; float noise_factor = pd->f_noise; + float flow_falloff = efd->falloff; if (noise_factor > 0.0f) { strength += wind_func(rng, noise_factor); @@ -1027,6 +1091,7 @@ static void do_physical_effector(EffectorCache *eff, break; case PFIELD_FLUIDFLOW: zero_v3(force); + flow_falloff = 0; #ifdef WITH_FLUID if (pd->f_source) { float density; @@ -1036,8 +1101,7 @@ static void do_physical_effector(EffectorCache *eff, influence *= density; } mul_v3_fl(force, influence); - /* apply flow */ - madd_v3_v3fl(total_force, point->vel, -pd->f_flow * influence); + flow_falloff = influence; } } #endif @@ -1047,9 +1111,8 @@ static void do_physical_effector(EffectorCache *eff, if (pd->flag & PFIELD_DO_LOCATION) { madd_v3_v3fl(total_force, force, 1.0f / point->vel_to_sec); - if (ELEM(pd->forcefield, PFIELD_HARMONIC, PFIELD_DRAG, PFIELD_FLUIDFLOW) == 0 && - pd->f_flow != 0.0f) { - madd_v3_v3fl(total_force, point->vel, -pd->f_flow * efd->falloff); + if (!ELEM(pd->forcefield, PFIELD_HARMONIC, PFIELD_DRAG) && pd->f_flow != 0.0f) { + madd_v3_v3fl(total_force, point->vel, -pd->f_flow * flow_falloff); } } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 59248e5f9f8..7fe009d51ca 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -3225,7 +3225,7 @@ static void update_effectors( ListBase *effectors; /* make sure smoke flow influence is 0.0f */ fds->effector_weights->weight[PFIELD_FLUIDFLOW] = 0.0f; - effectors = BKE_effectors_create(depsgraph, ob, NULL, fds->effector_weights); + effectors = BKE_effectors_create(depsgraph, ob, NULL, fds->effector_weights, false); if (effectors) { /* Precalculate wind forces. */ diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 02588bf144e..622f6d7a7b8 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -941,7 +941,7 @@ Mesh *BKE_mesh_new_nomain_from_template_ex(const Mesh *me_src, Mesh *me_dst = BKE_id_new_nomain(ID_ME, NULL); - me_dst->mselect = MEM_dupallocN(me_dst->mselect); + me_dst->mselect = MEM_dupallocN(me_src->mselect); me_dst->totvert = verts_len; me_dst->totedge = edges_len; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 39e23e34341..2dbfa85b8e1 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -3549,6 +3549,9 @@ void nodeSetSocketAvailability(bNodeSocket *sock, bool is_available) int nodeSocketLinkLimit(const bNodeSocket *sock) { bNodeSocketType *stype = sock->typeinfo; + if (sock->flag & SOCK_MULTI_INPUT) { + return 4095; + } if (stype != nullptr && stype->use_link_limits_of_type) { int limit = (sock->in_out == SOCK_IN) ? stype->input_link_limit : stype->output_link_limit; return limit; @@ -4770,6 +4773,7 @@ static void registerGeometryNodes() register_node_type_geo_align_rotation_to_vector(); register_node_type_geo_sample_texture(); register_node_type_geo_points_to_volume(); + register_node_type_geo_collection_info(); } static void registerFunctionNodes() diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 55d4043f96c..c727a144c87 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -1400,8 +1400,9 @@ void psys_update_particle_tree(ParticleSystem *psys, float cfra) static void psys_update_effectors(ParticleSimulationData *sim) { BKE_effectors_free(sim->psys->effectors); + bool use_rotation = (sim->psys->part->flag & PART_ROT_DYN) != 0; sim->psys->effectors = BKE_effectors_create( - sim->depsgraph, sim->ob, sim->psys, sim->psys->part->effector_weights); + sim->depsgraph, sim->ob, sim->psys, sim->psys->part->effector_weights, use_rotation); precalc_guides(sim, sim->psys->effectors); } diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 586aeb274a5..19078446009 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -1720,7 +1720,7 @@ static void rigidbody_update_sim_ob( ListBase *effectors; /* get effectors present in the group specified by effector_weights */ - effectors = BKE_effectors_create(depsgraph, ob, NULL, effector_weights); + effectors = BKE_effectors_create(depsgraph, ob, NULL, effector_weights, false); if (effectors) { float eff_force[3] = {0.0f, 0.0f, 0.0f}; float eff_loc[3], eff_vel[3]; diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index 736acd76dfd..de88e8a941c 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -1539,7 +1539,8 @@ static void sb_sfesf_threads_run(struct Depsgraph *depsgraph, * or even be UI option sb->spawn_cf_threads_nopts */ int lowsprings = 100; - ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, ob->soft->effector_weights); + ListBase *effectors = BKE_effectors_create( + depsgraph, ob, NULL, ob->soft->effector_weights, false); /* figure the number of threads while preventing pretty pointless threading overhead */ totthread = BKE_scene_num_threads(scene); @@ -2300,7 +2301,7 @@ static void softbody_calc_forces( } /* after spring scan because it uses Effoctors too */ - ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights); + ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights, false); if (do_deflector) { float defforce[3]; diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index 5f99f629f42..643510cf652 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -251,42 +251,10 @@ static void undosys_stack_validate(UndoStack *ustack, bool expect_non_empty) BLI_assert(!BLI_listbase_is_empty(&ustack->steps)); } } - -/* Return whether `us_item` is before (-1), after (1) or same as (0) `us_anchor` step. */ -static int undosys_stack_order(const UndoStack *ustack, - const UndoStep *us_anchor, - const UndoStep *us_item) -{ - const int index_anchor = BLI_findindex(&ustack->steps, us_anchor); - const int index_item = BLI_findindex(&ustack->steps, us_item); - BLI_assert(index_anchor >= 0); - BLI_assert(index_item >= 0); - - return (index_item == index_anchor) ? 0 : (index_item < index_anchor) ? -1 : 1; -} - -# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo) \ - { \ - const UndoStep *_us_anchor = (_ustack)->step_active; \ - BLI_assert(_us_anchor == NULL || \ - (undosys_stack_order((_ustack), _us_anchor, (_us_undo)) <= 0)); \ - } \ - (void)0 - -# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo) \ - { \ - const UndoStep *_us_anchor = (_ustack)->step_active; \ - BLI_assert(_us_anchor == NULL || \ - (undosys_stack_order((_ustack), _us_anchor, (_us_redo)) >= 0)); \ - } \ - (void)0 - #else static void undosys_stack_validate(UndoStack *UNUSED(ustack), bool UNUSED(expect_non_empty)) { } -# define ASSERT_VALID_UNDO_STEP(_ustack, _us_undo) -# define ASSERT_VALID_REDO_STEP(_ustack, _us_redo) #endif UndoStack *BKE_undosys_stack_create(void) @@ -701,37 +669,91 @@ UndoStep *BKE_undosys_step_find_by_type(UndoStack *ustack, const UndoType *ut) return NULL; } -bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, - bContext *C, - UndoStep *us, - bool use_skip) +/** + * Return direction of the undo/redo from `us_reference` (or `ustack->step_active` if NULL), and + * `us_target`. + * + * \note If `us_reference` and `us_target` are the same, we consider this is an undo. + * + * \return -1 for undo, 1 for redo, 0 in case of error. + */ +int BKE_undosys_step_calc_direction(const UndoStack *ustack, + const UndoStep *us_target, + const UndoStep *us_reference) +{ + if (us_reference == NULL) { + us_reference = ustack->step_active; + } + + BLI_assert(us_reference != NULL); + + /* Note that we use heuristics to make this lookup as fast as possible in most common cases, + * assuming that: + * - Most cases are just undo or redo of one step from active one. + * - Otherwise, it is typically faster to check future steps since active one is usually close + * to the end of the list, rather than its start. */ + /* NOTE: in case target step is the active one, we assume we are in an undo case... */ + if (ELEM(us_target, us_reference, us_reference->prev)) { + return -1; + } + if (us_target == us_reference->next) { + return 1; + } + + /* Search forward, and then backward. */ + for (UndoStep *us_iter = us_reference->next; us_iter != NULL; us_iter = us_iter->next) { + if (us_iter == us_target) { + return 1; + } + } + for (UndoStep *us_iter = us_reference->prev; us_iter != NULL; us_iter = us_iter->prev) { + if (us_iter == us_target) { + return -1; + } + } + + BLI_assert(!"Target undo step not found, this should not happen and may indicate an undo stack corruption"); + return 0; +} + +/** + * Undo/Redo until the given `us_target` step becomes the active (currently loaded) one. + * + * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the + * active step. + * + * \note In case `use_skip` is true, the final target will always be **beyond** the given one (if + * the given one has to be skipped). + * + * \param us_reference If NULL, will be set to current active step in the undo stack. Otherwise, it + * is assumed to match the current state, and will be used as basis for the + * undo/redo process (i.e. all steps in-between `us_reference` and `us_target` + * will be processed). + */ +bool BKE_undosys_step_load_data_ex(UndoStack *ustack, + bContext *C, + UndoStep *us_target, + UndoStep *us_reference, + const bool use_skip) { UNDO_NESTED_ASSERT(false); - if (us == NULL) { - CLOG_ERROR(&LOG, "called with a NULL step"); + if (us_target == NULL) { + CLOG_ERROR(&LOG, "called with a NULL target step"); return false; } undosys_stack_validate(ustack, true); - /* We expect to get next-from-actual-target step here (i.e. active step in case we only undo - * once)? - * FIXME: this is very confusing now that we may have to undo several steps anyway, this function - * should just get the target final step, not assume that it is getting the active one by default - * (or the step after the target one when undoing more than one step). */ - UndoStep *us_target = us->prev; - if (us_target == NULL) { - CLOG_ERROR(&LOG, "could not find a valid target step"); + if (us_reference == NULL) { + us_reference = ustack->step_active; + } + if (us_reference == NULL) { + CLOG_ERROR(&LOG, "could not find a valid initial active target step as reference"); return false; } - ASSERT_VALID_UNDO_STEP(ustack, us_target); - /* This will be active once complete. */ - UndoStep *us_active = us_target; - if (use_skip) { - while (us_active && us_active->skip) { - us_active = us_active->prev; - } - } + /* This considers we are in undo case if both `us_target` and `us_reference` are the same. */ + const int undo_dir = BKE_undosys_step_calc_direction(ustack, us_target, us_reference); + BLI_assert(undo_dir != 0); /* This will be the active step once the undo process is complete. * @@ -740,7 +762,7 @@ bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, UndoStep *us_target_active = us_target; if (use_skip) { while (us_target_active != NULL && us_target_active->skip) { - us_target_active = us_target_active->prev; + us_target_active = (undo_dir == -1) ? us_target_active->prev : us_target_active->next; } } if (us_target_active == NULL) { @@ -748,42 +770,47 @@ bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, return false; } - CLOG_INFO( - &LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name); + CLOG_INFO(&LOG, + 1, + "addr=%p, name='%s', type='%s', undo_dir=%d", + us_target, + us_target->name, + us_target->type->name, + undo_dir); - /* Undo steps until we reach original given target, if we do have a current active step. + /* Undo/Redo steps until we reach given target step (or beyond if it has to be skipped), from + * given reference step. * * NOTE: Unlike with redo case, where we can expect current active step to fully reflect current * data status, in undo case we also do reload the active step. * FIXME: this feels weak, and should probably not be actually needed? Or should also be done in * redo case? */ - if (ustack->step_active != NULL) { - for (UndoStep *us_iter = ustack->step_active; us_iter != us_target; us_iter = us_iter->prev) { - BLI_assert(us_iter != NULL); - undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, false); - ustack->step_active = us_iter; - } - } + bool is_processing_extra_skipped_steps = false; + for (UndoStep *us_iter = (undo_dir == -1) ? us_reference : us_reference->next; us_iter != NULL; + us_iter = (undo_dir == -1) ? us_iter->prev : us_iter->next) { + BLI_assert(us_iter != NULL); - /* Undo target step, and all potential extra ones if some steps have to be 'skipped'. */ - for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->prev) { const bool is_final = (us_iter == us_target_active); - if (!is_final) { + if (!is_final && is_processing_extra_skipped_steps) { BLI_assert(us_iter->skip == true); CLOG_INFO(&LOG, 2, - "undo continue with skip addr=%p, name='%s', type='%s'", + "undo/redo continue with skip addr=%p, name='%s', type='%s'", us_iter, us_iter->name, us_iter->type->name); } - undosys_step_decode(C, G_MAIN, ustack, us_iter, -1, is_final); + undosys_step_decode(C, G_MAIN, ustack, us_iter, undo_dir, is_final); ustack->step_active = us_iter; + if (us_iter == us_target) { + is_processing_extra_skipped_steps = true; + } + if (is_final) { - /* Undo process is finished and successful. */ + /* Undo/Redo process is finished and successful. */ return true; } } @@ -793,139 +820,117 @@ bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, return false; } -bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us) -{ - return BKE_undosys_step_undo_with_data_ex(ustack, C, us, true); -} - -bool BKE_undosys_step_undo(UndoStack *ustack, bContext *C) +/** + * Undo/Redo until the given `us_target` step becomes the active (currently loaded) one. + */ +bool BKE_undosys_step_load_data(UndoStack *ustack, bContext *C, UndoStep *us_target) { - return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active); + /* Note that here we do not skip 'skipped' steps by default. */ + return BKE_undosys_step_load_data_ex(ustack, C, us_target, NULL, false); } -void BKE_undosys_step_undo_from_index(UndoStack *ustack, bContext *C, int index) +/** + * Undo/Redo until the step matching given `index` in the undo stack becomes the active (currently + * loaded) one. + */ +void BKE_undosys_step_load_from_index(UndoStack *ustack, bContext *C, const int index) { - UndoStep *us = BLI_findlink(&ustack->steps, index); - BLI_assert(us->skip == false); - BKE_undosys_step_load_data(ustack, C, us); + UndoStep *us_target = BLI_findlink(&ustack->steps, index); + BLI_assert(us_target->skip == false); + BKE_undosys_step_load_data(ustack, C, us_target); } -bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, +/** + * Undo until `us_target` step becomes the active (currently loaded) one. + * + * \warning This function assumes that the given target step is _before_ current active one. + * + * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the + * active step. + * + * \note In case `use_skip` is true, the final target will always be **before** the given one (if + * the given one has to be skipped). + */ +bool BKE_undosys_step_undo_with_data_ex(UndoStack *ustack, bContext *C, - UndoStep *us, + UndoStep *us_target, bool use_skip) { - UNDO_NESTED_ASSERT(false); - if (us == NULL) { - CLOG_ERROR(&LOG, "called with a NULL step"); - return false; - } - undosys_stack_validate(ustack, true); + /* In case there is no active step, we consider we just load given step, so reference must be + * itself (due to weird 'load current active step in undo case' thing, see comments in + * #BKE_undosys_step_load_data_ex). */ + UndoStep *us_reference = ustack->step_active != NULL ? ustack->step_active : us_target; - /* We expect to get previous-from-actual-target step here (i.e. active step in case we only redo - * once)? - * FIXME: this is very confusing now that we may have to redo several steps anyway, this function - * should just get the target final step, not assume that it is getting the active one by default - * (or the step before the target one when redoing more than one step). */ - UndoStep *us_target = us->next; - if (us_target == NULL) { - CLOG_ERROR(&LOG, "could not find a valid target step"); - return false; - } - ASSERT_VALID_REDO_STEP(ustack, us_target); + BLI_assert(BKE_undosys_step_calc_direction(ustack, us_target, us_reference) == -1); - /* This will be the active step once the redo process is complete. - * - * In case we do skip 'skipped' steps, the final active step may be several steps forward the one - * passed as parameter. */ - UndoStep *us_target_active = us_target; - if (use_skip) { - while (us_target_active != NULL && us_target_active->skip) { - us_target_active = us_target_active->next; - } - } - if (us_target_active == NULL) { - CLOG_ERROR(&LOG, "could not find a valid final active target step"); - return false; - } + return BKE_undosys_step_load_data_ex(ustack, C, us_target, us_reference, use_skip); +} - CLOG_INFO( - &LOG, 1, "addr=%p, name='%s', type='%s'", us_target, us_target->name, us_target->type->name); +/** + * Undo until `us_target` step becomes the active (currently loaded) one. + * + * \note See #BKE_undosys_step_undo_with_data_ex for details. + */ +bool BKE_undosys_step_undo_with_data(UndoStack *ustack, bContext *C, UndoStep *us_target) +{ + return BKE_undosys_step_undo_with_data_ex(ustack, C, us_target, true); +} - /* Redo steps until we reach original given target, if we do have a current active step. */ +/** + * Undo one step from current active (currently loaded) one. + */ +bool BKE_undosys_step_undo(UndoStack *ustack, bContext *C) +{ if (ustack->step_active != NULL) { - for (UndoStep *us_iter = ustack->step_active->next; us_iter != us_target; - us_iter = us_iter->next) { - BLI_assert(us_iter != NULL); - undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, false); - ustack->step_active = us_iter; - } + return BKE_undosys_step_undo_with_data(ustack, C, ustack->step_active->prev); } - - /* Redo target step, and all potential extra ones if some steps have to be 'skipped'. */ - for (UndoStep *us_iter = us_target; us_iter != NULL; us_iter = us_iter->next) { - const bool is_final = (us_iter == us_target_active); - - if (!is_final) { - BLI_assert(us_iter->skip == true); - CLOG_INFO(&LOG, - 2, - "redo continue with skip addr=%p, name='%s', type='%s'", - us_iter, - us_iter->name, - us_iter->type->name); - } - - undosys_step_decode(C, G_MAIN, ustack, us_iter, 1, is_final); - ustack->step_active = us_iter; - - if (is_final) { - /* Redo process is finished and successful. */ - return true; - } - } - - BLI_assert( - !"This should never be reached, either undo stack is corrupted, or code above is buggy"); return false; } -bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us) +/** + * Redo until `us_target` step becomes the active (currently loaded) one. + * + * \warning This function assumes that the given target step is _after_ current active one. + * + * \note Unless `us_target` is a 'skipped' one and `use_skip` is true, `us_target` will become the + * active step. + * + * \note In case `use_skip` is true, the final target will always be **after** the given one (if + * the given one has to be skipped). + */ +bool BKE_undosys_step_redo_with_data_ex(UndoStack *ustack, + bContext *C, + UndoStep *us_target, + bool use_skip) { - return BKE_undosys_step_redo_with_data_ex(ustack, C, us, true); + /* In case there is no active step, we consider we just load given step, so reference must be + * the previous one. */ + UndoStep *us_reference = ustack->step_active != NULL ? ustack->step_active : us_target->prev; + + BLI_assert(BKE_undosys_step_calc_direction(ustack, us_target, us_reference) == 1); + + return BKE_undosys_step_load_data_ex(ustack, C, us_target, us_reference, use_skip); } -bool BKE_undosys_step_redo(UndoStack *ustack, bContext *C) +/** + * Redo until `us_target` step becomes the active (currently loaded) one. + * + * \note See #BKE_undosys_step_redo_with_data_ex for details. + */ +bool BKE_undosys_step_redo_with_data(UndoStack *ustack, bContext *C, UndoStep *us_target) { - return BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active); + return BKE_undosys_step_redo_with_data_ex(ustack, C, us_target, true); } -bool BKE_undosys_step_load_data(UndoStack *ustack, bContext *C, UndoStep *us) +/** + * Redo one step from current active one. + */ +bool BKE_undosys_step_redo(UndoStack *ustack, bContext *C) { - UNDO_NESTED_ASSERT(false); - const int index_active = BLI_findindex(&ustack->steps, ustack->step_active); - const int index_target = BLI_findindex(&ustack->steps, us); - BLI_assert(!ELEM(-1, index_active, index_target)); - bool ok = true; - - if (index_target < index_active) { - uint i = index_active - index_target; - while (i-- && ok) { - ok = BKE_undosys_step_undo_with_data_ex(ustack, C, ustack->step_active, false); - } - } - else if (index_target > index_active) { - uint i = index_target - index_active; - while (i-- && ok) { - ok = BKE_undosys_step_redo_with_data_ex(ustack, C, ustack->step_active, false); - } - } - - if (ok) { - BLI_assert(ustack->step_active == us); + if (ustack->step_active != NULL) { + return BKE_undosys_step_redo_with_data(ustack, C, ustack->step_active->next); } - - return ok; + return false; } /** diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index 4ba533c72aa..b770a267eee 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -1636,9 +1636,9 @@ float BLI_noise_mg_ridged_multi_fractal(float x, float signal = powf(offset - fabsf(noisefunc(x, y, z)), 2); float result = signal; + float pwHL = powf(lacunarity, -H); + float pwr = pwHL; /* starts with i=1 instead of 0 */ for (int i = 1; i < (int)octaves; i++) { - float pwHL = powf(lacunarity, -H); - float pwr = pwHL; /* starts with i=1 instead of 0 */ x *= lacunarity; y *= lacunarity; z *= lacunarity; diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 1d7c5d8a1d3..7dbe73cbd6a 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -129,7 +129,9 @@ BlendHandle *BLO_blendhandle_from_memory(const void *mem, int memsize); struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, - int *tot_names); + + const bool use_assets_only, + int *r_tot_names); struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, int *tot_info_items); diff --git a/source/blender/blenloader/intern/blend_validate.c b/source/blender/blenloader/intern/blend_validate.c index c281e2fa643..ea3ecc21ecf 100644 --- a/source/blender/blenloader/intern/blend_validate.c +++ b/source/blender/blenloader/intern/blend_validate.c @@ -112,7 +112,7 @@ bool BLO_main_validate_libraries(Main *bmain, ReportList *reports) } int totnames = 0; - LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), &totnames); + LinkNode *names = BLO_blendhandle_get_datablock_names(bh, GS(id->name), false, &totnames); for (; id != NULL; id = id->next) { if (id->lib == NULL) { is_valid = false; diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 296480fc2e4..f1b15b61d06 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -135,9 +135,14 @@ void BLO_blendhandle_print_sizes(BlendHandle *bh, void *fp) * \param bh: The blendhandle to access. * \param ofblocktype: The type of names to get. * \param tot_names: The length of the returned list. + * \param use_assets_only: Only list IDs marked as assets. * \return A BLI_linklist of strings. The string links should be freed with #MEM_freeN(). */ -LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, int *tot_names) +LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, + int ofblocktype, + + const bool use_assets_only, + int *r_tot_names) { FileData *fd = (FileData *)bh; LinkNode *names = NULL; @@ -147,6 +152,9 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { if (bhead->code == ofblocktype) { const char *idname = blo_bhead_id_name(fd, bhead); + if (use_assets_only && blo_bhead_id_asset_data_address(fd, bhead) == NULL) { + continue; + } BLI_linklist_prepend(&names, BLI_strdup(idname + 2)); tot++; @@ -156,7 +164,7 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, int ofblocktype, } } - *tot_names = tot; + *r_tot_names = tot; return names; } diff --git a/source/blender/blentranslation/intern/blt_lang.c b/source/blender/blentranslation/intern/blt_lang.c index bd0352d3e80..6de1d85a6a8 100644 --- a/source/blender/blentranslation/intern/blt_lang.c +++ b/source/blender/blentranslation/intern/blt_lang.c @@ -48,10 +48,6 @@ #include "MEM_guardedalloc.h" -/* Cached IME support flags */ -static bool ime_is_lang_supported = false; -static void blt_lang_check_ime_supported(void); - #ifdef WITH_INTERNATIONAL # include "BLI_fileops.h" @@ -286,7 +282,6 @@ void BLT_lang_set(const char *str) #else (void)str; #endif - blt_lang_check_ime_supported(); IMB_thumb_clear_translations(); } @@ -380,24 +375,13 @@ void BLT_lang_locale_explode(const char *locale, } } -/** - * Test if the translation context allows IME input - used to - * avoid weird character drawing if IME inputs non-ascii chars. - */ -static void blt_lang_check_ime_supported(void) -{ -#ifdef WITH_INPUT_IME - const char *uilng = BLT_lang_get(); - ime_is_lang_supported = STR_ELEM(uilng, "zh_CN", "zh_TW", "ja_JP"); -#else - ime_is_lang_supported = false; -#endif -} - +/* Note that "lang" here is the _output_ display language. We used to restrict + * IME for keyboard _input_ language because our multilingual font was only used + * when some output languages were selected. That font is used all the time now. */ bool BLT_lang_is_ime_supported(void) { #ifdef WITH_INPUT_IME - return ime_is_lang_supported; + return true; #else return false; #endif diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 31e54e371b1..13a3f1766a9 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -503,7 +503,7 @@ static void eevee_cryptomatte_postprocess_weights(EEVEE_Data *vedata) volumetric_transmittance_buffer = GPU_texture_read( txl->volume_transmittance_accum, GPU_DATA_FLOAT, 0); } - const int num_samples = effects->taa_current_sample; + const int num_samples = effects->taa_current_sample - 1; int accum_pixel_index = 0; int accum_pixel_stride = eevee_cryptomatte_pixel_stride(view_layer); diff --git a/source/blender/draw/engines/eevee/eevee_renderpasses.c b/source/blender/draw/engines/eevee/eevee_renderpasses.c index ce5292fbbb0..52160248d75 100644 --- a/source/blender/draw/engines/eevee/eevee_renderpasses.c +++ b/source/blender/draw/engines/eevee/eevee_renderpasses.c @@ -282,7 +282,9 @@ void EEVEE_renderpasses_postprocess(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_PrivateData *g_data = stl->g_data; EEVEE_EffectsInfo *effects = stl->effects; - const int current_sample = effects->taa_current_sample; + /* Compensate for taa_current_sample being incremented after last drawing in + * EEVEE_temporal_sampling_draw. */ + const int current_sample = effects->taa_current_sample - 1; g_data->renderpass_current_sample = current_sample; g_data->renderpass_type = renderpass_type; g_data->renderpass_postprocess = PASS_POST_UNDEFINED; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 7905483fac9..1d66119d569 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3374,6 +3374,13 @@ static void node_geometry_buts_points_to_volume(uiLayout *layout, uiItemR(layout, ptr, "input_type_radius", DEFAULT_FLAGS, IFACE_("Radius"), ICON_NONE); } +static void node_geometry_buts_collection_info(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "transform_space", UI_ITEM_R_EXPAND, NULL, ICON_NONE); +} + static void node_geometry_set_butfunc(bNodeType *ntype) { switch (ntype->type) { @@ -3434,6 +3441,9 @@ static void node_geometry_set_butfunc(bNodeType *ntype) case GEO_NODE_POINTS_TO_VOLUME: ntype->draw_buttons = node_geometry_buts_points_to_volume; break; + case GEO_NODE_COLLECTION_INFO: + ntype->draw_buttons = node_geometry_buts_collection_info; + break; } } diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index a2b04fa9665..51333fd5a09 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -51,6 +51,7 @@ typedef struct bNodeLinkDrag { * This way the links can be added to the node tree while being stored in this list. */ ListBase links; + bool from_multi_input_socket; int in_out; } bNodeLinkDrag; diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index ee7c8bca2f8..0744adb1371 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -836,31 +836,38 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor nldrag = MEM_callocN(sizeof(bNodeLinkDrag), "drag link op customdata"); const int num_links = nodeCountSocketLinks(snode->edittree, sock); - int link_limit = nodeSocketLinkLimit(sock); - if (num_links > 0 && (num_links >= link_limit || detach)) { + if (num_links > 0) { /* dragged links are fixed on output side */ nldrag->in_out = SOCK_OUT; /* detach current links and store them in the operator data */ + bNodeLink *link_to_pick; LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &snode->edittree->links) { if (link->tosock == sock) { - LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); - bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); - linkdata->data = oplink; - *oplink = *link; - oplink->next = oplink->prev = NULL; - oplink->flag |= NODE_LINK_VALID; - oplink->flag &= ~NODE_LINK_TEST; - if (node_connected_to_output(bmain, snode->edittree, link->tonode)) { - oplink->flag |= NODE_LINK_TEST; + if (sock->flag & SOCK_MULTI_INPUT) { + nldrag->from_multi_input_socket = true; } + link_to_pick = link; + } + } - BLI_addtail(&nldrag->links, linkdata); - nodeRemLink(snode->edittree, link); + if (link_to_pick != NULL && !nldrag->from_multi_input_socket) { + LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data"); + bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link"); + linkdata->data = oplink; + *oplink = *link_to_pick; + oplink->next = oplink->prev = NULL; + oplink->flag |= NODE_LINK_VALID; + oplink->flag &= ~NODE_LINK_TEST; + if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { + oplink->flag |= NODE_LINK_TEST; + } - /* send changed event to original link->tonode */ - if (node) { - snode_update(snode, node); - } + BLI_addtail(&nldrag->links, linkdata); + nodeRemLink(snode->edittree, link_to_pick); + + /* send changed event to original link->tonode */ + if (node) { + snode_update(snode, node); } } } @@ -896,6 +903,8 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) float cursor[2]; UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + RNA_float_set_array(op->ptr, "drag_start", cursor); + RNA_boolean_set(op->ptr, "has_link_picked", false); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); @@ -941,7 +950,28 @@ void NODE_OT_link(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + PropertyRNA *prop; + RNA_def_boolean(ot->srna, "detach", false, "Detach", "Detach and redirect existing links"); + prop = RNA_def_boolean( + ot->srna, + "has_link_picked", + false, + "Has Link Picked", + "The operation has placed a link. Only used for multi-input sockets, where the " + "link is picked later"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_float_array(ot->srna, + "drag_start", + 2, + 0, + -UI_PRECISION_FLOAT_MAX, + UI_PRECISION_FLOAT_MAX, + "Drag Start", + "The position of the mouse cursor at the start of the operation.", + -UI_PRECISION_FLOAT_MAX, + UI_PRECISION_FLOAT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /* ********************** Make Link operator ***************** */ diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index b6001b8b014..0771d0254e8 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -297,8 +297,7 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis /** Undo the step matching given name. * May undo several steps at once. - * The target step will be the one immediately before given named one. - * Only supposed to undo (will assert in case given named step is after current active one). */ + * The target step will be the one immediately before given named one. */ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList *reports) { BLI_assert(undo_name != NULL); @@ -316,28 +315,26 @@ static int ed_undo_step_by_name(bContext *C, const char *undo_name, ReportList * return OPERATOR_CANCELLED; } - /* TODO(campbell), could use simple optimization. */ - /* Pointers match on redo. */ - const int target_step_index = BLI_findindex(&wm->undo_stack->steps, undo_step_from_name); - const int active_step_index = BLI_findindex(&wm->undo_stack->steps, wm->undo_stack->step_active); - /* NOTE: when current and target active steps are the same, we are in undo case. */ - const enum eUndoStepDir undo_dir = (target_step_index <= active_step_index) ? STEP_UNDO : - STEP_REDO; + UndoStep *undo_step_target = undo_step_from_name->prev; + if (undo_step_target == NULL) { + CLOG_ERROR(&LOG, "Step name='%s' cannot be undone", undo_name); + + return OPERATOR_CANCELLED; + } + + const int undo_dir_i = BKE_undosys_step_calc_direction(wm->undo_stack, undo_step_target, NULL); + BLI_assert(ELEM(undo_dir_i, -1, 1)); + const enum eUndoStepDir undo_dir = (undo_dir_i == -1) ? STEP_UNDO : STEP_REDO; CLOG_INFO(&LOG, 1, - "name='%s', found direction=%s, index=%d", + "name='%s', found direction=%s", undo_name, - (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO", - target_step_index); - - /* This function is currently not supposed to redo ever. - * TODO: Will be fixed in future in continuing undo code refactor effort. */ - BLI_assert(undo_dir == STEP_UNDO); + (undo_dir == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); ed_undo_step_pre(C, wm, undo_dir, reports); - BKE_undosys_step_undo_with_data(wm->undo_stack, C, undo_step_from_name); + BKE_undosys_step_load_data_ex(wm->undo_stack, C, undo_step_target, NULL, true); ed_undo_step_post(C, wm, undo_dir, reports); @@ -368,7 +365,7 @@ static int ed_undo_step_by_index(bContext *C, const int undo_index, ReportList * ed_undo_step_pre(C, wm, undo_dir, reports); - BKE_undosys_step_undo_from_index(wm->undo_stack, C, undo_index); + BKE_undosys_step_load_from_index(wm->undo_stack, C, undo_index); ed_undo_step_post(C, wm, undo_dir, reports); diff --git a/source/blender/gpu/opengl/gl_drawlist.cc b/source/blender/gpu/opengl/gl_drawlist.cc index 0270020b9d8..d3401036154 100644 --- a/source/blender/gpu/opengl/gl_drawlist.cc +++ b/source/blender/gpu/opengl/gl_drawlist.cc @@ -105,7 +105,8 @@ void GLDrawList::init() glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffer_id_); /* If buffer is full, orphan buffer data and start fresh. */ - if (data_offset_ >= buffer_size_) { + size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); + if (data_offset_ + command_size > buffer_size_) { glBufferData(GL_DRAW_INDIRECT_BUFFER, buffer_size_, nullptr, GL_DYNAMIC_DRAW); data_offset_ = 0; } @@ -152,7 +153,6 @@ void GLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count) cmd->i_count = i_count; cmd->base_index = base_index_; cmd->i_first = i_first; - command_offset_ += sizeof(GLDrawCommandIndexed); } else { GLDrawCommand *cmd = reinterpret_cast<GLDrawCommand *>(data_ + command_offset_); @@ -160,12 +160,15 @@ void GLDrawList::append(GPUBatch *gpu_batch, int i_first, int i_count) cmd->v_count = v_count_; cmd->i_count = i_count; cmd->i_first = i_first; - command_offset_ += sizeof(GLDrawCommand); } + size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); + + command_offset_ += command_size; command_len_++; - if (command_offset_ >= data_size_) { + /* Check if we can fit at least one other command. */ + if (command_offset_ + command_size > data_size_) { this->submit(); } } @@ -180,10 +183,12 @@ void GLDrawList::submit() BLI_assert(data_); BLI_assert(GLContext::get()->shader != nullptr); + size_t command_size = MDI_INDEXED ? sizeof(GLDrawCommandIndexed) : sizeof(GLDrawCommand); + /* Only do multi-draw indirect if doing more than 2 drawcall. This avoids the overhead of * buffer mapping if scene is not very instance friendly. BUT we also need to take into * account the case where only a few instances are needed to finish filling a call buffer. */ - const bool is_finishing_a_buffer = (command_offset_ >= data_size_); + const bool is_finishing_a_buffer = (command_offset_ + command_size > data_size_); if (command_len_ > 2 || is_finishing_a_buffer) { GLenum prim = to_gl(batch_->prim_type); void *offset = (void *)data_offset_; diff --git a/source/blender/gpu/opengl/gl_framebuffer.cc b/source/blender/gpu/opengl/gl_framebuffer.cc index cbb332388dc..aea19295311 100644 --- a/source/blender/gpu/opengl/gl_framebuffer.cc +++ b/source/blender/gpu/opengl/gl_framebuffer.cc @@ -142,13 +142,13 @@ bool GLFrameBuffer::check(char err_out[256]) #undef FORMAT_STATUS - const char *format = "GPUFrameBuffer: frame-buffer status %s\n"; + const char *format = "GPUFrameBuffer: %s status %s\n"; if (err_out) { - BLI_snprintf(err_out, 256, format, err); + BLI_snprintf(err_out, 256, format, this->name_, err); } else { - fprintf(stderr, format, err); + fprintf(stderr, format, this->name_, err); } return false; diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index 486db07597f..0d1fa354b3e 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -57,7 +57,7 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const /* Note: we should handle all previews for a same group at once, would avoid reopening * `.blend` file for each and every ID. However, this adds some complexity, * so keep it for later. */ - names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames); + names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, false, &nnames); previews = BLO_blendhandle_get_previews(libfiledata, idcode, &nprevs); BLO_blendhandle_close(libfiledata); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 0c92099e23b..a69af18ded2 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -200,6 +200,8 @@ typedef enum eNodeSocketFlag { SOCK_NO_INTERNAL_LINK = (1 << 9), /** Draw socket in a more compact form. */ SOCK_COMPACT = (1 << 10), + /** Make the input socket accept multiple incoming links in the UI. */ + SOCK_MULTI_INPUT = (1 << 11), } eNodeSocketFlag; /* limit data in bNode to what we want to see saved? */ @@ -1193,6 +1195,13 @@ typedef struct NodeGeometryPointsToVolume { char _pad[6]; } NodeGeometryPointsToVolume; +typedef struct NodeGeometryCollectionInfo { + /* GeometryNodeTransformSpace. */ + uint8_t transform_space; + + char _pad[7]; +} NodeGeometryCollectionInfo; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index a5a73d9d8ca..ab6d4fa985c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -8911,6 +8911,33 @@ static void def_geo_points_to_volume(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_collection_info(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem rna_node_geometry_collection_info_transform_space_items[] = { + {GEO_NODE_TRANSFORM_SPACE_ORIGINAL, + "ORIGINAL", + 0, + "Original", + "Output the geometry relative to the collection offset"}, + {GEO_NODE_TRANSFORM_SPACE_RELATIVE, + "RELATIVE", + 0, + "Relative", + "Bring the input collection geometry into the modified object, maintaining the relative " + "position between the objects in the scene."}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCollectionInfo", "storage"); + + prop = RNA_def_property(srna, "transform_space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_collection_info_transform_space_items); + RNA_def_property_ui_text(prop, "Transform Space", "The transformation of the geometry output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 8d4578067f9..952054a6c53 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -5739,10 +5739,11 @@ static void rna_def_userdef_input(BlenderRNA *brna) prop = RNA_def_property(srna, "use_mouse_continuous", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "uiflag", USER_CONTINUOUS_MOUSE); - RNA_def_property_ui_text(prop, - "Continuous Grab", - "Allow moving the mouse outside the view on some manipulations " - "(transform, ui control drag)"); + RNA_def_property_ui_text( + prop, + "Continuous Grab", + "Let the mouse wrap around the view boundaries so mouse movements are not limited by the " + "screen size (used by transform, dragging of UI controls, etc.)"); prop = RNA_def_property(srna, "use_drag_immediately", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", USER_RELEASECONFIRM); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 6ce288e8ac5..74cfcefc842 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -215,7 +215,7 @@ static bool isDisabled(const struct Scene *UNUSED(scene), class GeometryNodesEvaluator { private: blender::LinearAllocator<> allocator_; - Map<const DInputSocket *, GMutablePointer> value_by_input_; + Map<std::pair<const DInputSocket *, const DOutputSocket *>, GMutablePointer> value_by_input_; Vector<const DInputSocket *> group_outputs_; blender::nodes::MultiFunctionByNode &mf_by_node_; const blender::nodes::DataTypeConversions &conversions_; @@ -246,8 +246,8 @@ class GeometryNodesEvaluator { { Vector<GMutablePointer> results; for (const DInputSocket *group_output : group_outputs_) { - GMutablePointer result = this->get_input_value(*group_output); - results.append(result); + Vector<GMutablePointer> result = this->get_input_values(*group_output); + results.append(result[0]); } for (GMutablePointer value : value_by_input_.values()) { value.destruct(); @@ -256,32 +256,53 @@ class GeometryNodesEvaluator { } private: - GMutablePointer get_input_value(const DInputSocket &socket_to_compute) + Vector<GMutablePointer> get_input_values(const DInputSocket &socket_to_compute) { - std::optional<GMutablePointer> value = value_by_input_.pop_try(&socket_to_compute); - if (value.has_value()) { - /* This input has been computed before, return it directly. */ - return *value; - } Span<const DOutputSocket *> from_sockets = socket_to_compute.linked_sockets(); Span<const DGroupInput *> from_group_inputs = socket_to_compute.linked_group_inputs(); const int total_inputs = from_sockets.size() + from_group_inputs.size(); - BLI_assert(total_inputs <= 1); if (total_inputs == 0) { /* The input is not connected, use the value from the socket itself. */ - return get_unlinked_input_value(socket_to_compute); + return {get_unlinked_input_value(socket_to_compute)}; } + if (from_group_inputs.size() == 1) { - /* The input gets its value from the input of a group that is not further connected. */ - return get_unlinked_input_value(socket_to_compute); + return {get_unlinked_input_value(socket_to_compute)}; + } + + /* Multi-input sockets contain a vector of inputs. */ + if (socket_to_compute.is_multi_input_socket()) { + Vector<GMutablePointer> values; + for (const DOutputSocket *from_socket : from_sockets) { + const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair( + &socket_to_compute, from_socket); + std::optional<GMutablePointer> value = value_by_input_.pop_try(key); + if (value.has_value()) { + values.append(value.value()); + } + else { + this->compute_output_and_forward(*from_socket); + GMutablePointer value = value_by_input_.pop(key); + values.append(value); + } + } + return values; } - /* Compute the socket now. */ const DOutputSocket &from_socket = *from_sockets[0]; + const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair( + &socket_to_compute, &from_socket); + std::optional<GMutablePointer> value = value_by_input_.pop_try(key); + if (value.has_value()) { + /* This input has been computed before, return it directly. */ + return {*value}; + } + + /* Compute the socket now. */ this->compute_output_and_forward(from_socket); - return value_by_input_.pop(&socket_to_compute); + return {value_by_input_.pop(key)}; } void compute_output_and_forward(const DOutputSocket &socket_to_compute) @@ -302,8 +323,14 @@ class GeometryNodesEvaluator { GValueMap<StringRef> node_inputs_map{allocator_}; for (const DInputSocket *input_socket : node.inputs()) { if (input_socket->is_available()) { - GMutablePointer value = this->get_input_value(*input_socket); - node_inputs_map.add_new_direct(input_socket->identifier(), value); + Vector<GMutablePointer> values = this->get_input_values(*input_socket); + for (int i = 0; i < values.size(); ++i) { + /* Values from Multi Input Sockets are stored in input map with the format + * <identifier>[<index>]. */ + blender::StringRefNull key = allocator_.copy_string( + input_socket->identifier() + (i > 0 ? ("[" + std::to_string(i)) + "]" : "")); + node_inputs_map.add_new_direct(key, std::move(values[i])); + } } } @@ -393,13 +420,15 @@ class GeometryNodesEvaluator { void forward_to_inputs(const DOutputSocket &from_socket, GMutablePointer value_to_forward) { + /* For all sockets that are linked with the from_socket push the value to their node. */ Span<const DInputSocket *> to_sockets_all = from_socket.linked_sockets(); const CPPType &from_type = *value_to_forward.type(); - Vector<const DInputSocket *> to_sockets_same_type; for (const DInputSocket *to_socket : to_sockets_all) { const CPPType &to_type = *blender::nodes::socket_cpp_type_get(*to_socket->typeinfo()); + const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair( + to_socket, &from_socket); if (from_type == to_type) { to_sockets_same_type.append(to_socket); } @@ -411,7 +440,7 @@ class GeometryNodesEvaluator { else { to_type.copy_to_uninitialized(to_type.default_value(), buffer); } - value_by_input_.add_new(to_socket, GMutablePointer{to_type, buffer}); + add_value_to_input_socket(key, GMutablePointer{to_type, buffer}); } } @@ -422,23 +451,35 @@ class GeometryNodesEvaluator { else if (to_sockets_same_type.size() == 1) { /* This value is only used on one input socket, no need to copy it. */ const DInputSocket *to_socket = to_sockets_same_type[0]; - value_by_input_.add_new(to_socket, value_to_forward); + const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair( + to_socket, &from_socket); + + add_value_to_input_socket(key, value_to_forward); } else { /* Multiple inputs use the value, make a copy for every input except for one. */ const DInputSocket *first_to_socket = to_sockets_same_type[0]; Span<const DInputSocket *> other_to_sockets = to_sockets_same_type.as_span().drop_front(1); const CPPType &type = *value_to_forward.type(); - - value_by_input_.add_new(first_to_socket, value_to_forward); + const std::pair<const DInputSocket *, const DOutputSocket *> first_key = std::make_pair( + first_to_socket, &from_socket); + add_value_to_input_socket(first_key, value_to_forward); for (const DInputSocket *to_socket : other_to_sockets) { + const std::pair<const DInputSocket *, const DOutputSocket *> key = std::make_pair( + to_socket, &from_socket); void *buffer = allocator_.allocate(type.size(), type.alignment()); type.copy_to_uninitialized(value_to_forward.get(), buffer); - value_by_input_.add_new(to_socket, GMutablePointer{type, buffer}); + add_value_to_input_socket(key, GMutablePointer{type, buffer}); } } } + void add_value_to_input_socket(const std::pair<const DInputSocket *, const DOutputSocket *> key, + GMutablePointer value) + { + value_by_input_.add_new(key, value); + } + GMutablePointer get_unlinked_input_value(const DInputSocket &socket) { bNodeSocket *bsocket; diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 405a8dcbf46..9982962cabd 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -150,6 +150,7 @@ set(SRC geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_attribute_vector_math.cc geometry/nodes/node_geo_boolean.cc + geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_edge_split.cc geometry/nodes/node_geo_join_geometry.cc diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 4d594a77ebc..62affe43895 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -80,6 +80,7 @@ class DInputSocket : public DSocket { private: Vector<DOutputSocket *> linked_sockets_; Vector<DGroupInput *> linked_group_inputs_; + bool is_multi_input_socket_; friend DerivedNodeTree; @@ -90,6 +91,7 @@ class DInputSocket : public DSocket { Span<const DGroupInput *> linked_group_inputs() const; bool is_linked() const; + bool is_multi_input_socket() const; }; class DOutputSocket : public DSocket { @@ -362,6 +364,11 @@ inline bool DInputSocket::is_linked() const return linked_sockets_.size() > 0 || linked_group_inputs_.size() > 0; } +inline bool DInputSocket::is_multi_input_socket() const +{ + return is_multi_input_socket_; +} + /* -------------------------------------------------------------------- * DOutputSocket inline methods. */ diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index d78f76e0b52..178831d5d96 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -49,6 +49,7 @@ void register_node_type_geo_point_rotate(void); void register_node_type_geo_align_rotation_to_vector(void); void register_node_type_geo_sample_texture(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_collection_info(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index 454c9e96246..91d46e3951f 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -104,6 +104,25 @@ class GeoNodeExecParams { } /** + * Get input as vector for multi input socket with the given identifier. + * + * This method can only be called once for each identifier. + */ + template<typename T> Vector<T> extract_multi_input(StringRef identifier) + { + Vector<T> values; + values.append(input_values_.extract<T>(identifier)); + int i = 1; + std::string sub_identifier = identifier + "[1]"; + while (input_values_.contains(sub_identifier)) { + values.append(input_values_.extract<T>(sub_identifier)); + i++; + sub_identifier = identifier + "[" + std::to_string(i) + "]"; + } + return values; + } + + /** * Get the input value for the input socket with the given identifier. * * This makes a copy of the value, which is fine for most types but should be avoided for diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index cc2f6a294f2..cbc5f715db0 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -291,6 +291,7 @@ DefNode(GeometryNode, GEO_NODE_POINT_SCALE, def_geo_point_scale, "POINT_SCALE", DefNode(GeometryNode, GEO_NODE_POINT_TRANSLATE, def_geo_point_translate, "POINT_TRANSLATE", PointTranslate, "Point Translate", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE, def_geo_attribute_sample_texture, "ATTRIBUTE_SAMPLE_TEXTURE", AttributeSampleTexture, "Attribute Sample Texture", "") DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") /* undefine macros */ #undef DefNode diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc new file mode 100644 index 00000000000..637a117e2af --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -0,0 +1,99 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BLI_math_matrix.h" + +#include "DNA_collection_types.h" + +static bNodeSocketTemplate geo_node_collection_info_in[] = { + {SOCK_COLLECTION, N_("Collection")}, + {-1, ""}, +}; + +static bNodeSocketTemplate geo_node_collection_info_out[] = { + {SOCK_GEOMETRY, N_("Geometry")}, + {-1, ""}, +}; + +namespace blender::nodes { + +static void geo_node_collection_info_exec(GeoNodeExecParams params) +{ + bke::PersistentCollectionHandle collection_handle = + params.extract_input<bke::PersistentCollectionHandle>("Collection"); + Collection *collection = params.handle_map().lookup(collection_handle); + + GeometrySet geometry_set_out; + + if (collection == nullptr) { + params.set_output("Geometry", geometry_set_out); + return; + } + + const bNode &bnode = params.node(); + NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; + const bool transform_space_relative = (node_storage->transform_space == + GEO_NODE_TRANSFORM_SPACE_RELATIVE); + + InstancedData instance; + instance.type = INSTANCE_DATA_TYPE_COLLECTION; + instance.data.collection = collection; + + InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>(); + + float transform_mat[4][4]; + unit_m4(transform_mat); + const Object *self_object = params.self_object(); + + if (transform_space_relative) { + copy_v3_v3(transform_mat[3], collection->instance_offset); + + mul_m4_m4_pre(transform_mat, self_object->imat); + + float3 self_loc; + copy_v3_v3(self_loc, self_object->obmat[3]); + } + instances.add_instance(instance, transform_mat, -1); + + params.set_output("Geometry", geometry_set_out); +} + +static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCollectionInfo *data = (NodeGeometryCollectionInfo *)MEM_callocN( + sizeof(NodeGeometryCollectionInfo), __func__); + data->transform_space = GEO_NODE_TRANSFORM_SPACE_ORIGINAL; + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_geo_collection_info() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_COLLECTION_INFO, "Collection Info", NODE_CLASS_INPUT, 0); + node_type_socket_templates(&ntype, geo_node_collection_info_in, geo_node_collection_info_out); + node_type_init(&ntype, blender::nodes::geo_node_collection_info_node_init); + node_type_storage(&ntype, + "NodeGeometryCollectionInfo", + node_free_standard_storage, + node_copy_standard_storage); + ntype.geometry_node_execute = blender::nodes::geo_node_collection_info_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index c693047ff40..f5a0e14f18b 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -91,7 +91,7 @@ DNode &DerivedNodeTree::create_node(const NodeRef &node_ref, for (int i : node.inputs_.index_range()) { const InputSocketRef &socket_ref = node_ref.input(i); DInputSocket &socket = *node.inputs_[i]; - + socket.is_multi_input_socket_ = socket_ref.bsocket()->flag & SOCK_MULTI_INPUT; socket.id_ = UNINITIALIZED_ID; socket.node_ = &node; socket.socket_ref_ = &socket_ref; diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 9669dc6496b..c7c3ced4e56 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -281,25 +281,30 @@ static int node_count_links(bNodeTree *ntree, bNodeSocket *sock) return count; } -/* find an eligible socket for linking */ +/* Find an eligible socket for linking. */ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNodeSocket *cur) { - /* link swapping: try to find a free slot with a matching name */ - bNodeSocket *first = cur->in_out == SOCK_IN ? node->inputs.first : node->outputs.first; bNodeSocket *sock; - sock = cur->next ? cur->next : first; /* wrap around the list end */ + /* Iterate over all sockets of the target node, to find one that matches the same socket type. + * The idea behind this is: When a user connects an input to a socket that is + * already linked (and if its not an Multi Input Socket), we try to find a replacement socket for + * the link that we try to overwrite and connect that previous link to the new socket. */ + sock = cur->next ? cur->next : first; /* Wrap around the list end. */ while (sock != cur) { if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) { - int link_count = node_count_links(ntree, sock); - /* take +1 into account since we would add a new link */ - if (link_count + 1 <= nodeSocketLinkLimit(sock)) { - return sock; /* found a valid free socket we can swap to */ - } + break; } + sock = sock->next ? sock->next : first; /* Wrap around the list end. */ + } - sock = sock->next ? sock->next : first; /* wrap around the list end */ + if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) { + int link_count = node_count_links(ntree, sock); + /* Take +1 into account since we would add a new link. */ + if (link_count + 1 <= nodeSocketLinkLimit(sock)) { + return sock; /* Found a valid free socket we can swap to. */ + } } return NULL; } @@ -309,7 +314,6 @@ void node_insert_link_default(bNodeTree *ntree, bNode *node, bNodeLink *link) bNodeSocket *sock = link->tosock; bNodeLink *tlink, *tlink_next; - /* inputs can have one link only, outputs can have unlimited links */ if (node != link->tonode) { return; } diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index c6f0fbd3a2b..69a6bd1ebbf 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -173,7 +173,7 @@ static PyTypeObject bpy_lib_Type = { PyDoc_STRVAR( bpy_lib_load_doc, - ".. method:: load(filepath, link=False, relative=False)\n" + ".. method:: load(filepath, link=False, assets_only=False, relative=False)\n" "\n" " Returns a context manager which exposes 2 library objects on entering.\n" " Each object has attributes matching bpy.data which are lists of strings to be linked.\n" @@ -182,6 +182,8 @@ PyDoc_STRVAR( " :type filepath: string\n" " :arg link: When False reference to the original file is lost.\n" " :type link: bool\n" + " :arg assets_only: If True, only list data-blocks marked as assets.\n" + " :type assets_only: bool\n" " :arg relative: When True the path is stored relative to the open blend file.\n" " :type relative: bool\n"); static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject *kw) @@ -189,12 +191,20 @@ static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject * Main *bmain = CTX_data_main(BPY_context_get()); BPy_Library *ret; const char *filename = NULL; - bool is_rel = false, is_link = false; - - static const char *_keywords[] = {"filepath", "link", "relative", NULL}; - static _PyArg_Parser _parser = {"s|O&O&:load", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kw, &_parser, &filename, PyC_ParseBool, &is_link, PyC_ParseBool, &is_rel)) { + bool is_rel = false, is_link = false, use_assets_only = false; + + static const char *_keywords[] = {"filepath", "link", "relative", "assets_only", NULL}; + static _PyArg_Parser _parser = {"s|O&O&O&:load", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + &filename, + PyC_ParseBool, + &is_link, + PyC_ParseBool, + &is_rel, + PyC_ParseBool, + &use_assets_only)) { return NULL; } @@ -205,7 +215,8 @@ static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject * BLI_path_abs(ret->abspath, BKE_main_blendfile_path(bmain)); ret->blo_handle = NULL; - ret->flag = ((is_link ? FILE_LINK : 0) | (is_rel ? FILE_RELPATH : 0)); + ret->flag = ((is_link ? FILE_LINK : 0) | (is_rel ? FILE_RELPATH : 0) | + (use_assets_only ? FILE_ASSETS_ONLY : 0)); ret->dict = _PyDict_NewPresized(MAX_LIBARRAY); @@ -218,7 +229,8 @@ static PyObject *_bpy_names(BPy_Library *self, int blocktype) LinkNode *l, *names; int totnames; - names = BLO_blendhandle_get_datablock_names(self->blo_handle, blocktype, &totnames); + names = BLO_blendhandle_get_datablock_names( + self->blo_handle, blocktype, (self->flag & FILE_ASSETS_ONLY) != 0, &totnames); list = PyList_New(totnames); if (names) { |