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-09-25 04:04:21 +0300
committerHans Goudey <h.goudey@me.com>2021-09-25 04:04:21 +0300
commit2460b0d5a195bc781d00c5145507f98b04c1689b (patch)
treec620fc353971b2059ba5798efd3dc906859ce6ad
parent6525f47afa7a4ee6604ceb77da42331942325ef8 (diff)
parent2dd39683358100a39d7e7774e1051136ec1df7d9 (diff)
Merge branch 'master' into nodes-attribute-toggle-only-fields
-rw-r--r--intern/cycles/app/cycles_standalone.cpp40
-rw-r--r--intern/cycles/blender/blender_curves.cpp15
-rw-r--r--intern/cycles/blender/blender_session.cpp7
-rw-r--r--intern/cycles/device/cpu/device_impl.cpp149
-rw-r--r--intern/cycles/device/cuda/device_impl.cpp136
-rw-r--r--intern/cycles/device/cuda/device_impl.h1
-rw-r--r--intern/cycles/device/device.cpp1
-rw-r--r--intern/cycles/kernel/kernel_types.h1
-rw-r--r--intern/cycles/kernel/osl/osl_services.cpp1
-rw-r--r--intern/cycles/kernel/osl/osl_services.h1
-rw-r--r--intern/cycles/kernel/shaders/node_hair_info.osl2
-rw-r--r--intern/cycles/kernel/svm/svm_geometry.h2
-rw-r--r--intern/cycles/kernel/svm/svm_types.h1
-rw-r--r--intern/cycles/render/attribute.cpp5
-rw-r--r--intern/cycles/render/buffers.cpp1
-rw-r--r--intern/cycles/render/nodes.cpp10
-rw-r--r--intern/cycles/render/session.cpp20
-rw-r--r--intern/cycles/render/session.h2
-rw-r--r--intern/cycles/util/util_progress.h24
-rw-r--r--release/scripts/modules/rna_manual_reference.py1
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py1
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py1
-rw-r--r--release/scripts/startup/bl_ui/space_userpref.py1
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py2
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py34
-rw-r--r--release/scripts/startup/nodeitems_builtins.py7
-rw-r--r--source/blender/blenkernel/BKE_asset_catalog.hh17
-rw-r--r--source/blender/blenkernel/BKE_font.h9
-rw-r--r--source/blender/blenkernel/BKE_geometry_set.hh8
-rw-r--r--source/blender/blenkernel/BKE_lib_override.h2
-rw-r--r--source/blender/blenkernel/BKE_node.h6
-rw-r--r--source/blender/blenkernel/intern/asset_catalog.cc9
-rw-r--r--source/blender/blenkernel/intern/asset_catalog_test.cc42
-rw-r--r--source/blender/blenkernel/intern/blendfile.c7
-rw-r--r--source/blender/blenkernel/intern/customdata.c3
-rw-r--r--source/blender/blenkernel/intern/font.c20
-rw-r--r--source/blender/blenkernel/intern/lib_override.c71
-rw-r--r--source/blender/blenkernel/intern/node.cc19
-rw-r--r--source/blender/blenkernel/intern/object.c2
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh7
-rw-r--r--source/blender/blenlib/BLI_noise.hh40
-rw-r--r--source/blender/blenlib/BLI_uuid.h5
-rw-r--r--source/blender/blenlib/intern/noise.cc104
-rw-r--r--source/blender/blenlib/intern/uuid.cc19
-rw-r--r--source/blender/blenlib/tests/BLI_uuid_test.cc23
-rw-r--r--source/blender/blenloader/BLO_readfile.h13
-rw-r--r--source/blender/blenloader/intern/readblenentry.c33
-rw-r--r--source/blender/blenloader/intern/versioning_290.c2
-rw-r--r--source/blender/blenloader/intern/versioning_300.c32
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.cc15
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_nodes.h2
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.cc15
-rw-r--r--source/blender/depsgraph/intern/builder/deg_builder_relations.h2
-rw-r--r--source/blender/draw/engines/eevee/eevee_cryptomatte.c2
-rw-r--r--source/blender/draw/engines/eevee/eevee_materials.c7
-rw-r--r--source/blender/draw/engines/eevee/eevee_motion_blur.c2
-rw-r--r--source/blender/draw/engines/workbench/workbench_engine.c2
-rw-r--r--source/blender/draw/intern/draw_cache_impl_curve.cc3
-rw-r--r--source/blender/draw/intern/draw_cache_impl_hair.c61
-rw-r--r--source/blender/draw/intern/draw_cache_impl_particles.c89
-rw-r--r--source/blender/draw/intern/draw_common.h4
-rw-r--r--source/blender/draw/intern/draw_hair.c25
-rw-r--r--source/blender/draw/intern/draw_hair_private.h6
-rw-r--r--source/blender/draw/intern/shaders/common_hair_lib.glsl6
-rw-r--r--source/blender/editors/asset/intern/asset_list.cc2
-rw-r--r--source/blender/editors/interface/tree_view.cc2
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c161
-rw-r--r--source/blender/editors/screen/screen_context.c11
-rw-r--r--source/blender/editors/space_file/filelist.c305
-rw-r--r--source/blender/editors/space_file/filelist.h5
-rw-r--r--source/blender/editors/space_file/filesel.c3
-rw-r--r--source/blender/editors/space_node/node_draw.cc34
-rw-r--r--source/blender/functions/FN_field.hh9
-rw-r--r--source/blender/functions/intern/field.cc24
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c6
-rw-r--r--source/blender/gpu/intern/gpu_codegen.c16
-rw-r--r--source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl10
-rw-r--r--source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl2
-rw-r--r--source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl5
-rw-r--r--source/blender/makesdna/DNA_customdata_types.h9
-rw-r--r--source/blender/makesdna/DNA_node_types.h42
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesdna/DNA_userdef_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_gpencil_modifier.c28
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c169
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c7
-rw-r--r--source/blender/modifiers/intern/MOD_nodes_evaluator.cc14
-rw-r--r--source/blender/nodes/CMakeLists.txt11
-rw-r--r--source/blender/nodes/NOD_function.h5
-rw-r--r--source/blender/nodes/NOD_geometry.h4
-rw-r--r--source/blender/nodes/NOD_node_declaration.hh12
-rw-r--r--source/blender/nodes/NOD_static_types.h7
-rw-r--r--source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc (renamed from source/blender/nodes/function/nodes/node_fn_random_float.cc)12
-rw-r--r--source/blender/nodes/function/nodes/node_fn_input_special_characters.cc74
-rw-r--r--source/blender/nodes/function/nodes/node_fn_random_value.cc299
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc)26
-rw-r--r--source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc (renamed from source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc)0
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc753
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_input_index.cc20
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc18
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_set_position.cc2
-rw-r--r--source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc307
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_hair_info.c7
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_tex_noise.cc2
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc2
-rw-r--r--source/blender/windowmanager/intern/wm_files.c14
108 files changed, 2832 insertions, 815 deletions
diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp
index 270096d70b0..258e67b3459 100644
--- a/intern/cycles/app/cycles_standalone.cpp
+++ b/intern/cycles/app/cycles_standalone.cpp
@@ -53,7 +53,7 @@ struct Options {
SessionParams session_params;
bool quiet;
bool show_help, interactive, pause;
- string output_path;
+ string output_filepath;
} options;
static void session_print(const string &str)
@@ -160,7 +160,7 @@ static void session_init()
/* load scene */
scene_init();
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
options.session->start();
}
@@ -222,9 +222,7 @@ static void display_info(Progress &progress)
static void display()
{
- static DeviceDrawParams draw_params = DeviceDrawParams();
-
- options.session->draw(session_buffer_params(), draw_params);
+ options.session->draw();
display_info(options.session->progress);
}
@@ -254,7 +252,7 @@ static void motion(int x, int y, int button)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
@@ -271,7 +269,7 @@ static void resize(int width, int height)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
@@ -283,7 +281,7 @@ static void keyboard(unsigned char key)
/* Reset */
else if (key == 'r')
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
/* Cancel */
else if (key == 27) // escape
@@ -320,7 +318,7 @@ static void keyboard(unsigned char key)
options.session->scene->camera->need_flags_update = true;
options.session->scene->camera->need_device_update = true;
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
/* Set Max Bounces */
@@ -346,7 +344,7 @@ static void keyboard(unsigned char key)
options.session->scene->integrator->set_max_bounce(bounce);
- options.session->reset(session_buffer_params(), options.session_params.samples);
+ options.session->reset(options.session_params, session_buffer_params());
}
}
#endif
@@ -361,11 +359,13 @@ static int files_parse(int argc, const char *argv[])
static void options_parse(int argc, const char **argv)
{
- options.width = 0;
- options.height = 0;
+ options.width = 1024;
+ options.height = 512;
options.filepath = "";
options.session = NULL;
options.quiet = false;
+ options.session_params.use_auto_tile = false;
+ options.session_params.tile_size = 0;
/* device names */
string device_names = "";
@@ -411,7 +411,7 @@ static void options_parse(int argc, const char **argv)
&options.session_params.samples,
"Number of samples to render",
"--output %s",
- &options.output_path,
+ &options.output_filepath,
"File path to write output image",
"--threads %d",
&options.session_params.threads,
@@ -422,12 +422,9 @@ static void options_parse(int argc, const char **argv)
"--height %d",
&options.height,
"Window height in pixel",
- "--tile-width %d",
- &options.session_params.tile_size.x,
- "Tile width in pixels",
- "--tile-height %d",
- &options.session_params.tile_size.y,
- "Tile height in pixels",
+ "--tile-size %d",
+ &options.session_params.tile_size,
+ "Tile size in pixels",
"--list-devices",
&list,
"List information about all available devices",
@@ -489,8 +486,9 @@ static void options_parse(int argc, const char **argv)
options.session_params.background = true;
#endif
- /* Use progressive rendering */
- options.session_params.progressive = true;
+ if (options.session_params.tile_size > 0) {
+ options.session_params.use_auto_tile = true;
+ }
/* find matching device */
DeviceType device_type = Device::type_from_string(devicename.c_str());
diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp
index 6fe5ea41fff..b6b4f206620 100644
--- a/intern/cycles/blender/blender_curves.cpp
+++ b/intern/cycles/blender/blender_curves.cpp
@@ -283,10 +283,13 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
return;
Attribute *attr_intercept = NULL;
+ Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT))
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH))
+ attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM))
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
@@ -336,6 +339,10 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa
num_curve_keys++;
}
+ if (attr_length != NULL) {
+ attr_length->add(CData->curve_length[curve]);
+ }
+
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(num_curves, 0));
}
@@ -657,11 +664,15 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
/* Add requested attributes. */
Attribute *attr_intercept = NULL;
+ Attribute *attr_length = NULL;
Attribute *attr_random = NULL;
if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) {
attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT);
}
+ if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) {
+ attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH);
+ }
if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) {
attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM);
}
@@ -714,6 +725,10 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair)
}
}
+ if (attr_length) {
+ attr_length->add(length);
+ }
+
/* Random number per curve. */
if (attr_random != NULL) {
attr_random->add(hash_uint2_to_float(b_curve.index(), 0));
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 33092129ddf..c4a3f99ec2d 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -311,6 +311,8 @@ void BlenderSession::read_render_tile()
for (BL::RenderPass &b_pass : b_rlay.passes) {
session->set_render_tile_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect());
}
+
+ b_engine.end_result(b_rr, false, false, false);
}
void BlenderSession::write_render_tile()
@@ -993,8 +995,9 @@ void BlenderSession::update_status_progress()
get_status(status, substatus);
get_progress(progress, total_time, render_time);
- if (progress > 0)
- remaining_time = (1.0 - (double)progress) * (render_time / (double)progress);
+ if (progress > 0) {
+ remaining_time = session->get_estimated_remaining_time();
+ }
if (background) {
if (scene)
diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp
index bd0a32e610b..d02c18daee9 100644
--- a/intern/cycles/device/cpu/device_impl.cpp
+++ b/intern/cycles/device/cpu/device_impl.cpp
@@ -54,7 +54,6 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_map.h"
-#include "util/util_opengl.h"
#include "util/util_openimagedenoise.h"
#include "util/util_optimization.h"
#include "util/util_progress.h"
@@ -298,154 +297,6 @@ void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit)
Device::build_bvh(bvh, progress, refit);
}
-#if 0
-void CPUDevice::render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg)
-{
- const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE;
-
- scoped_timer timer(&tile.buffers->render_time);
-
- Coverage coverage(kg, tile);
- if (use_coverage) {
- coverage.init_path_trace();
- }
-
- float *render_buffer = (float *)tile.buffer;
- int start_sample = tile.start_sample;
- int end_sample = tile.start_sample + tile.num_samples;
-
- /* Needed for Embree. */
- SIMD_SET_FLUSH_TO_ZERO;
-
- for (int sample = start_sample; sample < end_sample; sample++) {
- if (task.get_cancel() || TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- break;
- }
-
- if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) {
- tile.stealing_state = RenderTile::WAS_STOLEN;
- break;
- }
-
- if (tile.task == RenderTile::PATH_TRACE) {
- for (int y = tile.y; y < tile.y + tile.h; y++) {
- for (int x = tile.x; x < tile.x + tile.w; x++) {
- if (use_coverage) {
- coverage.init_pixel(x, y);
- }
- kernels.path_trace(kg, render_buffer, sample, x, y, tile.offset, tile.stride);
- }
- }
- }
- else {
- for (int y = tile.y; y < tile.y + tile.h; y++) {
- for (int x = tile.x; x < tile.x + tile.w; x++) {
- kernels.bake(kg, render_buffer, sample, x, y, tile.offset, tile.stride);
- }
- }
- }
- tile.sample = sample + 1;
-
- if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) {
- const bool stop = adaptive_sampling_filter(kg, tile, sample);
- if (stop) {
- const int num_progress_samples = end_sample - sample;
- tile.sample = end_sample;
- task.update_progress(&tile, tile.w * tile.h * num_progress_samples);
- break;
- }
- }
-
- task.update_progress(&tile, tile.w * tile.h);
- }
- if (use_coverage) {
- coverage.finalize();
- }
-
- if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) {
- adaptive_sampling_post(tile, kg);
- }
-}
-
-void CPUDevice::thread_render(DeviceTask &task)
-{
- if (TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- return;
- }
-
- /* allocate buffer for kernel globals */
- CPUKernelThreadGlobals kg(kernel_globals, get_cpu_osl_memory());
-
- profiler.add_state(&kg.profiler);
-
- /* NLM denoiser. */
- DenoisingTask *denoising = NULL;
-
- /* OpenImageDenoise: we can only denoise with one thread at a time, so to
- * avoid waiting with mutex locks in the denoiser, we let only a single
- * thread acquire denoising tiles. */
- uint tile_types = task.tile_types;
- bool hold_denoise_lock = false;
- if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) {
- if (!oidn_task_lock.try_lock()) {
- tile_types &= ~RenderTile::DENOISE;
- hold_denoise_lock = true;
- }
- }
-
- RenderTile tile;
- while (task.acquire_tile(this, tile, tile_types)) {
- if (tile.task == RenderTile::PATH_TRACE) {
- render(task, tile, &kg);
- }
- else if (tile.task == RenderTile::BAKE) {
- render(task, tile, &kg);
- }
- else if (tile.task == RenderTile::DENOISE) {
- denoise_openimagedenoise(task, tile);
- task.update_progress(&tile, tile.w * tile.h);
- }
-
- task.release_tile(tile);
-
- if (TaskPool::canceled()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- if (hold_denoise_lock) {
- oidn_task_lock.unlock();
- }
-
- profiler.remove_state(&kg.profiler);
-
- delete denoising;
-}
-
-void CPUDevice::thread_denoise(DeviceTask &task)
-{
- RenderTile tile;
- tile.x = task.x;
- tile.y = task.y;
- tile.w = task.w;
- tile.h = task.h;
- tile.buffer = task.buffer;
- tile.sample = task.sample + task.num_samples;
- tile.num_samples = task.num_samples;
- tile.start_sample = task.sample;
- tile.offset = task.offset;
- tile.stride = task.stride;
- tile.buffers = task.buffers;
-
- denoise_openimagedenoise(task, tile);
-
- task.update_progress(&tile, tile.w * tile.h);
-}
-#endif
-
const CPUKernels *CPUDevice::get_cpu_kernels() const
{
return &kernels;
diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp
index 0f10119e24f..5e1a63c04df 100644
--- a/intern/cycles/device/cuda/device_impl.cpp
+++ b/intern/cycles/device/cuda/device_impl.cpp
@@ -31,7 +31,6 @@
# include "util/util_logging.h"
# include "util/util_map.h"
# include "util/util_md5.h"
-# include "util/util_opengl.h"
# include "util/util_path.h"
# include "util/util_string.h"
# include "util/util_system.h"
@@ -1169,141 +1168,6 @@ void CUDADevice::tex_free(device_texture &mem)
}
}
-# if 0
-void CUDADevice::render(DeviceTask &task,
- RenderTile &rtile,
- device_vector<KernelWorkTile> &work_tiles)
-{
- scoped_timer timer(&rtile.buffers->render_time);
-
- if (have_error())
- return;
-
- CUDAContextScope scope(this);
- CUfunction cuRender;
-
- /* Get kernel function. */
- if (rtile.task == RenderTile::BAKE) {
- cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake"));
- }
- else {
- cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace"));
- }
-
- if (have_error()) {
- return;
- }
-
- cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1));
-
- /* Allocate work tile. */
- work_tiles.alloc(1);
-
- KernelWorkTile *wtile = work_tiles.data();
- wtile->x = rtile.x;
- wtile->y = rtile.y;
- wtile->w = rtile.w;
- wtile->h = rtile.h;
- wtile->offset = rtile.offset;
- wtile->stride = rtile.stride;
- wtile->buffer = (float *)(CUdeviceptr)rtile.buffer;
-
- /* Prepare work size. More step samples render faster, but for now we
- * remain conservative for GPUs connected to a display to avoid driver
- * timeouts and display freezing. */
- int min_blocks, num_threads_per_block;
- cuda_assert(
- cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0));
- if (!info.display_device) {
- min_blocks *= 8;
- }
-
- uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h);
-
- /* Render all samples. */
- uint start_sample = rtile.start_sample;
- uint end_sample = rtile.start_sample + rtile.num_samples;
-
- for (int sample = start_sample; sample < end_sample;) {
- /* Setup and copy work tile to device. */
- wtile->start_sample = sample;
- wtile->num_samples = step_samples;
- if (task.adaptive_sampling.use) {
- wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples);
- }
- wtile->num_samples = min(wtile->num_samples, end_sample - sample);
- work_tiles.copy_to_device();
-
- CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
- uint total_work_size = wtile->w * wtile->h * wtile->num_samples;
- uint num_blocks = divide_up(total_work_size, num_threads_per_block);
-
- /* Launch kernel. */
- void *args[] = {&d_work_tiles, &total_work_size};
-
- cuda_assert(
- cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0));
-
- /* Run the adaptive sampling kernels at selected samples aligned to step samples. */
- uint filter_sample = sample + wtile->num_samples - 1;
- if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) {
- adaptive_sampling_filter(filter_sample, wtile, d_work_tiles);
- }
-
- cuda_assert(cuCtxSynchronize());
-
- /* Update progress. */
- sample += wtile->num_samples;
- rtile.sample = sample;
- task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
-
- if (task.get_cancel()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- /* Finalize adaptive sampling. */
- if (task.adaptive_sampling.use) {
- CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer;
- adaptive_sampling_post(rtile, wtile, d_work_tiles);
- cuda_assert(cuCtxSynchronize());
- task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples);
- }
-}
-
-void CUDADevice::thread_run(DeviceTask &task)
-{
- CUDAContextScope scope(this);
-
- if (task.type == DeviceTask::RENDER) {
- device_vector<KernelWorkTile> work_tiles(this, "work_tiles", MEM_READ_ONLY);
-
- /* keep rendering tiles until done */
- RenderTile tile;
- DenoisingTask denoising(this, task);
-
- while (task.acquire_tile(this, tile, task.tile_types)) {
- if (tile.task == RenderTile::PATH_TRACE) {
- render(task, tile, work_tiles);
- }
- else if (tile.task == RenderTile::BAKE) {
- render(task, tile, work_tiles);
- }
-
- task.release_tile(tile);
-
- if (task.get_cancel()) {
- if (task.need_finish_queue == false)
- break;
- }
- }
-
- work_tiles.free();
- }
-}
-# endif
-
unique_ptr<DeviceQueue> CUDADevice::gpu_queue_create()
{
return make_unique<CUDADeviceQueue>(this);
diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h
index 76c09d84651..c0316d18ba0 100644
--- a/intern/cycles/device/cuda/device_impl.h
+++ b/intern/cycles/device/cuda/device_impl.h
@@ -26,7 +26,6 @@
# ifdef WITH_CUDA_DYNLOAD
# include "cuew.h"
# else
-# include "util/util_opengl.h"
# include <cuda.h>
# include <cudaGL.h>
# endif
diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp
index 6ccedcf54ef..70935598437 100644
--- a/intern/cycles/device/device.cpp
+++ b/intern/cycles/device/device.cpp
@@ -32,7 +32,6 @@
#include "util/util_half.h"
#include "util/util_logging.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_string.h"
#include "util/util_system.h"
#include "util/util_time.h"
diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h
index 66b7310ab65..3cc42bf7a85 100644
--- a/intern/cycles/kernel/kernel_types.h
+++ b/intern/cycles/kernel/kernel_types.h
@@ -572,6 +572,7 @@ typedef enum AttributeStandard {
ATTR_STD_MOTION_VERTEX_NORMAL,
ATTR_STD_PARTICLE,
ATTR_STD_CURVE_INTERCEPT,
+ ATTR_STD_CURVE_LENGTH,
ATTR_STD_CURVE_RANDOM,
ATTR_STD_PTEX_FACE_ID,
ATTR_STD_PTEX_UV,
diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp
index 396f42080e4..4fc46a255a8 100644
--- a/intern/cycles/kernel/osl/osl_services.cpp
+++ b/intern/cycles/kernel/osl/osl_services.cpp
@@ -107,6 +107,7 @@ ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
ustring OSLRenderServices::u_is_curve("geom:is_curve");
ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness");
+ustring OSLRenderServices::u_curve_length("geom:curve_length");
ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal");
ustring OSLRenderServices::u_curve_random("geom:curve_random");
ustring OSLRenderServices::u_path_ray_length("path:ray_length");
diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/osl_services.h
index 58accb46e7d..2a5400282b3 100644
--- a/intern/cycles/kernel/osl/osl_services.h
+++ b/intern/cycles/kernel/osl/osl_services.h
@@ -294,6 +294,7 @@ class OSLRenderServices : public OSL::RendererServices {
static ustring u_is_smooth;
static ustring u_is_curve;
static ustring u_curve_thickness;
+ static ustring u_curve_length;
static ustring u_curve_tangent_normal;
static ustring u_curve_random;
static ustring u_path_ray_length;
diff --git a/intern/cycles/kernel/shaders/node_hair_info.osl b/intern/cycles/kernel/shaders/node_hair_info.osl
index ee08ea57e68..ddc2e28b83a 100644
--- a/intern/cycles/kernel/shaders/node_hair_info.osl
+++ b/intern/cycles/kernel/shaders/node_hair_info.osl
@@ -18,12 +18,14 @@
shader node_hair_info(output float IsStrand = 0.0,
output float Intercept = 0.0,
+ output float Length = 0.0,
output float Thickness = 0.0,
output normal TangentNormal = N,
output float Random = 0)
{
getattribute("geom:is_curve", IsStrand);
getattribute("geom:curve_intercept", Intercept);
+ getattribute("geom:curve_length", Length);
getattribute("geom:curve_thickness", Thickness);
getattribute("geom:curve_tangent_normal", TangentNormal);
getattribute("geom:curve_random", Random);
diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/svm_geometry.h
index 10e9f291d0e..432529eb061 100644
--- a/intern/cycles/kernel/svm/svm_geometry.h
+++ b/intern/cycles/kernel/svm/svm_geometry.h
@@ -213,6 +213,8 @@ ccl_device_noinline void svm_node_hair_info(
}
case NODE_INFO_CURVE_INTERCEPT:
break; /* handled as attribute */
+ case NODE_INFO_CURVE_LENGTH:
+ break; /* handled as attribute */
case NODE_INFO_CURVE_RANDOM:
break; /* handled as attribute */
case NODE_INFO_CURVE_THICKNESS: {
diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h
index c053be96c51..313bc3235b9 100644
--- a/intern/cycles/kernel/svm/svm_types.h
+++ b/intern/cycles/kernel/svm/svm_types.h
@@ -173,6 +173,7 @@ typedef enum NodeParticleInfo {
typedef enum NodeHairInfo {
NODE_INFO_CURVE_IS_STRAND,
NODE_INFO_CURVE_INTERCEPT,
+ NODE_INFO_CURVE_LENGTH,
NODE_INFO_CURVE_THICKNESS,
/* Fade for minimum hair width transiency. */
// NODE_INFO_CURVE_FADE,
diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/render/attribute.cpp
index ea5a5f50f2d..aaf21ad9fd2 100644
--- a/intern/cycles/render/attribute.cpp
+++ b/intern/cycles/render/attribute.cpp
@@ -342,6 +342,8 @@ const char *Attribute::standard_name(AttributeStandard std)
return "particle";
case ATTR_STD_CURVE_INTERCEPT:
return "curve_intercept";
+ case ATTR_STD_CURVE_LENGTH:
+ return "curve_length";
case ATTR_STD_CURVE_RANDOM:
return "curve_random";
case ATTR_STD_PTEX_FACE_ID:
@@ -586,6 +588,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name)
case ATTR_STD_CURVE_INTERCEPT:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY);
break;
+ case ATTR_STD_CURVE_LENGTH:
+ attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
+ break;
case ATTR_STD_CURVE_RANDOM:
attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE);
break;
diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp
index 1882510cd70..3682b55049a 100644
--- a/intern/cycles/render/buffers.cpp
+++ b/intern/cycles/render/buffers.cpp
@@ -22,7 +22,6 @@
#include "util/util_foreach.h"
#include "util/util_hash.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_time.h"
#include "util/util_types.h"
diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp
index 03b79d7de3e..90f70cf19ec 100644
--- a/intern/cycles/render/nodes.cpp
+++ b/intern/cycles/render/nodes.cpp
@@ -4368,6 +4368,7 @@ NODE_DEFINE(HairInfoNode)
SOCKET_OUT_FLOAT(is_strand, "Is Strand");
SOCKET_OUT_FLOAT(intercept, "Intercept");
+ SOCKET_OUT_FLOAT(size, "Length");
SOCKET_OUT_FLOAT(thickness, "Thickness");
SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal");
#if 0 /* Output for minimum hair width transparency - deactivated. */
@@ -4390,6 +4391,9 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes)
if (!intercept_out->links.empty())
attributes->add(ATTR_STD_CURVE_INTERCEPT);
+ if (!output("Length")->links.empty())
+ attributes->add(ATTR_STD_CURVE_LENGTH);
+
if (!output("Random")->links.empty())
attributes->add(ATTR_STD_CURVE_RANDOM);
}
@@ -4412,6 +4416,12 @@ void HairInfoNode::compile(SVMCompiler &compiler)
compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
}
+ out = output("Length");
+ if (!out->links.empty()) {
+ int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH);
+ compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT);
+ }
+
out = output("Thickness");
if (!out->links.empty()) {
compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out));
diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp
index 823c34ed519..56d92fb0ad8 100644
--- a/intern/cycles/render/session.cpp
+++ b/intern/cycles/render/session.cpp
@@ -38,7 +38,6 @@
#include "util/util_function.h"
#include "util/util_logging.h"
#include "util/util_math.h"
-#include "util/util_opengl.h"
#include "util/util_task.h"
#include "util/util_time.h"
@@ -520,6 +519,25 @@ void Session::set_gpu_display(unique_ptr<GPUDisplay> gpu_display)
path_trace_->set_gpu_display(move(gpu_display));
}
+double Session::get_estimated_remaining_time() const
+{
+ const float completed = progress.get_progress();
+ if (completed == 0.0f) {
+ return 0.0;
+ }
+
+ double total_time, render_time;
+ progress.get_time(total_time, render_time);
+ double remaining = (1.0 - (double)completed) * (render_time / (double)completed);
+
+ const double time_limit = render_scheduler_.get_time_limit();
+ if (time_limit != 0.0) {
+ remaining = min(remaining, max(time_limit - render_time, 0.0));
+ }
+
+ return remaining;
+}
+
void Session::wait()
{
if (session_thread_) {
diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h
index 5623604bfe8..e3056e7778b 100644
--- a/intern/cycles/render/session.h
+++ b/intern/cycles/render/session.h
@@ -145,6 +145,8 @@ class Session {
void set_gpu_display(unique_ptr<GPUDisplay> gpu_display);
+ double get_estimated_remaining_time() const;
+
void device_free();
/* Returns the rendering progress or 0 if no progress can be determined
diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/util_progress.h
index dca8d3d0ab5..176ee11e1e9 100644
--- a/intern/cycles/util/util_progress.h
+++ b/intern/cycles/util/util_progress.h
@@ -100,7 +100,7 @@ class Progress {
cancel = true;
}
- bool get_cancel()
+ bool get_cancel() const
{
if (!cancel && cancel_cb)
cancel_cb();
@@ -108,7 +108,7 @@ class Progress {
return cancel;
}
- string get_cancel_message()
+ string get_cancel_message() const
{
thread_scoped_lock lock(progress_mutex);
return cancel_message;
@@ -130,12 +130,12 @@ class Progress {
cancel = true;
}
- bool get_error()
+ bool get_error() const
{
return error;
}
- string get_error_message()
+ string get_error_message() const
{
thread_scoped_lock lock(progress_mutex);
return error_message;
@@ -168,7 +168,7 @@ class Progress {
}
}
- void get_time(double &total_time_, double &render_time_)
+ void get_time(double &total_time_, double &render_time_) const
{
thread_scoped_lock lock(progress_mutex);
@@ -200,7 +200,7 @@ class Progress {
total_pixel_samples = total_pixel_samples_;
}
- float get_progress()
+ float get_progress() const
{
thread_scoped_lock lock(progress_mutex);
@@ -236,7 +236,7 @@ class Progress {
}
}
- int get_current_sample()
+ int get_current_sample() const
{
thread_scoped_lock lock(progress_mutex);
/* Note that the value here always belongs to the last tile that updated,
@@ -244,13 +244,13 @@ class Progress {
return current_tile_sample;
}
- int get_rendered_tiles()
+ int get_rendered_tiles() const
{
thread_scoped_lock lock(progress_mutex);
return rendered_tiles;
}
- int get_denoised_tiles()
+ int get_denoised_tiles() const
{
thread_scoped_lock lock(progress_mutex);
return denoised_tiles;
@@ -300,7 +300,7 @@ class Progress {
set_update();
}
- void get_status(string &status_, string &substatus_)
+ void get_status(string &status_, string &substatus_) const
{
thread_scoped_lock lock(progress_mutex);
@@ -330,8 +330,8 @@ class Progress {
}
protected:
- thread_mutex progress_mutex;
- thread_mutex update_mutex;
+ mutable thread_mutex progress_mutex;
+ mutable thread_mutex update_mutex;
function<void()> update_cb;
function<void()> cancel_cb;
diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py
index 40f59307bec..30cbcf4ea05 100644
--- a/release/scripts/modules/rna_manual_reference.py
+++ b/release/scripts/modules/rna_manual_reference.py
@@ -1944,7 +1944,6 @@ url_manual_mapping = (
("bpy.ops.object.origin_set*", "scene_layout/object/origin.html#bpy-ops-object-origin-set"),
("bpy.ops.object.parent_set*", "scene_layout/object/editing/parent.html#bpy-ops-object-parent-set"),
("bpy.ops.object.pointcloud*", "modeling/point_cloud.html#bpy-ops-object-pointcloud"),
- ("bpy.ops.object.proxy_make*", "files/linked_libraries/library_proxies.html#bpy-ops-object-proxy-make"),
("bpy.ops.object.select_all*", "scene_layout/object/selecting.html#bpy-ops-object-select-all"),
("bpy.ops.object.shade_flat*", "scene_layout/object/editing/shading.html#bpy-ops-object-shade-flat"),
("bpy.ops.pose.group_assign*", "animation/armatures/properties/bone_groups.html#bpy-ops-pose-group-assign"),
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 3da434ac9d9..ada8824519e 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -4396,7 +4396,6 @@ def km_object_mode(params):
("object.duplicates_make_real", {"type": 'A', "value": 'PRESS', "shift": True, "ctrl": True}, None),
op_menu("VIEW3D_MT_make_single_user", {"type": 'U', "value": 'PRESS'}),
("object.convert", {"type": 'C', "value": 'PRESS', "alt": True}, None),
- ("object.proxy_make", {"type": 'P', "value": 'PRESS', "ctrl": True, "alt": True}, None),
("object.make_local", {"type": 'L', "value": 'PRESS'}, None),
("object.data_transfer", {"type": 'T', "value": 'PRESS', "shift": True, "ctrl": True}, None),
])
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index a4a51cb9910..7bdc3ab160f 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -1691,6 +1691,7 @@ class _defs_weight_paint:
props = tool.operator_properties("paint.weight_gradient")
layout.prop(props, "type", expand=True)
+ layout.popover("VIEW3D_PT_tools_weight_gradient")
return dict(
idname="builtin.gradient",
diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py
index 0093110d326..ef1c7a77a2b 100644
--- a/release/scripts/startup/bl_ui/space_userpref.py
+++ b/release/scripts/startup/bl_ui/space_userpref.py
@@ -2283,6 +2283,7 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel):
context, (
({"property": "use_undo_legacy"}, "T60695"),
({"property": "override_auto_resync"}, "T83811"),
+ ({"property": "proxy_to_override_auto_conversion"}, "T91671"),
({"property": "use_cycles_debug"}, None),
),
)
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 3879f7de250..5d609c0afdf 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -2234,8 +2234,6 @@ class VIEW3D_MT_object_relations(Menu):
def draw(self, _context):
layout = self.layout
- layout.operator("object.proxy_make", text="Make Proxy...")
-
layout.operator("object.make_override_library", text="Make Library Override...")
layout.operator("object.convert_proxy_to_override")
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 16b5ed33f3f..acc3d933b85 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -688,6 +688,39 @@ class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, Smooth
bl_options = {'DEFAULT_CLOSED'}
+class VIEW3D_PT_tools_weight_gradient(Panel, View3DPaintPanel):
+ bl_context = ".weightpaint" # dot on purpose (access from topbar)
+ bl_label = "Falloff"
+ bl_options = {'DEFAULT_CLOSED'}
+
+ @classmethod
+ def poll(cls, context):
+ settings = context.tool_settings.weight_paint
+ brush = settings.brush
+ return brush is not None
+
+ def draw(self, context):
+ layout = self.layout
+ settings = context.tool_settings.weight_paint
+ brush = settings.brush
+
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(brush, "curve_preset", text="")
+
+ if brush.curve_preset == 'CUSTOM':
+ layout.template_curve_mapping(brush, "curve", brush=True)
+
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH'
+ row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND'
+ row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT'
+ row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP'
+ row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE'
+ row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX'
+
+
# TODO, move to space_view3d.py
class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel):
bl_context = ".paint_common" # dot on purpose (access from topbar)
@@ -2219,6 +2252,7 @@ classes = (
VIEW3D_PT_tools_brush_falloff_frontface,
VIEW3D_PT_tools_brush_falloff_normal,
VIEW3D_PT_tools_brush_display,
+ VIEW3D_PT_tools_weight_gradient,
VIEW3D_PT_sculpt_dyntopo,
VIEW3D_PT_sculpt_voxel_remesh,
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 9ad162da7dc..e658706a946 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -554,9 +554,10 @@ geometry_node_categories = [
NodeItem("GeometryNodeRealizeInstances", poll=geometry_nodes_fields_poll),
]),
GeometryNodeCategory("GEO_INPUT", "Input", items=[
+ NodeItem("FunctionNodeLegacyRandomFloat", poll=geometry_nodes_fields_legacy_poll),
+
NodeItem("GeometryNodeObjectInfo"),
NodeItem("GeometryNodeCollectionInfo"),
- NodeItem("FunctionNodeRandomFloat"),
NodeItem("ShaderNodeValue"),
NodeItem("FunctionNodeInputString"),
NodeItem("FunctionNodeInputVector"),
@@ -592,6 +593,7 @@ geometry_node_categories = [
NodeItem("GeometryNodeMeshUVSphere"),
]),
GeometryNodeCategory("GEO_POINT", "Point", items=[
+ NodeItem("GeometryNodeDistributePointsOnFaces", poll=geometry_nodes_fields_poll),
NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll),
NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll),
@@ -605,6 +607,8 @@ geometry_node_categories = [
NodeItem("FunctionNodeStringSubstring"),
NodeItem("FunctionNodeValueToString"),
NodeItem("GeometryNodeStringJoin"),
+ NodeItem("FunctionNodeInputSpecialCharacters"),
+ NodeItem("GeometryNodeStringToCurves"),
]),
GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[
NodeItem("ShaderNodeMapRange"),
@@ -614,6 +618,7 @@ geometry_node_categories = [
NodeItem("FunctionNodeFloatCompare"),
NodeItem("FunctionNodeFloatToInt"),
NodeItem("GeometryNodeSwitch"),
+ NodeItem("FunctionNodeRandomValue", poll=geometry_nodes_fields_poll),
]),
GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[
NodeItem("ShaderNodeTexNoise", poll=geometry_nodes_fields_poll),
diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh
index 5fd68e33f4a..cf9d581b148 100644
--- a/source/blender/blenkernel/BKE_asset_catalog.hh
+++ b/source/blender/blenkernel/BKE_asset_catalog.hh
@@ -32,6 +32,7 @@
#include <map>
#include <memory>
+#include <set>
#include <string>
namespace blender::bke {
@@ -255,4 +256,20 @@ class AssetCatalog {
static std::string sensible_simple_name_for_path(const CatalogPath &path);
};
+/** Comparator for asset catalogs, ordering by (path, UUID). */
+struct AssetCatalogPathCmp {
+ bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const
+ {
+ if (lhs->path == rhs->path) {
+ return lhs->catalog_id < rhs->catalog_id;
+ }
+ return lhs->path < rhs->path;
+ }
+};
+
+/**
+ * Set that stores catalogs ordered by (path, UUID).
+ * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */
+using AssetCatalogOrderedSet = std::set<const AssetCatalog *, AssetCatalogPathCmp>;
+
} // namespace blender::bke
diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_font.h
index 522d3843bb2..827ae1b6a0f 100644
--- a/source/blender/blenkernel/BKE_font.h
+++ b/source/blender/blenkernel/BKE_font.h
@@ -85,6 +85,15 @@ bool BKE_vfont_to_curve_ex(struct Object *ob,
struct CharTrans **r_chartransdata);
bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase);
bool BKE_vfont_to_curve(struct Object *ob, int mode);
+void BKE_vfont_build_char(struct Curve *cu,
+ struct ListBase *nubase,
+ unsigned int character,
+ struct CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize);
int BKE_vfont_select_get(struct Object *ob, int *r_start, int *r_end);
void BKE_vfont_select_clamp(struct Object *ob);
diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh
index a6c77c74b9e..5fcdbc83e25 100644
--- a/source/blender/blenkernel/BKE_geometry_set.hh
+++ b/source/blender/blenkernel/BKE_geometry_set.hh
@@ -715,6 +715,14 @@ class AnonymousAttributeFieldInput : public fn::FieldInput {
{
}
+ template<typename T> static fn::Field<T> Create(StrongAnonymousAttributeID anonymous_id)
+ {
+ const CPPType &type = CPPType::get<T>();
+ auto field_input = std::make_shared<AnonymousAttributeFieldInput>(std::move(anonymous_id),
+ type);
+ return fn::Field<T>{field_input};
+ }
+
const GVArray *get_varray_for_context(const fn::FieldContext &context,
IndexMask mask,
ResourceScope &scope) const override;
diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h
index c6658ff424a..20128e2608a 100644
--- a/source/blender/blenkernel/BKE_lib_override.h
+++ b/source/blender/blenkernel/BKE_lib_override.h
@@ -84,6 +84,8 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
struct Object *ob_proxy);
+void BKE_lib_override_library_main_proxy_convert(struct Main *bmain,
+ struct BlendFileReadReport *reports);
bool BKE_lib_override_library_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer,
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index 2e843e82a9f..52f8a3d8136 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -1500,6 +1500,8 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define GEO_NODE_STRING_JOIN 1087
#define GEO_NODE_CURVE_PARAMETER 1088
#define GEO_NODE_CURVE_FILLET 1089
+#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090
+#define GEO_NODE_STRING_TO_CURVES 1091
/** \} */
@@ -1509,13 +1511,15 @@ int ntreeTexExecTree(struct bNodeTree *ntree,
#define FN_NODE_BOOLEAN_MATH 1200
#define FN_NODE_FLOAT_COMPARE 1202
-#define FN_NODE_RANDOM_FLOAT 1206
+#define FN_NODE_LEGACY_RANDOM_FLOAT 1206
#define FN_NODE_INPUT_VECTOR 1207
#define FN_NODE_INPUT_STRING 1208
#define FN_NODE_FLOAT_TO_INT 1209
#define FN_NODE_VALUE_TO_STRING 1210
#define FN_NODE_STRING_LENGTH 1211
#define FN_NODE_STRING_SUBSTRING 1212
+#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213
+#define FN_NODE_RANDOM_VALUE 1214
/** \} */
diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc
index 20f2dc0a571..4f1de09e148 100644
--- a/source/blender/blenkernel/intern/asset_catalog.cc
+++ b/source/blender/blenkernel/intern/asset_catalog.cc
@@ -30,6 +30,7 @@
#endif
#include <fstream>
+#include <set>
namespace blender::bke {
@@ -525,10 +526,16 @@ bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &des
// Write the catalogs.
// TODO(@sybren): order them by Catalog ID or Catalog Path.
- for (const auto &catalog : catalogs_.values()) {
+
+ AssetCatalogOrderedSet catalogs_by_path;
+ for (const AssetCatalog *catalog : catalogs_.values()) {
if (catalog->flags.is_deleted) {
continue;
}
+ catalogs_by_path.insert(catalog);
+ }
+
+ for (const AssetCatalog *catalog : catalogs_by_path) {
output << catalog->catalog_id << ":" << catalog->path << ":" << catalog->simple_name
<< std::endl;
}
diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc
index 0f389999d6d..b40aae5d64a 100644
--- a/source/blender/blenkernel/intern/asset_catalog_test.cc
+++ b/source/blender/blenkernel/intern/asset_catalog_test.cc
@@ -167,6 +167,7 @@ TEST_F(AssetCatalogTest, load_single_file_into_tree)
{"character", 0},
{"character/Ellie", 1},
{"character/Ellie/poselib", 2},
+ {"character/Ellie/poselib/tailslash", 3},
{"character/Ellie/poselib/white space", 3},
{"character/Ružena", 1},
{"character/Ružena/poselib", 2},
@@ -289,7 +290,7 @@ TEST_F(AssetCatalogTest, create_catalog_after_loading_file)
<< "expecting newly added catalog to not yet be saved to " << temp_lib_root;
/* Write and reload the catalog file. */
- service.write_to_disk(temp_lib_root.c_str());
+ service.write_to_disk(temp_lib_root);
AssetCatalogService reloaded_service(temp_lib_root);
reloaded_service.load_from_disk();
EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE))
@@ -338,6 +339,7 @@ TEST_F(AssetCatalogTest, delete_catalog_leaf)
{"character", 0},
{"character/Ellie", 1},
{"character/Ellie/poselib", 2},
+ {"character/Ellie/poselib/tailslash", 3},
{"character/Ellie/poselib/white space", 3},
{"character/Ružena", 1},
{"character/Ružena/poselib", 2},
@@ -440,4 +442,42 @@ TEST_F(AssetCatalogTest, backups)
EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE));
}
+TEST_F(AssetCatalogTest, order_by_path)
+{
+ const bUUID cat2_uuid("22222222-b847-44d9-bdca-ff04db1c24f5");
+ const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); // Sorts earlier than above.
+ const AssetCatalog cat1(BLI_uuid_generate_random(), "simple/path/child", "");
+ const AssetCatalog cat2(cat2_uuid, "simple/path", "");
+ const AssetCatalog cat3(BLI_uuid_generate_random(), "complex/path/...or/is/it?", "");
+ const AssetCatalog cat4(cat4_uuid, "simple/path", "different ID, same path"); // should be kept
+ const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); // disappears
+
+ AssetCatalogOrderedSet by_path;
+ by_path.insert(&cat1);
+ by_path.insert(&cat2);
+ by_path.insert(&cat3);
+ by_path.insert(&cat4);
+ by_path.insert(&cat5);
+
+ AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin();
+
+ EXPECT_EQ(1, by_path.count(&cat1));
+ EXPECT_EQ(1, by_path.count(&cat2));
+ EXPECT_EQ(1, by_path.count(&cat3));
+ EXPECT_EQ(1, by_path.count(&cat4));
+ ASSERT_EQ(4, by_path.size()) << "Expecting cat5 to not be stored in the set, as it duplicates "
+ "an already-existing path + UUID";
+
+ EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); // complex/path
+ EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 111.. ID
+ EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); // simple/path with 222.. ID
+ EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); // simple/path/child
+
+ if (set_iter != by_path.end()) {
+ const AssetCatalog *next_cat = *set_iter;
+ FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":"
+ << next_cat->path;
+ }
+}
+
} // namespace blender::bke::tests
diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c
index 1c5d8804280..6957f9b5a69 100644
--- a/source/blender/blenkernel/intern/blendfile.c
+++ b/source/blender/blenkernel/intern/blendfile.c
@@ -344,6 +344,13 @@ static void setup_app_data(bContext *C,
do_versions_ipos_to_animato(bmain);
}
+ /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */
+ /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a
+ * version bump and check here. */
+ if (!USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) {
+ BKE_lib_override_library_main_proxy_convert(bmain, reports);
+ }
+
bmain->recovered = 0;
/* startup.blend or recovered startup */
diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c
index ad2d5d267d5..3bb02e1856b 100644
--- a/source/blender/blenkernel/intern/customdata.c
+++ b/source/blender/blenkernel/intern/customdata.c
@@ -1856,6 +1856,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
NULL,
NULL,
NULL},
+ /* 51: CD_HAIRLENGTH */
+ {sizeof(float), "float", 1, NULL, NULL, NULL, NULL, NULL, NULL},
};
static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
@@ -1912,6 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
"CDPropFloat3",
"CDPropFloat2",
"CDPropBoolean",
+ "CDHairLength",
};
const CustomData_MeshMasks CD_MASK_BAREMESH = {
diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c
index aa13f86523a..1053b727cbc 100644
--- a/source/blender/blenkernel/intern/font.c
+++ b/source/blender/blenkernel/intern/font.c
@@ -490,15 +490,15 @@ static void build_underline(Curve *cu,
mul_v2_fl(bp[3].vec, font_size);
}
-static void buildchar(Curve *cu,
- ListBase *nubase,
- unsigned int character,
- CharInfo *info,
- float ofsx,
- float ofsy,
- float rot,
- int charidx,
- const float fsize)
+void BKE_vfont_build_char(Curve *cu,
+ ListBase *nubase,
+ unsigned int character,
+ CharInfo *info,
+ float ofsx,
+ float ofsy,
+ float rot,
+ int charidx,
+ const float fsize)
{
VFontData *vfd = vfont_get_data(which_vfont(cu, info));
if (!vfd) {
@@ -1525,7 +1525,7 @@ static bool vfont_to_curve(Object *ob,
}
/* We do not want to see any character for \n or \r */
if (cha != '\n') {
- buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
+ BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size);
}
if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) {
diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c
index c60a9104144..f382fc0614b 100644
--- a/source/blender/blenkernel/intern/lib_override.c
+++ b/source/blender/blenkernel/intern/lib_override.c
@@ -1003,6 +1003,77 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain,
return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL);
}
+static void lib_override_library_proxy_convert_do(Main *bmain,
+ Scene *scene,
+ Object *ob_proxy,
+ BlendFileReadReport *reports)
+{
+ Object *ob_proxy_group = ob_proxy->proxy_group;
+ const bool is_override_instancing_object = ob_proxy_group != NULL;
+
+ const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy);
+
+ if (success) {
+ CLOG_INFO(&LOG,
+ 4,
+ "Proxy object '%s' successfuly converted to library overrides",
+ ob_proxy->id.name);
+ /* Remove the instance empty from this scene, the items now have an overridden collection
+ * instead. */
+ if (is_override_instancing_object) {
+ BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true);
+ }
+ reports->count.proxies_to_lib_overrides_success++;
+ }
+}
+
+/**
+ * Convert all proxy objects into library overrides.
+ *
+ * \note Only affects local proxies, linked ones are not affected.
+ *
+ * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in
+ * which case \a scene's master collection children hierarchy is used instead).
+ */
+void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports)
+{
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy_group == NULL) {
+ continue;
+ }
+
+ lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ }
+ FOREACH_SCENE_OBJECT_END;
+
+ FOREACH_SCENE_OBJECT_BEGIN (scene, object) {
+ if (object->proxy == NULL) {
+ continue;
+ }
+
+ lib_override_library_proxy_convert_do(bmain, scene, object, reports);
+ }
+ FOREACH_SCENE_OBJECT_END;
+ }
+
+ LISTBASE_FOREACH (Object *, object, &bmain->objects) {
+ if (ID_IS_LINKED(object)) {
+ if (object->proxy != NULL) {
+ CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name);
+ reports->count.linked_proxies++;
+ }
+ continue;
+ }
+
+ if (object->proxy_group != NULL || object->proxy != NULL) {
+ CLOG_WARN(
+ &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name);
+ reports->count.proxies_to_lib_overrides_failures++;
+ }
+ }
+}
+
/**
* Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked
* data, from an existing override hierarchy.
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index dd74ee47cf3..1cf136347cc 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -4509,13 +4509,17 @@ static InputSocketFieldType get_interface_input_field_type(const NodeRef &node,
/* Node declarations should be implemented for nodes involved here. */
BLI_assert(node_decl != nullptr);
+ /* Get the field type from the declaration. */
+ const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()];
+ const InputSocketFieldType field_type = socket_decl.input_field_type();
+ if (field_type == InputSocketFieldType::Implicit) {
+ return field_type;
+ }
if (node_decl->is_function_node()) {
/* In a function node, every socket supports fields. */
return InputSocketFieldType::IsSupported;
}
- /* Get the field type from the declaration. */
- const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()];
- return socket_decl.input_field_type();
+ return field_type;
}
static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node,
@@ -5699,6 +5703,7 @@ static void registerGeometryNodes()
{
register_node_type_geo_group();
+ register_node_type_geo_legacy_attribute_randomize();
register_node_type_geo_legacy_material_assign();
register_node_type_geo_legacy_select_by_material();
@@ -5715,7 +5720,6 @@ static void registerGeometryNodes()
register_node_type_geo_attribute_math();
register_node_type_geo_attribute_mix();
register_node_type_geo_attribute_proximity();
- register_node_type_geo_attribute_randomize();
register_node_type_geo_attribute_remove();
register_node_type_geo_attribute_separate_xyz();
register_node_type_geo_attribute_statistic();
@@ -5748,6 +5752,7 @@ static void registerGeometryNodes()
register_node_type_geo_curve_to_points();
register_node_type_geo_curve_trim();
register_node_type_geo_delete_geometry();
+ register_node_type_geo_distribute_points_on_faces();
register_node_type_geo_edge_split();
register_node_type_geo_input_index();
register_node_type_geo_input_material();
@@ -5784,6 +5789,7 @@ static void registerGeometryNodes()
register_node_type_geo_material_selection();
register_node_type_geo_separate_components();
register_node_type_geo_set_position();
+ register_node_type_geo_string_to_curves();
register_node_type_geo_subdivision_surface();
register_node_type_geo_switch();
register_node_type_geo_transform();
@@ -5794,12 +5800,15 @@ static void registerGeometryNodes()
static void registerFunctionNodes()
{
+ register_node_type_fn_legacy_random_float();
+
register_node_type_fn_boolean_math();
register_node_type_fn_float_compare();
register_node_type_fn_float_to_int();
+ register_node_type_fn_input_special_characters();
register_node_type_fn_input_string();
register_node_type_fn_input_vector();
- register_node_type_fn_random_float();
+ register_node_type_fn_random_value();
register_node_type_fn_string_length();
register_node_type_fn_string_substring();
register_node_type_fn_value_to_string();
diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c
index 465ec9dc665..fbdf99c91c2 100644
--- a/source/blender/blenkernel/intern/object.c
+++ b/source/blender/blenkernel/intern/object.c
@@ -2656,8 +2656,6 @@ Object *BKE_object_duplicate(Main *bmain,
return obn;
}
- BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag);
-
if (dupflag & USER_DUP_MAT) {
for (int i = 0; i < obn->totcol; i++) {
BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag);
diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh
index 347ce2caa34..14e61d53845 100644
--- a/source/blender/blenlib/BLI_float4x4.hh
+++ b/source/blender/blenlib/BLI_float4x4.hh
@@ -45,6 +45,13 @@ struct float4x4 {
return mat;
}
+ static float4x4 from_location(const float3 location)
+ {
+ float4x4 mat = float4x4::identity();
+ copy_v3_v3(mat.values[3], location);
+ return mat;
+ }
+
static float4x4 from_normalized_axis_data(const float3 location,
const float3 forward,
const float3 up)
diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh
index 760ff082d06..7e1655f7864 100644
--- a/source/blender/blenlib/BLI_noise.hh
+++ b/source/blender/blenlib/BLI_noise.hh
@@ -22,36 +22,66 @@
namespace blender::noise {
-/* Perlin noise in the range [-1, 1]. */
+/* --------------------------------------------------------------------
+ * Hash functions.
+
+ * Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh`
+ * these functions produce better randomness but are more expensive to compute.
+ */
+
+/* Hash integers to `uint32_t`. */
+uint32_t hash(uint32_t kx);
+uint32_t hash(uint32_t kx, uint32_t ky);
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz);
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw);
+
+/* Hash floats to `uint32_t`. */
+uint32_t hash_float(float kx);
+uint32_t hash_float(float2 k);
+uint32_t hash_float(float3 k);
+uint32_t hash_float(float4 k);
+
+/* Hash integers to `float` between 0 and 1. */
+float hash_to_float(uint32_t kx);
+float hash_to_float(uint32_t kx, uint32_t ky);
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz);
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw);
+
+/* Hash floats to `float` between 0 and 1. */
+float hash_float_to_float(float k);
+float hash_float_to_float(float2 k);
+float hash_float_to_float(float3 k);
+float hash_float_to_float(float4 k);
+/* --------------------------------------------------------------------
+ * Perlin noise.
+ */
+
+/* Perlin noise in the range [-1, 1]. */
float perlin_signed(float position);
float perlin_signed(float2 position);
float perlin_signed(float3 position);
float perlin_signed(float4 position);
/* Perlin noise in the range [0, 1]. */
-
float perlin(float position);
float perlin(float2 position);
float perlin(float3 position);
float perlin(float4 position);
/* Fractal perlin noise in the range [0, 1]. */
-
float perlin_fractal(float position, float octaves, float roughness);
float perlin_fractal(float2 position, float octaves, float roughness);
float perlin_fractal(float3 position, float octaves, float roughness);
float perlin_fractal(float4 position, float octaves, float roughness);
/* Positive distorted fractal perlin noise. */
-
float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion);
float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion);
/* Positive distorted fractal perlin noise that outputs a float3. */
-
float3 perlin_float3_fractal_distorted(float position,
float octaves,
float roughness,
diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h
index 98a600a5de8..ed0d31b625f 100644
--- a/source/blender/blenlib/BLI_uuid.h
+++ b/source/blender/blenlib/BLI_uuid.h
@@ -98,6 +98,11 @@ class bUUID : public ::bUUID {
bool operator==(bUUID uuid1, bUUID uuid2);
bool operator!=(bUUID uuid1, bUUID uuid2);
+/**
+ * Lexicographic comparison of the UUIDs.
+ * Equivalent to string comparison on the formatted UUIDs. */
+bool operator<(bUUID uuid1, bUUID uuid2);
+
} // namespace blender
#endif
diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc
index c057c12e543..e80975f618c 100644
--- a/source/blender/blenlib/intern/noise.cc
+++ b/source/blender/blenlib/intern/noise.cc
@@ -109,7 +109,7 @@ BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c)
c -= hash_bit_rotate(b, 24);
}
-BLI_INLINE uint32_t hash(uint32_t kx)
+uint32_t hash(uint32_t kx)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (1 << 2) + 13;
@@ -120,7 +120,7 @@ BLI_INLINE uint32_t hash(uint32_t kx)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky)
+uint32_t hash(uint32_t kx, uint32_t ky)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (2 << 2) + 13;
@@ -132,7 +132,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (3 << 2) + 13;
@@ -145,7 +145,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz)
return c;
}
-BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
{
uint32_t a, b, c;
a = b = c = 0xdeadbeef + (4 << 2) + 13;
@@ -161,59 +161,83 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
return c;
}
-/* Hashing a number of uint32_t into a float in the range [0, 1]. */
+BLI_INLINE uint32_t float_as_uint(float f)
+{
+ union {
+ uint32_t i;
+ float f;
+ } u;
+ u.f = f;
+ return u.i;
+}
-BLI_INLINE float hash_to_float(uint32_t kx)
+uint32_t hash_float(float kx)
{
- return static_cast<float>(hash(kx)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(kx));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky)
+uint32_t hash_float(float2 k)
{
- return static_cast<float>(hash(kx, ky)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
+uint32_t hash_float(float3 k)
{
- return static_cast<float>(hash(kx, ky, kz)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z));
}
-BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+uint32_t hash_float(float4 k)
{
- return static_cast<float>(hash(kx, ky, kz, kw)) / static_cast<float>(0xFFFFFFFFu);
+ return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
}
-/* Hashing a number of floats into a float in the range [0, 1]. */
+/* Hashing a number of uint32_t into a float in the range [0, 1]. */
-BLI_INLINE uint32_t float_as_uint(float f)
+BLI_INLINE float uint_to_float_01(uint32_t k)
{
- union {
- uint32_t i;
- float f;
- } u;
- u.f = f;
- return u.i;
+ return static_cast<float>(k) / static_cast<float>(0xFFFFFFFFu);
+}
+
+float hash_to_float(uint32_t kx)
+{
+ return uint_to_float_01(hash(kx));
+}
+
+float hash_to_float(uint32_t kx, uint32_t ky)
+{
+ return uint_to_float_01(hash(kx, ky));
}
-BLI_INLINE float hash_to_float(float k)
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz)
+{
+ return uint_to_float_01(hash(kx, ky, kz));
+}
+
+float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw)
+{
+ return uint_to_float_01(hash(kx, ky, kz, kw));
+}
+
+/* Hashing a number of floats into a float in the range [0, 1]. */
+
+float hash_float_to_float(float k)
{
- return hash_to_float(float_as_uint(k));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float2 k)
+float hash_float_to_float(float2 k)
{
- return hash_to_float(float_as_uint(k.x), float_as_uint(k.y));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float3 k)
+float hash_float_to_float(float3 k)
{
- return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z));
+ return hash_to_float(hash_float(k));
}
-BLI_INLINE float hash_to_float(float4 k)
+float hash_float_to_float(float4 k)
{
- return hash_to_float(
- float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w));
+ return hash_to_float(hash_float(k));
}
/* ------------
@@ -565,28 +589,28 @@ float perlin_fractal(float4 position, float octaves, float roughness)
BLI_INLINE float random_float_offset(float seed)
{
- return 100.0f + hash_to_float(seed) * 100.0f;
+ return 100.0f + hash_float_to_float(seed) * 100.0f;
}
BLI_INLINE float2 random_float2_offset(float seed)
{
- return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f);
+ return float2(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f);
}
BLI_INLINE float3 random_float3_offset(float seed)
{
- return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f);
+ return float3(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f);
}
BLI_INLINE float4 random_float4_offset(float seed)
{
- return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f,
- 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f);
+ return float4(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f,
+ 100.0f + hash_float_to_float(float2(seed, 3.0f)) * 100.0f);
}
/* Perlin noises to be added to the position to distort other noises. */
diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc
index fe237b8aae6..3c86238036c 100644
--- a/source/blender/blenlib/intern/uuid.cc
+++ b/source/blender/blenlib/intern/uuid.cc
@@ -27,6 +27,7 @@
#include <random>
#include <sstream>
#include <string>
+#include <tuple>
/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */
static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly");
@@ -189,4 +190,22 @@ bool operator!=(const bUUID uuid1, const bUUID uuid2)
return !(uuid1 == uuid2);
}
+bool operator<(const bUUID uuid1, const bUUID uuid2)
+{
+ auto simple_fields1 = std::tie(uuid1.time_low,
+ uuid1.time_mid,
+ uuid1.time_hi_and_version,
+ uuid1.clock_seq_hi_and_reserved,
+ uuid1.clock_seq_low);
+ auto simple_fields2 = std::tie(uuid2.time_low,
+ uuid2.time_mid,
+ uuid2.time_hi_and_version,
+ uuid2.clock_seq_hi_and_reserved,
+ uuid2.clock_seq_low);
+ if (simple_fields1 == simple_fields2) {
+ return std::memcmp(uuid1.node, uuid2.node, sizeof(uuid1.node)) < 0;
+ }
+ return simple_fields1 < simple_fields2;
+}
+
} // namespace blender
diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc
index f2160f614ba..b406a0521a1 100644
--- a/source/blender/blenlib/tests/BLI_uuid_test.cc
+++ b/source/blender/blenlib/tests/BLI_uuid_test.cc
@@ -75,6 +75,29 @@ TEST(BLI_uuid, equality)
EXPECT_NE(uuid1, uuid2);
}
+TEST(BLI_uuid, comparison_trivial)
+{
+ const bUUID uuid0{};
+ const bUUID uuid1("11111111-1111-1111-1111-111111111111");
+ const bUUID uuid2("22222222-2222-2222-2222-222222222222");
+
+ EXPECT_LT(uuid0, uuid1);
+ EXPECT_LT(uuid0, uuid2);
+ EXPECT_LT(uuid1, uuid2);
+}
+
+TEST(BLI_uuid, comparison_byte_order_check)
+{
+ const bUUID uuid0{};
+ /* Chosen to test byte ordering is taken into account correctly when comparing. */
+ const bUUID uuid12("12222222-2222-2222-2222-222222222222");
+ const bUUID uuid21("21111111-1111-1111-1111-111111111111");
+
+ EXPECT_LT(uuid0, uuid12);
+ EXPECT_LT(uuid0, uuid21);
+ EXPECT_LT(uuid12, uuid21);
+}
+
TEST(BLI_uuid, string_formatting)
{
bUUID uuid;
diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h
index c8615545df9..9093c6fd85b 100644
--- a/source/blender/blenloader/BLO_readfile.h
+++ b/source/blender/blenloader/BLO_readfile.h
@@ -111,8 +111,16 @@ typedef struct BlendFileReadReport {
/* Some sub-categories of the above `missing_linked_id` counter. */
int missing_obdata;
int missing_obproxies;
+
/* Number of root override IDs that were resynced. */
int resynced_lib_overrides;
+
+ /* Number of (non-converted) linked proxies. */
+ int linked_proxies;
+ /* Number of proxies converted to library overrides. */
+ int proxies_to_lib_overrides_success;
+ /* Number of proxies that failed to convert to library overrides. */
+ int proxies_to_lib_overrides_failures;
} count;
/* Number of libraries which had overrides that needed to be resynced, and a single linked list
@@ -168,9 +176,8 @@ struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
const bool use_assets_only,
int *r_tot_names);
-struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
- int ofblocktype,
- int *r_tot_info_items);
+struct LinkNode * /*BLODataBlockInfo */ BLO_blendhandle_get_datablock_info(
+ BlendHandle *bh, int ofblocktype, const bool use_assets_only, int *r_tot_info_items);
struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev);
struct PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh,
int ofblocktype,
diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c
index f88b470809c..3306eb9e454 100644
--- a/source/blender/blenloader/intern/readblenentry.c
+++ b/source/blender/blenloader/intern/readblenentry.c
@@ -170,17 +170,19 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh,
}
/**
- * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain
- * type (e.g. all the scene names in a file).
+ * Gets the names and asset-data (if ID is an asset) of data-blocks in a file of a certain type.
+ * The data-blocks can be limited to assets.
*
* \param bh: The blendhandle to access.
* \param ofblocktype: The type of names to get.
+ * \param use_assets_only: Limit the result to assets only.
* \param tot_info_items: The length of the returned list.
* \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should
* be freed with MEM_freeN.
*/
LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
int ofblocktype,
+ const bool use_assets_only,
int *r_tot_info_items)
{
FileData *fd = (FileData *)bh;
@@ -189,27 +191,34 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh,
int tot = 0;
for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) {
+ if (bhead->code == ENDB) {
+ break;
+ }
if (bhead->code == ofblocktype) {
- struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
const char *name = blo_bhead_id_name(fd, bhead) + 2;
+ AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead);
- STRNCPY(info->name, name);
+ const bool is_asset = asset_meta_data != NULL;
+ const bool skip_datablock = use_assets_only && !is_asset;
+ if (skip_datablock) {
+ continue;
+ }
+ struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__);
/* Lastly, read asset data from the following blocks. */
- info->asset_data = blo_bhead_id_asset_data_address(fd, bhead);
- if (info->asset_data) {
- bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data);
- /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next
- * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
+ if (asset_meta_data) {
+ bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data);
+ /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the
+ * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */
bhead = blo_bhead_prev(fd, bhead);
}
+ STRNCPY(info->name, name);
+ info->asset_data = asset_meta_data;
+
BLI_linklist_prepend(&infos, info);
tot++;
}
- else if (bhead->code == ENDB) {
- break;
- }
}
*r_tot_info_items = tot;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index be8c4b735be..bf5b0bdbf3c 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1540,7 +1540,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
- STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
+ STRNCPY(node->idname, "GeometryLegacyNodeAttributeRandomize");
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 0d333ac2edc..caba972c744 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -536,8 +536,9 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
}
}
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
- if (brush->clone.image != NULL && ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT,
- IMA_TYPE_COMPOSITE)) { brush->clone.image = NULL;
+ if (brush->clone.image != NULL &&
+ ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) {
+ brush->clone.image = NULL;
}
}
}
@@ -807,6 +808,7 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree)
}
}
}
+
static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data))
{
StripTransform *transform = seq->strip->transform;
@@ -1479,5 +1481,29 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Deprecate the random float node in favor of the random float node. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ if (ntree->type != NTREE_GEOMETRY) {
+ continue;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->type != FN_NODE_LEGACY_RANDOM_FLOAT) {
+ continue;
+ }
+ if (strstr(node->idname, "Legacy")) {
+ /* Make sure we haven't changed this idname already. */
+ continue;
+ }
+
+ char temp_idname[sizeof(node->idname)];
+ BLI_strncpy(temp_idname, node->idname, sizeof(node->idname));
+
+ BLI_snprintf(node->idname,
+ sizeof(node->idname),
+ "FunctionNodeLegacy%s",
+ temp_idname + strlen("FunctionNode"));
+ }
+ }
}
-}
+} \ No newline at end of file
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
index 36c6b56caae..463bb02afa4 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc
@@ -63,6 +63,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_world_types.h"
#include "BKE_action.h"
@@ -1764,6 +1765,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
else if (id_type == ID_MC) {
build_movieclip((MovieClip *)id);
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -2015,6 +2019,17 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation)
});
}
+void DepsgraphNodeBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+ add_operation_node(
+ &vfont->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE);
+}
+
static bool seq_node_build_cb(Sequence *seq, void *user_data)
{
DepsgraphNodeBuilder *nb = (DepsgraphNodeBuilder *)user_data;
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
index 2378f3fc100..d31290ecbff 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h
@@ -55,6 +55,7 @@ struct Scene;
struct Simulation;
struct Speaker;
struct Tex;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -235,6 +236,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
/* Per-ID information about what was already in the dependency graph.
* Allows to re-use certain values, to speed up following evaluation. */
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
index 28cfc5a9e1a..55e8c5ed033 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc
@@ -65,6 +65,7 @@
#include "DNA_sound_types.h"
#include "DNA_speaker_types.h"
#include "DNA_texture_types.h"
+#include "DNA_vfont_types.h"
#include "DNA_volume_types.h"
#include "DNA_world_types.h"
@@ -2496,6 +2497,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL);
add_relation(clip_key, shading_key, "Clip -> Node");
}
+ else if (id_type == ID_VF) {
+ build_vfont((VFont *)id);
+ ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK);
+ add_relation(vfont_key, shading_key, "VFont -> Node");
+ }
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
@@ -2842,6 +2848,15 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer
}
}
+void DepsgraphRelationBuilder::build_vfont(VFont *vfont)
+{
+ if (built_map_.checkIsBuiltAndTag(vfont)) {
+ return;
+ }
+ build_parameters(&vfont->id);
+ build_idproperties(vfont->id.properties);
+}
+
void DepsgraphRelationBuilder::build_copy_on_write_relations()
{
for (IDNode *id_node : graph_->id_nodes) {
diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
index 1ad61c25305..f0393544511 100644
--- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h
+++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h
@@ -72,6 +72,7 @@ struct Simulation;
struct Speaker;
struct Tex;
struct ViewLayer;
+struct VFont;
struct World;
struct bAction;
struct bArmature;
@@ -296,6 +297,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder {
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
+ virtual void build_vfont(VFont *vfont);
virtual void build_nested_datablock(ID *owner, ID *id);
virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree);
diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
index 49780abc6f4..ea60dd0753e 100644
--- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c
+++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c
@@ -255,7 +255,7 @@ static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata,
{
DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create(
vedata, sldata, ob, material, true);
- DRW_shgroup_hair_create_sub(ob, psys, md, grp);
+ DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata,
diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c
index 9ecb737192e..a627bcd9488 100644
--- a/source/blender/draw/engines/eevee/eevee_materials.c
+++ b/source/blender/draw/engines/eevee/eevee_materials.c
@@ -769,15 +769,16 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata,
EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true);
if (matcache.depth_grp) {
- *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp);
+ *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat);
}
if (matcache.shading_grp) {
- *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp);
+ *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(
+ ob, psys, md, matcache.shading_grp, matcache.shading_gpumat);
DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat);
}
if (matcache.shadow_grp) {
- *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp);
+ *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL);
DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat);
*cast_shadow = true;
}
diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c
index 2e200c8e053..e8c2514d908 100644
--- a/source/blender/draw/engines/eevee/eevee_motion_blur.c
+++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c
@@ -269,7 +269,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata),
GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV];
GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT];
- grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp);
+ grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL);
DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]);
DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]);
DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]);
diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c
index 635aa7cef25..a5281427fa8 100644
--- a/source/blender/draw/engines/workbench/workbench_engine.c
+++ b/source/blender/draw/engines/workbench/workbench_engine.c
@@ -238,7 +238,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd,
workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, state) :
workbench_material_hair_setup(wpd, ob, matnr, color_type);
- DRW_shgroup_hair_create_sub(ob, psys, md, grp);
+ DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL);
}
/**
diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc
index 0804745fab5..dc8f382b7f8 100644
--- a/source/blender/draw/intern/draw_cache_impl_curve.cc
+++ b/source/blender/draw/intern/draw_cache_impl_curve.cc
@@ -342,6 +342,9 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers,
case CD_ORCO:
*cd_layers |= CD_MASK_ORCO;
break;
+ case CD_HAIRLENGTH:
+ *cd_layers |= CD_MASK_HAIRLENGTH;
+ break;
}
}
}
diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c
index 6424b21666d..41a0cca8a8f 100644
--- a/source/blender/draw/intern/draw_cache_impl_hair.c
+++ b/source/blender/draw/intern/draw_cache_impl_hair.c
@@ -27,6 +27,7 @@
#include "MEM_guardedalloc.h"
+#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_math_vector.h"
#include "BLI_utildefines.h"
@@ -37,6 +38,7 @@
#include "BKE_hair.h"
#include "GPU_batch.h"
+#include "GPU_material.h"
#include "GPU_texture.h"
#include "draw_cache_impl.h" /* own include */
@@ -141,7 +143,9 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache)
}
}
-static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step)
+static void hair_batch_cache_fill_segments_proc_pos(Hair *hair,
+ GPUVertBufRaw *attr_step,
+ GPUVertBufRaw *length_step)
{
/* TODO: use hair radius layer if available. */
HairCurve *curve = hair->curves;
@@ -162,6 +166,8 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
seg_data[3] = total_len;
co_prev = curve_co[j];
}
+ /* Assign length value*/
+ *(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) {
@@ -171,28 +177,48 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a
}
}
-static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache)
+static void hair_batch_cache_ensure_procedural_pos(Hair *hair,
+ ParticleHairCache *cache,
+ GPUMaterial *gpu_material)
{
- if (cache->proc_point_buf != NULL) {
- return;
- }
+ if (cache->proc_point_buf == NULL) {
+ /* initialize vertex format */
+ GPUVertFormat format = {0};
+ uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- /* initialize vertex format */
- GPUVertFormat format = {0};
- uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
+ GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
- cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
+ GPUVertBufRaw point_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &point_step);
- GPUVertBufRaw pos_step;
- GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
+ GPUVertFormat length_format = {0};
+ uint length_id = GPU_vertformat_attr_add(
+ &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- hair_batch_cache_fill_segments_proc_pos(hair, &pos_step);
+ cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
- /* Create vbo immediately to bind to texture buffer. */
- GPU_vertbuf_use(cache->proc_point_buf);
+ GPUVertBufRaw length_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
+
+ hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step);
- cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_point_buf);
+ cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf);
+ }
+
+ if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex) {
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
+ if (attr->type == CD_HAIRLENGTH) {
+ GPU_vertbuf_use(cache->proc_length_buf);
+ cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
+ break;
+ }
+ }
+ }
}
static void hair_batch_cache_fill_strands_data(Hair *hair,
@@ -310,6 +336,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair,
/* Ensure all textures and buffers needed for GPU accelerated drawing. */
bool hair_ensure_procedural_data(Object *object,
ParticleHairCache **r_hair_cache,
+ GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@@ -325,7 +352,7 @@ bool hair_ensure_procedural_data(Object *object,
/* Refreshed on combing and simulation. */
if ((*r_hair_cache)->proc_point_buf == NULL) {
ensure_seg_pt_count(hair, &cache->hair);
- hair_batch_cache_ensure_procedural_pos(hair, &cache->hair);
+ hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material);
need_ft_update = true;
}
diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c
index 5c51f24a435..087d88f4b1f 100644
--- a/source/blender/draw/intern/draw_cache_impl_particles.c
+++ b/source/blender/draw/intern/draw_cache_impl_particles.c
@@ -46,6 +46,7 @@
#include "ED_particle.h"
#include "GPU_batch.h"
+#include "GPU_material.h"
#include "DEG_depsgraph_query.h"
@@ -183,7 +184,9 @@ void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache)
{
/* TODO: more granular update tagging. */
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf);
+ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_length_buf);
DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex);
+ DRW_TEXTURE_FREE_SAFE(hair_cache->length_tex);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf);
GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_seg_buf);
@@ -609,7 +612,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys,
static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_cache,
const int num_path_keys,
- GPUVertBufRaw *attr_step)
+ GPUVertBufRaw *attr_step,
+ GPUVertBufRaw *length_step)
{
for (int i = 0; i < num_path_keys; i++) {
ParticleCacheKey *path = path_cache[i];
@@ -630,6 +634,8 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_
seg_data[3] = total_len;
co_prev = path[j].co;
}
+ /* Assign length value*/
+ *(float *)GPU_vertbuf_raw_step(length_step) = total_len;
if (total_len > 0.0f) {
/* Divide by total length to have a [0-1] number. */
for (int j = 0; j <= path->segments; j++, seg_data_first += 4) {
@@ -1079,40 +1085,64 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit,
static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit,
ParticleSystem *psys,
- ParticleHairCache *cache)
+ ParticleHairCache *cache,
+ GPUMaterial *gpu_material)
{
- if (cache->proc_point_buf != NULL) {
- return;
- }
+ if (cache->proc_point_buf == NULL) {
+ /* initialize vertex format */
+ GPUVertFormat pos_format = {0};
+ uint pos_id = GPU_vertformat_attr_add(
+ &pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
- /* initialize vertex format */
- GPUVertFormat format = {0};
- uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
+ cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format);
+ GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
- cache->proc_point_buf = GPU_vertbuf_create_with_format(&format);
- GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len);
+ GPUVertBufRaw pos_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
- GPUVertBufRaw pos_step;
- GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step);
+ GPUVertFormat length_format = {0};
+ uint length_id = GPU_vertformat_attr_add(
+ &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
- if (edit != NULL && edit->pathcache != NULL) {
- particle_batch_cache_fill_segments_proc_pos(edit->pathcache, edit->totcached, &pos_step);
- }
- else {
- if ((psys->pathcache != NULL) &&
- (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
- particle_batch_cache_fill_segments_proc_pos(psys->pathcache, psys->totpart, &pos_step);
+ cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format);
+ GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len);
+
+ GPUVertBufRaw length_step;
+ GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step);
+
+ if (edit != NULL && edit->pathcache != NULL) {
+ particle_batch_cache_fill_segments_proc_pos(
+ edit->pathcache, edit->totcached, &pos_step, &length_step);
}
- if (psys->childcache) {
- const int child_count = psys->totchild * psys->part->disp / 100;
- particle_batch_cache_fill_segments_proc_pos(psys->childcache, child_count, &pos_step);
+ else {
+ if ((psys->pathcache != NULL) &&
+ (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) {
+ particle_batch_cache_fill_segments_proc_pos(
+ psys->pathcache, psys->totpart, &pos_step, &length_step);
+ }
+ if (psys->childcache) {
+ const int child_count = psys->totchild * psys->part->disp / 100;
+ particle_batch_cache_fill_segments_proc_pos(
+ psys->childcache, child_count, &pos_step, &length_step);
+ }
}
- }
- /* Create vbo immediately to bind to texture buffer. */
- GPU_vertbuf_use(cache->proc_point_buf);
+ /* Create vbo immediately to bind to texture buffer. */
+ GPU_vertbuf_use(cache->proc_point_buf);
+ cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
+ }
- cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf);
+ /* Checking hair length seperatly, only allocating gpu memory when needed */
+ if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) {
+ ListBase gpu_attrs = GPU_material_attributes(gpu_material);
+ LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) {
+ if (attr->type == CD_HAIRLENGTH) {
+ GPU_vertbuf_use(cache->proc_length_buf);
+ cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf);
+ break;
+ }
+ }
+ }
}
static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit,
@@ -1649,6 +1679,7 @@ bool particles_ensure_procedural_data(Object *object,
ParticleSystem *psys,
ModifierData *md,
ParticleHairCache **r_hair_cache,
+ GPUMaterial *gpu_material,
int subdiv,
int thickness_res)
{
@@ -1666,9 +1697,11 @@ bool particles_ensure_procedural_data(Object *object,
(*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv);
/* Refreshed on combing and simulation. */
- if ((*r_hair_cache)->proc_point_buf == NULL) {
+ if ((*r_hair_cache)->proc_point_buf == NULL ||
+ (gpu_material && (*r_hair_cache)->length_tex == NULL)) {
ensure_seg_pt_count(source.edit, source.psys, &cache->hair);
- particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair);
+ particle_batch_cache_ensure_procedural_pos(
+ source.edit, source.psys, &cache->hair, gpu_material);
need_ft_update = true;
}
diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h
index 1eaf2bee236..2913877c9c1 100644
--- a/source/blender/draw/intern/draw_common.h
+++ b/source/blender/draw/intern/draw_common.h
@@ -29,6 +29,7 @@ struct Object;
struct ParticleSystem;
struct RegionView3D;
struct ViewLayer;
+struct GPUMaterial;
#define UBO_FIRST_COLOR colorWire
#define UBO_LAST_COLOR colorUVShadow
@@ -175,7 +176,8 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis);
struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
- struct DRWShadingGroup *shgrp);
+ struct DRWShadingGroup *shgrp,
+ struct GPUMaterial *gpu_material);
struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md);
diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c
index c2e25389091..5c7eb083fc9 100644
--- a/source/blender/draw/intern/draw_hair.c
+++ b/source/blender/draw/intern/draw_hair.c
@@ -38,6 +38,7 @@
#include "GPU_batch.h"
#include "GPU_capabilities.h"
#include "GPU_compute.h"
+#include "GPU_material.h"
#include "GPU_shader.h"
#include "GPU_texture.h"
#include "GPU_vertex_buffer.h"
@@ -172,18 +173,23 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache
}
}
-static ParticleHairCache *drw_hair_particle_cache_get(
- Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res)
+static ParticleHairCache *drw_hair_particle_cache_get(Object *object,
+ ParticleSystem *psys,
+ ModifierData *md,
+ GPUMaterial *gpu_material,
+ int subdiv,
+ int thickness_res)
{
bool update;
ParticleHairCache *cache;
if (psys) {
/* Old particle hair. */
- update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res);
+ update = particles_ensure_procedural_data(
+ object, psys, md, &cache, gpu_material, subdiv, thickness_res);
}
else {
/* New hair object. */
- update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res);
+ update = hair_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res);
}
if (update) {
@@ -206,7 +212,8 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi
int subdiv = scene->r.hair_subdiv;
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
- ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res);
+ ParticleHairCache *cache = drw_hair_particle_cache_get(
+ object, psys, md, NULL, subdiv, thickness_res);
return cache->final[subdiv].proc_buf;
}
@@ -248,7 +255,8 @@ void DRW_hair_duplimat_get(Object *object,
DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
ParticleSystem *psys,
ModifierData *md,
- DRWShadingGroup *shgrp_parent)
+ DRWShadingGroup *shgrp_parent,
+ GPUMaterial *gpu_material)
{
const DRWContextState *draw_ctx = DRW_context_state_get();
Scene *scene = draw_ctx->scene;
@@ -258,7 +266,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2;
ParticleHairCache *hair_cache = drw_hair_particle_cache_get(
- object, psys, md, subdiv, thickness_res);
+ object, psys, md, gpu_material, subdiv, thickness_res);
DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent);
@@ -308,6 +316,9 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object,
}
DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex);
+ if (hair_cache->length_tex) {
+ DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex);
+ }
DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1);
DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res);
DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape);
diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h
index 1f58d8d0ead..289a1690fc6 100644
--- a/source/blender/draw/intern/draw_hair_private.h
+++ b/source/blender/draw/intern/draw_hair_private.h
@@ -66,6 +66,10 @@ typedef struct ParticleHairCache {
GPUVertBuf *proc_strand_buf;
GPUTexture *strand_tex;
+ /* Hair Length */
+ GPUVertBuf *proc_length_buf;
+ GPUTexture *length_tex;
+
GPUVertBuf *proc_strand_seg_buf;
GPUTexture *strand_seg_tex;
@@ -93,11 +97,13 @@ bool particles_ensure_procedural_data(struct Object *object,
struct ParticleSystem *psys,
struct ModifierData *md,
struct ParticleHairCache **r_hair_cache,
+ struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);
bool hair_ensure_procedural_data(struct Object *object,
struct ParticleHairCache **r_hair_cache,
+ struct GPUMaterial *gpu_material,
int subdiv,
int thickness_res);
diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl
index 02c335ddae2..6cc7f09a852 100644
--- a/source/blender/draw/intern/shaders/common_hair_lib.glsl
+++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl
@@ -210,6 +210,12 @@ void hair_get_pos_tan_binor_time(bool is_persp,
}
}
+float hair_get_customdata_float(const samplerBuffer cd_buf)
+{
+ int id = hair_get_strand_id();
+ return texelFetch(cd_buf, id).r;
+}
+
vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf)
{
int id = hair_get_strand_id();
diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc
index 57c25e1614b..cda117533cd 100644
--- a/source/blender/editors/asset/intern/asset_list.cc
+++ b/source/blender/editors/asset/intern/asset_list.cc
@@ -390,7 +390,7 @@ std::optional<eFileSelectType> AssetListStorage::asset_library_reference_to_file
{
switch (library_reference.type) {
case ASSET_LIBRARY_CUSTOM:
- return FILE_LOADLIB;
+ return FILE_ASSET_LIBRARY;
case ASSET_LIBRARY_LOCAL:
return FILE_MAIN_ASSET;
}
diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc
index c2ecab08ace..ee50126f974 100644
--- a/source/blender/editors/interface/tree_view.cc
+++ b/source/blender/editors/interface/tree_view.cc
@@ -57,7 +57,7 @@ AbstractTreeViewItem &TreeViewItemContainer::add_tree_item(
void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const
{
- for (auto &child : children_) {
+ for (const auto &child : children_) {
iter_fn(*child);
if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) {
continue;
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index 50dd9322c5c..d00e6efeb29 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -78,7 +78,6 @@ void OBJECT_OT_mode_set(struct wmOperatorType *ot);
void OBJECT_OT_mode_set_with_submode(struct wmOperatorType *ot);
void OBJECT_OT_editmode_toggle(struct wmOperatorType *ot);
void OBJECT_OT_posemode_toggle(struct wmOperatorType *ot);
-void OBJECT_OT_proxy_make(struct wmOperatorType *ot);
void OBJECT_OT_shade_smooth(struct wmOperatorType *ot);
void OBJECT_OT_shade_flat(struct wmOperatorType *ot);
void OBJECT_OT_paths_calculate(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index aa9ae082317..fa0208a7022 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -56,7 +56,6 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_mode_set_with_submode);
WM_operatortype_append(OBJECT_OT_editmode_toggle);
WM_operatortype_append(OBJECT_OT_posemode_toggle);
- WM_operatortype_append(OBJECT_OT_proxy_make);
WM_operatortype_append(OBJECT_OT_shade_smooth);
WM_operatortype_append(OBJECT_OT_shade_flat);
WM_operatortype_append(OBJECT_OT_paths_calculate);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 75269dffec8..5c7e1e1fa01 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -331,167 +331,6 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot)
/** \} */
/* ------------------------------------------------------------------- */
-/** \name Make Proxy Operator
- * \{ */
-
-/* set the object to proxify */
-static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- Scene *scene = CTX_data_scene(C);
- Object *ob = ED_object_active_context(C);
-
- /* sanity checks */
- if (!scene || ID_IS_LINKED(scene) || !ob) {
- return OPERATOR_CANCELLED;
- }
-
- /* Get object to work on - use a menu if we need to... */
- if (ob->instance_collection && ID_IS_LINKED(ob->instance_collection)) {
- /* gives menu with list of objects in group */
- /* proxy_group_objects_menu(C, op, ob, ob->instance_collection); */
- WM_enum_search_invoke(C, op, event);
- return OPERATOR_CANCELLED;
- }
- if (ID_IS_LINKED(ob)) {
- uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
- uiLayout *layout = UI_popup_menu_layout(pup);
-
- /* create operator menu item with relevant properties filled in */
- PointerRNA opptr_dummy;
- uiItemFullO_ptr(
- layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy);
-
- /* present the menu and be done... */
- UI_popup_menu_end(C, pup);
-
- /* this invoke just calls another instance of this operator... */
- return OPERATOR_INTERFACE;
- }
-
- /* error.. cannot continue */
- BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection");
- return OPERATOR_CANCELLED;
-}
-
-static int make_proxy_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Object *ob, *gob = ED_object_active_context(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
-
- if (gob->instance_collection != NULL) {
- const ListBase instance_collection_objects = BKE_collection_object_cache_get(
- gob->instance_collection);
- Base *base = BLI_findlink(&instance_collection_objects, RNA_enum_get(op->ptr, "object"));
- ob = base->object;
- }
- else {
- ob = gob;
- gob = NULL;
- }
-
- if (ob) {
- Object *newob;
- char name[MAX_ID_NAME + 4];
-
- BLI_snprintf(name, sizeof(name), "%s_proxy", ((ID *)(gob ? gob : ob))->name + 2);
-
- /* Add new object for the proxy */
- newob = BKE_object_add_from(bmain, scene, view_layer, OB_EMPTY, name, gob ? gob : ob);
-
- /* set layers OK */
- BKE_object_make_proxy(bmain, newob, ob, gob);
-
- /* Set back pointer immediately so dependency graph knows that this is
- * is a proxy and will act accordingly. Otherwise correctness of graph
- * will depend on order of bases.
- *
- * TODO(sergey): We really need to get rid of this bi-directional links
- * in proxies with something like library overrides.
- */
- if (newob->proxy != NULL) {
- newob->proxy->proxy_from = newob;
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "Unable to assign proxy");
- }
-
- /* depsgraph flushes are needed for the new data */
- DEG_relations_tag_update(bmain);
- DEG_id_tag_update(&newob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, newob);
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "No object to make proxy for");
- return OPERATOR_CANCELLED;
- }
-
- return OPERATOR_FINISHED;
-}
-
-/* Generic itemf's for operators that take library args */
-static const EnumPropertyItem *proxy_collection_object_itemf(bContext *C,
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
-{
- EnumPropertyItem item_tmp = {0}, *item = NULL;
- int totitem = 0;
- int i = 0;
- Object *ob = ED_object_active_context(C);
-
- if (!ob || !ob->instance_collection) {
- return DummyRNA_DEFAULT_items;
- }
-
- /* find the object to affect */
- FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, object) {
- item_tmp.identifier = item_tmp.name = object->id.name + 2;
- item_tmp.value = i++;
- RNA_enum_item_add(&item, &totitem, &item_tmp);
- }
- FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
-
- RNA_enum_item_end(&item, &totitem);
- *r_free = true;
-
- return item;
-}
-
-void OBJECT_OT_proxy_make(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Make Proxy";
- ot->idname = "OBJECT_OT_proxy_make";
- ot->description = "Add empty object to become local replacement data of a library-linked object";
-
- /* callbacks */
- ot->invoke = make_proxy_invoke;
- ot->exec = make_proxy_exec;
- ot->poll = ED_operator_object_active;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- /* XXX, relies on hard coded ID at the moment */
- prop = RNA_def_enum(ot->srna,
- "object",
- DummyRNA_DEFAULT_items,
- 0,
- "Proxy Object",
- "Name of library-linked/collection object to make a proxy for");
- RNA_def_enum_funcs(prop, proxy_collection_object_itemf);
- RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
- ot->prop = prop;
-}
-
-/** \} */
-
-/* ------------------------------------------------------------------- */
/** \name Clear Parent Operator
* \{ */
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 2ccefb993c7..3d447d90626 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -1073,9 +1073,14 @@ static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult *
{
wmWindow *win = CTX_wm_window(C);
ARegion *region = CTX_wm_region(C);
- uiList *list = UI_list_find_mouse_over(region, win->eventstate);
- CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
- return CTX_RESULT_OK;
+ if (region) {
+ uiList *list = UI_list_find_mouse_over(region, win->eventstate);
+ if (list) {
+ CTX_data_pointer_set(result, NULL, &RNA_UIList, list);
+ return CTX_RESULT_OK;
+ }
+ }
+ return CTX_RESULT_NO_DATA;
}
/* Registry of context callback functions. */
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index 4f881184990..194e577e19e 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -473,6 +473,10 @@ static void filelist_readjob_dir(struct FileListReadJob *job_params,
short *stop,
short *do_update,
float *progress);
+static void filelist_readjob_asset_library(struct FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress);
static void filelist_readjob_main_assets(struct FileListReadJob *job_params,
short *stop,
short *do_update,
@@ -1725,6 +1729,11 @@ void filelist_settype(FileList *filelist, short type)
filelist->read_job_fn = filelist_readjob_lib;
filelist->filter_fn = is_filtered_lib;
break;
+ case FILE_ASSET_LIBRARY:
+ filelist->check_dir_fn = filelist_checkdir_lib;
+ filelist->read_job_fn = filelist_readjob_asset_library;
+ filelist->filter_fn = is_filtered_lib;
+ break;
case FILE_MAIN_ASSET:
filelist->check_dir_fn = filelist_checkdir_main_assets;
filelist->read_job_fn = filelist_readjob_main_assets;
@@ -1741,7 +1750,10 @@ void filelist_settype(FileList *filelist, short type)
filelist->flags |= FL_FORCE_RESET;
}
-void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
+void filelist_clear_ex(struct FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection)
{
if (!filelist) {
return;
@@ -1761,7 +1773,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo
BLI_ghash_clear(filelist->selection_state, NULL, NULL);
}
- if (filelist->asset_library != NULL) {
+ if (do_asset_library && (filelist->asset_library != NULL)) {
/* There is no way to refresh the catalogs stored by the AssetLibrary struct, so instead of
* "clearing" it, the entire struct is freed. It will be reallocated when needed. */
BKE_asset_library_free(filelist->asset_library);
@@ -1771,7 +1783,7 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo
void filelist_clear(struct FileList *filelist)
{
- filelist_clear_ex(filelist, true, true);
+ filelist_clear_ex(filelist, true, true, true);
}
void filelist_free(struct FileList *filelist)
@@ -1782,7 +1794,7 @@ void filelist_free(struct FileList *filelist)
}
/* No need to clear cache & selection_state, we free them anyway. */
- filelist_clear_ex(filelist, false, false);
+ filelist_clear_ex(filelist, true, false, false);
filelist_cache_free(&filelist->filelist_cache);
if (filelist->selection_state) {
@@ -2888,76 +2900,129 @@ static int filelist_readjob_list_dir(const char *root,
return nbr_entries;
}
-static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
+typedef enum ListLibOptions {
+ /* Will read both the groups + actual ids from the library. Reduces the amount of times that
+ * a library needs to be opened. */
+ LIST_LIB_RECURSIVE = (1 << 0),
+
+ /* Will only list assets. */
+ LIST_LIB_ASSETS_ONLY = (1 << 1),
+
+ /* Add given root as result. */
+ LIST_LIB_ADD_PARENT = (1 << 2),
+} ListLibOptions;
+
+static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode,
+ const char *group_name)
+{
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(group_name);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR;
+ entry->blentype = idcode;
+ return entry;
+}
+
+static void filelist_readjob_list_lib_add_datablocks(ListBase *entries,
+ LinkNode *datablock_infos,
+ const bool prefix_relpath_with_group_name,
+ const int idcode,
+ const char *group_name)
+{
+ for (LinkNode *ln = datablock_infos; ln; ln = ln->next) {
+ struct BLODataBlockInfo *info = ln->link;
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
+ if (prefix_relpath_with_group_name) {
+ entry->relpath = BLI_sprintfN("%s/%s", group_name, info->name);
+ }
+ else {
+ entry->relpath = BLI_strdup(info->name);
+ }
+ entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (info && info->asset_data) {
+ entry->typeflag |= FILE_TYPE_ASSET;
+ /* Moves ownership! */
+ entry->imported_asset_data = info->asset_data;
+ }
+ entry->blentype = idcode;
+ BLI_addtail(entries, entry);
+ }
+}
+
+static int filelist_readjob_list_lib(const char *root,
+ ListBase *entries,
+ const ListLibOptions options)
{
- FileListInternEntry *entry;
- LinkNode *ln, *names = NULL, *datablock_infos = NULL;
- int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
- bool ok;
struct BlendHandle *libfiledata = NULL;
- /* name test */
- ok = BLO_library_path_explode(root, dir, &group, NULL);
- if (!ok) {
- return nbr_entries;
+ /* Check if the given root is actually a library. All folders are passed to
+ * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do`
+ * will do a dir listing only when this function does not return any entries. */
+ /* TODO: We should consider introducing its own function to detect if it is a lib and
+ * call it directly from `filelist_readjob_do` to increase readability. */
+ const bool is_lib = BLO_library_path_explode(root, dir, &group, NULL);
+ if (!is_lib) {
+ return 0;
}
- /* there we go */
+ /* Open the library file. */
BlendFileReadReport bf_reports = {.reports = NULL};
libfiledata = BLO_blendhandle_from_file(dir, &bf_reports);
if (libfiledata == NULL) {
- return nbr_entries;
- }
-
- /* memory for strings is passed into filelist[i].entry->relpath
- * and freed in filelist_entry_free. */
- if (group) {
- idcode = groupname_to_code(group);
- datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
- }
- else {
- names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nitems = BLI_linklist_count(names);
+ return 0;
}
- BLO_blendhandle_close(libfiledata);
-
- if (!skip_currpar) {
- entry = MEM_callocN(sizeof(*entry), __func__);
+ /* Add current parent when requested. */
+ int parent_len = 0;
+ if (options & LIST_LIB_ADD_PARENT) {
+ FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(FILENAME_PARENT);
entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR);
BLI_addtail(entries, entry);
- nbr_entries++;
+ parent_len = 1;
}
- for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) {
- struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL;
- const char *blockname = info ? info->name : ln->link;
-
- entry = MEM_callocN(sizeof(*entry), __func__);
- entry->relpath = BLI_strdup(blockname);
- entry->typeflag |= FILE_TYPE_BLENDERLIB;
- if (info && info->asset_data) {
- entry->typeflag |= FILE_TYPE_ASSET;
- /* Moves ownership! */
- entry->imported_asset_data = info->asset_data;
- }
- if (!(group && idcode)) {
- entry->typeflag |= FILE_TYPE_DIR;
- entry->blentype = groupname_to_code(blockname);
- }
- else {
- entry->blentype = idcode;
+ int group_len = 0;
+ int datablock_len = 0;
+ const bool group_came_from_path = group != NULL;
+ if (group_came_from_path) {
+ const int idcode = groupname_to_code(group);
+ LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len);
+ filelist_readjob_list_lib_add_datablocks(entries, datablock_infos, false, idcode, group);
+ BLI_linklist_freeN(datablock_infos);
+ }
+ else {
+ LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata);
+ group_len = BLI_linklist_count(groups);
+
+ for (LinkNode *ln = groups; ln; ln = ln->next) {
+ const char *group_name = ln->link;
+ const int idcode = groupname_to_code(group_name);
+ FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode,
+ group_name);
+ BLI_addtail(entries, group_entry);
+
+ if (options & LIST_LIB_RECURSIVE) {
+ int group_datablock_len;
+ LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info(
+ libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len);
+ filelist_readjob_list_lib_add_datablocks(
+ entries, group_datablock_infos, true, idcode, group_name);
+ BLI_linklist_freeN(group_datablock_infos);
+ datablock_len += group_datablock_len;
+ }
}
- BLI_addtail(entries, entry);
- nbr_entries++;
+
+ BLI_linklist_freeN(groups);
}
- BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
+ BLO_blendhandle_close(libfiledata);
- return nbr_entries;
+ /* Return the number of items added to entries. */
+ int added_entries_len = group_len + datablock_len + parent_len;
+ return added_entries_len;
}
#if 0
@@ -3153,6 +3218,35 @@ typedef struct FileListReadJob {
struct FileList *tmp_filelist;
} FileListReadJob;
+static bool filelist_readjob_should_recurse_into_entry(const int max_recursion,
+ const int current_recursion_level,
+ FileListInternEntry *entry)
+{
+ if (max_recursion == 0) {
+ /* Recursive loading is disabled. */
+ return false;
+ }
+ if (current_recursion_level >= max_recursion) {
+ /* No more levels of recursion left. */
+ return false;
+ }
+ if (entry->typeflag & FILE_TYPE_BLENDERLIB) {
+ /* Libraries are already loaded recursively when recursive loaded is used. No need to add
+ * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */
+ return false;
+ }
+ if (!(entry->typeflag & FILE_TYPE_DIR)) {
+ /* Cannot recurse into regular file entries. */
+ return false;
+ }
+ if (FILENAME_IS_CURRPAR(entry->relpath)) {
+ /* Don't schedule go to parent entry, (`..`) */
+ return false;
+ }
+
+ return true;
+}
+
static void filelist_readjob_do(const bool do_lib,
FileListReadJob *job_params,
const short *stop,
@@ -3189,7 +3283,6 @@ static void filelist_readjob_do(const bool do_lib,
while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) {
FileListInternEntry *entry;
int nbr_entries = 0;
- bool is_lib = do_lib;
char *subdir;
char rel_subdir[FILE_MAX_LIBEXTRA];
@@ -3212,45 +3305,54 @@ static void filelist_readjob_do(const bool do_lib,
BLI_path_normalize_dir(root, rel_subdir);
BLI_path_rel(rel_subdir, root);
+ bool is_lib = false;
if (do_lib) {
- nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar);
+ ListLibOptions list_lib_options = 0;
+ if (!skip_currpar) {
+ list_lib_options |= LIST_LIB_ADD_PARENT;
+ }
+
+ /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is
+ * still a recursion level over. */
+ if (max_recursion > 0) {
+ list_lib_options |= LIST_LIB_RECURSIVE;
+ }
+ /* Only load assets when browsing an asset library. For normal file browsing we return all
+ * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user.*/
+ if (filelist->asset_library_ref) {
+ list_lib_options |= LIST_LIB_ASSETS_ONLY;
+ }
+ nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options);
+ if (nbr_entries > 0) {
+ is_lib = true;
+ }
}
- if (!nbr_entries) {
- is_lib = false;
+
+ if (!is_lib) {
nbr_entries = filelist_readjob_list_dir(
subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar);
}
for (entry = entries.first; entry; entry = entry->next) {
- BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
-
entry->uid = filelist_uid_generate(filelist);
- /* Only thing we change in direntry here, so we need to free it first. */
+ /* When loading entries recursive, the rel_path should be relative from the root dir.
+ * we combine the relative path to the subdir with the relative path of the entry. */
+ BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath);
MEM_freeN(entry->relpath);
entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//'
* added by BLI_path_rel to rel_subdir. */
entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir);
entry->free_name = true;
- /* Here we decide whether current filedirentry is to be listed too, or not. */
- if (max_recursion && (is_lib || (recursion_level <= max_recursion))) {
- if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) {
- /* Skip... */
- }
- else if (!is_lib && (recursion_level >= max_recursion) &&
- ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) {
- /* Do not recurse in real directories in this case, only in .blend libs. */
- }
- else {
- /* We have a directory we want to list, add it to todo list! */
- BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
- BLI_path_normalize_dir(job_params->main_name, dir);
- td_dir = BLI_stack_push_r(todo_dirs);
- td_dir->level = recursion_level + 1;
- td_dir->dir = BLI_strdup(dir);
- nbr_todo_dirs++;
- }
+ if (filelist_readjob_should_recurse_into_entry(max_recursion, recursion_level, entry)) {
+ /* We have a directory we want to list, add it to todo list! */
+ BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath);
+ BLI_path_normalize_dir(job_params->main_name, dir);
+ td_dir = BLI_stack_push_r(todo_dirs);
+ td_dir->level = recursion_level + 1;
+ td_dir->dir = BLI_strdup(dir);
+ nbr_todo_dirs++;
}
}
@@ -3278,13 +3380,6 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_discard(todo_dirs);
}
BLI_stack_free(todo_dirs);
-
- /* Check whether assets catalogs need to be loaded. */
- if (job_params->filelist->asset_library_ref != NULL) {
- /* Load asset catalogs, into the temp filelist for thread-safety.
- * #filelist_readjob_endjob() will move it into the real filelist. */
- job_params->tmp_filelist->asset_library = BKE_asset_library_load(filelist->filelist.root);
- }
}
static void filelist_readjob_dir(FileListReadJob *job_params,
@@ -3303,6 +3398,28 @@ static void filelist_readjob_lib(FileListReadJob *job_params,
filelist_readjob_do(true, job_params, stop, do_update, progress);
}
+static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update)
+{
+ FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */
+
+ /* Check whether assets catalogs need to be loaded. */
+ if (job_params->filelist->asset_library_ref != NULL) {
+ /* Load asset catalogs, into the temp filelist for thread-safety.
+ * #filelist_readjob_endjob() will move it into the real filelist. */
+ tmp_filelist->asset_library = BKE_asset_library_load(tmp_filelist->filelist.root);
+ *do_update = true;
+ }
+}
+
+static void filelist_readjob_asset_library(FileListReadJob *job_params,
+ short *stop,
+ short *do_update,
+ float *progress)
+{
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+ filelist_readjob_lib(job_params, stop, do_update, progress);
+}
+
static void filelist_readjob_main(FileListReadJob *job_params,
short *stop,
short *do_update,
@@ -3324,6 +3441,8 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params,
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
(filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+ filelist_readjob_load_asset_library_data(job_params, do_update);
+
/* A valid, but empty directory from now. */
filelist->filelist.nbr_entries = 0;
@@ -3415,11 +3534,17 @@ static void filelist_readjob_update(void *flrjv)
flrj->tmp_filelist->filelist.nbr_entries = 0;
}
+ if (flrj->tmp_filelist->asset_library) {
+ flrj->filelist->asset_library = flrj->tmp_filelist->asset_library;
+ flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */
+ }
+
BLI_mutex_unlock(&flrj->lock);
if (new_nbr_entries) {
- /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! */
- filelist_clear_ex(flrj->filelist, true, false);
+ /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep
+ * the asset library data we just read. */
+ filelist_clear_ex(flrj->filelist, false, true, false);
flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING);
}
@@ -3436,12 +3561,6 @@ static void filelist_readjob_endjob(void *flrjv)
/* In case there would be some dangling update... */
filelist_readjob_update(flrjv);
- /* Move ownership of the asset library from the temporary list to the true filelist. */
- BLI_assert_msg(flrj->filelist->asset_library == NULL,
- "asset library should not already have been allocated");
- flrj->filelist->asset_library = flrj->tmp_filelist->asset_library;
- flrj->tmp_filelist->asset_library = NULL; /* MUST be NULL to avoid double-free. */
-
flrj->filelist->flags &= ~FL_IS_PENDING;
flrj->filelist->flags |= FL_IS_READY;
}
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 1fb05e0f9ac..b51ceee4aa0 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -86,7 +86,10 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m
struct FileList *filelist_new(short type);
void filelist_settype(struct FileList *filelist, short type);
void filelist_clear(struct FileList *filelist);
-void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
+void filelist_clear_ex(struct FileList *filelist,
+ const bool do_asset_library,
+ const bool do_cache,
+ const bool do_selection);
void filelist_free(struct FileList *filelist);
const char *filelist_dir(struct FileList *filelist);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index f7bdb4326a5..a741f2582ee 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -440,7 +440,8 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
break;
}
- base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+ base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET :
+ FILE_ASSET_LIBRARY;
}
void fileselect_refresh_params(SpaceFile *sfile)
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index b57696cb676..499ccb8a889 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -79,6 +79,7 @@
#include "RNA_access.h"
#include "NOD_geometry_nodes_eval_log.hh"
+#include "NOD_node_declaration.hh"
#include "FN_field_cpp_type.hh"
@@ -1085,12 +1086,37 @@ static void node_socket_draw_nested(const bContext *C,
but,
[](bContext *C, void *argN, const char *UNUSED(tip)) {
SocketTooltipData *data = (SocketTooltipData *)argN;
- std::optional<std::string> str = create_socket_inspection_string(
+ std::optional<std::string> socket_inspection_str = create_socket_inspection_string(
C, *data->ntree, *data->node, *data->socket);
- if (str.has_value()) {
- return BLI_strdup(str->c_str());
+
+ std::stringstream output;
+ if (data->node->declaration != nullptr) {
+ ListBase *list;
+ Span<blender::nodes::SocketDeclarationPtr> decl_list;
+
+ if (data->socket->in_out == SOCK_IN) {
+ list = &data->node->inputs;
+ decl_list = data->node->declaration->inputs();
+ }
+ else {
+ list = &data->node->outputs;
+ decl_list = data->node->declaration->outputs();
+ }
+
+ const int socket_index = BLI_findindex(list, data->socket);
+ const blender::nodes::SocketDeclaration &socket_decl = *decl_list[socket_index];
+ blender::StringRef description = socket_decl.description();
+ if (!description.is_empty()) {
+ output << TIP_(description.data()) << ".\n\n";
+ }
+
+ if (socket_inspection_str.has_value()) {
+ output << *socket_inspection_str;
+ return BLI_strdup(output.str().c_str());
+ }
}
- return BLI_strdup(TIP_("The socket value has not been computed yet"));
+ output << TIP_("The socket value has not been computed yet");
+ return BLI_strdup(output.str().c_str());
},
data,
MEM_freeN);
diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh
index d4375b625ce..3ce0993da59 100644
--- a/source/blender/functions/FN_field.hh
+++ b/source/blender/functions/FN_field.hh
@@ -484,4 +484,13 @@ template<typename T> Field<T> make_constant_field(T value)
GField make_field_constant_if_possible(GField field);
+class IndexFieldInput final : public FieldInput {
+ public:
+ IndexFieldInput();
+
+ const GVArray *get_varray_for_context(const FieldContext &context,
+ IndexMask mask,
+ ResourceScope &scope) const final;
+};
+
} // namespace blender::fn
diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc
index 599e4d4595a..4f7ea8ec0ef 100644
--- a/source/blender/functions/intern/field.cc
+++ b/source/blender/functions/intern/field.cc
@@ -418,7 +418,10 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
build_multi_function_procedure_for_fields(
procedure, scope, field_tree_info, constant_fields_to_evaluate);
MFProcedureExecutor procedure_executor{"Procedure", procedure};
- MFParamsBuilder mf_params{procedure_executor, 1};
+ /* Run the code below even when the mask is empty, so that outputs are properly prepared.
+ * Higher level code can detect this as well and just skip evaluating the field. */
+ const int mask_size = mask.is_empty() ? 0 : 1;
+ MFParamsBuilder mf_params{procedure_executor, mask_size};
MFContextBuilder mf_context;
/* Provide inputs to the procedure executor. */
@@ -435,11 +438,11 @@ Vector<const GVArray *> evaluate_fields(ResourceScope &scope,
/* Use this to make sure that the value is destructed in the end. */
PartiallyInitializedArray &destruct_helper = scope.construct<PartiallyInitializedArray>();
destruct_helper.buffer = buffer;
- destruct_helper.mask = IndexRange(1);
+ destruct_helper.mask = IndexRange(mask_size);
destruct_helper.type = &type;
/* Pass output buffer to the procedure executor. */
- mf_params.add_uninitialized_single_output({type, buffer, 1});
+ mf_params.add_uninitialized_single_output({type, buffer, mask_size});
/* Create virtual array that can be used after the procedure has been executed below. */
const int out_index = constant_field_indices[i];
@@ -526,6 +529,21 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input,
return field_input.get_varray_for_context(*this, mask, scope);
}
+IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
+{
+}
+
+const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context),
+ IndexMask mask,
+ ResourceScope &scope) const
+{
+ /* TODO: Investigate a similar method to IndexRange::as_span() */
+ auto index_func = [](int i) { return i; };
+ return &scope.construct<
+ fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
+ mask.min_array_size(), mask.min_array_size(), index_func);
+}
+
/* --------------------------------------------------------------------
* FieldOperation.
*/
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
index 0885828a3a0..4079485de8d 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c
@@ -83,13 +83,13 @@ static float calc_point_weight_by_distance(Object *ob,
float dist = len_v3v3(mmd->object->obmat[3], gvert);
if (dist > dist_max) {
- weight = 0.0f;
+ weight = 1.0f;
}
else if (dist <= dist_max && dist > dist_min) {
- weight = (dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f);
+ weight = 1.0f - ((dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f));
}
else {
- weight = 1.0f;
+ weight = 0.0f;
}
return weight;
diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c
index bb1ebc0e85d..6cc8d7d9d05 100644
--- a/source/blender/gpu/intern/gpu_codegen.c
+++ b/source/blender/gpu/intern/gpu_codegen.c
@@ -656,6 +656,8 @@ static const char *attr_prefix_get(CustomDataType type)
return "c";
case CD_AUTO_FROM_NAME:
return "a";
+ case CD_HAIRLENGTH:
+ return "hl";
default:
BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!");
return "";
@@ -675,7 +677,12 @@ static char *code_generate_interface(GPUNodeGraph *graph, int builtins)
BLI_dynstr_append(ds, "\n");
LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) {
- BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_appendf(ds, "float var%d;\n", attr->id);
+ }
+ else {
+ BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id);
+ }
}
if (builtins & GPU_BARYCENTRIC_TEXCO) {
BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n");
@@ -711,6 +718,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n");
}
+ if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl);
+ BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n");
+ }
else if (attr->name[0] == '\0') {
BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix);
BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix);
@@ -755,6 +766,9 @@ static char *code_generate_vertex(GPUNodeGraph *graph,
BLI_dynstr_appendf(
ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id);
}
+ else if (attr->type == CD_HAIRLENGTH) {
+ BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id);
+ }
else {
const char *type_str = gpu_data_type_to_string(attr->gputype);
BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id);
diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
index f7bf3d33361..193a4190cbf 100644
--- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl
@@ -42,6 +42,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const sampler
return orco_madd[0].xyz + orco * orco_madd[1].xyz;
}
+float hair_len_get(int id, const samplerBuffer len)
+{
+ return texelFetch(len, id).x;
+}
+
vec4 tangent_get(const samplerBuffer attr, mat3 normalmat)
{
/* Unsupported */
@@ -71,6 +76,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco)
}
}
+float hair_len_get(int id, const float len)
+{
+ return len;
+}
+
vec4 tangent_get(vec4 attr, mat3 normalmat)
{
vec4 tangent;
diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl
index f0ff70f7690..a3b61dca8b4 100644
--- a/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl
@@ -67,7 +67,7 @@ void main()
if (outline_dist < 0) {
/* Middle dot */
if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) {
- alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius));
+ alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], length(absPos)));
}
/* Up and down arrow-like shading. */
diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
index 6330daa4391..6ffa6b59572 100644
--- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
+++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl
@@ -10,12 +10,15 @@ float wang_hash_noise(uint s)
return fract(float(s) / 4294967296.0);
}
-void node_hair_info(out float is_strand,
+void node_hair_info(float hair_length,
+ out float is_strand,
out float intercept,
+ out float length,
out float thickness,
out vec3 tangent,
out float random)
{
+ length = hair_length;
#ifdef HAIR_SHADER
is_strand = 1.0;
intercept = hairTime;
diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h
index 6acea8da15f..36bdd4915ff 100644
--- a/source/blender/makesdna/DNA_customdata_types.h
+++ b/source/blender/makesdna/DNA_customdata_types.h
@@ -84,7 +84,8 @@ typedef struct CustomData {
* MUST be >= CD_NUMTYPES, but we can't use a define here.
* Correct size is ensured in CustomData_update_typemap assert().
*/
- int typemap[51];
+ int typemap[52];
+ char _pad[4];
/** Number of layers, size of layers array. */
int totlayer, maxlayer;
/** In editmode, total size of all data layers. */
@@ -166,7 +167,9 @@ typedef enum CustomDataType {
CD_PROP_BOOL = 50,
- CD_NUMTYPES = 51,
+ CD_HAIRLENGTH = 51,
+
+ CD_NUMTYPES = 52,
} CustomDataType;
/* Bits for CustomDataMask */
@@ -220,6 +223,8 @@ typedef enum CustomDataType {
#define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2)
#define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL)
+#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH)
+
/** Multires loop data. */
#define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK)
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 9719a23791f..3c5fd5beea9 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -1238,6 +1238,11 @@ typedef struct NodeAttributeMix {
uint8_t input_type_b;
} NodeAttributeMix;
+typedef struct NodeRandomValue {
+ /* CustomDataType. */
+ uint8_t data_type;
+} NodeRandomValue;
+
typedef struct NodeAttributeRandomize {
/* CustomDataType. */
uint8_t data_type;
@@ -1510,6 +1515,16 @@ typedef struct NodeGeometryAttributeCapture {
int8_t domain;
} NodeGeometryAttributeCapture;
+typedef struct NodeGeometryStringToCurves {
+ /* GeometryNodeStringToCurvesOverflowMode */
+ uint8_t overflow;
+ /* GeometryNodeStringToCurvesAlignXMode */
+ uint8_t align_x;
+ /* GeometryNodeStringToCurvesAlignYMode */
+ uint8_t align_y;
+ char _pad[1];
+} NodeGeometryStringToCurves;
+
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1
@@ -1988,6 +2003,11 @@ typedef enum GeometryNodePointDistributeMode {
GEO_NODE_POINT_DISTRIBUTE_POISSON = 1,
} GeometryNodePointDistributeMode;
+typedef enum GeometryNodeDistributePointsOnFacesMode {
+ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0,
+ GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1,
+} GeometryNodeDistributePointsOnFacesMode;
+
typedef enum GeometryNodeRotatePointsType {
GEO_NODE_POINT_ROTATE_TYPE_EULER = 0,
GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1,
@@ -2101,6 +2121,28 @@ typedef enum GeometryNodeCurveFillMode {
GEO_NODE_CURVE_FILL_MODE_NGONS = 1,
} GeometryNodeCurveFillMode;
+typedef enum GeometryNodeStringToCurvesOverflowMode {
+ GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0,
+ GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1,
+ GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE = 2,
+} GeometryNodeStringToCurvesOverflowMode;
+
+typedef enum GeometryNodeStringToCurvesAlignXMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH = 4,
+} GeometryNodeStringToCurvesAlignXMode;
+
+typedef enum GeometryNodeStringToCurvesAlignYMode {
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE = 0,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP = 1,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE = 2,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE = 3,
+ GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4,
+} GeometryNodeStringToCurvesAlignYMode;
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index cebe8fc61fe..075575775ed 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -965,7 +965,10 @@ enum eFileDetails {
typedef enum eFileSelectType {
FILE_LOADLIB = 1,
FILE_MAIN = 2,
+ /** Load assets from #Main. */
FILE_MAIN_ASSET = 3,
+ /** Load assets of an asset library containing external files. */
+ FILE_ASSET_LIBRARY = 4,
FILE_UNIX = 8,
FILE_BLENDER = 8, /* don't display relative paths */
diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h
index 4f86201ced2..2399fb324d7 100644
--- a/source/blender/makesdna/DNA_userdef_types.h
+++ b/source/blender/makesdna/DNA_userdef_types.h
@@ -635,6 +635,7 @@ typedef struct UserDef_Experimental {
/* Debug options, always available. */
char use_undo_legacy;
char no_override_auto_resync;
+ char no_proxy_to_override_conversion;
char use_cycles_debug;
char SANITIZE_AFTER_HERE;
/* The following options are automatically sanitized (set to 0)
@@ -647,7 +648,7 @@ typedef struct UserDef_Experimental {
char use_extended_asset_browser;
char use_override_templates;
char use_geometry_nodes_fields;
- char _pad[4];
+ char _pad[3];
/** `makesdna` does not allow empty structs. */
} UserDef_Experimental;
diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c
index 6480d16ccd2..5817a200192 100644
--- a/source/blender/makesrna/intern/rna_gpencil_modifier.c
+++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c
@@ -59,6 +59,12 @@
const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
{0, "", 0, N_("Modify"), ""},
+ {eGpencilModifierType_Texture,
+ "GP_TEXTURE",
+ ICON_MOD_UVPROJECT,
+ "Texture Mapping",
+ "Change stroke uv texture values"},
+ {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"},
{eGpencilModifierType_WeightAngle,
"GP_WEIGHT_ANGLE",
ICON_MOD_VERTEX_WEIGHT,
@@ -143,7 +149,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_THICKNESS,
"Thickness",
"Change stroke thickness"},
- {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"},
{0, "", 0, N_("Color"), ""},
{eGpencilModifierType_Color,
"GP_COLOR",
@@ -155,11 +160,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = {
ICON_MOD_OPACITY,
"Opacity",
"Opacity of the strokes"},
- {eGpencilModifierType_Texture,
- "GP_TEXTURE",
- ICON_TEXTURE,
- "Texture Mapping",
- "Change stroke uv texture values"},
{eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"},
{0, NULL, 0, NULL, NULL},
};
@@ -2685,7 +2685,7 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna)
RNA_def_struct_ui_text(
srna, "Texture Modifier", "Transform stroke texture coordinates Modifier");
RNA_def_struct_sdna(srna, "TextureGpencilModifierData");
- RNA_def_struct_ui_icon(srna, ICON_TEXTURE);
+ RNA_def_struct_ui_icon(srna, ICON_MOD_UVPROJECT);
RNA_define_lib_overridable(true);
@@ -2864,10 +2864,11 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK);
RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update");
- prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "dist_start");
- RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
- RNA_def_property_ui_text(prop, "Lowest", "Start value for distance calculation");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Lowest", "Distance mapping to 0.0 weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR);
@@ -2875,10 +2876,11 @@ static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
- prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE);
+ prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_DISTANCE);
RNA_def_property_float_sdna(prop, NULL, "dist_end");
- RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2);
- RNA_def_property_ui_text(prop, "Highest", "Max value for distance calculation");
+ RNA_def_property_range(prop, 0.0, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2);
+ RNA_def_property_ui_text(prop, "Highest", "Distance mapping to 1.0 weight");
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index b631e76c094..1cf0684448d 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2092,6 +2092,19 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf(
return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported);
}
+static bool random_value_type_supported(const EnumPropertyItem *item)
+{
+ return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32);
+}
+static const EnumPropertyItem *rna_FunctionNodeRandomValue_type_itemf(bContext *UNUSED(C),
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ *r_free = true;
+ return itemf_function_check(rna_enum_attribute_type_items, random_value_type_supported);
+}
+
static const EnumPropertyItem *rna_GeometryNodeAttributeRandomize_operation_itemf(
bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free)
{
@@ -9168,6 +9181,21 @@ static void def_geo_subdivision_surface(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_fn_random_value(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ RNA_def_struct_sdna_from(srna, "NodeRandomValue", "storage");
+
+ prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "data_type");
+ RNA_def_property_enum_items(prop, rna_enum_attribute_type_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FunctionNodeRandomValue_type_itemf");
+ RNA_def_property_enum_default(prop, CD_PROP_FLOAT);
+ RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_attribute_randomize(StructRNA *srna)
{
PropertyRNA *prop;
@@ -9479,6 +9507,33 @@ static void def_geo_point_distribute(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
+static void def_geo_distribute_points_on_faces(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ static const EnumPropertyItem rna_node_geometry_distribute_points_on_faces_mode_items[] = {
+ {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM,
+ "RANDOM",
+ 0,
+ "Random",
+ "Distribute points randomly on the surface"},
+ {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON,
+ "POISSON",
+ 0,
+ "Poisson Disk",
+ "Distribute the points randomly on the surface while taking a minimum distance between "
+ "points into account"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "custom1");
+ RNA_def_property_enum_items(prop, rna_node_geometry_distribute_points_on_faces_mode_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+}
+
static void def_geo_attribute_color_ramp(StructRNA *srna)
{
PropertyRNA *prop;
@@ -10398,6 +10453,120 @@ static void def_geo_attribute_capture(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_geo_string_to_curves(StructRNA *srna)
+{
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW,
+ "OVERFLOW",
+ ICON_NONE,
+ "Overflow",
+ "Let the text use more space than the specified height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT,
+ "SCALE_TO_FIT",
+ ICON_NONE,
+ "Scale To Fit",
+ "Scale the text size to fit inside the width and height"},
+ {GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE,
+ "TRUNCATE",
+ ICON_NONE,
+ "Truncate",
+ "Only output curves that fit within the width and height. Output the remainder to the "
+ "\"Remainder\" output"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_x_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT,
+ "LEFT",
+ ICON_ALIGN_LEFT,
+ "Left",
+ "Align text to the left"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER,
+ "CENTER",
+ ICON_ALIGN_CENTER,
+ "Center",
+ "Align text to the center"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT,
+ "RIGHT",
+ ICON_ALIGN_RIGHT,
+ "Right",
+ "Align text to the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY,
+ "JUSTIFY",
+ ICON_ALIGN_JUSTIFY,
+ "Justify",
+ "Align text to the left and the right"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH,
+ "FLUSH",
+ ICON_ALIGN_FLUSH,
+ "Flush",
+ "Align text to the left and the right, with equal character spacing"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem rna_node_geometry_string_to_curves_align_y_items[] = {
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE,
+ "TOP_BASELINE",
+ ICON_ALIGN_TOP,
+ "Top Baseline",
+ "Align text to the top baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP,
+ "TOP",
+ ICON_ALIGN_TOP,
+ "Top",
+ "Align text to the top"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE,
+ "MIDDLE",
+ ICON_ALIGN_MIDDLE,
+ "Middle",
+ "Align text to the middle"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE,
+ "BOTTOM_BASELINE",
+ ICON_ALIGN_BOTTOM,
+ "Bottom Baseline",
+ "Align text to the bottom baseline"},
+ {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM,
+ "BOTTOM",
+ ICON_ALIGN_BOTTOM,
+ "Bottom",
+ "Align text to the bottom"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "id");
+ RNA_def_property_struct_type(prop, "VectorFont");
+ RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ RNA_def_struct_sdna_from(srna, "NodeGeometryStringToCurves", "storage");
+
+ prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "overflow");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_overflow_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ RNA_def_property_ui_text(prop, "Overflow", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
+
+ prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_x");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_x_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT);
+ RNA_def_property_ui_text(prop, "Align X", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "align_y");
+ RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_y_items);
+ RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE);
+ RNA_def_property_ui_text(prop, "Align Y", "");
+ 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 563c6ea35e0..16f33507bed 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -6285,6 +6285,13 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
"Enable library overrides automatic resync detection and process on file load. Disable when "
"dealing with older .blend files that need manual Resync (Enforce) handling");
+ prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1);
+ RNA_def_property_ui_text(
+ prop,
+ "Proxy to Override Auto Conversion",
+ "Enable automatic conversion of proxies to library overrides on file load");
+
prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1);
RNA_def_property_ui_text(
diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
index e50c07ce6f2..fd0205cffc5 100644
--- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
+++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc
@@ -334,6 +334,10 @@ static void get_socket_value(const SocketRef &socket, void *r_value)
std::make_shared<bke::AttributeFieldInput>("position", CPPType::get<float3>()));
return;
}
+ if (bsocket.type == SOCK_INT && bnode.type == FN_NODE_RANDOM_VALUE) {
+ new (r_value) Field<int>(std::make_shared<fn::IndexFieldInput>());
+ return;
+ }
}
const bNodeSocketType *typeinfo = socket.typeinfo();
typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value);
@@ -883,6 +887,16 @@ class GeometryNodesEvaluator {
const MultiFunction &fn,
NodeState &node_state)
{
+ if (USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) {
+ if (node->idname().find("Legacy") != StringRef::not_found) {
+ /* Create geometry nodes params just for creating an error message. */
+ NodeParamsProvider params_provider{*this, node, node_state};
+ GeoNodeExecParams params{params_provider};
+ params.error_message_add(geo_log::NodeWarningType::Legacy,
+ TIP_("Legacy node will be removed before Blender 4.0"));
+ }
+ }
+
LinearAllocator<> &allocator = local_allocators_.local();
/* Prepare the inputs for the multi function. */
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index e6af3ecafbc..e1cceae2964 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -133,19 +133,24 @@ set(SRC
composite/node_composite_tree.c
composite/node_composite_util.c
+ function/nodes/legacy/node_fn_random_float.cc
+
function/nodes/node_fn_boolean_math.cc
function/nodes/node_fn_float_compare.cc
function/nodes/node_fn_float_to_int.cc
+ function/nodes/node_fn_input_special_characters.cc
function/nodes/node_fn_input_string.cc
function/nodes/node_fn_input_vector.cc
- function/nodes/node_fn_random_float.cc
+ function/nodes/node_fn_random_value.cc
function/nodes/node_fn_string_length.cc
function/nodes/node_fn_string_substring.cc
function/nodes/node_fn_value_to_string.cc
function/node_function_util.cc
+ geometry/nodes/legacy/node_geo_attribute_randomize.cc
geometry/nodes/legacy/node_geo_material_assign.cc
geometry/nodes/legacy/node_geo_select_by_material.cc
+ geometry/nodes/legacy/node_geo_point_distribute.cc
geometry/nodes/node_geo_align_rotation_to_vector.cc
geometry/nodes/node_geo_attribute_capture.cc
@@ -160,7 +165,6 @@ set(SRC
geometry/nodes/node_geo_attribute_math.cc
geometry/nodes/node_geo_attribute_mix.cc
geometry/nodes/node_geo_attribute_proximity.cc
- geometry/nodes/node_geo_attribute_randomize.cc
geometry/nodes/node_geo_attribute_remove.cc
geometry/nodes/node_geo_attribute_sample_texture.cc
geometry/nodes/node_geo_attribute_separate_xyz.cc
@@ -196,6 +200,7 @@ set(SRC
geometry/nodes/node_geo_curve_to_points.cc
geometry/nodes/node_geo_curve_trim.cc
geometry/nodes/node_geo_delete_geometry.cc
+ geometry/nodes/node_geo_distribute_points_on_faces.cc
geometry/nodes/node_geo_edge_split.cc
geometry/nodes/node_geo_input_material.cc
geometry/nodes/node_geo_input_normal.cc
@@ -218,7 +223,6 @@ set(SRC
geometry/nodes/node_geo_mesh_subdivide.cc
geometry/nodes/node_geo_mesh_to_curve.cc
geometry/nodes/node_geo_object_info.cc
- geometry/nodes/node_geo_point_distribute.cc
geometry/nodes/node_geo_point_instance.cc
geometry/nodes/node_geo_point_rotate.cc
geometry/nodes/node_geo_point_scale.cc
@@ -230,6 +234,7 @@ set(SRC
geometry/nodes/node_geo_separate_components.cc
geometry/nodes/node_geo_set_position.cc
geometry/nodes/node_geo_string_join.cc
+ geometry/nodes/node_geo_string_to_curves.cc
geometry/nodes/node_geo_subdivision_surface.cc
geometry/nodes/node_geo_switch.cc
geometry/nodes/node_geo_transform.cc
diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h
index a67458418f2..9aa4c04000e 100644
--- a/source/blender/nodes/NOD_function.h
+++ b/source/blender/nodes/NOD_function.h
@@ -20,12 +20,15 @@
extern "C" {
#endif
+void register_node_type_fn_legacy_random_float(void);
+
void register_node_type_fn_boolean_math(void);
void register_node_type_fn_float_compare(void);
void register_node_type_fn_float_to_int(void);
+void register_node_type_fn_input_special_characters(void);
void register_node_type_fn_input_string(void);
void register_node_type_fn_input_vector(void);
-void register_node_type_fn_random_float(void);
+void register_node_type_fn_random_value(void);
void register_node_type_fn_string_length(void);
void register_node_type_fn_string_substring(void);
void register_node_type_fn_value_to_string(void);
diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h
index 47bc54132eb..b37d4956e7a 100644
--- a/source/blender/nodes/NOD_geometry.h
+++ b/source/blender/nodes/NOD_geometry.h
@@ -29,6 +29,7 @@ void register_node_tree_type_geo(void);
void register_node_type_geo_group(void);
void register_node_type_geo_custom_group(bNodeType *ntype);
+void register_node_type_geo_legacy_attribute_randomize(void);
void register_node_type_geo_legacy_material_assign(void);
void register_node_type_geo_legacy_select_by_material(void);
@@ -45,7 +46,6 @@ void register_node_type_geo_attribute_map_range(void);
void register_node_type_geo_attribute_math(void);
void register_node_type_geo_attribute_mix(void);
void register_node_type_geo_attribute_proximity(void);
-void register_node_type_geo_attribute_randomize(void);
void register_node_type_geo_attribute_remove(void);
void register_node_type_geo_attribute_separate_xyz(void);
void register_node_type_geo_attribute_statistic(void);
@@ -78,6 +78,7 @@ void register_node_type_geo_curve_to_mesh(void);
void register_node_type_geo_curve_to_points(void);
void register_node_type_geo_curve_trim(void);
void register_node_type_geo_delete_geometry(void);
+void register_node_type_geo_distribute_points_on_faces(void);
void register_node_type_geo_edge_split(void);
void register_node_type_geo_input_index(void);
void register_node_type_geo_input_material(void);
@@ -114,6 +115,7 @@ void register_node_type_geo_select_by_handle_type(void);
void register_node_type_geo_separate_components(void);
void register_node_type_geo_set_position(void);
void register_node_type_geo_string_join(void);
+void register_node_type_geo_string_to_curves(void);
void register_node_type_geo_subdivision_surface(void);
void register_node_type_geo_switch(void);
void register_node_type_geo_transform(void);
diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh
index 04e4ce2afd8..07d4e05cda8 100644
--- a/source/blender/nodes/NOD_node_declaration.hh
+++ b/source/blender/nodes/NOD_node_declaration.hh
@@ -137,6 +137,7 @@ class SocketDeclaration {
protected:
std::string name_;
std::string identifier_;
+ std::string description_;
bool hide_label_ = false;
bool hide_value_ = false;
bool is_multi_input_ = false;
@@ -156,6 +157,7 @@ class SocketDeclaration {
virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const;
StringRefNull name() const;
+ StringRefNull description() const;
StringRefNull identifier() const;
InputSocketFieldType input_field_type() const;
@@ -204,6 +206,11 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
return *(Self *)this;
}
+ Self &description(std::string value = "")
+ {
+ decl_->description_ = std::move(value);
+ return *(Self *)this;
+ }
Self &no_muted_links(bool value = true)
{
decl_->no_mute_links_ = value;
@@ -220,6 +227,7 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder {
/** The input supports a field and is a field by default when nothing is connected. */
Self &implicit_field()
{
+ this->hide_value();
decl_->input_field_type_ = InputSocketFieldType::Implicit;
return *(Self *)this;
}
@@ -314,6 +322,10 @@ inline StringRefNull SocketDeclaration::identifier() const
return identifier_;
}
+inline StringRefNull SocketDeclaration::description() const
+{
+ return description_;
+}
inline InputSocketFieldType SocketDeclaration::input_field_type() const
{
return input_field_type_;
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index ab673d814bb..6af9a7b4e98 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -262,12 +262,15 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO
DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" )
DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" )
+DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "")
+
DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "")
DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "")
DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "")
+DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "")
DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "")
DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "")
-DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "")
+DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "")
DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "")
DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "")
DefNode(FunctionNode, FN_NODE_STRING_SUBSTRING, 0, "STRING_SUBSTRING", StringSubstring, "String Substring", "")
@@ -332,6 +335,7 @@ DefNode(GeometryNode, GEO_NODE_CURVE_FILLET, def_geo_curve_fillet, "CURVE_FILLET
DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "")
DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "")
+DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "")
DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "")
DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "")
DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "")
@@ -357,6 +361,7 @@ DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", Realiz
DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "")
DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "")
DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "String Join", "")
+DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "")
DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "")
DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "")
DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "")
diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
index 20f5fa87685..7f6f554ba93 100644
--- a/source/blender/nodes/function/nodes/node_fn_random_float.cc
+++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc
@@ -20,7 +20,7 @@
namespace blender::nodes {
-static void fn_node_random_float_declare(NodeDeclarationBuilder &b)
+static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
b.add_input<decl::Float>("Min").min(-10000.0f).max(10000.0f);
@@ -68,19 +68,19 @@ class RandomFloatFunction : public blender::fn::MultiFunction {
}
};
-static void fn_node_random_float_build_multi_function(
+static void fn_node_legacy_random_float_build_multi_function(
blender::nodes::NodeMultiFunctionBuilder &builder)
{
static RandomFloatFunction fn;
builder.set_matching_fn(fn);
}
-void register_node_type_fn_random_float()
+void register_node_type_fn_legacy_random_float()
{
static bNodeType ntype;
- fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0);
- ntype.declare = blender::nodes::fn_node_random_float_declare;
- ntype.build_multi_function = fn_node_random_float_build_multi_function;
+ fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0, 0);
+ ntype.declare = blender::nodes::fn_node_legacy_random_float_declare;
+ ntype.build_multi_function = fn_node_legacy_random_float_build_multi_function;
nodeRegisterType(&ntype);
}
diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
new file mode 100644
index 00000000000..11c64d3f694
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc
@@ -0,0 +1,74 @@
+/*
+ * 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_function_util.hh"
+
+namespace blender::nodes {
+
+static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b)
+{
+ b.add_output<decl::String>("Line Break");
+ b.add_output<decl::String>("Tab");
+};
+
+class MF_SpecialCharacters : public fn::MultiFunction {
+ public:
+ MF_SpecialCharacters()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Special Characters"};
+ signature.single_output<std::string>("Line Break");
+ signature.single_output<std::string>("Tab");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ MutableSpan<std::string> lb = params.uninitialized_single_output<std::string>(0, "Line Break");
+ MutableSpan<std::string> tab = params.uninitialized_single_output<std::string>(1, "Tab");
+
+ for (const int i : mask) {
+ new (&lb[i]) std::string("\n");
+ new (&tab[i]) std::string("\t");
+ }
+ }
+};
+
+static void fn_node_input_special_characters_build_multi_function(
+ NodeMultiFunctionBuilder &builder)
+{
+ static MF_SpecialCharacters special_characters_fn;
+ builder.set_matching_fn(special_characters_fn);
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_input_special_characters()
+{
+ static bNodeType ntype;
+
+ fn_node_type_base(
+ &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT, 0);
+ ntype.declare = blender::nodes::fn_node_input_special_characters_declare;
+ ntype.build_multi_function =
+ blender::nodes::fn_node_input_special_characters_build_multi_function;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc
new file mode 100644
index 00000000000..53ca77aab0c
--- /dev/null
+++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc
@@ -0,0 +1,299 @@
+/*
+ * 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 "BLI_hash.h"
+#include "BLI_noise.hh"
+
+#include "node_function_util.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+namespace blender::nodes {
+
+static void fn_node_random_value_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Vector>("Min").supports_field();
+ b.add_input<decl::Vector>("Max").default_value({1.0f, 1.0f, 1.0f}).supports_field();
+ b.add_input<decl::Float>("Min", "Min_001").supports_field();
+ b.add_input<decl::Float>("Max", "Max_001").default_value(1.0f).supports_field();
+ b.add_input<decl::Int>("Min", "Min_002").min(-100000).max(100000).supports_field();
+ b.add_input<decl::Int>("Max", "Max_002")
+ .default_value(100)
+ .min(-100000)
+ .max(100000)
+ .supports_field();
+ b.add_input<decl::Float>("Probability")
+ .min(0.0f)
+ .max(1.0f)
+ .default_value(0.5f)
+ .subtype(PROP_FACTOR)
+ .supports_field();
+ b.add_input<decl::Int>("ID").implicit_field();
+ b.add_input<decl::Int>("Seed").default_value(0).min(-10000).max(10000).supports_field();
+
+ b.add_output<decl::Vector>("Value").dependent_field();
+ b.add_output<decl::Float>("Value", "Value_001").dependent_field();
+ b.add_output<decl::Int>("Value", "Value_002").dependent_field();
+ b.add_output<decl::Bool>("Value", "Value_003").dependent_field();
+}
+
+static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
+}
+
+static void fn_node_random_value_init(bNodeTree *UNUSED(tree), bNode *node)
+{
+ NodeRandomValue *data = (NodeRandomValue *)MEM_callocN(sizeof(NodeRandomValue), __func__);
+ data->data_type = CD_PROP_FLOAT;
+ node->storage = data;
+}
+
+static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeRandomValue &storage = *(const NodeRandomValue *)node->storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first;
+ bNodeSocket *sock_max_vector = sock_min_vector->next;
+ bNodeSocket *sock_min_float = sock_max_vector->next;
+ bNodeSocket *sock_max_float = sock_min_float->next;
+ bNodeSocket *sock_min_int = sock_max_float->next;
+ bNodeSocket *sock_max_int = sock_min_int->next;
+ bNodeSocket *sock_probability = sock_max_int->next;
+
+ bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first;
+ bNodeSocket *sock_out_float = sock_out_vector->next;
+ bNodeSocket *sock_out_int = sock_out_float->next;
+ bNodeSocket *sock_out_bool = sock_out_int->next;
+
+ nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_probability, data_type == CD_PROP_BOOL);
+
+ nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3);
+ nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT);
+ nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32);
+ nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL);
+}
+
+class RandomVectorFunction : public fn::MultiFunction {
+ public:
+ RandomVectorFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float3>("Min");
+ signature.single_input<float3>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<float3>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float3> &min_values = params.readonly_single_input<float3>(0, "Min");
+ const VArray<float3> &max_values = params.readonly_single_input<float3>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<float3> values = params.uninitialized_single_output<float3>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float3 min_value = min_values[i];
+ const float3 max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float x = noise::hash_to_float(seed, id, 0);
+ const float y = noise::hash_to_float(seed, id, 1);
+ const float z = noise::hash_to_float(seed, id, 2);
+
+ values[i] = float3(x, y, z) * (max_value - min_value) + min_value;
+ }
+ }
+};
+
+class RandomFloatFunction : public fn::MultiFunction {
+ public:
+ RandomFloatFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float>("Min");
+ signature.single_input<float>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<float>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &min_values = params.readonly_single_input<float>(0, "Min");
+ const VArray<float> &max_values = params.readonly_single_input<float>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<float> values = params.uninitialized_single_output<float>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float min_value = min_values[i];
+ const float max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float value = noise::hash_to_float(seed, id);
+ values[i] = value * (max_value - min_value) + min_value;
+ }
+ }
+};
+
+class RandomIntFunction : public fn::MultiFunction {
+ public:
+ RandomIntFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<int>("Min");
+ signature.single_input<int>("Max");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<int>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<int> &min_values = params.readonly_single_input<int>(0, "Min");
+ const VArray<int> &max_values = params.readonly_single_input<int>(1, "Max");
+ const VArray<int> &ids = params.readonly_single_input<int>(2, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(3, "Seed");
+ MutableSpan<int> values = params.uninitialized_single_output<int>(4, "Value");
+
+ for (int64_t i : mask) {
+ const float min_value = min_values[i];
+ const float max_value = max_values[i];
+ const int seed = seeds[i];
+ const int id = ids[i];
+
+ const float value = noise::hash_to_float(id, seed);
+ values[i] = round_fl_to_int(value * (max_value - min_value) + min_value);
+ }
+ }
+};
+
+class RandomBoolFunction : public fn::MultiFunction {
+ public:
+ RandomBoolFunction()
+ {
+ static fn::MFSignature signature = create_signature();
+ this->set_signature(&signature);
+ }
+
+ static fn::MFSignature create_signature()
+ {
+ fn::MFSignatureBuilder signature{"Random Value"};
+ signature.single_input<float>("Probability");
+ signature.single_input<int>("ID");
+ signature.single_input<int>("Seed");
+ signature.single_output<bool>("Value");
+ return signature.build();
+ }
+
+ void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override
+ {
+ const VArray<float> &probabilities = params.readonly_single_input<float>(0, "Probability");
+ const VArray<int> &ids = params.readonly_single_input<int>(1, "ID");
+ const VArray<int> &seeds = params.readonly_single_input<int>(2, "Seed");
+ MutableSpan<bool> values = params.uninitialized_single_output<bool>(3, "Value");
+
+ for (int64_t i : mask) {
+ const int seed = seeds[i];
+ const int id = ids[i];
+ const float probability = probabilities[i];
+ values[i] = noise::hash_to_float(id, seed) <= probability;
+ }
+ }
+};
+
+static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder)
+{
+ const NodeRandomValue &storage = *(const NodeRandomValue *)builder.node().storage;
+ const CustomDataType data_type = static_cast<CustomDataType>(storage.data_type);
+
+ switch (data_type) {
+ case CD_PROP_FLOAT3: {
+ static RandomVectorFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_FLOAT: {
+ static RandomFloatFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_INT32: {
+ static RandomIntFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ case CD_PROP_BOOL: {
+ static RandomBoolFunction fn;
+ builder.set_matching_fn(fn);
+ break;
+ }
+ default: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_fn_random_value()
+{
+ static bNodeType ntype;
+ fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER, 0);
+ node_type_init(&ntype, blender::nodes::fn_node_random_value_init);
+ node_type_update(&ntype, blender::nodes::fn_node_random_value_update);
+ ntype.draw_buttons = blender::nodes::fn_node_random_value_layout;
+ ntype.declare = blender::nodes::fn_node_random_value_declare;
+ ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function;
+ node_type_storage(
+ &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage);
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
index 60b9910399c..2e6ba456725 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc
@@ -25,7 +25,7 @@
namespace blender::nodes {
-static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
+static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
b.add_input<decl::String>("Attribute");
@@ -39,15 +39,15 @@ static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Geometry>("Geometry");
}
-static void geo_node_attribute_random_layout(uiLayout *layout,
- bContext *UNUSED(C),
- PointerRNA *ptr)
+static void geo_node_legacy_attribute_random_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
{
uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE);
uiItemR(layout, ptr, "operation", 0, "", ICON_NONE);
}
-static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
+static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node)
{
NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN(
sizeof(NodeAttributeRandomize), __func__);
@@ -57,7 +57,7 @@ static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *no
node->storage = data;
}
-static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
+static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node)
{
bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2);
bNodeSocket *sock_max_vector = sock_min_vector->next;
@@ -280,7 +280,7 @@ static void randomize_attribute_on_component(GeometryComponent &component,
attribute.save();
}
-static void geo_node_random_attribute_exec(GeoNodeExecParams params)
+static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params)
{
GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
const std::string attribute_name = params.get_input<std::string>("Attribute");
@@ -326,18 +326,18 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params)
} // namespace blender::nodes
-void register_node_type_geo_attribute_randomize()
+void register_node_type_geo_legacy_attribute_randomize()
{
static bNodeType ntype;
geo_node_type_base(
&ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0);
- node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init);
- node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update);
+ node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init);
+ node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update);
- ntype.declare = blender::nodes::geo_node_attribute_randomize_declare;
- ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec;
- ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout;
+ ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout;
node_type_storage(
&ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
index f95b0da86ed..f95b0da86ed 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc
+++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc
diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
new file mode 100644
index 00000000000..95987a15f32
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc
@@ -0,0 +1,753 @@
+/*
+ * 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 "BLI_kdtree.h"
+#include "BLI_noise.hh"
+#include "BLI_rand.hh"
+#include "BLI_timeit.hh"
+
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_pointcloud_types.h"
+
+#include "BKE_attribute_math.hh"
+#include "BKE_bvhutils.h"
+#include "BKE_geometry_set_instances.hh"
+#include "BKE_mesh.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_mesh_sample.hh"
+#include "BKE_pointcloud.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+using blender::bke::GeometryInstanceGroup;
+
+namespace blender::nodes {
+
+static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::Geometry>("Geometry");
+ b.add_input<decl::Float>("Distance Min").min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Density Max").default_value(10.0f).min(0.0f);
+ b.add_input<decl::Float>("Density").default_value(10.0f).supports_field();
+ b.add_input<decl::Float>("Density Factor")
+ .default_value(1.0f)
+ .min(0.0f)
+ .max(1.0f)
+ .supports_field();
+ b.add_input<decl::Int>("Seed");
+ b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
+
+ b.add_output<decl::Geometry>("Points");
+ b.add_output<decl::Vector>("Normal").field_source();
+ b.add_output<decl::Vector>("Rotation").subtype(PROP_EULER).field_source();
+ b.add_output<decl::Int>("Stable ID").field_source();
+}
+
+static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE);
+}
+
+static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 1);
+ bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next;
+ bNodeSocket *sock_density = sock_density_max->next;
+ bNodeSocket *sock_density_factor = sock_density->next;
+ nodeSetSocketAvailability(sock_distance_min,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density_max,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+ nodeSetSocketAvailability(sock_density,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM);
+ nodeSetSocketAvailability(sock_density_factor,
+ node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON);
+}
+
+/**
+ * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node.
+ */
+static float3 normal_to_euler_rotation(const float3 normal)
+{
+ float quat[4];
+ vec_to_quat(quat, normal, OB_NEGZ, OB_POSY);
+ float3 rotation;
+ quat_to_eul(rotation, quat);
+ return rotation;
+}
+
+static void sample_mesh_surface(const Mesh &mesh,
+ const float4x4 &transform,
+ const float base_density,
+ const Span<float> density_factors,
+ const int seed,
+ Vector<float3> &r_positions,
+ Vector<float3> &r_bary_coords,
+ Vector<int> &r_looptri_indices)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const int looptri_index : looptris.index_range()) {
+ const MLoopTri &looptri = looptris[looptri_index];
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+ const int v0_index = mesh.mloop[v0_loop].v;
+ const int v1_index = mesh.mloop[v1_loop].v;
+ const int v2_index = mesh.mloop[v2_loop].v;
+ const float3 v0_pos = transform * float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = transform * float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = transform * float3(mesh.mvert[v2_index].co);
+
+ float looptri_density_factor = 1.0f;
+ if (!density_factors.is_empty()) {
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
+ looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f;
+ }
+ const float area = area_tri_v3(v0_pos, v1_pos, v2_pos);
+
+ const int looptri_seed = noise::hash(looptri_index, seed);
+ RandomNumberGenerator looptri_rng(looptri_seed);
+
+ const float points_amount_fl = area * base_density * looptri_density_factor;
+ const float add_point_probability = fractf(points_amount_fl);
+ const bool add_point = add_point_probability > looptri_rng.get_float();
+ const int point_amount = (int)points_amount_fl + (int)add_point;
+
+ for (int i = 0; i < point_amount; i++) {
+ const float3 bary_coord = looptri_rng.get_barycentric_coordinates();
+ float3 point_pos;
+ interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord);
+ r_positions.append(point_pos);
+ r_bary_coords.append(bary_coord);
+ r_looptri_indices.append(looptri_index);
+ }
+ }
+}
+
+BLI_NOINLINE static KDTree_3d *build_kdtree(Span<Vector<float3>> positions_all,
+ const int initial_points_len)
+{
+ KDTree_3d *kdtree = BLI_kdtree_3d_new(initial_points_len);
+
+ int i_point = 0;
+ for (const Vector<float3> &positions : positions_all) {
+ for (const float3 position : positions) {
+ BLI_kdtree_3d_insert(kdtree, i_point, position);
+ i_point++;
+ }
+ }
+ BLI_kdtree_3d_balance(kdtree);
+ return kdtree;
+}
+
+BLI_NOINLINE static void update_elimination_mask_for_close_points(
+ Span<Vector<float3>> positions_all,
+ Span<int> instance_start_offsets,
+ const float minimum_distance,
+ MutableSpan<bool> elimination_mask,
+ const int initial_points_len)
+{
+ if (minimum_distance <= 0.0f) {
+ return;
+ }
+
+ KDTree_3d *kdtree = build_kdtree(positions_all, initial_points_len);
+
+ /* The elimination mask is a flattened array for every point,
+ * so keep track of the index to it separately. */
+ for (const int i_instance : positions_all.index_range()) {
+ Span<float3> positions = positions_all[i_instance];
+ const int offset = instance_start_offsets[i_instance];
+
+ for (const int i : positions.index_range()) {
+ if (elimination_mask[offset + i]) {
+ continue;
+ }
+
+ struct CallbackData {
+ int index;
+ MutableSpan<bool> elimination_mask;
+ } callback_data = {offset + i, elimination_mask};
+
+ BLI_kdtree_3d_range_search_cb(
+ kdtree,
+ positions[i],
+ minimum_distance,
+ [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) {
+ CallbackData &callback_data = *static_cast<CallbackData *>(user_data);
+ if (index != callback_data.index) {
+ callback_data.elimination_mask[index] = true;
+ }
+ return true;
+ },
+ &callback_data);
+ }
+ }
+ BLI_kdtree_3d_free(kdtree);
+}
+
+BLI_NOINLINE static void update_elimination_mask_based_on_density_factors(
+ const Mesh &mesh,
+ const Span<float> density_factors,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const MutableSpan<bool> elimination_mask)
+{
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+ for (const int i : bary_coords.index_range()) {
+ if (elimination_mask[i]) {
+ continue;
+ }
+
+ const MLoopTri &looptri = looptris[looptri_indices[i]];
+ const float3 bary_coord = bary_coords[i];
+
+ const int v0_loop = looptri.tri[0];
+ const int v1_loop = looptri.tri[1];
+ const int v2_loop = looptri.tri[2];
+
+ const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]);
+ const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]);
+ const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]);
+
+ const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y +
+ v2_density_factor * bary_coord.z;
+
+ const float hash = noise::hash_float_to_float(bary_coord);
+ if (hash > probablity) {
+ elimination_mask[i] = true;
+ }
+ }
+}
+
+BLI_NOINLINE static void eliminate_points_based_on_mask(const Span<bool> elimination_mask,
+ Vector<float3> &positions,
+ Vector<float3> &bary_coords,
+ Vector<int> &looptri_indices)
+{
+ for (int i = positions.size() - 1; i >= 0; i--) {
+ if (elimination_mask[i]) {
+ positions.remove_and_reorder(i);
+ bary_coords.remove_and_reorder(i);
+ looptri_indices.remove_and_reorder(i);
+ }
+ }
+}
+
+BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh,
+ const Span<float3> bary_coords,
+ const Span<int> looptri_indices,
+ const AttributeDomain source_domain,
+ const GVArray &source_data,
+ GMutableSpan output_data)
+{
+ switch (source_domain) {
+ case ATTR_DOMAIN_POINT: {
+ bke::mesh_surface_sample::sample_point_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_CORNER: {
+ bke::mesh_surface_sample::sample_corner_attribute(
+ mesh, looptri_indices, bary_coords, source_data, output_data);
+ break;
+ }
+ case ATTR_DOMAIN_FACE: {
+ bke::mesh_surface_sample::sample_face_attribute(
+ mesh, looptri_indices, source_data, output_data);
+ break;
+ }
+ default: {
+ /* Not supported currently. */
+ return;
+ }
+ }
+}
+
+BLI_NOINLINE static void propagate_existing_attributes(
+ const Span<GeometryInstanceGroup> set_groups,
+ const Span<int> instance_start_offsets,
+ const Map<AttributeIDRef, AttributeKind> &attributes,
+ GeometryComponent &component,
+ const Span<Vector<float3>> bary_coords_array,
+ const Span<Vector<int>> looptri_indices_array)
+{
+ for (Map<AttributeIDRef, AttributeKind>::Item entry : attributes.items()) {
+ const AttributeIDRef attribute_id = entry.key;
+ const CustomDataType output_data_type = entry.value.data_type;
+ /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */
+ OutputAttribute attribute_out = component.attribute_try_get_for_output_only(
+ attribute_id, ATTR_DOMAIN_POINT, output_data_type);
+ if (!attribute_out) {
+ continue;
+ }
+
+ GMutableSpan out_span = attribute_out.as_span();
+
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &source_component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *source_component.get_for_read();
+
+ std::optional<AttributeMetaData> attribute_info = component.attribute_get_meta_data(
+ attribute_id);
+ if (!attribute_info) {
+ i_instance += set_group.transforms.size();
+ continue;
+ }
+
+ const AttributeDomain source_domain = attribute_info->domain;
+ GVArrayPtr source_attribute = source_component.attribute_get_for_read(
+ attribute_id, source_domain, output_data_type, nullptr);
+ if (!source_attribute) {
+ i_instance += set_group.transforms.size();
+ continue;
+ }
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ const int offset = instance_start_offsets[i_instance];
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+
+ GMutableSpan instance_span = out_span.slice(offset, bary_coords.size());
+ interpolate_attribute(
+ mesh, bary_coords, looptri_indices, source_domain, *source_attribute, instance_span);
+
+ i_instance++;
+ }
+
+ attribute_math::convert_to_static_type(output_data_type, [&](auto dummy) {
+ using T = decltype(dummy);
+
+ GVArray_Span<T> source_span{*source_attribute};
+ });
+ }
+
+ attribute_out.save();
+ }
+}
+
+namespace {
+struct AttributeOutputs {
+ StrongAnonymousAttributeID normal_id;
+ StrongAnonymousAttributeID rotation_id;
+ StrongAnonymousAttributeID stable_id_id;
+};
+} // namespace
+
+BLI_NOINLINE static void compute_attribute_outputs(const Span<GeometryInstanceGroup> sets,
+ const Span<int> instance_start_offsets,
+ GeometryComponent &component,
+ const Span<Vector<float3>> bary_coords_array,
+ const Span<Vector<int>> looptri_indices_array,
+ const AttributeOutputs &attribute_outputs)
+{
+ std::optional<OutputAttribute_Typed<int>> id_attribute;
+ std::optional<OutputAttribute_Typed<float3>> normal_attribute;
+ std::optional<OutputAttribute_Typed<float3>> rotation_attribute;
+
+ MutableSpan<int> result_ids;
+ MutableSpan<float3> result_normals;
+ MutableSpan<float3> result_rotations;
+
+ if (attribute_outputs.stable_id_id) {
+ id_attribute.emplace(component.attribute_try_get_for_output_only<int>(
+ attribute_outputs.stable_id_id.get(), ATTR_DOMAIN_POINT));
+ result_ids = id_attribute->as_span();
+ }
+ if (attribute_outputs.normal_id) {
+ normal_attribute.emplace(component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT));
+ result_normals = normal_attribute->as_span();
+ }
+ if (attribute_outputs.rotation_id) {
+ rotation_attribute.emplace(component.attribute_try_get_for_output_only<float3>(
+ attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT));
+ result_rotations = rotation_attribute->as_span();
+ }
+
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : sets) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh),
+ BKE_mesh_runtime_looptri_len(&mesh)};
+
+ for (const float4x4 &transform : set_group.transforms) {
+ const int offset = instance_start_offsets[i_instance];
+
+ Span<float3> bary_coords = bary_coords_array[i_instance];
+ Span<int> looptri_indices = looptri_indices_array[i_instance];
+ MutableSpan<int> ids = result_ids.slice(offset, bary_coords.size());
+ MutableSpan<float3> normals = result_normals.slice(offset, bary_coords.size());
+ MutableSpan<float3> rotations = result_rotations.slice(offset, bary_coords.size());
+
+ /* Use one matrix multiplication per point instead of three (for each triangle corner). */
+ float rotation_matrix[3][3];
+ mat4_to_rot(rotation_matrix, transform.values);
+
+ for (const int i : bary_coords.index_range()) {
+ const int looptri_index = looptri_indices[i];
+ const MLoopTri &looptri = looptris[looptri_index];
+ const float3 &bary_coord = bary_coords[i];
+
+ const int v0_index = mesh.mloop[looptri.tri[0]].v;
+ const int v1_index = mesh.mloop[looptri.tri[1]].v;
+ const int v2_index = mesh.mloop[looptri.tri[2]].v;
+ const float3 v0_pos = float3(mesh.mvert[v0_index].co);
+ const float3 v1_pos = float3(mesh.mvert[v1_index].co);
+ const float3 v2_pos = float3(mesh.mvert[v2_index].co);
+
+ if (!result_ids.is_empty()) {
+ ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index);
+ }
+ float3 normal;
+ if (!result_normals.is_empty() || !result_rotations.is_empty()) {
+ normal_tri_v3(normal, v0_pos, v1_pos, v2_pos);
+ mul_m3_v3(rotation_matrix, normal);
+ }
+ if (!result_normals.is_empty()) {
+ normals[i] = normal;
+ }
+ if (!result_rotations.is_empty()) {
+ rotations[i] = normal_to_euler_rotation(normal);
+ }
+ }
+
+ i_instance++;
+ }
+ }
+
+ if (id_attribute) {
+ id_attribute->save();
+ }
+ if (normal_attribute) {
+ normal_attribute->save();
+ }
+ if (rotation_attribute) {
+ rotation_attribute->save();
+ }
+}
+
+static Array<float> calc_full_density_factors_with_selection(const MeshComponent &component,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field)
+{
+ const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER;
+ GeometryComponentFieldContext field_context{component, attribute_domain};
+ const int domain_size = component.attribute_domain_size(attribute_domain);
+
+ fn::FieldEvaluator selection_evaluator{field_context, domain_size};
+ selection_evaluator.add(selection_field);
+ selection_evaluator.evaluate();
+ const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0);
+
+ Array<float> densities(domain_size, 0.0f);
+
+ fn::FieldEvaluator density_evaluator{field_context, &selection_mask};
+ density_evaluator.add_with_destination(density_field, densities.as_mutable_span());
+ density_evaluator.evaluate();
+ return densities;
+}
+
+static void distribute_points_random(Span<GeometryInstanceGroup> set_groups,
+ const Field<float> &density_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ MutableSpan<Vector<float3>> positions_all,
+ MutableSpan<Vector<float3>> bary_coords_all,
+ MutableSpan<Vector<int>> looptri_indices_all)
+{
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Array<float> densities = calc_full_density_factors_with_selection(
+ component, density_field, selection_field);
+ const Mesh &mesh = *component.get_for_read();
+ for (const float4x4 &transform : set_group.transforms) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+ const int instance_seed = noise::hash(seed, i_instance);
+ sample_mesh_surface(mesh,
+ transform,
+ 1.0f,
+ densities,
+ instance_seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+ i_instance++;
+ }
+ }
+}
+
+static void distribute_points_poisson_disk(Span<GeometryInstanceGroup> set_groups,
+ const float minimum_distance,
+ const float max_density,
+ const Field<float> &density_factor_field,
+ const Field<bool> &selection_field,
+ const int seed,
+ MutableSpan<Vector<float3>> positions_all,
+ MutableSpan<Vector<float3>> bary_coords_all,
+ MutableSpan<Vector<int>> looptri_indices_all)
+{
+ Array<int> instance_start_offsets(positions_all.size());
+ int initial_points_len = 0;
+ int i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+ for (const float4x4 &transform : set_group.transforms) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+ const int instance_seed = noise::hash(seed, i_instance);
+ sample_mesh_surface(mesh,
+ transform,
+ max_density,
+ {},
+ instance_seed,
+ positions,
+ bary_coords,
+ looptri_indices);
+
+ instance_start_offsets[i_instance] = initial_points_len;
+ initial_points_len += positions.size();
+ i_instance++;
+ }
+ }
+
+ /* Unlike the other result arrays, the elimination mask in stored as a flat array for every
+ * point, in order to simplify culling points from the KDTree (which needs to know about all
+ * points at once). */
+ Array<bool> elimination_mask(initial_points_len, false);
+ update_elimination_mask_for_close_points(positions_all,
+ instance_start_offsets,
+ minimum_distance,
+ elimination_mask,
+ initial_points_len);
+
+ i_instance = 0;
+ for (const GeometryInstanceGroup &set_group : set_groups) {
+ const GeometrySet &set = set_group.geometry_set;
+ const MeshComponent &component = *set.get_component_for_read<MeshComponent>();
+ const Mesh &mesh = *component.get_for_read();
+
+ const Array<float> density_factors = calc_full_density_factors_with_selection(
+ component, density_factor_field, selection_field);
+
+ for (const int UNUSED(i_set_instance) : set_group.transforms.index_range()) {
+ Vector<float3> &positions = positions_all[i_instance];
+ Vector<float3> &bary_coords = bary_coords_all[i_instance];
+ Vector<int> &looptri_indices = looptri_indices_all[i_instance];
+
+ const int offset = instance_start_offsets[i_instance];
+ update_elimination_mask_based_on_density_factors(
+ mesh,
+ density_factors,
+ bary_coords,
+ looptri_indices,
+ elimination_mask.as_mutable_span().slice(offset, positions.size()));
+
+ eliminate_points_based_on_mask(elimination_mask.as_span().slice(offset, positions.size()),
+ positions,
+ bary_coords,
+ looptri_indices);
+
+ i_instance++;
+ }
+ }
+}
+
+static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params)
+{
+ GeometrySet geometry_set = params.extract_input<GeometrySet>("Geometry");
+
+ const GeometryNodeDistributePointsOnFacesMode distribute_method =
+ static_cast<GeometryNodeDistributePointsOnFacesMode>(params.node().custom1);
+
+ const int seed = params.get_input<int>("Seed") * 5383843;
+ const Field<bool> selection_field = params.extract_input<Field<bool>>("Selection");
+
+ Vector<GeometryInstanceGroup> set_groups;
+ geometry_set_gather_instances(geometry_set, set_groups);
+ if (set_groups.is_empty()) {
+ params.set_output("Points", GeometrySet());
+ return;
+ }
+
+ /* Remove any set inputs that don't contain a mesh, to avoid checking later on. */
+ for (int i = set_groups.size() - 1; i >= 0; i--) {
+ const GeometrySet &set = set_groups[i].geometry_set;
+ if (!set.has_mesh()) {
+ set_groups.remove_and_reorder(i);
+ }
+ }
+
+ if (set_groups.is_empty()) {
+ params.error_message_add(NodeWarningType::Error, TIP_("Input geometry must contain a mesh"));
+ params.set_output("Points", GeometrySet());
+ return;
+ }
+
+ int instances_len = 0;
+ for (GeometryInstanceGroup &set_group : set_groups) {
+ instances_len += set_group.transforms.size();
+ }
+
+ /* Store data per-instance in order to simplify attribute access after the scattering,
+ * and to make the point elimination simpler for the poisson disk mode. Note that some
+ * vectors will be empty if any instances don't contain mesh data. */
+ Array<Vector<float3>> positions_all(instances_len);
+ Array<Vector<float3>> bary_coords_all(instances_len);
+ Array<Vector<int>> looptri_indices_all(instances_len);
+
+ switch (distribute_method) {
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: {
+ const Field<float> density_field = params.extract_input<Field<float>>("Density");
+ distribute_points_random(set_groups,
+ density_field,
+ selection_field,
+ seed,
+ positions_all,
+ bary_coords_all,
+ looptri_indices_all);
+ break;
+ }
+ case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: {
+ const float minimum_distance = params.extract_input<float>("Distance Min");
+ const float density_max = params.extract_input<float>("Density Max");
+ const Field<float> density_factors_field = params.extract_input<Field<float>>(
+ "Density Factor");
+ distribute_points_poisson_disk(set_groups,
+ minimum_distance,
+ density_max,
+ density_factors_field,
+ selection_field,
+ seed,
+ positions_all,
+ bary_coords_all,
+ looptri_indices_all);
+ break;
+ }
+ }
+
+ int final_points_len = 0;
+ Array<int> instance_start_offsets(set_groups.size());
+ for (const int i : positions_all.index_range()) {
+ Vector<float3> &positions = positions_all[i];
+ instance_start_offsets[i] = final_points_len;
+ final_points_len += positions.size();
+ }
+
+ PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len);
+ for (const int instance_index : positions_all.index_range()) {
+ const int offset = instance_start_offsets[instance_index];
+ Span<float3> positions = positions_all[instance_index];
+ memcpy(pointcloud->co + offset, positions.data(), sizeof(float3) * positions.size());
+ }
+
+ uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f);
+
+ GeometrySet geometry_set_out = GeometrySet::create_with_pointcloud(pointcloud);
+ PointCloudComponent &point_component =
+ geometry_set_out.get_component_for_write<PointCloudComponent>();
+
+ Map<AttributeIDRef, AttributeKind> attributes;
+ geometry_set.gather_attributes_for_propagation(
+ {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, true, attributes);
+
+ /* Position is set separately. */
+ attributes.remove("position");
+
+ propagate_existing_attributes(set_groups,
+ instance_start_offsets,
+ attributes,
+ point_component,
+ bary_coords_all,
+ looptri_indices_all);
+
+ AttributeOutputs attribute_outputs;
+ if (params.output_is_required("Normal")) {
+ attribute_outputs.normal_id = StrongAnonymousAttributeID("normal");
+ }
+ if (params.output_is_required("Rotation")) {
+ attribute_outputs.rotation_id = StrongAnonymousAttributeID("rotation");
+ }
+ if (params.output_is_required("Stable ID")) {
+ attribute_outputs.stable_id_id = StrongAnonymousAttributeID("stable id");
+ }
+
+ compute_attribute_outputs(set_groups,
+ instance_start_offsets,
+ point_component,
+ bary_coords_all,
+ looptri_indices_all,
+ attribute_outputs);
+
+ params.set_output("Points", std::move(geometry_set_out));
+
+ if (attribute_outputs.normal_id) {
+ params.set_output(
+ "Normal",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.normal_id)));
+ }
+ if (attribute_outputs.rotation_id) {
+ params.set_output(
+ "Rotation",
+ AnonymousAttributeFieldInput::Create<float3>(std::move(attribute_outputs.rotation_id)));
+ }
+ if (attribute_outputs.stable_id_id) {
+ params.set_output(
+ "Stable ID",
+ AnonymousAttributeFieldInput::Create<int>(std::move(attribute_outputs.stable_id_id)));
+ }
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_distribute_points_on_faces()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(&ntype,
+ GEO_NODE_DISTRIBUTE_POINTS_ON_FACES,
+ "Distribute Points on Faces",
+ NODE_CLASS_GEOMETRY,
+ 0);
+ node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update);
+ node_type_size(&ntype, 170, 100, 320);
+ ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec;
+ ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
index f18b0ab090a..7fcbaf429dd 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc
@@ -23,27 +23,9 @@ static void geo_node_input_index_declare(NodeDeclarationBuilder &b)
b.add_output<decl::Int>("Index").field_source();
}
-class IndexFieldInput final : public fn::FieldInput {
- public:
- IndexFieldInput() : FieldInput(CPPType::get<int>(), "Index")
- {
- }
-
- const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context),
- IndexMask mask,
- ResourceScope &scope) const final
- {
- /* TODO: Investigate a similar method to IndexRange::as_span() */
- auto index_func = [](int i) { return i; };
- return &scope.construct<
- fn::GVArray_For_EmbeddedVArray<int, VArray_For_Func<int, decltype(index_func)>>>(
- mask.min_array_size(), mask.min_array_size(), index_func);
- }
-};
-
static void geo_node_input_index_exec(GeoNodeExecParams params)
{
- Field<int> index_field{std::make_shared<IndexFieldInput>()};
+ Field<int> index_field{std::make_shared<fn::IndexFieldInput>()};
params.set_output("Index", std::move(index_field));
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
index ed701c921ca..8c4defc3ca3 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc
@@ -29,9 +29,21 @@ namespace blender::nodes {
static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b)
{
- b.add_input<decl::Int>("Vertices").default_value(32).min(3).max(4096);
- b.add_input<decl::Float>("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
- b.add_input<decl::Float>("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Int>("Vertices")
+ .default_value(32)
+ .min(3)
+ .max(4096)
+ .description("The number of vertices around the circumference");
+ b.add_input<decl::Float>("Radius")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The radius of the cylinder");
+ b.add_input<decl::Float>("Depth")
+ .default_value(2.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE)
+ .description("The height of the cylinder on the Z axis");
b.add_output<decl::Geometry>("Geometry");
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
index 78bdac1b01b..be923fdccb0 100644
--- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
+++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc
@@ -23,7 +23,7 @@ namespace blender::nodes {
static void geo_node_set_position_declare(NodeDeclarationBuilder &b)
{
b.add_input<decl::Geometry>("Geometry");
- b.add_input<decl::Vector>("Position").hide_value().implicit_field();
+ b.add_input<decl::Vector>("Position").implicit_field();
b.add_input<decl::Bool>("Selection").default_value(true).hide_value().supports_field();
b.add_output<decl::Geometry>("Geometry");
}
diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
new file mode 100644
index 00000000000..0bd050ac3d1
--- /dev/null
+++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc
@@ -0,0 +1,307 @@
+/*
+ * 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 <codecvt>
+#include <locale>
+
+#include "DNA_curve_types.h"
+#include "DNA_vfont_types.h"
+
+#include "BKE_curve.h"
+#include "BKE_font.h"
+#include "BKE_spline.hh"
+
+#include "BLI_hash.h"
+#include "BLI_string_utf8.h"
+#include "BLI_task.hh"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "node_geometry_util.hh"
+
+namespace blender::nodes {
+
+static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b)
+{
+ b.add_input<decl::String>("String");
+ b.add_input<decl::Float>("Size").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Character Spacing")
+ .default_value(1.0f)
+ .min(0.0f)
+ .subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Word Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Line Spacing").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Width").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_input<decl::Float>("Text Box Height").default_value(0.0f).min(0.0f).subtype(PROP_DISTANCE);
+ b.add_output<decl::Geometry>("Curves");
+ b.add_output<decl::String>("Remainder");
+}
+
+static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr)
+{
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+ uiTemplateID(layout,
+ C,
+ ptr,
+ "font",
+ nullptr,
+ "FONT_OT_open",
+ "FONT_OT_unlink",
+ UI_TEMPLATE_ID_FILTER_ALL,
+ false,
+ nullptr);
+ uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE);
+ uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE);
+}
+
+static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN(
+ sizeof(NodeGeometryStringToCurves), __func__);
+
+ data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW;
+ data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT;
+ data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE;
+ node->storage = data;
+ node->id = (ID *)BKE_vfont_builtin_get();
+}
+
+static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node)
+{
+ const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage->overflow;
+ bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next;
+ nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE);
+
+ bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last;
+ bNodeSocket *width_socket = height_socket->prev;
+ nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW);
+ node_sock_label(width_socket,
+ overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") :
+ N_("Text Box Width"));
+}
+
+struct TextLayout {
+ /* Position of each character. */
+ Vector<float2> positions;
+
+ /* The text that fit into the text box, with newline character sequences replaced. */
+ std::string text;
+
+ /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */
+ std::string truncated_text;
+
+ /* Font size could be modified if in 'Scale to fit'-mode. */
+ float final_font_size;
+};
+
+static TextLayout get_text_layout(GeoNodeExecParams &params)
+{
+ TextLayout layout;
+ layout.text = params.extract_input<std::string>("String");
+ if (layout.text.empty()) {
+ return {};
+ }
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode)
+ storage.overflow;
+ const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode)
+ storage.align_x;
+ const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode)
+ storage.align_y;
+
+ const float font_size = std::max(params.extract_input<float>("Size"), 0.0f);
+ const float char_spacing = params.extract_input<float>("Character Spacing");
+ const float word_spacing = params.extract_input<float>("Word Spacing");
+ const float line_spacing = params.extract_input<float>("Line Spacing");
+ const float textbox_w = params.extract_input<float>("Text Box Width");
+ const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ?
+ 0.0f :
+ params.extract_input<float>("Text Box Height");
+ VFont *vfont = (VFont *)params.node().id;
+
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ /* Set defaults */
+ cu.resolu = 12;
+ cu.smallcaps_scale = 0.75f;
+ cu.wordspace = 1.0f;
+ /* Set values from inputs */
+ cu.spacemode = align_x;
+ cu.align_y = align_y;
+ cu.fsize = font_size;
+ cu.spacing = char_spacing;
+ cu.wordspace = word_spacing;
+ cu.linedist = line_spacing;
+ cu.vfont = vfont;
+ cu.overflow = overflow;
+ cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__);
+ cu.tb->w = textbox_w;
+ cu.tb->h = textbox_h;
+ cu.totbox = 1;
+ size_t len_bytes;
+ size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes);
+ cu.len_char32 = len_chars;
+ cu.len = len_bytes;
+ cu.pos = len_chars;
+ /* The reason for the additional character here is unknown, but reflects other code elsewhere. */
+ cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__);
+ cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__);
+ BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1);
+
+ struct CharTrans *chartransdata = nullptr;
+ int text_len;
+ bool text_free;
+ const char32_t *r_text = nullptr;
+ /* Mode FO_DUPLI used because it doesn't create curve splines. */
+ BKE_vfont_to_curve_ex(
+ nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata);
+
+ if (text_free) {
+ MEM_freeN((void *)r_text);
+ }
+
+ Span<CharInfo> info{cu.strinfo, text_len};
+ layout.final_font_size = cu.fsize_realtime;
+ layout.positions.reserve(text_len);
+
+ for (const int i : IndexRange(text_len)) {
+ CharTrans &ct = chartransdata[i];
+ layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size);
+
+ if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) {
+ const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1);
+ layout.truncated_text = layout.text.substr(offset);
+ layout.text = layout.text.substr(0, offset);
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(chartransdata);
+ MEM_SAFE_FREE(cu.str);
+ MEM_SAFE_FREE(cu.strinfo);
+ MEM_SAFE_FREE(cu.tb);
+
+ return layout;
+}
+
+/* Returns a mapping of UTF-32 character code to instance handle. */
+static Map<int, int> create_curve_instances(GeoNodeExecParams &params,
+ const float fontsize,
+ const std::u32string &charcodes,
+ InstancesComponent &instance_component)
+{
+ VFont *vfont = (VFont *)params.node().id;
+ Map<int, int> handles;
+
+ for (int i : IndexRange(charcodes.length())) {
+ if (handles.contains(charcodes[i])) {
+ continue;
+ }
+ Curve cu = {nullptr};
+ cu.type = OB_FONT;
+ cu.resolu = 12;
+ cu.vfont = vfont;
+ CharInfo charinfo = {0};
+ charinfo.mat_nr = 1;
+
+ BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1);
+ std::unique_ptr<CurveEval> curve_eval = curve_eval_from_dna_curve(cu);
+ BKE_nurbList_free(&cu.nurb);
+ float4x4 size_matrix = float4x4::identity();
+ size_matrix.apply_scale(fontsize);
+ curve_eval->transform(size_matrix);
+
+ GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release());
+ handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve)));
+ }
+ return handles;
+}
+
+static void add_instances_from_handles(InstancesComponent &instances,
+ const Map<int, int> &char_handles,
+ const std::u32string &charcodes,
+ const Span<float2> positions)
+{
+ instances.resize(positions.size());
+ MutableSpan<int> handles = instances.instance_reference_handles();
+ MutableSpan<float4x4> transforms = instances.instance_transforms();
+ MutableSpan<int> instance_ids = instances.instance_ids();
+
+ threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) {
+ for (const int i : range) {
+ handles[i] = char_handles.lookup(charcodes[i]);
+ transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0});
+ instance_ids[i] = i;
+ }
+ });
+}
+
+static void geo_node_string_to_curves_exec(GeoNodeExecParams params)
+{
+ TextLayout layout = get_text_layout(params);
+
+ const NodeGeometryStringToCurves &storage =
+ *(const NodeGeometryStringToCurves *)params.node().storage;
+ if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) {
+ params.set_output("Remainder", std::move(layout.truncated_text));
+ }
+
+ if (layout.positions.size() == 0) {
+ params.set_output("Curves", GeometrySet());
+ return;
+ }
+
+ /* Convert UTF-8 encoded string to UTF-32. */
+ std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> converter;
+ std::u32string utf32_text = converter.from_bytes(layout.text);
+
+ /* Create and add instances. */
+ GeometrySet geometry_set_out;
+ InstancesComponent &instances = geometry_set_out.get_component_for_write<InstancesComponent>();
+ Map<int, int> char_handles = create_curve_instances(
+ params, layout.final_font_size, utf32_text, instances);
+ add_instances_from_handles(instances, char_handles, utf32_text, layout.positions);
+
+ params.set_output("Curves", std::move(geometry_set_out));
+}
+
+} // namespace blender::nodes
+
+void register_node_type_geo_string_to_curves()
+{
+ static bNodeType ntype;
+
+ geo_node_type_base(
+ &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0);
+ ntype.declare = blender::nodes::geo_node_string_to_curves_declare;
+ ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec;
+ node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init);
+ node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update);
+ node_type_size(&ntype, 190, 120, 700);
+ node_type_storage(&ntype,
+ "NodeGeometryStringToCurves",
+ node_free_standard_storage,
+ node_copy_standard_storage);
+ ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout;
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.c
index 843185befb6..c721fb9c77a 100644
--- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c
+++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.c
@@ -22,6 +22,7 @@
static bNodeSocketTemplate outputs[] = {
{SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
+ {SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
// { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
@@ -35,7 +36,11 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
- return GPU_stack_link(mat, node, "node_hair_info", in, out);
+ /* Length: don't request length if not needed. */
+ static const float zero = 0;
+ GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) :
+ GPU_attribute(mat, CD_HAIRLENGTH, "");
+ return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link);
}
/* node type definition */
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
index 5bf5e0f9876..6ffc8979815 100644
--- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc
@@ -26,7 +26,7 @@ namespace blender::nodes {
static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b)
{
b.is_function_node();
- b.add_input<decl::Vector>("Vector").hide_value().implicit_field();
+ b.add_input<decl::Vector>("Vector").implicit_field();
b.add_input<decl::Float>("W").min(-1000.0f).max(1000.0f);
b.add_input<decl::Float>("Scale").min(-1000.0f).max(1000.0f).default_value(5.0f);
b.add_input<decl::Float>("Detail").min(0.0f).max(16.0f).default_value(2.0f);
diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
index fecd203e80a..75c72754af6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
+++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc
@@ -33,7 +33,7 @@ static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b)
b.add_input<decl::Vector>("Axis").min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f});
b.add_input<decl::Float>("Angle").subtype(PROP_ANGLE);
b.add_input<decl::Vector>("Rotation").subtype(PROP_EULER);
- b.add_output<decl::Vector>("Value");
+ b.add_output<decl::Vector>("Vector");
};
} // namespace blender::nodes
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 2ce2bcc2f3c..bbab3a8b326 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -857,6 +857,20 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports)
duration_lib_override_recursive_resync_seconds);
}
+ if (bf_reports->count.linked_proxies != 0 ||
+ bf_reports->count.proxies_to_lib_overrides_success != 0 ||
+ bf_reports->count.proxies_to_lib_overrides_failures != 0) {
+ BKE_reportf(bf_reports->reports,
+ RPT_WARNING,
+ "Proxies are deprecated (%d proxies were automatically converted to library "
+ "overrides, %d proxies could not be converted and %d linked proxies were kept "
+ "untouched). If you need to keep proxies for the time being, please disable the "
+ "`Proxy to Override Auto Conversion` in Experimental user preferences",
+ bf_reports->count.proxies_to_lib_overrides_success,
+ bf_reports->count.proxies_to_lib_overrides_failures,
+ bf_reports->count.linked_proxies);
+ }
+
BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL);
bf_reports->resynced_lib_overrides_libraries = NULL;
}