Mesh: Replace auto smooth with node group #108014

Merged
Hans Goudey merged 149 commits from HooglyBoogly/blender:refactor-mesh-corner-normals-lazy into main 2023-10-20 16:54:20 +02:00
55 changed files with 1068 additions and 553 deletions
Showing only changes of commit a103cbf0ea - Show all commits

View File

@ -1589,6 +1589,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
add_check_c_compiler_flag(C_REMOVE_STRICT_FLAGS C_WARN_NO_DEPRECATED_DECLARATIONS -Wno-deprecated-declarations)
add_check_c_compiler_flag(C_REMOVE_STRICT_FLAGS C_WARN_NO_STRICT_PROTOTYPES -Wno-strict-prototypes)
add_check_c_compiler_flag(C_REMOVE_STRICT_FLAGS C_WARN_NO_BITWISE_INSTEAD_OF_LOGICAL -Wno-bitwise-instead-of-logical)
add_check_c_compiler_flag(C_REMOVE_STRICT_FLAGS C_WARN_NO_IMPLICIT_CONST_INT_FLOAT_CONVERSION -Wno-implicit-const-int-float-conversion)
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PARAMETER -Wno-unused-parameter)
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_UNUSED_PRIVATE_FIELD -Wno-unused-private-field)
@ -1603,6 +1604,7 @@ elseif(CMAKE_C_COMPILER_ID MATCHES "Clang")
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_INSTANTIATION_AFTER_SPECIALIZATION -Wno-instantiation-after-specialization)
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_MISLEADING_INDENTATION -Wno-misleading-indentation)
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_BITWISE_INSTEAD_OF_LOGICAL -Wno-bitwise-instead-of-logical)
add_check_cxx_compiler_flag(CXX_REMOVE_STRICT_FLAGS CXX_WARN_NO_IMPLICIT_CONST_INT_FLOAT_CONVERSION -Wno-implicit-const-int-float-conversion)
elseif(CMAKE_C_COMPILER_ID MATCHES "Intel")

View File

@ -39,9 +39,9 @@ ccl_device_forceinline float intersection_t_offset(const float t)
* The check relies on the fact that comparison of de-normal values with zero
* returns true. */
if (t == 0.0f) {
/* The value of std::numeric_limits<float>::min() and __FLT_MIN__, inlined
* to ensure matched behavior on all platforms and compilers. */
return 0x1p-126;
/* The exact bit value of this should be 0x1p-126, but hex floating point values notation is
* not available in CUDA/OptiX. */
return FLT_MIN;
}
const uint32_t bits = __float_as_uint(t) + 1;

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

@ -43,9 +43,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
@ -207,7 +208,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

@ -276,7 +276,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):
@ -304,7 +304,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
@ -385,7 +386,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

