Geometry Nodes: add simulation support #104924

Closed
Hans Goudey wants to merge 211 commits from geometry-nodes-simulation into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
19 changed files with 298 additions and 150 deletions
Showing only changes of commit 81317b7429 - Show all commits

View File

@ -5782,8 +5782,14 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
}
const GWL_ModifierInfo &mod_info = g_modifier_info_table[i];
const bool val = (state & (1 << seat->xkb_keymap_mod_index[i])) != 0;
bool val_l = seat->key_depressed.mods[GHOST_KEY_MODIFIER_TO_INDEX(mod_info.key_l)] > 0;
bool val_r = seat->key_depressed.mods[GHOST_KEY_MODIFIER_TO_INDEX(mod_info.key_r)] > 0;
/* NOTE(@ideasman42): it's important to write the XKB state back to #GWL_KeyboardDepressedState
* otherwise changes to modifiers in the future wont generate events.
* This can cause modifiers to be stuck when switching between windows in GNOME because
* window activation is handled before the keyboard enter callback runs, see: #107314. */
int16_t &depressed_l = seat->key_depressed.mods[GHOST_KEY_MODIFIER_TO_INDEX(mod_info.key_l)];
int16_t &depressed_r = seat->key_depressed.mods[GHOST_KEY_MODIFIER_TO_INDEX(mod_info.key_r)];
bool val_l = depressed_l > 0;
bool val_r = depressed_r > 0;
/* This shouldn't be needed, but guard against any possibility of modifiers being stuck.
* Warn so if this happens it can be investigated. */
@ -5796,6 +5802,7 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
}
/* Picking the left is arbitrary. */
val_l = true;
depressed_l = 1;
}
}
else {
@ -5807,6 +5814,8 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co
}
val_l = false;
val_r = false;
depressed_l = 0;
depressed_r = 0;
}
}

View File

@ -47,9 +47,10 @@ def get_context_modifier(context):
if context.area.type == 'PROPERTIES':
modifier = context.modifier
else:
if context.object is None:
ob = context.object
if ob is None:
return False
modifier = context.object.modifiers.active
modifier = ob.modifiers.active
if modifier is None or modifier.type != 'NODES':
return None
return modifier
@ -211,7 +212,8 @@ class NewGeometryNodesModifier(Operator):
return geometry_modifier_poll(context)
def execute(self, context):
modifier = context.object.modifiers.new(data_("GeometryNodes"), "NODES")
ob = context.object
modifier = ob.modifiers.new(data_("GeometryNodes"), 'NODES')
if not modifier:
return {'CANCELLED'}

View File

