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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-02-03 20:59:12 +0300
committerHans Goudey <h.goudey@me.com>2021-02-03 20:59:12 +0300
commitd726aaec133480f977d044493af60125aaec4c9e (patch)
tree64a1e0dff83b1df7f3dafd4360de7fc1aed1359a
parentc4076474695d096656fbc4a5b9c5df45a1620fc4 (diff)
parentc5514d3a2a03242ddc43f83be4bb72df7f85469f (diff)
Merge branch 'master' into temp-geometry-nodes-instances-api
-rwxr-xr-xbuild_files/build_environment/install_deps.sh22
-rw-r--r--release/darwin/Blender.app/Contents/Info.plist33
-rw-r--r--release/darwin/Blender.app/Contents/Resources/blender file icon.icnsbin284786 -> 0 bytes
-rw-r--r--release/scripts/startup/bl_ui/properties_physics_field.py7
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_effect.h3
-rw-r--r--source/blender/blenkernel/BKE_node.h1
-rw-r--r--source/blender/blenkernel/BKE_undo_system.h19
-rw-r--r--source/blender/blenkernel/intern/cloth.c2
-rw-r--r--source/blender/blenkernel/intern/dynamicpaint.c3
-rw-r--r--source/blender/blenkernel/intern/effect.c81
-rw-r--r--source/blender/blenkernel/intern/fluid.c2
-rw-r--r--source/blender/blenkernel/intern/mesh.c2
-rw-r--r--source/blender/blenkernel/intern/node.cc4
-rw-r--r--source/blender/blenkernel/intern/particle_system.c3
-rw-r--r--source/blender/blenkernel/intern/rigidbody.c2
-rw-r--r--source/blender/blenkernel/intern/softbody.c5
-rw-r--r--source/blender/blenkernel/intern/undo_system.c361
-rw-r--r--source/blender/blenlib/intern/noise.c4
-rw-r--r--source/blender/blenloader/BLO_readfile.h4
-rw-r--r--source/blender/blenloader/intern/blend_validate.c2
-rw-r--r--source/blender/blenloader/intern/readblenentry.c12
-rw-r--r--source/blender/blentranslation/intern/blt_lang.c24
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_renderpasses.c4
-rw-r--r--source/blender/editors/space_node/drawnode.c10
-rw-r--r--source/blender/editors/space_node/node_intern.h1
-rw-r--r--source/blender/editors/space_node/node_relationships.c64
-rw-r--r--source/blender/editors/undo/ed_undo.c33
-rw-r--r--source/blender/gpu/opengl/gl_drawlist.cc15
-rw-r--r--source/blender/gpu/opengl/gl_framebuffer.cc6
-rw-r--r--source/blender/imbuf/intern/thumbs_blend.c2
-rw-r--r--source/blender/makesdna/DNA_node_types.h9
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c27
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c9
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc87
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_derived_node_tree.hh7
-rw-r--r--source/blender/nodes/NOD_geometry.h1
-rw-r--r--source/blender/nodes/NOD_geometry_exec.hh19
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_collection_info.cc99
-rw-r--r--source/blender/nodes/intern/derived_node_tree.cc2
-rw-r--r--source/blender/nodes/intern/node_util.c26
-rw-r--r--source/blender/python/intern/bpy_library_load.c30
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
deleted file mode 100644
index 2bf78d3eb0d..00000000000
--- a/release/darwin/Blender.app/Contents/Resources/blender file icon.icns
+++ /dev/null
Binary files differ
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(&region->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) {