@ -38,6 +38,9 @@ class CurvesEditHints;
class Instances;
} // namespace blender::bke
class GeometryComponent;
using GeometryComponentPtr = blender::ImplicitSharingPtr<GeometryComponent>;
/**
* This is the base class for specialized geometry component types. A geometry component uses
* implicit sharing to avoid read-only copies. It also integrates with attribute API, which
@ -50,7 +53,7 @@ class GeometryComponent : public blender::ImplicitSharingMixin {
public:
GeometryComponent(GeometryComponentType type);
virtual ~GeometryComponent() = default;
static GeometryComponent *create(GeometryComponentType component_type);
static GeometryComponentPtr create(GeometryComponentType component_type);
int attribute_domain_size(eAttrDomain domain) const;
@ -64,6 +67,9 @@ class GeometryComponent : public blender::ImplicitSharingMixin {
/* The returned component should be of the same type as the type this is called on. */
virtual GeometryComponent *copy() const = 0;
/** Remove referenced data from the geometry component. */
virtual void clear() = 0;
/* Direct data is everything except for instances of objects/collections.
* If this returns true, the geometry set can be cached and is still valid after e.g. modifier
* evaluation ends. Instances can only be valid as long as the data they instance is valid. */
@ -76,6 +82,7 @@ class GeometryComponent : public blender::ImplicitSharingMixin {
private:
void delete_self() override;
void delete_data_only() override;
};
template<typename T>
@ -101,7 +108,6 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
*/
struct GeometrySet {
private:
using GeometryComponentPtr = blender::ImplicitSharingPtr<class GeometryComponent>;
/* Indexed by #GeometryComponentType. */
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
@ -377,7 +383,7 @@ class MeshComponent : public GeometryComponent {
~MeshComponent();
GeometryComponent *copy() const override;
void clear();
void clear() override;
bool has_mesh() const;
/**
* Clear the component and replace it with the new mesh.
@ -431,7 +437,7 @@ class PointCloudComponent : public GeometryComponent {
~PointCloudComponent();
GeometryComponent *copy() const override;
void clear();
void clear() override;
bool has_pointcloud() const;
/**
* Clear the component and replace it with the new point cloud.
@ -493,7 +499,7 @@ class CurveComponent : public GeometryComponent {
~CurveComponent();
GeometryComponent *copy() const override;
void clear();
void clear() override;
bool has_curves() const;
/**
* Clear the component and replace it with the new curve.
@ -534,7 +540,7 @@ class InstancesComponent : public GeometryComponent {
~InstancesComponent();
GeometryComponent *copy() const override;
void clear();
void clear() override;
const blender::bke::Instances *get_for_read() const;
blender::bke::Instances *get_for_write();
@ -568,7 +574,7 @@ class VolumeComponent : public GeometryComponent {
~VolumeComponent();
GeometryComponent *copy() const override;
void clear();
void clear() override;
bool has_volume() const;
/**
* Clear the component and replace it with the new volume.
@ -621,6 +627,8 @@ class GeometryComponentEditData final : public GeometryComponent {
bool owns_direct_data() const final;
void ensure_owns_direct_data() final;
void clear() override;
/**
* The first node that does topology changing operations on curves should store the curve point
* positions it retrieved as input. Without this, information about the deformed positions is

View File

@ -383,7 +383,8 @@ bool BKE_image_is_filename_tokenized(char *filepath);
* Ensures that `filename` contains a UDIM token if we find a supported format pattern.
* \note This must only be the name component (without slashes).
*/
void BKE_image_ensure_tile_token(char *filename);
void BKE_image_ensure_tile_token(char *filepath, size_t filepath_maxncpy);
void BKE_image_ensure_tile_token_filename_only(char *filename, size_t filename_maxncpy);
/**
* When provided with an absolute virtual `filepath`, check to see if at least

View File

@ -531,7 +531,7 @@ static bool absolute_convert_foreach_path_cb(BPathForeachPathData *bpath_data,
return false; /* Already absolute. */
}
BLI_strncpy(path_dst, path_src, FILENAME_MAX);
BLI_strncpy(path_dst, path_src, FILE_MAX);
BLI_path_abs(path_dst, data->basedir);
if (BLI_path_is_rel(path_dst) == false) {
data->count_changed++;

View File

@ -2358,21 +2358,30 @@ CustomData CustomData_shallow_copy_remove_non_bmesh_attributes(const CustomData
class CustomDataLayerImplicitSharing : public ImplicitSharingInfo {
private:
const void *data_;
const int totelem_;
int totelem_;
const eCustomDataType type_;
public:
CustomDataLayerImplicitSharing(const void *data, const int totelem, const eCustomDataType type)
: ImplicitSharingInfo(1), data_(data), totelem_(totelem), type_(type)
: ImplicitSharingInfo(), data_(data), totelem_(totelem), type_(type)
{
}
private:
void delete_self_with_data() override
{
free_layer_data(type_, data_, totelem_);
if (data_ != nullptr) {
free_layer_data(type_, data_, totelem_);
}
MEM_delete(this);
}
void delete_data_only() override
{
free_layer_data(type_, data_, totelem_);
data_ = nullptr;
totelem_ = 0;
}
};
/** Create a #ImplicitSharingInfo that takes ownership of the data. */
@ -2395,7 +2404,10 @@ static void ensure_layer_data_is_mutable(CustomDataLayer &layer, const int totel
/* Can not be shared without implicit-sharing data. */
return;
}
if (layer.sharing_info->is_shared()) {
if (layer.sharing_info->is_mutable()) {
layer.sharing_info->tag_ensured_mutable();
}
else {
const eCustomDataType type = eCustomDataType(layer.type);
const void *old_data = layer.data;
/* Copy the layer before removing the user because otherwise the data might be freed while

View File

@ -41,7 +41,7 @@ GeometryComponent *CurveComponent::copy() const
void CurveComponent::clear()
{
BLI_assert(this->is_mutable());
BLI_assert(this->is_mutable() || this->is_expired());
if (curves_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, curves_);

View File

@ -29,6 +29,12 @@ void GeometryComponentEditData::ensure_owns_direct_data()
/* Nothing to do. */
}
void GeometryComponentEditData::clear()
{
BLI_assert(this->is_mutable() || this->is_expired());
curves_edit_hints_.reset();
}
void GeometryComponentEditData::remember_deformed_curve_positions_if_necessary(
GeometrySet &geometry)
{

View File

@ -56,7 +56,7 @@ GeometryComponent *InstancesComponent::copy() const
void InstancesComponent::clear()
{
BLI_assert(this->is_mutable());
BLI_assert(this->is_mutable() || this->is_expired());
if (ownership_ == GeometryOwnershipType::Owned) {
delete instances_;
}

View File

@ -44,7 +44,7 @@ GeometryComponent *MeshComponent::copy() const
void MeshComponent::clear()
{
BLI_assert(this->is_mutable());
BLI_assert(this->is_mutable() || this->is_expired());
if (mesh_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, mesh_);

View File

@ -31,7 +31,7 @@ GeometryComponent *PointCloudComponent::copy() const
void PointCloudComponent::clear()
{
BLI_assert(this->is_mutable());
BLI_assert(this->is_mutable() || this->is_expired());
if (pointcloud_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, pointcloud_);

View File

@ -29,7 +29,7 @@ GeometryComponent *VolumeComponent::copy() const
void VolumeComponent::clear()
{
BLI_assert(this->is_mutable());
BLI_assert(this->is_mutable() || this->is_expired());
if (volume_ != nullptr) {
if (ownership_ == GeometryOwnershipType::Owned) {
BKE_id_free(nullptr, volume_);

View File

@ -41,7 +41,7 @@ using blender::bke::Instances;
GeometryComponent::GeometryComponent(GeometryComponentType type) : type_(type) {}
GeometryComponent *GeometryComponent::create(GeometryComponentType component_type)
GeometryComponentPtr GeometryComponent::create(GeometryComponentType component_type)
{
switch (component_type) {
case GEO_COMPONENT_TYPE_MESH:
@ -58,7 +58,7 @@ GeometryComponent *GeometryComponent::create(GeometryComponentType component_typ
return new GeometryComponentEditData();
}
BLI_assert_unreachable();
return nullptr;
return {};
}
int GeometryComponent::attribute_domain_size(const eAttrDomain domain) const
@ -97,6 +97,11 @@ void GeometryComponent::delete_self()
delete this;
}
void GeometryComponent::delete_data_only()
{
this->clear();
}
/** \} */
/* -------------------------------------------------------------------- */
@ -120,6 +125,7 @@ GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType co
}
if (component_ptr->is_mutable()) {
/* If the referenced component is already mutable, return it directly. */
component_ptr->tag_ensured_mutable();
return *component_ptr;
}
/* If the referenced component is shared, make a copy. The copy is not shared and is
@ -571,7 +577,7 @@ void GeometrySet::gather_attributes_for_propagation(
using namespace blender::bke;
/* Only needed right now to check if an attribute is built-in on this component type.
* TODO: Get rid of the dummy component. */
const GeometryComponent *dummy_component = GeometryComponent::create(dst_component_type);
const GeometryComponentPtr dummy_component = GeometryComponent::create(dst_component_type);
this->attribute_foreach(
component_types,
include_instances,
@ -611,7 +617,6 @@ void GeometrySet::gather_attributes_for_propagation(
};
r_attributes.add_or_modify(attribute_id, add_info, modify_info);
});
delete dummy_component;
}
static void gather_component_types_recursive(const GeometrySet &geometry_set,

View File

@ -3112,8 +3112,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal)
}
else {
/* When changing to UDIM, attempt to tokenize the filepath. */
char *filename = (char *)BLI_path_basename(ima->filepath);
BKE_image_ensure_tile_token(filename);
BKE_image_ensure_tile_token(ima->filepath, sizeof(ima->filepath));
}
/* image buffers for non-sequence multilayer will share buffers with RenderResult,
@ -3304,7 +3303,7 @@ bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *r_tile_start,
BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename));
if (!BKE_image_is_filename_tokenized(filename)) {
BKE_image_ensure_tile_token(filename);
BKE_image_ensure_tile_token_filename_only(filename, sizeof(filename));
}
eUDIM_TILE_FORMAT tile_format;
@ -3494,7 +3493,7 @@ bool BKE_image_is_filename_tokenized(char *filepath)
return strstr(filename, "<UDIM>") != nullptr || strstr(filename, "<UVTILE>") != nullptr;
}
void BKE_image_ensure_tile_token(char *filename)
void BKE_image_ensure_tile_token_filename_only(char *filename, size_t filename_maxncpy)
{
BLI_assert_msg(BLI_path_slash_find(filename) == nullptr,
"Only the file-name component should be used!");
@ -3511,18 +3510,24 @@ void BKE_image_ensure_tile_token(char *filename)
* 1000 through 2000 to provide better detection. */
std::regex pattern(R"((.*[._-])([12]\d{3})([._-].*))");
if (std::regex_search(path, match, pattern)) {
BLI_strncpy(filename, match.format("$1<UDIM>$3").c_str(), FILE_MAX);
BLI_strncpy(filename, match.format("$1<UDIM>$3").c_str(), filename_maxncpy);
return;
}
/* General `u##_v###` `uvtile` pattern. */
pattern = std::regex(R"((.*)(u\d{1,2}_v\d{1,3})(\D.*))");
if (std::regex_search(path, match, pattern)) {
BLI_strncpy(filename, match.format("$1<UVTILE>$3").c_str(), FILE_MAX);
BLI_strncpy(filename, match.format("$1<UVTILE>$3").c_str(), filename_maxncpy);
return;
}
}
void BKE_image_ensure_tile_token(char *filepath, size_t filepath_maxncpy)
{
char *filename = (char *)BLI_path_basename(filepath);
BKE_image_ensure_tile_token_filename_only(filename, filepath_maxncpy - (filename - filepath));
}
bool BKE_image_tile_filepath_exists(const char *filepath)
{
BLI_assert(!BLI_path_is_rel(filepath));
@ -5136,8 +5141,8 @@ void BKE_image_user_file_path_ex(const Main *bmain,
int index;
if (ima->source == IMA_SRC_SEQUENCE) {
index = iuser ? iuser->framenr : ima->lastframe;
BLI_path_sequence_decode(filepath, head, tail, &numlen);
BLI_path_sequence_encode(filepath, head, tail, numlen, index);
BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &numlen);
BLI_path_sequence_encode(filepath, FILE_MAX, head, tail, numlen, index);
}
else if (resolve_udim) {
index = image_get_tile_number_from_iuser(ima, iuser);
@ -5277,7 +5282,7 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int
int BKE_image_sequence_guess_offset(Image *image)
{
return BLI_path_sequence_decode(image->filepath, nullptr, nullptr, nullptr);
return BLI_path_sequence_decode(image->filepath, nullptr, 0, nullptr, 0, nullptr);
}
bool BKE_image_has_anim(Image *ima)

View File

@ -18,7 +18,7 @@ TEST(udim, image_ensure_tile_token)
char result[FILE_MAX];
BLI_strncpy(result, original, sizeof(result));
BKE_image_ensure_tile_token(result);
BKE_image_ensure_tile_token_filename_only(result, sizeof(result));
EXPECT_STREQ(result, expected);
};

View File

@ -481,15 +481,19 @@ static void get_sequence_filepath(const MovieClip *clip, const int framenr, char
int offset;
BLI_strncpy(filepath, clip->filepath, sizeof(clip->filepath));
BLI_path_sequence_decode(filepath, head, tail, &numlen);
BLI_path_sequence_decode(filepath, head, sizeof(head), tail, sizeof(tail), &numlen);
/* Movie-clips always points to first image from sequence, auto-guess offset for now.
* Could be something smarter in the future. */
offset = sequence_guess_offset(clip->filepath, strlen(head), numlen);
if (numlen) {
BLI_path_sequence_encode(
filepath, head, tail, numlen, offset + framenr - clip->start_frame + clip->frame_offset);
BLI_path_sequence_encode(filepath,
sizeof(filepath),
head,
tail,
numlen,
offset + framenr - clip->start_frame + clip->frame_offset);
}
else {
BLI_strncpy(filepath, clip->filepath, sizeof(clip->filepath));
@ -702,7 +706,7 @@ static void movieclip_calc_length(MovieClip *clip)
ushort numlen;
char filepath[FILE_MAX], head[FILE_MAX], tail[FILE_MAX];
BLI_path_sequence_decode(clip->filepath, head, tail, &numlen);
BLI_path_sequence_decode(clip->filepath, head, sizeof(head), tail, sizeof(tail), &numlen);
if (numlen == 0) {
/* there's no number group in file name, assume it's single framed sequence */
@ -790,7 +794,7 @@ static int user_frame_to_cache_frame(MovieClip *clip, int framenr)
ushort numlen;
char head[FILE_MAX], tail[FILE_MAX];
BLI_path_sequence_decode(clip->filepath, head, tail, &numlen);
BLI_path_sequence_decode(clip->filepath, head, sizeof(head), tail, sizeof(tail), &numlen);
/* see comment in get_sequence_filepath */
clip->cache->sequence_offset = sequence_guess_offset(clip->filepath, strlen(head), numlen);
@ -933,7 +937,7 @@ static bool put_imbuf_cache(
clip->cache->sequence_offset = -1;
if (clip->source == MCLIP_SRC_SEQUENCE) {
ushort numlen;
BLI_path_sequence_decode(clip->filepath, NULL, NULL, &numlen);
BLI_path_sequence_decode(clip->filepath, NULL, 0, NULL, 0, &numlen);
clip->cache->is_still_sequence = (numlen == 0);
}
}

View File

@ -654,8 +654,7 @@ int BKE_packedfile_unpack_image(Main *bmain,
BLI_strncpy(ima->filepath, new_file_path, sizeof(imapf->filepath));
if (ima->source == IMA_SRC_TILED) {
/* Ensure that the Image filepath is kept in a tokenized format. */
char *filename = (char *)BLI_path_basename(ima->filepath);
BKE_image_ensure_tile_token(filename);
BKE_image_ensure_tile_token(ima->filepath, sizeof(ima->filepath));
}
}
MEM_freeN(new_file_path);

View File

@ -1301,7 +1301,7 @@ static int ptcache_frame_from_filename(const char *filename, const char *ext)
#define MAX_PTCACHE_PATH FILE_MAX
#define MAX_PTCACHE_FILE (FILE_MAX * 2)
static int ptcache_path(PTCacheID *pid, char *dirname)
static int ptcache_path(PTCacheID *pid, char dirname[MAX_PTCACHE_PATH])
{
const char *blendfile_path = BKE_main_blendfile_path_from_global();
Library *lib = (pid->owner_id) ? pid->owner_id->lib : NULL;
@ -1311,13 +1311,13 @@ static int ptcache_path(PTCacheID *pid, char *dirname)
size_t i;
if (pid->cache->flag & PTCACHE_EXTERNAL) {
strcpy(dirname, pid->cache->path);
BLI_strncpy(dirname, pid->cache->path, MAX_PTCACHE_PATH);
if (BLI_path_is_rel(dirname)) {
BLI_path_abs(dirname, blendfilename);
}
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_FILE); /* new strlen() */
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_PATH); /* new strlen() */
}
if ((blendfile_path[0] != '\0') || lib) {
char file[MAX_PTCACHE_PATH]; /* we don't want the dir, only the file */
@ -1334,18 +1334,18 @@ static int ptcache_path(PTCacheID *pid, char *dirname)
BLI_snprintf(dirname, MAX_PTCACHE_PATH, "//" PTCACHE_PATH "%s", file);
BLI_path_abs(dirname, blendfilename);
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_FILE); /* new strlen() */
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_PATH); /* new strlen() */
}
/* use the temp path. this is weak but better than not using point cache at all */
/* temporary directory is assumed to exist and ALWAYS has a trailing slash */
BLI_snprintf(dirname, MAX_PTCACHE_PATH, "%s" PTCACHE_PATH, BKE_tempdir_session());
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_FILE); /* new strlen() */
return BLI_path_slash_ensure(dirname, MAX_PTCACHE_PATH); /* new strlen() */
}
static size_t ptcache_filepath_ext_append(PTCacheID *pid,
char *filepath,
char filepath[MAX_PTCACHE_FILE],
const size_t filepath_len,
const bool use_frame_number,
const int cfra)
@ -1396,8 +1396,11 @@ static size_t ptcache_filepath_ext_append(PTCacheID *pid,
return len;
}
static int ptcache_filepath(
PTCacheID *pid, char *filepath, int cfra, const bool do_path, const bool do_ext)
static int ptcache_filepath(PTCacheID *pid,
char filepath[MAX_PTCACHE_FILE],
int cfra,
const bool do_path,
const bool do_ext)
{
int len = 0;
char *idname;
@ -2591,7 +2594,7 @@ void BKE_ptcache_id_clear(PTCacheID *pid, int mode, uint cfra)
char path[MAX_PTCACHE_PATH];
char filepath[MAX_PTCACHE_FILE];
char path_full[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
char ext[MAX_PTCACHE_FILE];
if (!pid || !pid->cache || pid->cache->flag & PTCACHE_BAKED) {
return;
@ -2818,7 +2821,7 @@ void BKE_ptcache_id_time(
struct dirent *de;
char path[MAX_PTCACHE_PATH];
char filepath[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
char ext[MAX_PTCACHE_FILE];
uint len; /* store the length of the string */
ptcache_path(pid, path);
@ -3490,7 +3493,7 @@ void BKE_ptcache_disk_cache_rename(PTCacheID *pid, const char *name_src, const c
char old_filepath[MAX_PTCACHE_FILE];
char new_path_full[MAX_PTCACHE_FILE];
char old_path_full[MAX_PTCACHE_FILE];
char ext[MAX_PTCACHE_PATH];
char ext[MAX_PTCACHE_FILE];
/* If both names are the same, there is nothing to do. */
if (STREQ(name_src, name_dst)) {

View File

@ -820,6 +820,8 @@ static bool vfont_to_curve(Object *ob,
char32_t ascii;
bool ok = false;
const float font_size = cu->fsize * iter_data->scale_to_fit;
/* Shift down vertically to be 25% below & 75% above baseline (before font scale is applied). */
const float font_select_y_offset = 0.25;
const bool word_wrap = iter_data->word_wrap;
const float xof_scale = safe_divide(cu->xof, font_size);
const float yof_scale = safe_divide(cu->yof, font_size);
@ -1122,7 +1124,7 @@ static bool vfont_to_curve(Object *ob,
if (selboxes && (i >= selstart) && (i <= selend)) {
sb = &selboxes[i - selstart];
sb->y = yof * font_size - linedist * font_size * 0.1f;
sb->y = (yof - font_select_y_offset) * font_size - linedist * font_size * 0.1f;
sb->h = linedist * font_size;
sb->w = xof * font_size;
}
@ -1454,8 +1456,15 @@ static bool vfont_to_curve(Object *ob,
ct = chartransdata;
for (i = 0; i <= selend; i++, ct++) {
if (i >= selstart) {
selboxes[i - selstart].x = ct->xof * font_size;
selboxes[i - selstart].y = (ct->yof - 0.25f) * font_size;
EditFontSelBox *sb = &selboxes[i - selstart];
sb->x = ct->xof;
sb->y = ct->yof;
if (ct->rot != 0.0f) {
sb->x -= sinf(ct->rot) * font_select_y_offset;
sb->y -= cosf(ct->rot) * font_select_y_offset;
}
sb->x *= font_size;
sb->y *= font_size;
selboxes[i - selstart].h = font_size;
}
}
@ -1551,7 +1560,7 @@ static bool vfont_to_curve(Object *ob,
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;
rotation = chartransdata[max_ii(0, ef->selend - 1)].rot;
cursor_left = 0.0f;
}
else {
@ -1570,24 +1579,23 @@ static bool vfont_to_curve(Object *ob,
/* Bottom left. */
ef->textcurs[0][0] = cursor_left;
ef->textcurs[0][1] = 0.0f;
ef->textcurs[0][1] = 0.0f - font_select_y_offset;
/* Bottom right. */
ef->textcurs[1][0] = cursor_left + cursor_width;
ef->textcurs[1][1] = 0.0f;
ef->textcurs[1][1] = 0.0f - font_select_y_offset;
/* Top left. */
ef->textcurs[3][0] = cursor_left;
ef->textcurs[3][1] = 1.0f;
ef->textcurs[3][1] = 1.0f - font_select_y_offset;
/* Top right. */
ef->textcurs[2][0] = cursor_left + cursor_width;
ef->textcurs[2][1] = 1.0f;
ef->textcurs[2][1] = 1.0f - font_select_y_offset;
for (int vert = 0; vert < 4; vert++) {
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);
ef->textcurs[vert][1] = font_size * (yoffset + temp_fl[1]);
}
}

View File

@ -37,32 +37,91 @@ namespace blender {
*/
class ImplicitSharingInfo : NonCopyable, NonMovable {
private:
mutable std::atomic<int> users_;
/**
* Number of users that want to own the shared data. This can be in multiple states:
* - 0: The data is expired and likely freed. It must not be accessed anymore. The
* #ImplicitSharingInfo may still be alive when there are weak users.
* - 1: The data is mutable by the single owner.
* - >1: The data is shared and therefore immutable.
*/
mutable std::atomic<int> strong_users_ = 1;
/**
* Number of users that only keep a reference to the `ImplicitSharingInfo` but don't need to own
* the shared data. One additional weak user is added as long as there is at least one strong
* user. Together with the `version_` below this adds an efficient way to detect if data has been
* changed.
*/
mutable std::atomic<int> weak_users_ = 1;
/**
* The data referenced by an #ImplicitSharingInfo can change over time. This version is
* incremented whenever the referenced data is about to be changed. This allows checking if the
* data has been changed between points in time.
*/
mutable std::atomic<int64_t> version_ = 0;
public:
ImplicitSharingInfo(const int initial_users) : users_(initial_users) {}
virtual ~ImplicitSharingInfo()
{
BLI_assert(this->is_mutable());
BLI_assert(strong_users_ == 0);
BLI_assert(weak_users_ == 0);
}
/** True if there are other const references to the resource, meaning it cannot be modified. */
bool is_shared() const
{
return users_.load(std::memory_order_relaxed) >= 2;
}
/** Whether the resource can be modified without a copy because there is only one owner. */
/** Whether the resource can be modified inplace because there is only one owner. */
bool is_mutable() const
{
return !this->is_shared();
return strong_users_.load(std::memory_order_relaxed) == 1;
}
/**
* Weak users don't protect the referenced data from being freed. If the data is freed while
* there is still a weak referenced, this returns true.
*/
bool is_expired() const
{
return strong_users_.load(std::memory_order_acquire) == 0;
}
/** Call when a the data has a new additional owner. */
void add_user() const
{
users_.fetch_add(1, std::memory_order_relaxed);
BLI_assert(!this->is_expired());
strong_users_.fetch_add(1, std::memory_order_relaxed);
}
/**
* Adding a weak owner prevents the #ImplicitSharingInfo from being freed but not the referenced
* data.
*
* \note Unlike std::shared_ptr a weak user cannot be turned into a strong user. This is
* because some code might change the referenced data assuming that there is only one strong user
* while a new strong user is added by another thread.
*/
void add_weak_user() const
{
weak_users_.fetch_add(1, std::memory_order_relaxed);
}
/**
* Call this when making sure that the referenced data is mutable, which also implies that it is
* about to be modified. This allows other code to detect whether data has not been changed very
* efficiently.
*/
void tag_ensured_mutable() const
{
BLI_assert(this->is_mutable());
/* This might not need an atomic increment when the #version method below is only called when
* the code calling it is a strong user of this sharing info. Better be safe and use an atomic
* for now. */
version_.fetch_add(1, std::memory_order_acq_rel);
}
/**
* Get a version number that is increased when the data is modified. It can be used to detect if
* data has been changed.
*/
int64_t version() const
{
return version_.load(std::memory_order_acquire);
}
/**
@ -71,17 +130,51 @@ class ImplicitSharingInfo : NonCopyable, NonMovable {
*/
void remove_user_and_delete_if_last() const
{
const int old_user_count = users_.fetch_sub(1, std::memory_order_acq_rel);
const int old_user_count = strong_users_.fetch_sub(1, std::memory_order_acq_rel);
BLI_assert(old_user_count >= 1);
const bool was_last_user = old_user_count == 1;
if (was_last_user) {
const int old_weak_user_count = weak_users_.load(std::memory_order_acquire);
BLI_assert(old_weak_user_count >= 1);
if (old_weak_user_count == 1) {
/* If the weak user count is 1 it means that there is no actual weak user. The 1 just
* indicates that there was still at least one strong user. */
weak_users_ = 0;
const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
}
else {
/* There is still at least one actual weak user, so don't free the sharing info yet. The
* data can be freed though. */
const_cast<ImplicitSharingInfo *>(this)->delete_data_only();
/* Also remove the "fake" weak user that indicated that there was at least one strong
* user.*/
this->remove_weak_user_and_delete_if_last();
}
}
}
/**
* This might just decrement the weak user count or might delete the data. Should be used in
* conjunction with #add_weak_user.
*/
void remove_weak_user_and_delete_if_last() const
{
const int old_weak_user_count = weak_users_.fetch_sub(1, std::memory_order_acq_rel);
BLI_assert(old_weak_user_count >= 1);
const bool was_last_weak_user = old_weak_user_count == 1;
if (was_last_weak_user) {
/* It's possible that the data has been freed before already, but now it is definitely freed
* together with the sharing info. */
const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
}
}
private:
/** Has to free the #ImplicitSharingInfo and the referenced data. */
/** Has to free the #ImplicitSharingInfo and the referenced data. The data might have been freed
* before by #delete_data_only already. This case should be handled here. */
virtual void delete_self_with_data() = 0;
/** Can free the referenced data but the #ImplicitSharingInfo still has to be kept alive. */
virtual void delete_data_only() {}
};
/**
@ -89,8 +182,6 @@ class ImplicitSharingInfo : NonCopyable, NonMovable {
* class can be used with #ImplicitSharingPtr.
*/
class ImplicitSharingMixin : public ImplicitSharingInfo {
public:
ImplicitSharingMixin() : ImplicitSharingInfo(1) {}
private:
void delete_self_with_data() override

View File

@ -311,14 +311,20 @@ bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filenam
*/
int BLI_path_sequence_decode(const char *string,
char *head,
size_t head_maxncpy,
char *tail,
size_t tail_maxncpy,
unsigned short *r_digits_len);
/**
* Returns in area pointed to by string a string of the form `<head><pic><tail>`,
* where pic is formatted as `numlen` digits with leading zeroes.
*/
void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, unsigned short numlen, int pic);
void BLI_path_sequence_encode(char *string,
size_t string_maxncpy,
const char *head,
const char *tail,
unsigned short numlen,
int pic);
/**
* Remove redundant characters from \a path.

View File

@ -87,12 +87,7 @@ template<typename T, eValueType V> class PrimitiveValue;
using IntValue = PrimitiveValue<int64_t, eValueType::Int>;
using DoubleValue = PrimitiveValue<double, eValueType::Double>;
using BooleanValue = PrimitiveValue<bool, eValueType::Boolean>;
template<typename Container, eValueType V, typename ContainerItem = typename Container::value_type>
class ContainerValue;
/* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can
* be created by calling `create_lookup`. */
using ArrayValue = ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Array>;
class ArrayValue;
/**
* Class containing a (de)serializable value.
@ -214,7 +209,7 @@ template<
eValueType V,
/** Type of the data inside the container. */
typename ContainerItem>
typename ContainerItem = typename Container::value_type>
class ContainerValue : public Value {
public:
using Items = Container;
@ -237,6 +232,18 @@ class ContainerValue : public Value {
}
};
class ArrayValue : public ContainerValue<Vector<std::shared_ptr<Value>>, eValueType::Array> {
public:
void append(std::shared_ptr<Value> value);
void append_bool(bool value);
void append_int(int value);
void append_double(double value);
void append_str(std::string value);
void append_null();
std::shared_ptr<DictionaryValue> append_dict();
std::shared_ptr<ArrayValue> append_array();
};
/**
* Internal storage type for DictionaryValue.
*
@ -260,14 +267,21 @@ class DictionaryValue
*
* The lookup is owned by the caller.
*/
const Lookup create_lookup() const
{
Lookup result;
for (const Item &item : elements()) {
result.add_as(item.first, item.second);
}
return result;
}
const Lookup create_lookup() const;
const std::shared_ptr<Value> *lookup(const StringRef key) const;
std::optional<StringRefNull> lookup_str(const StringRef key) const;
std::optional<int64_t> lookup_int(const StringRef key) const;
std::optional<double> lookup_double(const StringRef key) const;
const DictionaryValue *lookup_dict(const StringRef key) const;
const ArrayValue *lookup_array(const StringRef key) const;
void append(std::string key, std::shared_ptr<Value> value);
void append_int(std::string key, int64_t value);
void append_double(std::string key, double value);
void append_str(std::string key, std::string value);
std::shared_ptr<DictionaryValue> append_dict(std::string key);
std::shared_ptr<ArrayValue> append_array(std::string key);
};
/**
@ -300,4 +314,7 @@ class JsonFormatter : public Formatter {
std::unique_ptr<Value> deserialize(std::istream &is) override;
};
void write_json_file(StringRef path, const Value &value);
std::shared_ptr<Value> read_json_file(StringRef path);
} // namespace blender::io::serialize

View File

@ -13,7 +13,7 @@ class MEMFreeImplicitSharing : public ImplicitSharingInfo {
public:
void *data;
MEMFreeImplicitSharing(void *data) : ImplicitSharingInfo(1), data(data)
MEMFreeImplicitSharing(void *data) : data(data)
{
BLI_assert(data != nullptr);
}
@ -44,7 +44,10 @@ void *make_trivial_data_mutable_impl(void *old_data,
}
BLI_assert(*sharing_info != nullptr);
if ((*sharing_info)->is_shared()) {
if ((*sharing_info)->is_mutable()) {
(*sharing_info)->tag_ensured_mutable();
}
else {
void *new_data = MEM_mallocN_aligned(size, alignment, __func__);
memcpy(new_data, old_data, size);
(*sharing_info)->remove_user_and_delete_if_last();
@ -85,6 +88,7 @@ void *resize_trivial_array_impl(void *old_data,
* could theoretically give better performance if the data can be reused in place. */
void *new_data = static_cast<int *>(MEM_reallocN(old_data, new_size));
info->data = new_data;
(*sharing_info)->tag_ensured_mutable();
return new_data;
}
}

View File

@ -47,8 +47,22 @@ static bool BLI_path_is_abs_win32(const char *name);
// #define DEBUG_STRSIZE
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
int BLI_path_sequence_decode(const char *string,
char *head,
const size_t head_maxncpy,
char *tail,
const size_t tail_maxncpy,
ushort *r_digits_len)
{
#ifdef DEBUG_STRSIZE
if (head) {
memset(head, 0xff, sizeof(*head) * head_maxncpy);
}
if (tail) {
memset(tail, 0xff, sizeof(*tail) * tail_maxncpy);
}
#endif
uint nums = 0, nume = 0;
int i;
bool found_digit = false;
@ -82,8 +96,7 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort
strcpy(tail, &string[nume + 1]);
}
if (head) {
strcpy(head, string);
head[nums] = 0;
BLI_strncpy(head, string, MIN2(head_maxncpy, nums + 1));
}
if (r_digits_len) {
*r_digits_len = nume - nums + 1;
@ -93,7 +106,7 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort
}
if (tail) {
strcpy(tail, string + name_end);
BLI_strncpy(tail, string + name_end, tail_maxncpy);
}
if (head) {
/* Name_end points to last character of head,
@ -106,10 +119,17 @@ int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort
return 0;
}
void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, ushort numlen, int pic)
void BLI_path_sequence_encode(char *string,
const size_t string_maxncpy,
const char *head,
const char *tail,
ushort numlen,
int pic)
{
BLI_sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
#ifdef DEBUG_STRSIZE
memset(string, 0xff, sizeof(*string) * string_maxncpy);
#endif
BLI_snprintf(string, string_maxncpy, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
}
void BLI_path_normalize(char *path)

View File

@ -1,5 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_fileops.hh"
#include "BLI_serialize.hh"
#include "json.hpp"
@ -197,6 +198,149 @@ static std::unique_ptr<Value> convert_from_json(const nlohmann::ordered_json &j)
return std::make_unique<NullValue>();
}
void ArrayValue::append(std::shared_ptr<Value> value)
{
this->elements().append(std::move(value));
}
void ArrayValue::append_bool(const bool value)
{
this->append(std::make_shared<BooleanValue>(value));
}
void ArrayValue::append_int(const int value)
{
this->append(std::make_shared<IntValue>(value));
}
void ArrayValue::append_double(const double value)
{
this->append(std::make_shared<DoubleValue>(value));
}
void ArrayValue::append_str(std::string value)
{
this->append(std::make_shared<StringValue>(std::move(value)));
}
void ArrayValue::append_null()
{
this->append(std::make_shared<NullValue>());
}
std::shared_ptr<DictionaryValue> ArrayValue::append_dict()
{
auto value = std::make_shared<DictionaryValue>();
this->append(value);
return value;
}
std::shared_ptr<ArrayValue> ArrayValue::append_array()
{
auto value = std::make_shared<ArrayValue>();
this->append(value);
return value;
}
const DictionaryValue::Lookup DictionaryValue::create_lookup() const
{
Lookup result;
for (const Item &item : elements()) {
result.add_as(item.first, item.second);
}
return result;
}
const std::shared_ptr<Value> *DictionaryValue::lookup(const StringRef key) const
{
for (const auto &item : this->elements()) {
if (item.first == key) {
return &item.second;
}
}
return nullptr;
}
std::optional<StringRefNull> DictionaryValue::lookup_str(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const StringValue *str_value = (*value)->as_string_value()) {
return StringRefNull(str_value->value());
}
}
return std::nullopt;
}
std::optional<int64_t> DictionaryValue::lookup_int(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const IntValue *int_value = (*value)->as_int_value()) {
return int_value->value();
}
}
return std::nullopt;
}
std::optional<double> DictionaryValue::lookup_double(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
if (const DoubleValue *double_value = (*value)->as_double_value()) {
return double_value->value();
}
}
return std::nullopt;
}
const DictionaryValue *DictionaryValue::lookup_dict(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
return (*value)->as_dictionary_value();
}
return nullptr;
}
const ArrayValue *DictionaryValue::lookup_array(const StringRef key) const
{
if (const std::shared_ptr<Value> *value = this->lookup(key)) {
return (*value)->as_array_value();
}
return nullptr;
}
void DictionaryValue::append(std::string key, std::shared_ptr<Value> value)
{
this->elements().append({std::move(key), std::move(value)});
}
void DictionaryValue::append_int(std::string key, const int64_t value)
{
this->append(std::move(key), std::make_shared<IntValue>(value));
}
void DictionaryValue::append_double(std::string key, const double value)
{
this->append(std::move(key), std::make_shared<DoubleValue>(value));
}
void DictionaryValue::append_str(std::string key, const std::string value)
{
this->append(std::move(key), std::make_shared<StringValue>(value));
}
std::shared_ptr<DictionaryValue> DictionaryValue::append_dict(std::string key)
{
auto value = std::make_shared<DictionaryValue>();
this->append(std::move(key), value);
return value;
}
std::shared_ptr<ArrayValue> DictionaryValue::append_array(std::string key)
{
auto value = std::make_shared<ArrayValue>();
this->append(std::move(key), value);
return value;
}
void JsonFormatter::serialize(std::ostream &os, const Value &value)
{
nlohmann::ordered_json j;
@ -216,4 +360,18 @@ std::unique_ptr<Value> JsonFormatter::deserialize(std::istream &is)
return convert_from_json(j);
}
void write_json_file(const StringRef path, const Value &value)
{
JsonFormatter formatter;
fstream stream(path, std::ios::out);
formatter.serialize(stream, value);
}
std::shared_ptr<Value> read_json_file(const StringRef path)
{
JsonFormatter formatter;
fstream stream(path, std::ios::in);
return formatter.deserialize(stream);
}
} // namespace blender::io::serialize

View File

@ -28,6 +28,11 @@ class SharedDataContainer {
public:
SharedDataContainer() : data_(MEM_new<ImplicitlySharedData>(__func__)) {}
const ImplicitSharingInfo *sharing_info() const
{
return data_.get();
}
const ImplicitlySharedData *get_for_read() const
{
return data_.get();
@ -39,6 +44,7 @@ class SharedDataContainer {
return nullptr;
}
if (data_->is_mutable()) {
data_->tag_ensured_mutable();
return data_.get();
}
data_ = data_->copy();
@ -80,4 +86,29 @@ TEST(implicit_sharing, CopyOnWriteAccess)
EXPECT_EQ(d.get_for_write(), data_b1);
}
TEST(implicit_sharing, WeakUser)
{
SharedDataContainer a;
const ImplicitSharingInfo *sharing_info = a.sharing_info();
EXPECT_FALSE(sharing_info->is_expired());
EXPECT_TRUE(sharing_info->is_mutable());
sharing_info->add_weak_user();
EXPECT_FALSE(sharing_info->is_expired());
EXPECT_TRUE(sharing_info->is_mutable());
a = {};
EXPECT_TRUE(sharing_info->is_expired());
sharing_info->remove_weak_user_and_delete_if_last();
}
TEST(implicit_sharing, Version)
{
SharedDataContainer a;
const ImplicitSharingInfo *sharing_info = a.sharing_info();
const int old_version = sharing_info->version();
a.get_for_read();
EXPECT_EQ(old_version, sharing_info->version());
a.get_for_write();
EXPECT_LT(old_version, sharing_info->version());
}
} // namespace blender::tests

View File

@ -1059,7 +1059,8 @@ TEST(path_util, FrameGet)
char head[FILE_MAX]; \
char tail[FILE_MAX]; \
ushort numdigits = 0; \
const int result = BLI_path_sequence_decode(path, head, tail, &numdigits); \
const int result = BLI_path_sequence_decode( \
path, head, sizeof(head), tail, sizeof(tail), &numdigits); \
EXPECT_EQ(result, expect_result); \
EXPECT_STREQ(head, expect_head); \
EXPECT_STREQ(tail, expect_tail); \

View File

@ -78,12 +78,11 @@ TEST(serialize, array_to_json)
JsonFormatter json;
std::stringstream out;
ArrayValue value_array;
ArrayValue::Items &array = value_array.elements();
array.append_as(new IntValue(42));
array.append_as(new StringValue("Hello JSON"));
array.append_as(new NullValue);
array.append_as(new BooleanValue(false));
array.append_as(new BooleanValue(true));
value_array.append_int(42);
value_array.append_str("Hello JSON");
value_array.append_null();
value_array.append_bool(false);
value_array.append_bool(true);
json.serialize(out, value_array);
EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]");
@ -94,8 +93,7 @@ TEST(serialize, object_to_json)
JsonFormatter json;
std::stringstream out;
DictionaryValue value_object;
DictionaryValue::Items &attributes = value_object.elements();
attributes.append_as(std::pair(std::string("best_number"), new IntValue(42)));
value_object.append_int("best_number", 42);
json.serialize(out, value_object);
EXPECT_EQ(out.str(), "{\"best_number\":42}");

View File

@ -1047,10 +1047,12 @@ class StringEscape : public testing::Test {
void testEscapeWords(const CompareWordsArray &items)
{
size_t dst_test_len;
char dst_test[64];
char dst_test[64]; /* Must be big enough for all input. */
for (const auto &item : items) {
/* Validate the static size is big enough (test the test it's self). */
EXPECT_LT((strlen(item[0]) * 2) + 1, sizeof(dst_test));
/* Escape the string. */
dst_test_len = BLI_str_escape(dst_test, item[0], SIZE_MAX);
dst_test_len = BLI_str_escape(dst_test, item[0], sizeof(dst_test));
EXPECT_STREQ(dst_test, item[1]);
EXPECT_EQ(dst_test_len, strlen(dst_test));
/* Escape back. */

View File

@ -428,10 +428,9 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene)
if (base->lay & (1 << layer)) {
/* Create collections when needed only. */
if (collections[layer] == NULL) {
char name[MAX_NAME];
char name[MAX_ID_NAME - 2];
BLI_snprintf(
name, sizeof(collection_master->id.name), DATA_("Collection %d"), layer + 1);
BLI_snprintf(name, sizeof(name), DATA_("Collection %d"), layer + 1);
Collection *collection = BKE_collection_add(bmain, collection_master, name);
collection->id.lib = scene->id.lib;

View File

@ -1263,8 +1263,7 @@ void do_versions_after_linking_300(FileData * /*fd*/, Main *bmain)
/* Ensure tiled image sources contain a UDIM token. */
LISTBASE_FOREACH (Image *, ima, &bmain->images) {
if (ima->source == IMA_SRC_TILED) {
char *filename = (char *)BLI_path_basename(ima->filepath);
BKE_image_ensure_tile_token(filename);
BKE_image_ensure_tile_token(ima->filepath, sizeof(ima->filepath));
}
}
}

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

@ -657,8 +657,7 @@ struct AssetIndex {
AssetIndex(const FileIndexerEntries &indexer_entries)
{
std::unique_ptr<DictionaryValue> root = std::make_unique<DictionaryValue>();
DictionaryValue::Items &root_attributes = root->elements();
root_attributes.append_as(std::pair(ATTRIBUTE_VERSION, new IntValue(CURRENT_VERSION)));
root->append_int(ATTRIBUTE_VERSION, CURRENT_VERSION);
init_value_from_file_indexer_entries(*root, indexer_entries);
contents = std::move(root);

View File

@ -281,7 +281,10 @@ static void um_arraystore_cd_compact(CustomData *cdata,
* solution might be to not pass "dynamic" layers (see `layer_type_is_dynamic`) to the
* array store at all. */
BLI_assert(layer->sharing_info->is_mutable());
MEM_delete(layer->sharing_info);
/* Intentionally don't call #MEM_delete, because we want to free the sharing info without
* the data here. In general this would not be allowed because one can't be sure how to
* free the data without the sharing info. */
MEM_freeN(const_cast<blender::ImplicitSharingInfo *>(layer->sharing_info));
}
MEM_freeN(layer->data);
layer->data = nullptr;

View File

@ -2975,7 +2975,7 @@ static void filenum_newname(char *name, size_t name_size, int add)
int pic;
ushort digits;
pic = BLI_path_sequence_decode(name, head, tail, &digits);
pic = BLI_path_sequence_decode(name, head, sizeof(head), tail, sizeof(tail), &digits);
/* are we going from 100 -> 99 or from 10 -> 9 */
if (add < 0 && digits > 0) {
@ -2993,7 +2993,7 @@ static void filenum_newname(char *name, size_t name_size, int add)
if (pic < 0) {
pic = 0;
}
BLI_path_sequence_encode(name_temp, head, tail, digits, pic);
BLI_path_sequence_encode(name_temp, sizeof(name_temp), head, tail, digits, pic);
BLI_strncpy(name, name_temp, name_size);
}

View File

@ -1586,8 +1586,7 @@ static int image_file_browse_exec(bContext *C, wmOperator *op)
/* If loading into a tiled texture, ensure that the filename is tokenized. */
if (ima->source == IMA_SRC_TILED) {
char *filename = (char *)BLI_path_basename(filepath);
BKE_image_ensure_tile_token(filename);
BKE_image_ensure_tile_token(filepath, sizeof(filepath));
}
PointerRNA imaptr;

View File

@ -55,7 +55,8 @@ static void image_sequence_get_frame_ranges(wmOperator *op, ListBase *ranges)
ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame");
/* use the first file in the list as base filename */
frame->framenr = BLI_path_sequence_decode(filename, head, tail, &digits);
frame->framenr = BLI_path_sequence_decode(
filename, head, sizeof(head), tail, sizeof(tail), &digits);
/* still in the same sequence */
if (do_frame_range && (range != NULL) && STREQLEN(base_head, head, FILE_MAX) &&

View File

@ -43,8 +43,44 @@
#include "view3d_navigate.h" /* own include */
/* Prototypes. */
static void viewops_data_init_context(bContext *C, ViewOpsData *vod);
static void viewops_data_init_navigation(bContext *C,
const wmEvent *event,
const eV3D_OpMode nav_type,
const bool use_cursor_init,
ViewOpsData *vod);
static void viewops_data_end_navigation(bContext *C, ViewOpsData *vod);
static int viewpan_invoke_impl(ViewOpsData *vod, PointerRNA *ptr);
const char *viewops_operator_idname_get(eV3D_OpMode nav_type)
{
switch (nav_type) {
case V3D_OP_MODE_ZOOM:
return "VIEW3D_OT_zoom";
case V3D_OP_MODE_ROTATE:
return "VIEW3D_OT_rotate";
case V3D_OP_MODE_MOVE:
return "VIEW3D_OT_move";
case V3D_OP_MODE_VIEW_PAN:
return "VIEW3D_OT_view_pan";
case V3D_OP_MODE_VIEW_ROLL:
return "VIEW3D_OT_view_roll";
case V3D_OP_MODE_DOLLY:
return "VIEW3D_OT_dolly";
#ifdef WITH_INPUT_NDOF
case V3D_OP_MODE_NDOF_ORBIT:
return "VIEW3D_OT_ndof_orbit";
case V3D_OP_MODE_NDOF_ORBIT_ZOOM:
return "VIEW3D_OT_ndof_orbit_zoom";
#endif
}
BLI_assert(false);
return nullptr;
}
/* -------------------------------------------------------------------- */
/** \name Navigation Polls
/** \name Generic Operator Callback Utils
* \{ */
static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
@ -57,6 +93,128 @@ static bool view3d_navigation_poll_impl(bContext *C, const char viewlock)
return !(RV3D_LOCK_FLAGS(rv3d) & viewlock);
}
static eV3D_OpEvent view3d_navigate_event(ViewOpsData *vod, const wmEvent *event)
{
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CONFIRM:
return VIEW_CONFIRM;
case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
vod->axis_snap = true;
return VIEW_APPLY;
case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
vod->rv3d->persp = vod->init.persp_with_auto_persp_applied;
vod->axis_snap = false;
return VIEW_APPLY;
case VIEWROT_MODAL_SWITCH_ZOOM:
case VIEWROT_MODAL_SWITCH_MOVE:
case VIEWROT_MODAL_SWITCH_ROTATE: {
const eV3D_OpMode nav_type_new = (event->val == VIEWROT_MODAL_SWITCH_ZOOM) ?
V3D_OP_MODE_ZOOM :
(event->val == VIEWROT_MODAL_SWITCH_MOVE) ?
V3D_OP_MODE_MOVE :
V3D_OP_MODE_ROTATE;
if (nav_type_new == vod->nav_type) {
break;
}
vod->nav_type = nav_type_new;
return VIEW_APPLY;
}
}
}
else {
if (event->type == TIMER && event->customdata == vod->timer) {
/* Zoom uses timer for continuous zoom. */
return VIEW_APPLY;
}
if (event->type == MOUSEMOVE) {
return VIEW_APPLY;
}
if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
return VIEW_CONFIRM;
}
if (event->type == EVT_ESCKEY && event->val == KM_PRESS) {
return VIEW_CANCEL;
}
}
return VIEW_PASS;
}
static int view3d_navigation_modal(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2])
{
switch (vod->nav_type) {
case V3D_OP_MODE_ZOOM:
return viewzoom_modal_impl(C, vod, event_code, xy);
case V3D_OP_MODE_ROTATE:
return viewrotate_modal_impl(C, vod, event_code, xy);
case V3D_OP_MODE_MOVE:
return viewmove_modal_impl(C, vod, event_code, xy);
default:
break;
}
return OPERATOR_CANCELLED;
}
static int view3d_navigation_invoke_generic(bContext *C,
ViewOpsData *vod,
const wmEvent *event,
PointerRNA *ptr,
const eV3D_OpMode nav_type)
{
bool use_cursor_init = false;
if (PropertyRNA *prop = RNA_struct_find_property(ptr, "use_cursor_init")) {
use_cursor_init = RNA_property_boolean_get(ptr, prop);
}
viewops_data_init_navigation(C, event, nav_type, use_cursor_init, vod);
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
switch (nav_type) {
case V3D_OP_MODE_ZOOM:
return viewzoom_invoke_impl(C, vod, event, ptr);
case V3D_OP_MODE_ROTATE:
return viewrotate_invoke_impl(vod, event);
case V3D_OP_MODE_MOVE:
return viewmove_invoke_impl(vod, event);
case V3D_OP_MODE_VIEW_PAN: {
return viewpan_invoke_impl(vod, ptr);
}
default:
break;
}
return OPERATOR_CANCELLED;
}
int view3d_navigate_invoke_impl(bContext *C,
wmOperator *op,
const wmEvent *event,
const eV3D_OpMode nav_type)
{
ViewOpsData *vod = MEM_cnew<ViewOpsData>(__func__);
viewops_data_init_context(C, vod);
int ret = view3d_navigation_invoke_generic(C, vod, event, op->ptr, nav_type);
op->customdata = (void *)vod;
if (ret == OPERATOR_RUNNING_MODAL) {
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
viewops_data_free(C, vod);
op->customdata = nullptr;
return ret;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Callbacks
* \{ */
bool view3d_location_poll(bContext *C)
{
return view3d_navigation_poll_impl(C, RV3D_LOCK_LOCATION);
@ -72,11 +230,32 @@ bool view3d_zoom_or_dolly_poll(bContext *C)
return view3d_navigation_poll_impl(C, RV3D_LOCK_ZOOM_AND_DOLLY);
}
/** \} */
int view3d_navigate_modal_fn(bContext *C, wmOperator *op, const wmEvent *event)
{
ViewOpsData *vod = static_cast<ViewOpsData *>(op->customdata);
/* -------------------------------------------------------------------- */
/** \name Generic Callbacks
* \{ */
const eV3D_OpMode nav_type_prev = vod->nav_type;
const eV3D_OpEvent event_code = view3d_navigate_event(vod, event);
if (nav_type_prev != vod->nav_type) {
wmOperatorType *ot_new = WM_operatortype_find(viewops_operator_idname_get(vod->nav_type),
false);
WM_operator_type_set(op, ot_new);
viewops_data_end_navigation(C, vod);
return view3d_navigation_invoke_generic(C, vod, event, op->ptr, vod->nav_type);
}
int ret = view3d_navigation_modal(C, vod, event_code, event->xy);
if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
if (ret & OPERATOR_FINISHED) {
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
}
viewops_data_free(C, vod);
op->customdata = nullptr;
}
return ret;
}
void view3d_navigate_cancel_fn(bContext *C, wmOperator *op)
{
@ -312,8 +491,12 @@ bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3])
return is_set;
}
static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth)
static eViewOpsFlag viewops_flag_from_prefs(void)
{
const bool use_select = (U.uiflag & USER_ORBIT_SELECTION) != 0;
const bool use_depth = (U.uiflag & VIEWOPS_FLAG_DEPTH_NAVIGATE) != 0;
const bool use_zoom_to_mouse = (U.uiflag & USER_ZOOM_TO_MOUSEPOS) != 0;
enum eViewOpsFlag flag = VIEWOPS_FLAG_NONE;
if (use_select) {
flag |= VIEWOPS_FLAG_ORBIT_SELECT;
@ -321,20 +504,15 @@ static enum eViewOpsFlag viewops_flag_from_args(bool use_select, bool use_depth)
if (use_depth) {
flag |= VIEWOPS_FLAG_DEPTH_NAVIGATE;
}
if (use_zoom_to_mouse) {
flag |= VIEWOPS_FLAG_ZOOM_TO_MOUSE;
}
return flag;
}
enum eViewOpsFlag viewops_flag_from_prefs(void)
static void viewops_data_init_context(bContext *C, ViewOpsData *vod)
{
return viewops_flag_from_args((U.uiflag & USER_ORBIT_SELECTION) != 0,
(U.uiflag & USER_DEPTH_NAVIGATE) != 0);
}
ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOpsFlag viewops_flag)
{
ViewOpsData *vod = MEM_cnew<ViewOpsData>(__func__);
/* Store data. */
vod->bmain = CTX_data_main(C);
vod->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -343,13 +521,46 @@ ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOp
vod->region = CTX_wm_region(C);
vod->v3d = static_cast<View3D *>(vod->area->spacedata.first);
vod->rv3d = static_cast<RegionView3D *>(vod->region->regiondata);
}
static void viewops_data_init_navigation(bContext *C,
const wmEvent *event,
const eV3D_OpMode nav_type,
const bool use_cursor_init,
ViewOpsData *vod)
{
Depsgraph *depsgraph = vod->depsgraph;
RegionView3D *rv3d = vod->rv3d;
eViewOpsFlag viewops_flag = viewops_flag_from_prefs();
if (use_cursor_init) {
viewops_flag |= VIEWOPS_FLAG_USE_MOUSE_INIT;
}
switch (nav_type) {
case V3D_OP_MODE_ZOOM:
case V3D_OP_MODE_MOVE:
case V3D_OP_MODE_VIEW_PAN:
case V3D_OP_MODE_DOLLY:
viewops_flag &= ~VIEWOPS_FLAG_ORBIT_SELECT;
break;
case V3D_OP_MODE_ROTATE:
viewops_flag |= VIEWOPS_FLAG_PERSP_ENSURE;
break;
#ifdef WITH_INPUT_NDOF
case V3D_OP_MODE_NDOF_ORBIT:
case V3D_OP_MODE_NDOF_ORBIT_ZOOM:
viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
break;
#endif
default:
break;
}
/* Could do this more nicely. */
if ((viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) == 0) {
viewops_flag &= ~VIEWOPS_FLAG_DEPTH_NAVIGATE;
viewops_flag &= ~(VIEWOPS_FLAG_DEPTH_NAVIGATE | VIEWOPS_FLAG_ZOOM_TO_MOUSE);
}
/* we need the depth info before changing any viewport options */
@ -496,12 +707,27 @@ ViewOpsData *viewops_data_create(bContext *C, const wmEvent *event, enum eViewOp
vod->reverse = -1.0f;
}
rv3d->rflag |= RV3D_NAVIGATING;
vod->nav_type = nav_type;
vod->viewops_flag = viewops_flag;
/* Default. */
vod->use_dyn_ofs_ortho_correction = false;
rv3d->rflag |= RV3D_NAVIGATING;
}
ViewOpsData *viewops_data_create(bContext *C,
const wmEvent *event,
const eV3D_OpMode nav_type,
const bool use_cursor_init)
{
ViewOpsData *vod = MEM_cnew<ViewOpsData>(__func__);
viewops_data_init_context(C, vod);
viewops_data_init_navigation(C, event, nav_type, use_cursor_init, vod);
return vod;
}
void viewops_data_free(bContext *C, ViewOpsData *vod)
static void viewops_data_end_navigation(bContext *C, ViewOpsData *vod)
{
ARegion *region;
if (vod) {
@ -512,11 +738,7 @@ void viewops_data_free(bContext *C, ViewOpsData *vod)
WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer);
}
if (vod->init.dial) {
MEM_freeN(vod->init.dial);
}
MEM_freeN(vod);
MEM_SAFE_FREE(vod->init.dial);
}
else {
region = CTX_wm_region(C);
@ -527,6 +749,14 @@ void viewops_data_free(bContext *C, ViewOpsData *vod)
ED_region_tag_redraw(region);
}
void viewops_data_free(bContext *C, ViewOpsData *vod)
{
viewops_data_end_navigation(C, vod);
if (vod) {
MEM_freeN(vod);
}
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1658,10 +1888,10 @@ static const EnumPropertyItem prop_view_pan_items[] = {
{0, nullptr, 0, nullptr, nullptr},
};
static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int viewpan_invoke_impl(ViewOpsData *vod, PointerRNA *ptr)
{
int x = 0, y = 0;
int pandir = RNA_enum_get(op->ptr, "type");
int pandir = RNA_enum_get(ptr, "type");
if (pandir == V3D_VIEW_PANRIGHT) {
x = -32;
@ -1676,23 +1906,22 @@ static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
y = 25;
}
ViewOpsData *vod = viewops_data_create(
C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT));
viewmove_apply(vod, vod->prev.event_xy[0] + x, vod->prev.event_xy[1] + y);
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
viewops_data_free(C, vod);
return OPERATOR_FINISHED;
}
static int viewpan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return view3d_navigate_invoke_impl(C, op, event, V3D_OP_MODE_VIEW_PAN);
}
void VIEW3D_OT_view_pan(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Pan View Direction";
ot->description = "Pan the view in a given direction";
ot->idname = "VIEW3D_OT_view_pan";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_VIEW_PAN);
/* api callbacks */
ot->invoke = viewpan_invoke;

View File

@ -32,6 +32,19 @@ struct rcti;
struct wmEvent;
struct wmOperator;
typedef enum eV3D_OpMode {
V3D_OP_MODE_ZOOM = 0,
V3D_OP_MODE_ROTATE,
V3D_OP_MODE_MOVE,
V3D_OP_MODE_VIEW_PAN,
V3D_OP_MODE_VIEW_ROLL,
V3D_OP_MODE_DOLLY,
#ifdef WITH_INPUT_NDOF
V3D_OP_MODE_NDOF_ORBIT,
V3D_OP_MODE_NDOF_ORBIT_ZOOM,
#endif
} eV3D_OpMode;
enum eV3D_OpPropFlag {
V3D_OP_PROP_MOUSE_CO = (1 << 0),
V3D_OP_PROP_DELTA = (1 << 1),
@ -39,13 +52,13 @@ enum eV3D_OpPropFlag {
V3D_OP_PROP_USE_MOUSE_INIT = (1 << 3),
};
enum {
typedef enum eV3D_OpEvent {
VIEW_PASS = 0,
VIEW_APPLY,
VIEW_CONFIRM,
/** Only supported by some viewport operators. */
VIEW_CANCEL,
};
} eV3D_OpEvent;
/* NOTE: these defines are saved in keymap files, do not change values but just add new ones */
enum {
@ -58,7 +71,7 @@ enum {
VIEWROT_MODAL_SWITCH_ROTATE = 6,
};
enum eViewOpsFlag {
typedef enum eViewOpsFlag {
VIEWOPS_FLAG_NONE = 0,
/** When enabled, rotate around the selection. */
VIEWOPS_FLAG_ORBIT_SELECT = (1 << 0),
@ -72,8 +85,10 @@ enum eViewOpsFlag {
VIEWOPS_FLAG_PERSP_ENSURE = (1 << 2),
/** When set, ignore any options that depend on initial cursor location. */
VIEWOPS_FLAG_USE_MOUSE_INIT = (1 << 3),
};
ENUM_OPERATORS(eViewOpsFlag, VIEWOPS_FLAG_USE_MOUSE_INIT);
VIEWOPS_FLAG_ZOOM_TO_MOUSE = (1 << 4),
} eViewOpsFlag;
ENUM_OPERATORS(eViewOpsFlag, VIEWOPS_FLAG_ZOOM_TO_MOUSE);
/** Generic View Operator Custom-Data */
typedef struct ViewOpsData {
@ -146,6 +161,9 @@ typedef struct ViewOpsData {
float viewquat[4];
} curr;
eV3D_OpMode nav_type;
eViewOpsFlag viewops_flag;
float reverse;
bool axis_snap; /* view rotate only */
@ -165,13 +183,22 @@ typedef struct ViewOpsData {
/* view3d_navigate.cc */
/**
* Navigation operators that share the `ViewOpsData` utility.
*/
const char *viewops_operator_idname_get(eV3D_OpMode nav_type);
bool view3d_location_poll(struct bContext *C);
bool view3d_rotation_poll(struct bContext *C);
bool view3d_zoom_or_dolly_poll(struct bContext *C);
int view3d_navigate_invoke_impl(bContext *C,
wmOperator *op,
const wmEvent *event,
const eV3D_OpMode nav_type);
int view3d_navigate_modal_fn(bContext *C, wmOperator *op, const wmEvent *event);
void view3d_navigate_cancel_fn(struct bContext *C, struct wmOperator *op);
enum eViewOpsFlag viewops_flag_from_prefs(void);
void calctrackballvec(const struct rcti *rect, const int event_xy[2], float r_dir[3]);
void viewmove_apply(ViewOpsData *vod, int x, int y);
void viewmove_apply_reset(ViewOpsData *vod);
@ -193,9 +220,10 @@ void viewops_data_free(struct bContext *C, ViewOpsData *vod);
/**
* Allocate, fill in context pointers and calculate the values for #ViewOpsData
*/
ViewOpsData *viewops_data_create(struct bContext *C,
const struct wmEvent *event,
enum eViewOpsFlag viewops_flag);
ViewOpsData *viewops_data_create(bContext *C,
const wmEvent *event,
const eV3D_OpMode nav_type,
const bool use_cursor_init);
void VIEW3D_OT_view_all(struct wmOperatorType *ot);
void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
@ -219,6 +247,11 @@ void VIEW3D_OT_fly(struct wmOperatorType *ot);
/* view3d_navigate_move.c */
int viewmove_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2]);
int viewmove_invoke_impl(ViewOpsData *vod, const wmEvent *event);
void viewmove_modal_keymap(struct wmKeyConfig *keyconf);
void VIEW3D_OT_move(struct wmOperatorType *ot);
@ -249,6 +282,11 @@ void VIEW3D_OT_view_roll(struct wmOperatorType *ot);
/* view3d_navigate_rotate.c */
int viewrotate_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2]);
int viewrotate_invoke_impl(ViewOpsData *vod, const wmEvent *event);
void viewrotate_modal_keymap(struct wmKeyConfig *keyconf);
void VIEW3D_OT_rotate(struct wmOperatorType *ot);
@ -326,6 +364,11 @@ void VIEW3D_OT_walk(struct wmOperatorType *ot);
/* view3d_navigate_zoom.c */
int viewzoom_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2]);
int viewzoom_invoke_impl(bContext *C, ViewOpsData *vod, const wmEvent *event, PointerRNA *ptr);
void viewzoom_modal_keymap(struct wmKeyConfig *keyconf);
void VIEW3D_OT_zoom(struct wmOperatorType *ot);

View File

@ -246,11 +246,7 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
vod = op->customdata = viewops_data_create(
C,
event,
(viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
(use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
vod = op->customdata = viewops_data_create(C, event, V3D_OP_MODE_DOLLY, use_cursor_init);
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
@ -314,7 +310,7 @@ void VIEW3D_OT_dolly(wmOperatorType *ot)
/* identifiers */
ot->name = "Dolly View";
ot->description = "Dolly in/out in the view";
ot->idname = "VIEW3D_OT_dolly";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_DOLLY);
/* api callbacks */
ot->invoke = viewdolly_invoke;

View File

@ -49,52 +49,17 @@ void viewmove_modal_keymap(wmKeyConfig *keyconf)
WM_modalkeymap_assign(keymap, "VIEW3D_OT_move");
}
static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
int viewmove_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2])
{
ViewOpsData *vod = op->customdata;
short event_code = VIEW_PASS;
bool use_autokey = false;
int ret = OPERATOR_RUNNING_MODAL;
/* Execute the events. */
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CONFIRM:
event_code = VIEW_CONFIRM;
break;
case VIEW_MODAL_CANCEL:
event_code = VIEW_CANCEL;
break;
case VIEWROT_MODAL_SWITCH_ZOOM:
WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
case VIEWROT_MODAL_SWITCH_ROTATE:
WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
}
}
else {
if (event->type == MOUSEMOVE) {
event_code = VIEW_APPLY;
}
else if (event->type == vod->init.event_type) {
if (event->val == KM_RELEASE) {
event_code = VIEW_CONFIRM;
}
}
else if (event->type == EVT_ESCKEY) {
if (event->val == KM_PRESS) {
event_code = VIEW_CANCEL;
}
}
}
switch (event_code) {
case VIEW_APPLY: {
viewmove_apply(vod, event->xy[0], event->xy[1]);
viewmove_apply(vod, xy[0], xy[1]);
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
use_autokey = true;
}
@ -111,66 +76,47 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event)
ret = OPERATOR_CANCELLED;
break;
}
case VIEW_PASS:
break;
}
if (use_autokey) {
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
}
if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
if (ret & OPERATOR_FINISHED) {
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
}
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
return ret;
}
static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int viewmove_invoke_impl(ViewOpsData *vod, const wmEvent *event)
{
ViewOpsData *vod;
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
vod = op->customdata = viewops_data_create(
C,
event,
(viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
(use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
vod = op->customdata;
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
if (event->type == MOUSEPAN) {
/* invert it, trackpad scroll follows same principle as 2d windows this way */
viewmove_apply(
vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
eV3D_OpEvent event_code = event->type == MOUSEPAN ? VIEW_CONFIRM : VIEW_PASS;
if (event_code == VIEW_CONFIRM) {
/* Invert it, trackpad scroll follows same principle as 2d windows this way. */
int mx = 2 * event->xy[0] - event->prev_xy[0];
int my = 2 * event->xy[1] - event->prev_xy[1];
viewmove_apply(vod, mx, my);
return OPERATOR_FINISHED;
}
/* add temp handler */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return view3d_navigate_invoke_impl(C, op, event, V3D_OP_MODE_MOVE);
}
void VIEW3D_OT_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Pan View";
ot->description = "Move the view";
ot->idname = "VIEW3D_OT_move";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_MOVE);
/* api callbacks */
ot->invoke = viewmove_invoke;
ot->modal = viewmove_modal;
ot->modal = view3d_navigate_modal_fn;
ot->poll = view3d_location_poll;
ot->cancel = view3d_navigate_cancel_fn;

View File

@ -435,8 +435,7 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const wmNDOFMotionData *ndof = event->customdata;
vod = op->customdata = viewops_data_create(
C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE));
vod = op->customdata = viewops_data_create(C, event, V3D_OP_MODE_NDOF_ORBIT, false);
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
@ -522,8 +521,7 @@ static int ndof_orbit_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev
const wmNDOFMotionData *ndof = event->customdata;
vod = op->customdata = viewops_data_create(
C, event, (viewops_flag_from_prefs() & ~VIEWOPS_FLAG_DEPTH_NAVIGATE));
vod = op->customdata = viewops_data_create(C, event, V3D_OP_MODE_NDOF_ORBIT_ZOOM, false);
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);

View File

@ -253,7 +253,7 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* makes op->customdata */
vod = op->customdata = viewops_data_create(C, event, viewops_flag_from_prefs());
vod = op->customdata = viewops_data_create(C, event, V3D_OP_MODE_VIEW_ROLL, false);
vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct),
BLI_rcti_cent_y(&vod->region->winrct)},
FLT_EPSILON);
@ -287,7 +287,7 @@ void VIEW3D_OT_view_roll(wmOperatorType *ot)
/* identifiers */
ot->name = "View Roll";
ot->description = "Roll the view";
ot->idname = "VIEW3D_OT_view_roll";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_VIEW_ROLL);
/* api callbacks */
ot->invoke = viewroll_invoke;

View File

@ -293,60 +293,17 @@ static void viewrotate_apply(ViewOpsData *vod, const int event_xy[2])
ED_region_tag_redraw(vod->region);
}
static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
int viewrotate_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2])
{
ViewOpsData *vod = op->customdata;
short event_code = VIEW_PASS;
bool use_autokey = false;
int ret = OPERATOR_RUNNING_MODAL;
/* Execute the events. */
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CONFIRM:
event_code = VIEW_CONFIRM;
break;
case VIEW_MODAL_CANCEL:
event_code = VIEW_CANCEL;
break;
case VIEWROT_MODAL_AXIS_SNAP_ENABLE:
vod->axis_snap = true;
event_code = VIEW_APPLY;
break;
case VIEWROT_MODAL_AXIS_SNAP_DISABLE:
vod->rv3d->persp = vod->init.persp_with_auto_persp_applied;
vod->axis_snap = false;
event_code = VIEW_APPLY;
break;
case VIEWROT_MODAL_SWITCH_ZOOM:
WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
case VIEWROT_MODAL_SWITCH_MOVE:
WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
}
}
else {
if (event->type == MOUSEMOVE) {
event_code = VIEW_APPLY;
}
else if (event->type == vod->init.event_type) {
if (event->val == KM_RELEASE) {
event_code = VIEW_CONFIRM;
}
}
else if (event->type == EVT_ESCKEY) {
if (event->val == KM_PRESS) {
event_code = VIEW_CANCEL;
}
}
}
switch (event_code) {
case VIEW_APPLY: {
viewrotate_apply(vod, event->xy);
viewrotate_apply(vod, xy);
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
use_autokey = true;
}
@ -376,84 +333,60 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event)
ret = OPERATOR_CANCELLED;
break;
}
case VIEW_PASS:
break;
}
if (use_autokey) {
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true);
}
if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
if (ret & OPERATOR_FINISHED) {
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
}
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
return ret;
}
static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int viewrotate_invoke_impl(ViewOpsData *vod, const wmEvent *event)
{
ViewOpsData *vod;
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
/* makes op->customdata */
vod = op->customdata = viewops_data_create(
C,
event,
viewops_flag_from_prefs() | VIEWOPS_FLAG_PERSP_ENSURE |
(use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
if (vod->use_dyn_ofs && (vod->rv3d->is_persp == false)) {
vod->use_dyn_ofs_ortho_correction = true;
}
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
eV3D_OpEvent event_code = ELEM(event->type, MOUSEROTATE, MOUSEPAN) ? VIEW_CONFIRM : VIEW_PASS;
if (ELEM(event->type, MOUSEPAN, MOUSEROTATE)) {
/* Rotate direction we keep always same */
int event_xy[2];
if (event_code == VIEW_CONFIRM) {
/* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
const bool is_inverted = (event->flag & WM_EVENT_SCROLL_INVERT) &&
(event->type != MOUSEROTATE);
if (event->type == MOUSEPAN) {
if (event->flag & WM_EVENT_SCROLL_INVERT) {
event_xy[0] = 2 * event->xy[0] - event->prev_xy[0];
event_xy[1] = 2 * event->xy[1] - event->prev_xy[1];
}
else {
copy_v2_v2_int(event_xy, event->prev_xy);
}
int m_xy[2];
if (is_inverted) {
m_xy[0] = 2 * event->xy[0] - event->prev_xy[0];
m_xy[1] = 2 * event->xy[1] - event->prev_xy[1];
}
else {
/* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */
copy_v2_v2_int(event_xy, event->prev_xy);
copy_v2_v2_int(m_xy, event->prev_xy);
}
viewrotate_apply(vod, event_xy);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
viewrotate_apply(vod, m_xy);
return OPERATOR_FINISHED;
}
/* add temp handler */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
return view3d_navigate_invoke_impl(C, op, event, V3D_OP_MODE_ROTATE);
}
void VIEW3D_OT_rotate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Rotate View";
ot->description = "Rotate the view";
ot->idname = "VIEW3D_OT_rotate";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_ROTATE);
/* api callbacks */
ot->invoke = viewrotate_invoke;
ot->modal = viewrotate_modal;
ot->modal = view3d_navigate_modal_fn;
ot->poll = view3d_rotation_poll;
ot->cancel = view3d_navigate_cancel_fn;

View File

@ -331,9 +331,10 @@ static void viewzoom_apply_3d(ViewOpsData *vod,
static void viewzoom_apply(ViewOpsData *vod,
const int xy[2],
const eViewZoom_Style viewzoom,
const bool zoom_invert,
const bool zoom_to_pos)
const bool zoom_invert)
{
const bool zoom_to_pos = (vod->viewops_flag & VIEWOPS_FLAG_ZOOM_TO_MOUSE) != 0;
if ((vod->rv3d->persp == RV3D_CAMOB) &&
(vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) {
viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert, zoom_to_pos);
@ -343,62 +344,17 @@ static void viewzoom_apply(ViewOpsData *vod,
}
}
static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
int viewzoom_modal_impl(bContext *C,
ViewOpsData *vod,
const eV3D_OpEvent event_code,
const int xy[2])
{
ViewOpsData *vod = op->customdata;
short event_code = VIEW_PASS;
bool use_autokey = false;
int ret = OPERATOR_RUNNING_MODAL;
/* Execute the events. */
if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case VIEW_MODAL_CONFIRM:
event_code = VIEW_CONFIRM;
break;
case VIEW_MODAL_CANCEL:
event_code = VIEW_CANCEL;
break;
case VIEWROT_MODAL_SWITCH_MOVE:
WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
case VIEWROT_MODAL_SWITCH_ROTATE:
WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event);
event_code = VIEW_CONFIRM;
break;
}
}
else {
if (event->type == MOUSEMOVE) {
event_code = VIEW_APPLY;
}
else if (event->type == TIMER) {
if (event->customdata == vod->timer) {
/* Continuous zoom. */
event_code = VIEW_APPLY;
}
}
else if (event->type == vod->init.event_type) {
if (event->val == KM_RELEASE) {
event_code = VIEW_CONFIRM;
}
}
else if (event->type == EVT_ESCKEY) {
if (event->val == KM_PRESS) {
event_code = VIEW_CANCEL;
}
}
}
switch (event_code) {
case VIEW_APPLY: {
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
viewzoom_apply(vod,
event->xy,
(eViewZoom_Style)U.viewzoom,
(U.uiflag & USER_ZOOM_INVERT) != 0,
(use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
viewzoom_apply(vod, xy, (eViewZoom_Style)U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0);
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
use_autokey = true;
}
@ -423,64 +379,33 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event)
ret = OPERATOR_CANCELLED;
break;
}
case VIEW_PASS:
break;
}
if (use_autokey) {
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
}
if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
if (ret & OPERATOR_FINISHED) {
ED_view3d_camera_lock_undo_push(op->type->name, vod->v3d, vod->rv3d, C);
}
viewops_data_free(C, op->customdata);
op->customdata = NULL;
}
return ret;
}
static int viewzoom_exec(bContext *C, wmOperator *op)
static void view_zoom_apply_step(bContext *C,
Depsgraph *depsgraph,
Scene *scene,
ScrArea *area,
ARegion *region,
const int delta,
const int zoom_xy[2])
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
View3D *v3d;
RegionView3D *rv3d;
ScrArea *area;
ARegion *region;
View3D *v3d = area->spacedata.first;
RegionView3D *rv3d = region->regiondata;
bool use_cam_zoom;
float dist_range[2];
const int delta = RNA_int_get(op->ptr, "delta");
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
if (op->customdata) {
ViewOpsData *vod = op->customdata;
area = vod->area;
region = vod->region;
}
else {
area = CTX_wm_area(C);
region = CTX_wm_region(C);
}
v3d = area->spacedata.first;
rv3d = region->regiondata;
use_cam_zoom = (rv3d->persp == RV3D_CAMOB) &&
!(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d));
int zoom_xy_buf[2];
const int *zoom_xy = NULL;
if (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)) {
zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") :
region->winx / 2;
zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") :
region->winy / 2;
zoom_xy = zoom_xy_buf;
}
ED_view3d_dist_range_get(v3d, dist_range);
if (delta < 0) {
@ -514,73 +439,94 @@ static int viewzoom_exec(bContext *C, wmOperator *op)
ED_view3d_camera_lock_autokey(v3d, rv3d, C, false, true);
ED_region_tag_redraw(region);
}
static int viewzoom_exec(bContext *C, wmOperator *op)
{
BLI_assert(op->customdata == NULL);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ScrArea *area = CTX_wm_area(C);
ARegion *region = CTX_wm_region(C);
View3D *v3d = area->spacedata.first;
RegionView3D *rv3d = region->regiondata;
const int delta = RNA_int_get(op->ptr, "delta");
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
int zoom_xy_buf[2];
const int *zoom_xy = NULL;
const bool do_zoom_to_mouse_pos = (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS));
if (do_zoom_to_mouse_pos) {
zoom_xy_buf[0] = RNA_struct_property_is_set(op->ptr, "mx") ? RNA_int_get(op->ptr, "mx") :
region->winx / 2;
zoom_xy_buf[1] = RNA_struct_property_is_set(op->ptr, "my") ? RNA_int_get(op->ptr, "my") :
region->winy / 2;
zoom_xy = zoom_xy_buf;
}
view_zoom_apply_step(C, depsgraph, scene, area, region, delta, zoom_xy);
ED_view3d_camera_lock_undo_grouped_push(op->type->name, v3d, rv3d, C);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
return OPERATOR_FINISHED;
}
int viewzoom_invoke_impl(bContext *C, ViewOpsData *vod, const wmEvent *event, PointerRNA *ptr)
{
eV3D_OpEvent event_code = ELEM(event->type, MOUSEZOOM, MOUSEPAN) ? VIEW_CONFIRM : VIEW_PASS;
if (event_code == VIEW_CONFIRM) {
int xy[2];
PropertyRNA *prop;
prop = RNA_struct_find_property(ptr, "mx");
xy[0] = RNA_property_is_set(ptr, prop) ? RNA_property_int_get(ptr, prop) : event->xy[0];
prop = RNA_struct_find_property(ptr, "my");
xy[1] = RNA_property_is_set(ptr, prop) ? RNA_property_int_get(ptr, prop) : event->xy[1];
const int delta = RNA_int_get(ptr, "delta");
if (delta) {
const bool do_zoom_to_mouse_pos = (vod->viewops_flag & VIEWOPS_FLAG_ZOOM_TO_MOUSE) != 0;
view_zoom_apply_step(C,
vod->depsgraph,
vod->scene,
vod->area,
vod->region,
delta,
do_zoom_to_mouse_pos ? xy : NULL);
return OPERATOR_FINISHED;
}
if (U.uiflag & USER_ZOOM_HORIZ) {
vod->init.event_xy[0] = vod->prev.event_xy[0] = xy[0];
}
else {
/* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + xy[0] -
event->prev_xy[0];
}
viewzoom_apply(vod, event->prev_xy, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0);
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
return OPERATOR_FINISHED;
}
if (U.viewzoom == USER_ZOOM_CONTINUE) {
/* needs a timer to continue redrawing */
vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
vod->prev.time = PIL_check_seconds_timer();
}
return OPERATOR_RUNNING_MODAL;
}
/* viewdolly_invoke() copied this function, changes here may apply there */
static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
ViewOpsData *vod;
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
vod = op->customdata = viewops_data_create(
C,
event,
(viewops_flag_from_prefs() & ~VIEWOPS_FLAG_ORBIT_SELECT) |
(use_cursor_init ? VIEWOPS_FLAG_USE_MOUSE_INIT : 0));
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
/* if one or the other zoom position aren't set, set from event */
if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) {
RNA_int_set(op->ptr, "mx", event->xy[0]);
RNA_int_set(op->ptr, "my", event->xy[1]);
}
if (RNA_struct_property_is_set(op->ptr, "delta")) {
viewzoom_exec(C, op);
}
else {
if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
if (U.uiflag & USER_ZOOM_HORIZ) {
vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
}
else {
/* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */
vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] -
event->prev_xy[0];
}
viewzoom_apply(vod,
event->prev_xy,
USER_ZOOM_DOLLY,
(U.uiflag & USER_ZOOM_INVERT) != 0,
(use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS)));
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true);
viewops_data_free(C, op->customdata);
op->customdata = NULL;
return OPERATOR_FINISHED;
}
if (U.viewzoom == USER_ZOOM_CONTINUE) {
/* needs a timer to continue redrawing */
vod->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
vod->prev.time = PIL_check_seconds_timer();
}
/* add temp handler */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_FINISHED;
return view3d_navigate_invoke_impl(C, op, event, V3D_OP_MODE_ZOOM);
}
void VIEW3D_OT_zoom(wmOperatorType *ot)
@ -588,12 +534,12 @@ void VIEW3D_OT_zoom(wmOperatorType *ot)
/* identifiers */
ot->name = "Zoom View";
ot->description = "Zoom in/out in the view";
ot->idname = "VIEW3D_OT_zoom";
ot->idname = viewops_operator_idname_get(V3D_OP_MODE_ZOOM);
/* api callbacks */
ot->invoke = viewzoom_invoke;
ot->exec = viewzoom_exec;
ot->modal = viewzoom_modal;
ot->modal = view3d_navigate_modal_fn;
ot->poll = view3d_zoom_or_dolly_poll;
ot->cancel = view3d_navigate_cancel_fn;