@ -1345,6 +1345,7 @@ rna_custom_property_name = StringProperty(
maxlen=63,
)
# Most useful entries of rna_enum_property_subtype_items:
rna_custom_property_type_items = (
('FLOAT', "Float", "A single floating-point value"),
('FLOAT_ARRAY', "Float Array", "An array of floating-point values"),
@ -1356,9 +1357,22 @@ rna_custom_property_type_items = (
('PYTHON', "Python", "Edit a python value directly, for unsupported property types"),
)
# Most useful entries of rna_enum_property_subtype_items for number arrays:
rna_vector_subtype_items = (
('NONE', "Plain Data", "Data values without special behavior"),
rna_custom_property_subtype_none_item = ('NONE', "Plain Data", "Data values without special behavior")
rna_custom_property_subtype_number_items = (
rna_custom_property_subtype_none_item,
('PIXEL', "Pixel", ""),
('PERCENTAGE', "Percentage", ""),
('FACTOR', "Factor", ""),
('ANGLE', "Angle", ""),
('TIME_ABSOLUTE', "Time", "Time specified in seconds"),
('DISTANCE', "Distance", ""),
('POWER', "Power", ""),
('TEMPERATURE', "Temperature", ""),
)
rna_custom_property_subtype_vector_items = (
rna_custom_property_subtype_none_item,
('COLOR', "Linear Color", "Color in the linear space"),
('COLOR_GAMMA', "Gamma-Corrected Color", "Color in the gamma corrected space"),
('EULER', "Euler Angles", "Euler rotation angles in radians"),
@ -1373,6 +1387,17 @@ class WM_OT_properties_edit(Operator):
# register only because invoke_props_popup requires.
bl_options = {'REGISTER', 'INTERNAL'}
def subtype_items_cb(self, context):
match self.property_type:
case 'FLOAT':
return rna_custom_property_subtype_number_items
case 'FLOAT_ARRAY':
return rna_custom_property_subtype_vector_items
return ()
def property_type_update_cb(self, context):
self.subtype = 'NONE'
# Common settings used for all property types. Generally, separate properties are used for each
# type to improve the experience when choosing UI data values.
@ -1381,6 +1406,7 @@ class WM_OT_properties_edit(Operator):
property_type: EnumProperty(
name="Type",
items=rna_custom_property_type_items,
update=property_type_update_cb
)
is_overridable_library: BoolProperty(
name="Library Overridable",
@ -1481,7 +1507,7 @@ class WM_OT_properties_edit(Operator):
)
subtype: EnumProperty(
name="Subtype",
items=WM_OT_properties_edit.subtype_items,
items=subtype_items_cb,
)
# String properties.
@ -1497,9 +1523,6 @@ class WM_OT_properties_edit(Operator):
description="Python value for unsupported custom property types",
)
type_items = rna_custom_property_type_items
subtype_items = rna_vector_subtype_items
# Helper method to avoid repetitive code to retrieve a single value from sequences and non-sequences.
@staticmethod
def _convert_new_value_single(old_value, new_type):
@ -1567,15 +1590,7 @@ class WM_OT_properties_edit(Operator):
return 'PYTHON'
def _init_subtype(self, subtype):
subtype = subtype or 'NONE'
subtype_items = rna_vector_subtype_items
# Add a temporary enum entry to preserve unknown subtypes
if not any(subtype == item[0] for item in subtype_items):
subtype_items += ((subtype, subtype, ""),)
WM_OT_properties_edit.subtype_items = subtype_items
self.subtype = subtype
self.subtype = subtype or 'NONE'
# Fill the operator's properties with the UI data properties from the existing custom property.
# Note that if the UI data doesn't exist yet, the access will create it and use those default values.
@ -1904,9 +1919,7 @@ class WM_OT_properties_edit(Operator):
layout.prop(self, "step_float")
layout.prop(self, "precision")
# Subtype is only supported for float properties currently.
if self.property_type != 'FLOAT':
layout.prop(self, "subtype")
layout.prop(self, "subtype")
elif self.property_type in {'INT', 'INT_ARRAY'}:
if self.property_type == 'INT_ARRAY':
layout.prop(self, "array_length")

View File

@ -110,7 +110,8 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
@classmethod
def poll(cls, context):
return (context.object and context.object.type == 'ARMATURE' and context.object.pose)
ob = context.object
return (ob and ob.type == 'ARMATURE' and ob.pose)
def draw(self, context):
layout = self.layout

View File

@ -288,7 +288,7 @@ class OBJECT_PT_instancing_size(ObjectButtonsPanel, Panel):
@classmethod
def poll(cls, context):
ob = context.object
return ob.instance_type == 'FACES'
return (ob is not None) and (ob.instance_type == 'FACES')
def draw_header(self, context):
@ -316,7 +316,8 @@ class OBJECT_PT_lineart(ObjectButtonsPanel, Panel):
def draw(self, context):
layout = self.layout
lineart = context.object.lineart
ob = context.object
lineart = ob.lineart
layout.use_property_split = True
@ -397,7 +398,7 @@ class OBJECT_PT_visibility(ObjectButtonsPanel, Panel):
col.prop(ob, "hide_viewport", text="Viewports", toggle=False, invert_checkbox=True)
col.prop(ob, "hide_render", text="Renders", toggle=False, invert_checkbox=True)
if context.object.type == 'GPENCIL':
if ob.type == 'GPENCIL':
col = layout.column(heading="Grease Pencil")
col.prop(ob, "use_grease_pencil_lights", toggle=False)

View File

@ -16,7 +16,6 @@
#include "BKE_customdata.h"
#include "BKE_mesh_types.h"
struct BLI_Stack;
struct BMesh;
struct BMeshCreateParams;
struct BMeshFromMeshParams;
@ -44,13 +43,6 @@ struct Scene;
extern "C" {
#endif
/* setting zero so we can catch bugs in OpenMP/BMesh */
#ifdef DEBUG
# define BKE_MESH_OMP_LIMIT 0
#else
# define BKE_MESH_OMP_LIMIT 10000
#endif
/* mesh_runtime.cc */
/**

View File

@ -412,7 +412,7 @@ static void shrinkwrap_calc_nearest_vertex(ShrinkwrapCalcData *calc)
data.tree = calc->tree;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
settings.use_threading = (calc->numVerts > 10000);
settings.userdata_chunk = &nearest;
settings.userdata_chunk_size = sizeof(nearest);
BLI_task_parallel_range(
@ -691,7 +691,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc)
data.local2aux = &local2aux;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
settings.use_threading = (calc->numVerts > 10000);
settings.userdata_chunk = &hit;
settings.userdata_chunk_size = sizeof(hit);
BLI_task_parallel_range(
@ -1363,7 +1363,7 @@ static void shrinkwrap_calc_nearest_surface_point(ShrinkwrapCalcData *calc)
data.tree = calc->tree;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = (calc->numVerts > BKE_MESH_OMP_LIMIT);
settings.use_threading = (calc->numVerts > 10000);
settings.userdata_chunk = &nearest;
settings.userdata_chunk_size = sizeof(nearest);
BLI_task_parallel_range(

View File

@ -1269,15 +1269,21 @@ int BKE_unit_base_of_type_get(int system, int type)
const char *BKE_unit_name_get(const void *usys_pt, int index)
{
return ((bUnitCollection *)usys_pt)->units[index].name;
const bUnitCollection *usys = usys_pt;
BLI_assert((uint)index < (uint)usys->length);
return usys->units[index].name;
}
const char *BKE_unit_display_name_get(const void *usys_pt, int index)
{
return ((bUnitCollection *)usys_pt)->units[index].name_display;
const bUnitCollection *usys = usys_pt;
BLI_assert((uint)index < (uint)usys->length);
return usys->units[index].name_display;
}
const char *BKE_unit_identifier_get(const void *usys_pt, int index)
{
const bUnitDef *unit = ((const bUnitCollection *)usys_pt)->units + index;
const bUnitCollection *usys = usys_pt;
BLI_assert((uint)index < (uint)usys->length);
const bUnitDef *unit = &usys->units[index];
if (unit->identifier == NULL) {
BLI_assert_msg(0, "identifier for this unit is not specified yet");
}
@ -1286,10 +1292,14 @@ const char *BKE_unit_identifier_get(const void *usys_pt, int index)
double BKE_unit_scalar_get(const void *usys_pt, int index)
{
return ((bUnitCollection *)usys_pt)->units[index].scalar;
const bUnitCollection *usys = usys_pt;
BLI_assert((uint)index < (uint)usys->length);
return usys->units[index].scalar;
}
bool BKE_unit_is_suppressed(const void *usys_pt, int index)
{
return (((bUnitCollection *)usys_pt)->units[index].flag & B_UNIT_DEF_SUPPRESS) != 0;
const bUnitCollection *usys = usys_pt;
BLI_assert((uint)index < (uint)usys->length);
return (usys->units[index].flag & B_UNIT_DEF_SUPPRESS) != 0;
}

View File

@ -810,7 +810,7 @@ static bool vfont_to_curve(Object *ob,
VChar *che;
struct CharTrans *chartransdata = NULL, *ct;
struct TempLineInfo *lineinfo;
float *f, xof, yof, xtrax, linedist;
float xof, yof, xtrax, linedist;
float twidth = 0, maxlen = 0;
int i, slen, j;
int curbox;
@ -1536,25 +1536,59 @@ static bool vfont_to_curve(Object *ob,
/* Cursor first. */
if (ef) {
float si, co;
ct = &chartransdata[ef->pos];
si = sinf(ct->rot);
co = cosf(ct->rot);
const float cursor_width = 0.04f;
const float cursor_half = 0.02f;
const float xoffset = ct->xof;
const float yoffset = ct->yof;
f = ef->textcurs[0];
/* By default the cursor is exactly between the characters
* and matches the rotation of the character to the right. */
float cursor_left = 0.0f - cursor_half;
float rotation = ct->rot;
f[0] = font_size * (-0.02f * co + ct->xof);
f[1] = font_size * (0.1f * si - (0.25f * co) + ct->yof);
if (ef->selboxes) {
if (ef->selend >= ef->selstart) {
/* Cursor at right edge of a text selection. Match rotation to the character at the
* end of selection. Cursor is further right to show the selected characters better. */
rotation = chartransdata[ef->selend - 1].rot;
cursor_left = 0.0f;
}
else {
/* Cursor at the left edge of a text selection. Cursor
* is further left to show the selected characters better. */
cursor_left = 0.0f - cursor_width;
}
}
else if ((ef->pos == ef->len) && (ef->len > 0)) {
/* Nothing selected, but at the end of the string. Match rotation to previous character. */
rotation = chartransdata[ef->len - 1].rot;
}
f[2] = font_size * (0.02f * co + ct->xof);
f[3] = font_size * (-0.1f * si - (0.25f * co) + ct->yof);
/* We need the rotation to be around the bottom-left corner. So we make
* that the zero point before rotation, rotate, then apply offsets afterward. */
f[4] = font_size * (0.02f * co + 0.8f * si + ct->xof);
f[5] = font_size * (-0.1f * si + 0.75f * co + ct->yof);
/* Bottom left. */
ef->textcurs[0][0] = cursor_left;
ef->textcurs[0][1] = 0.0f;
/* Bottom right. */
ef->textcurs[1][0] = cursor_left + cursor_width;
ef->textcurs[1][1] = 0.0f;
/* Top left. */
ef->textcurs[3][0] = cursor_left;
ef->textcurs[3][1] = 1.0f;
/* Top right. */
ef->textcurs[2][0] = cursor_left + cursor_width;
ef->textcurs[2][1] = 1.0f;
f[6] = font_size * (-0.02f * co + 0.8f * si + ct->xof);
f[7] = font_size * (0.1f * si + 0.75f * co + ct->yof);
for (int vert = 0; vert < 4; vert++) {
float temp_fl[2];
/* Rotate around the cursor's bottom-left corner. */
rotate_v2_v2fl(temp_fl, &ef->textcurs[vert][0], -rotation);
ef->textcurs[vert][0] = font_size * (xoffset + temp_fl[0]);
/* Shift down vertically so we are 25% below and 75% above baseline. */
ef->textcurs[vert][1] = font_size * (yoffset + temp_fl[1] - 0.25f);
}
}
if (mode == FO_SELCHANGE) {

View File

@ -321,12 +321,25 @@ void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, unsigned short numlen, int pic);
/**
* Remove redundant characters from \a path and optionally make absolute.
* Remove redundant characters from \a path.
*
* \param path: Can be any input, and this function converts it to a regular full path.
* Also removes garbage from directory paths, like `/../` or double slashes etc.
* The following operations are performed:
* - Redundant path components such as `//`, `/./` & `./` (prefix) are stripped.
* (with the exception of `//` prefix used for blend-file relative paths).
* - `..` are resolved so `<parent>/../<child>/` resolves to `<child>/`.
* Note that the resulting path may begin with `..` if it's relative.
*
* \note \a path isn't protected for max string names.
* Details:
* - The slash direction is expected to be native (see #SEP).
* When calculating a canonical paths you may need to run #BLI_path_slash_native first.
* #BLI_path_cmp_normalized can be used for canonical path comparison.
* - Trailing slashes are left intact (unlike Python which strips them).
* - Handling paths beginning with `..` depends on them being absolute or relative.
* For absolute paths they are removed (e.g. `/../path` becomes `/path`).
* For relative paths they are kept as it's valid to reference paths above a relative location
* such as `//../parent` or `../parent`.
*
* \param path: The path to a file or directory which can be absolute or relative.
*/
void BLI_path_normalize(char *path) ATTR_NONNULL(1);
/**

View File

@ -106,8 +106,8 @@ typedef struct BLI_mempool_chunk {
* The mempool, stores and tracks memory \a chunks and elements within those chunks \a free.
*/
struct BLI_mempool {
/* Serialize access to mempools when debugging wih ASAN. */
#ifdef WITH_ASAN
/** Serialize access to memory-pools when debugging with ASAN. */
ThreadMutex mutex;
#endif
/** Single linked list of allocated chunks. */

View File

@ -115,13 +115,12 @@ void BLI_path_sequence_encode(
void BLI_path_normalize(char *path)
{
const char *path_orig = path;
int path_len;
ptrdiff_t a;
char *start, *eind;
path_len = strlen(path);
int path_len = strlen(path);
/*
* Skip absolute prefix.
* ---------------------
*/
if (path[0] == '/' && path[1] == '/') {
path = path + 2; /* Leave the initial `//` untouched. */
path_len -= 2;
@ -157,10 +156,14 @@ void BLI_path_normalize(char *path)
}
}
#endif /* WIN32 */
/* Works on WIN32 as well, because the drive component is skipped. */
const bool is_relative = path[0] && (path[0] != SEP);
/*
* Strip redundant path components.
* --------------------------------
*/
/* NOTE(@ideasman42):
* `memmove(start, eind, strlen(eind) + 1);`
* is the same as
@ -189,7 +192,6 @@ void BLI_path_normalize(char *path)
else {
break;
}
} while (i > 0);
if (i < i_end) {
@ -200,8 +202,7 @@ void BLI_path_normalize(char *path)
}
}
}
/* Remove redundant `./` prefix, while it could be kept, it confuses the loop below. */
/* Remove redundant `./` prefix as it's redundant & complicates collapsing directories. */
if (is_relative) {
if ((path_len > 2) && (path[0] == '.') && (path[1] == SEP)) {
memmove(path, path + 2, (path_len - 2) + 1);
@ -209,69 +210,127 @@ void BLI_path_normalize(char *path)
}
}
const ptrdiff_t a_start = is_relative ? 0 : 1;
start = path;
while ((start = strstr(start, SEP_STR ".."))) {
if (!ELEM(start[3], SEP, '\0')) {
start += 3;
continue;
}
/*
* Collapse Parent Directories.
* ----------------------------
*
* Example: `<parent>/<child>/../` -> `<parent>/`
*
* Notes:
* - Leading `../` are skipped as they cannot be collapsed (see `start_base`).
* - Multiple parent directories are handled at once to reduce number of `memmove` calls.
*/
a = (start - path) - 1;
if (a >= a_start) {
/* `<prefix>/<parent>/../<postfix> => <prefix>/<postfix>`. */
eind = start + (4 - 1) /* `strlen("/../") - 1` */; /* Strip "/.." and keep the char after. */
while (a > 0 && path[a] != SEP) { /* Find start of `<parent>`. */
a--;
}
#define IS_PARENT_DIR(p) ((p)[0] == '.' && (p)[1] == '.' && ELEM((p)[2], SEP, '\0'))
if (is_relative && (a == 0) && *eind) {
/* When the path does not start with a slash, don't copy the first `/` to the destination
* as it will make a relative path into an absolute path. */
eind += 1;
}
const size_t eind_len = path_len - (eind - path);
BLI_assert(eind_len == strlen(eind));
/* Only remove the parent if it's not also a `..`. */
if (is_relative && STRPREFIX(path + ((path[a] == SEP) ? a + 1 : a), ".." SEP_STR)) {
start += 3 /* `strlen("/..")` */;
}
else {
start = path + a;
BLI_assert(start < eind);
memmove(start, eind, eind_len + 1);
path_len -= (eind - start);
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
}
}
else {
/* Support for odd paths: eg `/../home/me` --> `/home/me`
* this is a valid path in blender but we can't handle this the usual way below
* simply strip this prefix then evaluate the path as usual.
* Python's `os.path.normpath()` does this. */
/* NOTE: previous version of following call used an offset of 3 instead of 4,
* which meant that the `/../home/me` example actually became `home/me`.
* Using offset of 3 gives behavior consistent with the aforementioned
* Python routine. */
eind = start + 3;
const size_t eind_len = path_len - (eind - path);
memmove(start, eind, eind_len + 1);
path_len -= 3;
BLI_assert(strlen(path) == path_len);
BLI_assert(!is_relative || (path[0] != SEP));
}
/* First non prefix path component. */
char *path_first_non_slash_part = path;
while (*path_first_non_slash_part && *path_first_non_slash_part == SEP) {
path_first_non_slash_part++;
}
if (is_relative && path_len == 0 && (path == path_orig)) {
path[0] = '.';
path[1] = '\0';
path_len += 1;
/* Maintain a pointer to the end of leading `..` component.
* Skip leading parent directories because logically they cannot be collapsed. */
char *start_base = path_first_non_slash_part;
while (IS_PARENT_DIR(start_base)) {
start_base += 3;
}
/* It's possible the entire path is made of up `../`,
* in this case there is nothing to do. */
if (start_base < path + path_len) {
/* Step over directories, always starting out on the character after the slash. */
char *start = start_base;
char *start_temp;
while (((start_temp = strstr(start, SEP_STR ".." SEP_STR)) ||
/* Check if the string ends with `/..` & assign when found, else NULL. */
(start_temp = ((start <= &path[path_len - 3]) &&
STREQ(&path[path_len - 3], SEP_STR "..")) ?
&path[path_len - 3] :
NULL))) {
start = start_temp + 1; /* Skip the `/`. */
BLI_assert(start_base != start);
/* Step `end_all` forwards (over all `..`). */
char *end_all = start;
do {
BLI_assert(IS_PARENT_DIR(end_all));
end_all += 3;
BLI_assert(end_all <= path + path_len + 1);
} while (IS_PARENT_DIR(end_all));
/* Step `start` backwards (until `end` meets `end_all` or `start` meets `start_base`). */
char *end = start;
do {
BLI_assert(start_base < start);
BLI_assert(*(start - 1) == SEP);
/* Step `start` backwards one. */
do {
start--;
} while (start_base < start && *(start - 1) != SEP);
BLI_assert(*start != SEP); /* Ensure the loop ran at least once. */
BLI_assert(!IS_PARENT_DIR(start)); /* Clamping by `start_base` prevents this. */
end += 3;
} while ((start != start_base) && (end < end_all));
if (end > path + path_len) {
BLI_assert(*(end - 1) == '\0');
end--;
end_all--;
}
BLI_assert(start < end && start >= start_base);
const size_t start_len = path_len - (end - path);
memmove(start, end, start_len + 1);
path_len -= end - start;
BLI_assert(strlen(path) == path_len);
/* Other `..` directories may have been moved to the front, step `start_base` past them. */
if (UNLIKELY(start == start_base && (end != end_all))) {
start_base += (end_all - end);
start = (start_base < path + path_len) ? start_base : start_base - 1;
}
}
}
BLI_assert(strlen(path) == path_len);
/* Characters before the `start_base` must *only* be `../../../` (multiples of 3). */
BLI_assert((start_base - path_first_non_slash_part) % 3 == 0);
/* All `..` ahead of `start_base` were collapsed (including trailing `/..`). */
BLI_assert(!(start_base < path + path_len) ||
(!strstr(start_base, SEP_STR ".." SEP_STR) &&
!(path_len >= 3 && STREQ(&path[path_len - 3], SEP_STR ".."))));
/*
* Final Prefix Cleanup.
* ---------------------
*/
if (is_relative) {
if (path_len == 0 && (path == path_orig)) {
path[0] = '.';
path[1] = '\0';
path_len = 1;
}
}
else {
/* Support for odd paths: eg `/../home/me` --> `/home/me`
* this is a valid path in blender but we can't handle this the usual way below
* simply strip this prefix then evaluate the path as usual.
* Python's `os.path.normpath()` does this. */
if (start_base != path_first_non_slash_part) {
char *start = start_base > path + path_len ? start_base - 1 : start_base;
/* As long as `start` is set correctly, it should never begin with `../`
* as these directories are expected to be skipped. */
BLI_assert(!IS_PARENT_DIR(start));
const size_t start_len = path_len - (start - path);
memmove(path_first_non_slash_part, start, start_len + 1);
BLI_assert(strlen(start) == start_len);
path_len -= start - path_first_non_slash_part;
BLI_assert(strlen(path) == path_len);
}
}
BLI_assert(strlen(path) == path_len);
#undef IS_PARENT_DIR
}
void BLI_path_normalize_dir(char *dir, size_t dir_maxlen)

View File

@ -84,11 +84,16 @@ TEST(path_util, Normalize_Dot)
NORMALIZE("/a/./././b/", "/a/b/");
}
/* #BLI_path_normalize: complex "/./" -> "/", "//" -> "/", "./path/../" -> "./". */
TEST(path_util, Normalize_Complex)
TEST(path_util, Normalize_ComplexAbsolute)
{
NORMALIZE("/a/./b/./c/./.././.././", "/a/");
NORMALIZE("/a//.//b//.//c//.//..//.//..//.//", "/a/");
}
TEST(path_util, Normalize_ComplexRelative)
{
NORMALIZE("a/b/c/d/e/f/g/../a/../b/../../c/../../../d/../../../..", ".");
NORMALIZE("a/b/c/d/e/f/g/../a/../../../../b/../../../c/../../d/..", ".");
}
/* #BLI_path_normalize: "//" -> "/" */
TEST(path_util, Normalize_DoubleSlash)
{

View File

@ -71,18 +71,12 @@ static const DTreeContext *find_active_context(const DerivedNodeTree &tree)
}
/* Return the output node which is marked as NODE_DO_OUTPUT. If multiple types of output nodes are
* marked, then the preference will be CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER.
* marked, then the preference will be CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER > CMP_NODE_COMPOSITE.
* If no output node exists, a null node will be returned. */
static DNode find_output_in_context(const DTreeContext *context)
{
const bNodeTree &tree = context->btree();
for (const bNode *node : tree.nodes_by_type("CompositorNodeComposite")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
}
}
for (const bNode *node : tree.nodes_by_type("CompositorNodeViewer")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
@ -95,6 +89,12 @@ static DNode find_output_in_context(const DTreeContext *context)
}
}
for (const bNode *node : tree.nodes_by_type("CompositorNodeComposite")) {
if (node->flag & NODE_DO_OUTPUT) {
return DNode(context, node);
}
}
return DNode();
}

View File

@ -34,6 +34,7 @@ void node_composite_separate_rgba(vec4 color, out float r, out float g, out floa
void node_composite_combine_hsva(float h, float s, float v, float a, out vec4 color)
{
hsv_to_rgb(vec4(h, s, v, a), color);
color.rgb = max(color.rgb, vec3(0.0));
}
void node_composite_separate_hsva(vec4 color, out float h, out float s, out float v, out float a)
@ -51,6 +52,7 @@ void node_composite_separate_hsva(vec4 color, out float h, out float s, out floa
void node_composite_combine_hsla(float h, float s, float l, float a, out vec4 color)
{
hsl_to_rgb(vec4(h, s, l, a), color);
color.rgb = max(color.rgb, vec3(0.0));
}
void node_composite_separate_hsla(vec4 color, out float h, out float s, out float l, out float a)

View File

@ -184,29 +184,29 @@ BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me)
BLI_INLINE BMFace *bm_original_face_get(const MeshRenderData *mr, int idx)
{
return ((mr->p_origindex != NULL) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
return ((mr->p_origindex != nullptr) && (mr->p_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
BM_face_at_index(mr->bm, mr->p_origindex[idx]) :
NULL;
nullptr;
}
BLI_INLINE BMEdge *bm_original_edge_get(const MeshRenderData *mr, int idx)
{
return ((mr->e_origindex != NULL) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
return ((mr->e_origindex != nullptr) && (mr->e_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
BM_edge_at_index(mr->bm, mr->e_origindex[idx]) :
NULL;
nullptr;
}
BLI_INLINE BMVert *bm_original_vert_get(const MeshRenderData *mr, int idx)
{
return ((mr->v_origindex != NULL) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
return ((mr->v_origindex != nullptr) && (mr->v_origindex[idx] != ORIGINDEX_NONE) && mr->bm) ?
BM_vert_at_index(mr->bm, mr->v_origindex[idx]) :
NULL;
nullptr;
}
BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *eve)
{
const float(*vert_coords)[3] = mr->bm_vert_coords;
if (vert_coords != NULL) {
if (vert_coords != nullptr) {
return vert_coords[BM_elem_index_get(eve)];
}
@ -217,7 +217,7 @@ BLI_INLINE const float *bm_vert_co_get(const MeshRenderData *mr, const BMVert *e
BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *eve)
{
const float(*vert_normals)[3] = mr->bm_vert_normals;
if (vert_normals != NULL) {
if (vert_normals != nullptr) {
return vert_normals[BM_elem_index_get(eve)];
}
@ -228,7 +228,7 @@ BLI_INLINE const float *bm_vert_no_get(const MeshRenderData *mr, const BMVert *e
BLI_INLINE const float *bm_face_no_get(const MeshRenderData *mr, const BMFace *efa)
{
const float(*poly_normals)[3] = mr->bm_poly_normals;
if (poly_normals != NULL) {
if (poly_normals != nullptr) {
return poly_normals[BM_elem_index_get(efa)];
}

View File

@ -17,6 +17,7 @@
#include "BLI_linklist.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_memory_utils.hh"
#include "BLI_path_util.h"
#include "BLI_string.h"
@ -378,6 +379,7 @@ static std::string get_in_memory_texture_filename(Image *ima)
ImageFormatData imageFormat;
BKE_image_format_from_imbuf(&imageFormat, imbuf);
BKE_image_release_ibuf(ima, imbuf, nullptr);
char file_name[FILE_MAX];
/* Use the image name for the file name. */
@ -405,6 +407,7 @@ static void export_in_memory_texture(Image *ima,
}
ImBuf *imbuf = BKE_image_acquire_ibuf(ima, nullptr, nullptr);
BLI_SCOPED_DEFER([&]() { BKE_image_release_ibuf(ima, imbuf, nullptr); });
if (!imbuf) {
return;
}

View File

@ -687,10 +687,12 @@ typedef struct FluidDomainSettings {
int viewsettings;
char _pad12[4]; /* Unused. */
/* Pointcache options. */
/* Smoke uses only one cache from now on (index [0]), but keeping the array for now for reading
* old files. */
struct PointCache *point_cache[2]; /* Definition is in DNA_object_force_types.h. */
/**
* Point-cache options.
* Smoke uses only one cache from now on (index [0]),
* but keeping the array for now for reading old files.
*/
struct PointCache *point_cache[2];
struct ListBase ptcaches[2];
int cache_comp;
int cache_high_comp;

View File

@ -79,6 +79,7 @@ dict_custom = {
"decrement",
"decrementing",
"deduplicate",
"deduplicates",
"deduplicating",
"deduplication",
"defocus",
@ -88,6 +89,7 @@ dict_custom = {
"denoised",
"denoiser",
"denoising",
"denormalized",
"dereference",
"dereferenced",
"dereferences",