View File

@ -139,9 +139,14 @@ static int an_stringdec(const char *string, char *head, char *tail, ushort *numl
return true;
}
static void an_stringenc(char *string, const char *head, const char *tail, ushort numlen, int pic)
static void an_stringenc(char *string,
const size_t string_maxncpy,
const char *head,
const char *tail,
ushort numlen,
int pic)
{
BLI_path_sequence_encode(string, head, tail, numlen, pic);
BLI_path_sequence_encode(string, string_maxncpy, head, tail, numlen, pic);
}
#ifdef WITH_AVI
@ -1614,7 +1619,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
case ANIM_SEQUENCE:
pic = an_stringdec(anim->first, head, tail, &digits);
pic += position;
an_stringenc(anim->name, head, tail, digits, pic);
an_stringenc(anim->name, sizeof(anim->name), head, tail, digits, pic);
ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace);
if (ibuf) {
anim->cur_position = position;

View File

@ -519,7 +519,10 @@ static std::string float3_to_string(const float3 &numbers)
MTLWriter::MTLWriter(const char *obj_filepath) noexcept(false)
{
mtl_filepath_ = obj_filepath;
const bool ok = BLI_path_extension_replace(mtl_filepath_.data(), FILE_MAX, ".mtl");
/* It only makes sense to replace this extension if it's at least as long as the existing one. */
BLI_assert(strlen(BLI_path_extension(obj_filepath)) == 4);
const bool ok = BLI_path_extension_replace(
mtl_filepath_.data(), mtl_filepath_.size() + 1, ".mtl");
if (!ok) {
throw std::system_error(ENAMETOOLONG, std::system_category(), "");
}

View File

@ -155,8 +155,8 @@ static std::string get_image_filepath(const bNode *tex_node)
char head[FILE_MAX], tail[FILE_MAX];
ushort numlen;
int framenr = static_cast<NodeTexImage *>(tex_node->storage)->iuser.framenr;
BLI_path_sequence_decode(path, head, tail, &numlen);
BLI_path_sequence_encode(path, head, tail, numlen, framenr);
BLI_path_sequence_decode(path, head, sizeof(head), tail, sizeof(tail), &numlen);
BLI_path_sequence_encode(path, sizeof(path), head, tail, numlen, framenr);
}
return path;

View File

@ -279,7 +279,8 @@ class obj_exporter_regression_test : public obj_exporter_test {
strncpy(params.filepath, out_file_path.c_str(), FILE_MAX - 1);
params.blen_filepath = bfile->main->filepath;
std::string golden_file_path = blender::tests::flags_test_asset_dir() + SEP_STR + golden_obj;
BLI_split_dir_part(golden_file_path.c_str(), params.file_base_for_tests, PATH_MAX);
BLI_split_dir_part(
golden_file_path.c_str(), params.file_base_for_tests, sizeof(params.file_base_for_tests));
export_frame(depsgraph, params, out_file_path.c_str());
std::string output_str = read_temp_file_in_string(out_file_path);

View File

@ -667,8 +667,12 @@ static void build_pict_list_ex(
char filepath[FILE_MAX];
BLI_strncpy(filepath, first, sizeof(filepath));
fp_framenr = BLI_path_sequence_decode(
filepath, fp_decoded.head, fp_decoded.tail, &fp_decoded.digits);
fp_framenr = BLI_path_sequence_decode(filepath,
fp_decoded.head,
sizeof(fp_decoded.head),
fp_decoded.tail,
sizeof(fp_decoded.tail),
&fp_decoded.digits);
pupdate_time();
ptottime = 1.0;
@ -774,8 +778,12 @@ static void build_pict_list_ex(
/* create a new filepath each time */
fp_framenr += fstep;
BLI_path_sequence_encode(
filepath, fp_decoded.head, fp_decoded.tail, fp_decoded.digits, fp_framenr);
BLI_path_sequence_encode(filepath,
sizeof(filepath),
fp_decoded.head,
fp_decoded.tail,
fp_decoded.digits,
fp_framenr);
while ((has_event = GHOST_ProcessEvents(g_WS.ghost_system, false))) {
GHOST_DispatchEvents(g_WS.ghost_system);