Versioning function to replace legacy instancing panel by geometry node modifier #105494

Open
Iliya Katushenock wants to merge 44 commits from mod_moder/blender:instances into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
114 changed files with 1879 additions and 1302 deletions
Showing only changes of commit 95d7a28847 - 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

@ -653,6 +653,11 @@ class MutableAttributeAccessor : public AttributeAccessor {
return {};
}
/**
* Replace the existing attribute with a new one with a different name.
*/
bool rename(const AttributeIDRef &old_attribute_id, const AttributeIDRef &new_attribute_id);
/**
* Create a new attribute.
* \return True, when a new attribute has been created. False, when it's not possible to create

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

@ -16,7 +16,6 @@
#include "BKE_customdata.h"
#include "BKE_mesh_types.h"
struct BLI_Stack;
struct BMesh;
struct BMeshCreateParams;
struct BMeshFromMeshParams;
@ -44,13 +43,6 @@ struct Scene;
extern "C" {
#endif
/* setting zero so we can catch bugs in OpenMP/BMesh */
#ifdef DEBUG
# define BKE_MESH_OMP_LIMIT 0
#else
# define BKE_MESH_OMP_LIMIT 10000
#endif
/* mesh_runtime.cc */
/**
@ -397,9 +389,6 @@ typedef struct MLoopNorSpace {
* - BMLoop pointers. */
struct LinkNode *loops;
char flags;
/** To be used for extended processing related to loop normal spaces (aka smooth fans). */
void *user_data;
} MLoopNorSpace;
/**
* MLoopNorSpace.flags
@ -449,6 +438,9 @@ void BKE_lnor_spacearr_tls_join(MLoopNorSpaceArray *lnors_spacearr,
MLoopNorSpaceArray *lnors_spacearr_tls);
MLoopNorSpace *BKE_lnor_space_create(MLoopNorSpaceArray *lnors_spacearr);
#ifdef __cplusplus
/**
* Should only be called once.
* Beware, this modifies ref_vec and other_vec in place!
@ -459,7 +451,10 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
const float lnor[3],
float vec_ref[3],
float vec_other[3],
struct BLI_Stack *edge_vectors);
blender::Span<blender::float3> edge_vectors);
#endif
/**
* Add a new given loop to given lnor_space.
* Depending on \a lnor_space->data_type, we expect \a bm_loop to be a pointer to BMLoop struct

View File

@ -911,6 +911,41 @@ GSpanAttributeWriter MutableAttributeAccessor::lookup_or_add_for_write_only_span
return {};
}
bool MutableAttributeAccessor::rename(const AttributeIDRef &old_attribute_id,
const AttributeIDRef &new_attribute_id)
{
if (old_attribute_id == new_attribute_id) {
return true;
}
if (this->contains(new_attribute_id)) {
return false;
}
const GAttributeReader old_attribute = this->lookup(old_attribute_id);
if (!old_attribute) {
return false;
}
const eCustomDataType type = cpp_type_to_custom_data_type(old_attribute.varray.type());
if (old_attribute.sharing_info != nullptr && old_attribute.varray.is_span()) {
if (!this->add(new_attribute_id,
old_attribute.domain,
type,
AttributeInitShared{old_attribute.varray.get_internal_span().data(),
*old_attribute.sharing_info})) {
return false;
}
}
else {
if (!this->add(new_attribute_id,
old_attribute.domain,
type,
AttributeInitVArray{old_attribute.varray})) {
return false;
}
}
this->remove(old_attribute_id);
return true;
}
fn::GField AttributeValidator::validate_field_if_necessary(const fn::GField &field) const
{
if (function) {

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

@ -281,7 +281,7 @@ void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType
}
}
/* A potential performance optimization is only counting the changed indices. */
this->curve_types_for_write().fill_indices(selection, type);
this->curve_types_for_write().fill_indices(selection.indices(), type);
this->update_curve_types();
this->tag_topology_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

@ -19,13 +19,10 @@
#include "BLI_array_utils.hh"
#include "BLI_bit_vector.hh"
#include "BLI_linklist.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_memarena.h"
#include "BLI_span.hh"
#include "BLI_stack.h"
#include "BLI_task.h"
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BLI_utildefines.h"
@ -474,7 +471,7 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
const float lnor[3],
float vec_ref[3],
float vec_other[3],
BLI_Stack *edge_vectors)
const blender::Span<blender::float3> edge_vectors)
{
const float pi2 = float(M_PI) * 2.0f;
float tvec[3], dtp;
@ -486,31 +483,24 @@ void BKE_lnor_space_define(MLoopNorSpace *lnor_space,
/* If vec_ref or vec_other are too much aligned with lnor, we can't build lnor space,
* tag it as invalid and abort. */
lnor_space->ref_alpha = lnor_space->ref_beta = 0.0f;
if (edge_vectors) {
BLI_stack_clear(edge_vectors);
}
return;
}
copy_v3_v3(lnor_space->vec_lnor, lnor);
/* Compute ref alpha, average angle of all available edge vectors to lnor. */
if (edge_vectors) {
if (!edge_vectors.is_empty()) {
float alpha = 0.0f;
int count = 0;
while (!BLI_stack_is_empty(edge_vectors)) {
const float *vec = (const float *)BLI_stack_peek(edge_vectors);
for (const blender::float3 &vec : edge_vectors) {
alpha += saacosf(dot_v3v3(vec, lnor));
BLI_stack_discard(edge_vectors);
count++;
}
/* This piece of code shall only be called for more than one loop. */
/* NOTE: In theory, this could be `count > 2`,
* but there is one case where we only have two edges for two loops:
* a smooth vertex with only two edges and two faces (our Monkey's nose has that, e.g.).
*/
BLI_assert(count >= 2); /* This piece of code shall only be called for more than one loop. */
lnor_space->ref_alpha = alpha / float(count);
BLI_assert(edge_vectors.size() >= 2);
lnor_space->ref_alpha = alpha / float(edge_vectors.size());
}
else {
lnor_space->ref_alpha = (saacosf(dot_v3v3(vec_ref, lnor)) +
@ -666,23 +656,6 @@ void BKE_lnor_space_custom_normal_to_data(const MLoopNorSpace *lnor_space,
namespace blender::bke::mesh {
#define LOOP_SPLIT_TASK_BLOCK_SIZE 1024
struct LoopSplitTaskData {
enum class Type : int8_t {
BlockEnd = 0, /* Set implicitly by calloc. */
Fan = 1,
Single = 2,
};
/** We have to create those outside of tasks, since #MemArena is not thread-safe. */
MLoopNorSpace *lnor_space;
int ml_curr_index;
int poly_index;
Type flag;
};
struct LoopSplitTaskDataCommon {
/* Read/write.
* Note we do not need to protect it, though, since two different tasks will *always* affect
@ -855,54 +828,39 @@ static void loop_manifold_fan_around_vert_next(const Span<int> corner_verts,
}
}
static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopSplitTaskData *data)
static void lnor_space_for_single_fan(LoopSplitTaskDataCommon *common_data,
const int ml_curr_index,
MLoopNorSpace *lnor_space)
{
MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
const Span<short2> clnors_data = common_data->clnors_data;
const Span<float3> positions = common_data->positions;
const Span<int2> edges = common_data->edges;
const OffsetIndices polys = common_data->polys;
const Span<int> corner_verts = common_data->corner_verts;
const Span<int> corner_edges = common_data->corner_edges;
const Span<int> loop_to_poly = common_data->loop_to_poly;
const Span<float3> poly_normals = common_data->poly_normals;
MutableSpan<float3> loop_normals = common_data->loop_normals;
MLoopNorSpace *lnor_space = data->lnor_space;
const int ml_curr_index = data->ml_curr_index;
const int poly_index = data->poly_index;
loop_normals[ml_curr_index] = poly_normals[loop_to_poly[ml_curr_index]];
/* Simple case (both edges around that vertex are sharp in current polygon),
* this loop just takes its poly normal.
*/
loop_normals[ml_curr_index] = poly_normals[poly_index];
if (MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr) {
const Span<float3> positions = common_data->positions;
const Span<int2> edges = common_data->edges;
const OffsetIndices polys = common_data->polys;
const Span<int> corner_verts = common_data->corner_verts;
const Span<int> corner_edges = common_data->corner_edges;
const Span<short2> clnors_data = common_data->clnors_data;
#if 0
printf("BASIC: handling loop %d / edge %d / vert %d / poly %d\n",
ml_curr_index,
loops[ml_curr_index].e,
loops[ml_curr_index].v,
poly_index);
#endif
/* If needed, generate this (simple!) lnor space. */
if (lnors_spacearr) {
float vec_curr[3], vec_prev[3];
const int poly_index = loop_to_poly[ml_curr_index];
const int ml_prev_index = mesh::poly_corner_prev(polys[poly_index], ml_curr_index);
/* The vertex we are "fanning" around. */
const int vert_pivot = corner_verts[ml_curr_index];
const int2 &edge = edges[corner_edges[ml_curr_index]];
const int vert_2 = edge_other_vert(edge, vert_pivot);
const int2 &edge_prev = edges[corner_edges[ml_prev_index]];
const int vert_3 = edge_other_vert(edge_prev, vert_pivot);
const int vert_2 = edge_other_vert(edges[corner_edges[ml_curr_index]], vert_pivot);
const int vert_3 = edge_other_vert(edges[corner_edges[ml_prev_index]], vert_pivot);
sub_v3_v3v3(vec_curr, positions[vert_2], positions[vert_pivot]);
normalize_v3(vec_curr);
sub_v3_v3v3(vec_prev, positions[vert_3], positions[vert_pivot]);
normalize_v3(vec_prev);
BKE_lnor_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, nullptr);
BKE_lnor_space_define(lnor_space, loop_normals[ml_curr_index], vec_curr, vec_prev, {});
/* We know there is only one loop in this space, no need to create a link-list in this case. */
BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, ml_curr_index, nullptr, true);
@ -914,8 +872,9 @@ static void split_loop_nor_single_do(LoopSplitTaskDataCommon *common_data, LoopS
}
static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
LoopSplitTaskData *data,
BLI_Stack *edge_vectors)
const int ml_curr_index,
MLoopNorSpace *lnor_space,
Vector<float3> *edge_vectors)
{
MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
MutableSpan<float3> loop_normals = common_data->loop_normals;
@ -930,12 +889,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
const Span<int> loop_to_poly = common_data->loop_to_poly;
const Span<float3> poly_normals = common_data->poly_normals;
MLoopNorSpace *lnor_space = data->lnor_space;
#if 0 /* Not needed for 'fan' loops. */
float(*lnor)[3] = data->lnor;
#endif
const int ml_curr_index = data->ml_curr_index;
const int poly_index = data->poly_index;
const int poly_index = loop_to_poly[ml_curr_index];
const int ml_prev_index = poly_corner_prev(polys[poly_index], ml_curr_index);
/* Sigh! we have to fan around current vertex, until we find the other non-smooth edge,
@ -950,7 +904,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
const int2 &edge_orig = edges[corner_edges[ml_curr_index]];
float vec_curr[3], vec_prev[3], vec_org[3];
float lnor[3] = {0.0f, 0.0f, 0.0f};
float3 lnor(0.0f);
/* We validate clnors data on the fly - cheapest way to do! */
int clnors_avg[2] = {0, 0};
@ -958,10 +912,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
int clnors_count = 0;
bool clnors_invalid = false;
/* Temp loop normal stack. */
BLI_SMALLSTACK_DECLARE(normal, float *);
/* Temp clnors stack. */
BLI_SMALLSTACK_DECLARE(clnors, short *);
Vector<int, 8> processed_corners;
/* `mlfan_vert_index` the loop of our current edge might not be the loop of our current vertex!
*/
@ -981,7 +932,7 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
copy_v3_v3(vec_prev, vec_org);
if (lnors_spacearr) {
BLI_stack_push(edge_vectors, vec_org);
edge_vectors->append(vec_org);
}
}
@ -1021,20 +972,17 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
clnors_avg[0] += (*clnor)[0];
clnors_avg[1] += (*clnor)[1];
clnors_count++;
/* We store here a pointer to all custom loop_normals processed. */
BLI_SMALLSTACK_PUSH(clnors, (short *)*clnor);
}
}
/* We store here a pointer to all loop-normals processed. */
BLI_SMALLSTACK_PUSH(normal, (float *)(loop_normals[mlfan_vert_index]));
processed_corners.append(mlfan_vert_index);
if (lnors_spacearr) {
/* Assign current lnor space to current 'vertex' loop. */
BKE_lnor_space_add_loop(lnors_spacearr, lnor_space, mlfan_vert_index, nullptr, false);
if (edge != edge_orig) {
/* We store here all edges-normalized vectors processed. */
BLI_stack_push(edge_vectors, vec_curr);
edge_vectors->append(vec_curr);
}
}
@ -1071,23 +1019,19 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
lnor_len = 1.0f;
}
BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_curr, edge_vectors);
BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_curr, *edge_vectors);
edge_vectors->clear();
if (!clnors_data.is_empty()) {
if (clnors_invalid) {
short *clnor;
clnors_avg[0] /= clnors_count;
clnors_avg[1] /= clnors_count;
/* Fix/update all clnors of this fan with computed average value. */
if (G.debug & G_DEBUG) {
printf("Invalid clnors in this fan!\n");
}
while ((clnor = (short *)BLI_SMALLSTACK_POP(clnors))) {
// print_v2("org clnor", clnor);
clnor[0] = short(clnors_avg[0]);
clnor[1] = short(clnors_avg[1]);
}
clnors_data.fill_indices(processed_corners.as_span(),
short2(clnors_avg[0], clnors_avg[1]));
// print_v2("new clnors", clnors_avg);
}
/* Extra bonus: since small-stack is local to this function,
@ -1100,50 +1044,8 @@ static void split_loop_nor_fan_do(LoopSplitTaskDataCommon *common_data,
/* In case we get a zero normal here, just use vertex normal already set! */
if (LIKELY(lnor_len != 0.0f)) {
/* Copy back the final computed normal into all related loop-normals. */
float *nor;
while ((nor = (float *)BLI_SMALLSTACK_POP(normal))) {
copy_v3_v3(nor, lnor);
}
loop_normals.fill_indices(processed_corners.as_span(), lnor);
}
/* Extra bonus: since small-stack is local to this function,
* no more need to empty it at all cost! */
}
}
static void loop_split_worker_do(LoopSplitTaskDataCommon *common_data,
LoopSplitTaskData *data,
BLI_Stack *edge_vectors)
{
if (data->flag == LoopSplitTaskData::Type::Fan) {
BLI_assert((edge_vectors == nullptr) || BLI_stack_is_empty(edge_vectors));
split_loop_nor_fan_do(common_data, data, edge_vectors);
}
else {
/* No need for edge_vectors for 'single' case! */
split_loop_nor_single_do(common_data, data);
}
}
static void loop_split_worker(TaskPool *__restrict pool, void *taskdata)
{
LoopSplitTaskDataCommon *common_data = (LoopSplitTaskDataCommon *)BLI_task_pool_user_data(pool);
LoopSplitTaskData *data = (LoopSplitTaskData *)taskdata;
/* Temp edge vectors stack, only used when computing lnor spacearr. */
BLI_Stack *edge_vectors = common_data->lnors_spacearr ?
BLI_stack_new(sizeof(float[3]), __func__) :
nullptr;
for (int i = 0; i < LOOP_SPLIT_TASK_BLOCK_SIZE; i++, data++) {
if (data->flag == LoopSplitTaskData::Type::BlockEnd) {
break;
}
loop_split_worker_do(common_data, data, edge_vectors);
}
if (edge_vectors) {
BLI_stack_free(edge_vectors);
}
}
@ -1218,10 +1120,10 @@ static bool loop_split_generator_check_cyclic_smooth_fan(const Span<int> corner_
}
}
static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common_data)
static void loop_split_generator(LoopSplitTaskDataCommon *common_data,
Vector<int> &r_single_corners,
Vector<int> &r_fan_corners)
{
MLoopNorSpaceArray *lnors_spacearr = common_data->lnors_spacearr;
const Span<int> corner_verts = common_data->corner_verts;
const Span<int> corner_edges = common_data->corner_edges;
const OffsetIndices polys = common_data->polys;
@ -1230,23 +1132,10 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
BitVector<> skip_loops(corner_verts.size(), false);
LoopSplitTaskData *data_buff = nullptr;
int data_idx = 0;
/* Temp edge vectors stack, only used when computing lnor spacearr
* (and we are not multi-threading). */
BLI_Stack *edge_vectors = nullptr;
#ifdef DEBUG_TIME
SCOPED_TIMER_AVERAGED(__func__);
#endif
if (!pool) {
if (lnors_spacearr) {
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
}
}
/* We now know edges that can be smoothed (with their vector, and their two loops),
* and edges that will be hard! Now, time to generate the normals.
*/
@ -1290,30 +1179,11 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
// printf("SKIPPING!\n");
}
else {
LoopSplitTaskData *data, data_local;
// printf("PROCESSING!\n");
if (pool) {
if (data_idx == 0) {
data_buff = (LoopSplitTaskData *)MEM_calloc_arrayN(
LOOP_SPLIT_TASK_BLOCK_SIZE, sizeof(*data_buff), __func__);
}
data = &data_buff[data_idx];
}
else {
data = &data_local;
memset(data, 0, sizeof(*data));
}
if (IS_EDGE_SHARP(edge_to_loops[corner_edges[ml_curr_index]]) &&
IS_EDGE_SHARP(edge_to_loops[corner_edges[ml_prev_index]])) {
data->ml_curr_index = ml_curr_index;
data->flag = LoopSplitTaskData::Type::Single;
data->poly_index = poly_index;
if (lnors_spacearr) {
data->lnor_space = BKE_lnor_space_create(lnors_spacearr);
}
/* Simple case (both edges around that vertex are sharp in current polygon),
* this corner just takes its poly normal. */
r_single_corners.append(ml_curr_index);
}
else {
/* We do not need to check/tag loops as already computed. Due to the fact that a loop
@ -1323,35 +1193,11 @@ static void loop_split_generator(TaskPool *pool, LoopSplitTaskDataCommon *common
* current edge, smooth previous edge), and not the alternative (smooth current edge,
* sharp previous edge). All this due/thanks to the link between normals and loop
* ordering (i.e. winding). */
data->ml_curr_index = ml_curr_index;
data->flag = LoopSplitTaskData::Type::Fan;
data->poly_index = poly_index;
if (lnors_spacearr) {
data->lnor_space = BKE_lnor_space_create(lnors_spacearr);
}
}
if (pool) {
data_idx++;
if (data_idx == LOOP_SPLIT_TASK_BLOCK_SIZE) {
BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr);
data_idx = 0;
}
}
else {
loop_split_worker_do(common_data, data, edge_vectors);
r_fan_corners.append(ml_curr_index);
}
}
}
}
if (pool && data_idx) {
BLI_task_pool_push(pool, loop_split_worker, data_buff, true, nullptr);
}
if (edge_vectors) {
BLI_stack_free(edge_vectors);
}
}
void normals_calc_loop(const Span<float3> vert_positions,
@ -1472,19 +1318,34 @@ void normals_calc_loop(const Span<float3> vert_positions,
edge_to_loops,
{});
if (corner_verts.size() < LOOP_SPLIT_TASK_BLOCK_SIZE * 8) {
/* Not enough loops to be worth the whole threading overhead. */
loop_split_generator(nullptr, &common_data);
Vector<int> single_corners;
Vector<int> fan_corners;
loop_split_generator(&common_data, single_corners, fan_corners);
MLoopNorSpace *lnor_spaces = nullptr;
if (r_lnors_spacearr) {
r_lnors_spacearr->spaces_num = single_corners.size() + fan_corners.size();
lnor_spaces = static_cast<MLoopNorSpace *>(BLI_memarena_calloc(
r_lnors_spacearr->mem, sizeof(MLoopNorSpace) * r_lnors_spacearr->spaces_num));
}
else {
TaskPool *task_pool = BLI_task_pool_create(&common_data, TASK_PRIORITY_HIGH);
loop_split_generator(task_pool, &common_data);
threading::parallel_for(single_corners.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
const int corner = single_corners[i];
lnor_space_for_single_fan(&common_data, corner, lnor_spaces ? &lnor_spaces[i] : nullptr);
}
});
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
}
threading::parallel_for(fan_corners.index_range(), 1024, [&](const IndexRange range) {
Vector<float3> edge_vectors;
for (const int i : range) {
const int corner = fan_corners[i];
split_loop_nor_fan_do(&common_data,
corner,
lnor_spaces ? &lnor_spaces[single_corners.size() + i] : nullptr,
&edge_vectors);
}
});
if (r_lnors_spacearr) {
if (r_lnors_spacearr == &_lnors_spacearr) {
@ -1534,7 +1395,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
const bool use_split_normals = true;
const float split_angle = float(M_PI);
BLI_SMALLSTACK_DECLARE(clnors_data, short *);
Vector<short *> clnors_data;
/* Compute current lnor spacearr. */
normals_calc_loop(positions,
@ -1708,7 +1569,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
else {
int avg_nor_count = 0;
float avg_nor[3];
short clnor_data_tmp[2], *clnor_data;
short clnor_data_tmp[2];
zero_v3(avg_nor);
while (loop_link) {
@ -1718,7 +1579,7 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
avg_nor_count++;
add_v3_v3(avg_nor, nor);
BLI_SMALLSTACK_PUSH(clnors_data, (short *)r_clnors_data[lidx]);
clnors_data.append(r_clnors_data[lidx]);
loop_link = loop_link->next;
done_loops[lidx].reset();
@ -1727,7 +1588,8 @@ static void mesh_normals_loop_custom_set(Span<float3> positions,
mul_v3_fl(avg_nor, 1.0f / float(avg_nor_count));
BKE_lnor_space_custom_normal_to_data(lnors_spacearr.lspacearr[i], avg_nor, clnor_data_tmp);
while ((clnor_data = (short *)BLI_SMALLSTACK_POP(clnors_data))) {
while (!clnors_data.is_empty()) {
short *clnor_data = clnors_data.pop_last();
clnor_data[0] = clnor_data_tmp[0];
clnor_data[1] = clnor_data_tmp[1];
}

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

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

View File

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

View File

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

View File

@ -65,6 +65,11 @@ class GSpan {
return size_;
}
int64_t size_in_bytes() const
{
return type_->size() * size_;
}
const void *data() const
{
return data_;
@ -186,6 +191,11 @@ class GMutableSpan {
return size_;
}
int64_t size_in_bytes() const
{
return type_->size() * size_;
}
void *data() const
{
return data_;

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

View File

@ -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

@ -551,9 +551,10 @@ template<typename T> class MutableSpan {
* Replace a subset of all elements with the given value. This invokes undefined behavior when
* one of the indices is out of bounds.
*/
constexpr void fill_indices(Span<int64_t> indices, const T &value)
template<typename IndexT> constexpr void fill_indices(Span<IndexT> indices, const T &value)
{
for (int64_t i : indices) {
static_assert(std::is_integral_v<IndexT>);
for (IndexT i : indices) {
BLI_assert(i < size_);
data_[i] = value;
}

View File

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

View File

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

View File

@ -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

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

@ -225,7 +225,7 @@ TEST(span, FillIndices)
{
std::array<int, 5> a = {0, 0, 0, 0, 0};
MutableSpan<int> a_span(a);
a_span.fill_indices({0, 2, 3}, 1);
a_span.fill_indices(Span({0, 2, 3}), 1);
EXPECT_EQ(a[0], 1);
EXPECT_EQ(a[1], 0);
EXPECT_EQ(a[2], 1);

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

@ -15,9 +15,9 @@
#include "BLI_bitmap.h"
#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BLI_stack.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
@ -470,7 +470,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
const int cd_loop_clnors_offset,
const bool has_clnors,
/* Cache. */
BLI_Stack *edge_vectors,
blender::Vector<blender::float3, 16> *edge_vectors,
/* Iterate. */
BMLoop *l_curr,
/* Result. */
@ -534,7 +534,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
normalize_v3(vec_prev);
}
BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, nullptr);
BKE_lnor_space_define(lnor_space, r_lnos[l_curr_index], vec_curr, vec_prev, {});
/* We know there is only one loop in this space,
* no need to create a linklist in this case... */
BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, l_curr_index, l_curr, true);
@ -586,7 +586,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
MLoopNorSpace *lnor_space = r_lnors_spacearr ? BKE_lnor_space_create(r_lnors_spacearr) :
nullptr;
BLI_assert((edge_vectors == nullptr) || BLI_stack_is_empty(edge_vectors));
BLI_assert((edge_vectors == nullptr) || edge_vectors->is_empty());
lfan_pivot = l_curr;
lfan_pivot_index = BM_elem_index_get(lfan_pivot);
@ -605,7 +605,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
copy_v3_v3(vec_curr, vec_org);
if (r_lnors_spacearr) {
BLI_stack_push(edge_vectors, vec_org);
edge_vectors->append(vec_org);
}
}
@ -671,7 +671,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
BKE_lnor_space_add_loop(r_lnors_spacearr, lnor_space, lfan_pivot_index, lfan_pivot, false);
if (e_next != e_org) {
/* We store here all edges-normalized vectors processed. */
BLI_stack_push(edge_vectors, vec_next);
edge_vectors->append(vec_next);
}
}
@ -700,7 +700,7 @@ static int bm_mesh_loops_calc_normals_for_loop(BMesh *bm,
lnor_len = 1.0f;
}
BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, edge_vectors);
BKE_lnor_space_define(lnor_space, lnor, vec_org, vec_next, *edge_vectors);
if (has_clnors) {
if (clnors_invalid) {
@ -863,19 +863,20 @@ static void bm_edge_tag_from_smooth_and_set_sharp(const float (*fnos)[3],
* operating on vertices this is needed for multi-threading
* so there is a guarantee that each thread has isolated loops.
*/
static void bm_mesh_loops_calc_normals_for_vert_with_clnors(BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
const bool do_rebuild,
const float split_angle_cos,
/* TLS */
MLoopNorSpaceArray *r_lnors_spacearr,
BLI_Stack *edge_vectors,
/* Iterate over. */
BMVert *v)
static void bm_mesh_loops_calc_normals_for_vert_with_clnors(
BMesh *bm,
const float (*vcos)[3],
const float (*fnos)[3],
float (*r_lnos)[3],
const short (*clnors_data)[2],
const int cd_loop_clnors_offset,
const bool do_rebuild,
const float split_angle_cos,
/* TLS */
MLoopNorSpaceArray *r_lnors_spacearr,
blender::Vector<blender::float3, 16> *edge_vectors,
/* Iterate over. */
BMVert *v)
{
/* Respecting face order is necessary so the initial starting loop is consistent
* with looping over loops of all faces.
@ -992,7 +993,7 @@ static void bm_mesh_loops_calc_normals_for_vert_without_clnors(
const float split_angle_cos,
/* TLS */
MLoopNorSpaceArray *r_lnors_spacearr,
BLI_Stack *edge_vectors,
blender::Vector<blender::float3, 16> *edge_vectors,
/* Iterate over. */
BMVert *v)
{
@ -1078,7 +1079,7 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
MLoopNorSpaceArray _lnors_spacearr = {nullptr};
BLI_Stack *edge_vectors = nullptr;
std::unique_ptr<blender::Vector<blender::float3, 16>> edge_vectors = nullptr;
{
char htype = 0;
@ -1095,7 +1096,7 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
}
if (r_lnors_spacearr) {
BKE_lnor_spacearr_init(r_lnors_spacearr, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
edge_vectors = std::make_unique<blender::Vector<blender::float3, 16>>();
}
/* Clear all loops' tags (means none are to be skipped for now). */
@ -1138,7 +1139,7 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
clnors_data,
cd_loop_clnors_offset,
has_clnors,
edge_vectors,
edge_vectors.get(),
l_curr,
r_lnos,
r_lnors_spacearr);
@ -1146,7 +1147,6 @@ static void bm_mesh_loops_calc_normals__single_threaded(BMesh *bm,
}
if (r_lnors_spacearr) {
BLI_stack_free(edge_vectors);
if (r_lnors_spacearr == &_lnors_spacearr) {
BKE_lnor_spacearr_free(r_lnors_spacearr);
}
@ -1169,7 +1169,7 @@ typedef struct BMLoopsCalcNormalsWithCoordsData {
} BMLoopsCalcNormalsWithCoordsData;
typedef struct BMLoopsCalcNormalsWithCoords_TLS {
BLI_Stack *edge_vectors;
blender::Vector<blender::float3, 16> *edge_vectors;
/** Copied from #BMLoopsCalcNormalsWithCoordsData.r_lnors_spacearr when it's not nullptr. */
MLoopNorSpaceArray *lnors_spacearr;
@ -1182,7 +1182,7 @@ static void bm_mesh_loops_calc_normals_for_vert_init_fn(const void *__restrict u
auto *data = static_cast<const BMLoopsCalcNormalsWithCoordsData *>(userdata);
auto *tls_data = static_cast<BMLoopsCalcNormalsWithCoords_TLS *>(chunk);
if (data->r_lnors_spacearr) {
tls_data->edge_vectors = BLI_stack_new(sizeof(float[3]), __func__);
tls_data->edge_vectors = MEM_new<blender::Vector<blender::float3, 16>>(__func__);
BKE_lnor_spacearr_tls_init(data->r_lnors_spacearr, &tls_data->lnors_spacearr_buf);
tls_data->lnors_spacearr = &tls_data->lnors_spacearr_buf;
}
@ -1210,7 +1210,7 @@ static void bm_mesh_loops_calc_normals_for_vert_free_fn(const void *__restrict u
auto *tls_data = static_cast<BMLoopsCalcNormalsWithCoords_TLS *>(chunk);
if (data->r_lnors_spacearr) {
BLI_stack_free(tls_data->edge_vectors);
MEM_delete(tls_data->edge_vectors);
}
}

View File

@ -129,6 +129,7 @@ set(GLSL_SRC
shaders/compositor_morphological_step.glsl
shaders/compositor_normalize.glsl
shaders/compositor_parallel_reduction.glsl
shaders/compositor_plane_deform.glsl
shaders/compositor_projector_lens_distortion.glsl
shaders/compositor_read_pass.glsl
shaders/compositor_realize_on_domain.glsl
@ -227,6 +228,7 @@ set(SRC_SHADER_CREATE_INFOS
shaders/infos/compositor_morphological_step_info.hh
shaders/infos/compositor_normalize_info.hh
shaders/infos/compositor_parallel_reduction_info.hh
shaders/infos/compositor_plane_deform_info.hh
shaders/infos/compositor_projector_lens_distortion_info.hh
shaders/infos/compositor_read_pass_info.hh
shaders/infos/compositor_realize_on_domain_info.hh

View File

@ -11,6 +11,7 @@
#include "GPU_texture.h"
#include "BKE_image.h"
#include "BKE_texture.h"
#include "DNA_ID.h"
@ -50,6 +51,9 @@ bool operator==(const CachedTextureKey &a, const CachedTextureKey &b)
CachedTexture::CachedTexture(
Tex *texture, const Scene *scene, int2 size, float2 offset, float2 scale)
{
ImagePool *image_pool = BKE_image_pool_new();
BKE_texture_fetch_images_for_pool(texture, image_pool);
Array<float4> color_pixels(size.x * size.y);
Array<float> value_pixels(size.x * size.y);
threading::parallel_for(IndexRange(size.y), 1, [&](const IndexRange sub_y_range) {
@ -61,7 +65,7 @@ CachedTexture::CachedTexture(
/* Note that it is expected that the offset is scaled by the scale. */
coordinates = (coordinates + offset) * scale;
TexResult texture_result;
BKE_texture_get_value(scene, texture, coordinates, &texture_result, true);
BKE_texture_get_value_ex(scene, texture, coordinates, &texture_result, image_pool, true);
color_pixels[y * size.x + x] = float4(texture_result.trgba);
value_pixels[y * size.x + x] = texture_result.talpha ? texture_result.trgba[3] :
texture_result.tin;
@ -69,6 +73,8 @@ CachedTexture::CachedTexture(
}
});
BKE_image_pool_free(image_pool);
color_texture_ = GPU_texture_create_2d("Cached Color Texture",
size.x,
size.y,

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

@ -0,0 +1,24 @@
#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl)
void main()
{
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
/* Add 0.5 to evaluate the input sampler at the center of the pixel and divide by the image size
* to get the coordinates into the sampler's expected [0, 1] range. */
vec2 coordinates = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx));
vec3 transformed_coordinates = mat3(homography_matrix) * vec3(coordinates, 1.0);
vec2 projected_coordinates = transformed_coordinates.xy / transformed_coordinates.z;
/* The derivatives of the projected coordinates with respect to x and y are the first and
* second columns respectively, divided by the z projection factor as can be shown by
* differentiating the above matrix multiplication with respect to x and y. */
vec2 x_gradient = homography_matrix[0].xy / transformed_coordinates.z;
vec2 y_gradient = homography_matrix[1].xy / transformed_coordinates.z;
vec4 sampled_color = textureGrad(input_tx, projected_coordinates, x_gradient, y_gradient);
imageStore(output_img, texel, sampled_color);
imageStore(mask_img, texel, sampled_color.aaaa);
}

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "gpu_shader_create_info.hh"
GPU_SHADER_CREATE_INFO(compositor_plane_deform)
.local_group_size(16, 16)
.push_constant(Type::MAT4, "homography_matrix")
.sampler(0, ImageType::FLOAT_2D, "input_tx")
.image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img")
.image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "mask_img")
.compute_source("compositor_plane_deform.glsl")
.do_static_compilation(true);

View File

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

View File

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

View File

@ -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

@ -504,10 +504,10 @@ struct AddOperationExecutor {
{
if (self_->curve_roots_kdtree_ == nullptr) {
self_->curve_roots_kdtree_ = BLI_kdtree_3d_new(curves_orig_->curves_num());
const Span<int> offsets = curves_orig_->offsets();
const Span<float3> positions = curves_orig_->positions();
for (const int curve_i : curves_orig_->curves_range()) {
const int root_point_i = curves_orig_->offsets()[curve_i];
const float3 &root_pos_cu = curves_orig_->positions()[root_point_i];
BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, root_pos_cu);
BLI_kdtree_3d_insert(self_->curve_roots_kdtree_, curve_i, positions[offsets[curve_i]]);
}
BLI_kdtree_3d_balance(self_->curve_roots_kdtree_);
}

View File

@ -854,13 +854,13 @@ static void action_space_subtype_item_extend(bContext * /*C*/,
RNA_enum_items_add(item, totitem, rna_enum_space_action_mode_items);
}
static void action_blend_read_data(BlendDataReader * /*reader*/, SpaceLink *sl)
static void action_space_blend_read_data(BlendDataReader * /*reader*/, SpaceLink *sl)
{
SpaceAction *saction = (SpaceAction *)sl;
memset(&saction->runtime, 0x0, sizeof(saction->runtime));
}
static void action_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void action_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceAction *saction = (SpaceAction *)sl;
bDopeSheet *ads = &saction->ads;
@ -873,7 +873,7 @@ static void action_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLi
BLO_read_id_address(reader, parent_id->lib, &saction->action);
}
static void action_blend_write(BlendWriter *writer, SpaceLink *sl)
static void action_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceAction, sl);
}
@ -905,9 +905,9 @@ void ED_spacetype_action(void)
st->space_subtype_item_extend = action_space_subtype_item_extend;
st->space_subtype_get = action_space_subtype_get;
st->space_subtype_set = action_space_subtype_set;
st->blend_read_data = action_blend_read_data;
st->blend_read_lib = action_blend_read_lib;
st->blend_write = action_blend_write;
st->blend_read_data = action_space_blend_read_data;
st->blend_read_lib = action_space_blend_read_lib;
st->blend_write = action_space_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype action region");

View File

@ -907,7 +907,7 @@ static void buttons_id_remap(ScrArea *UNUSED(area),
}
}
static void buttons_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
static void buttons_space_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
{
SpaceProperties *sbuts = (SpaceProperties *)sl;
@ -918,7 +918,7 @@ static void buttons_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *
sbuts->runtime = NULL;
}
static void buttons_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void buttons_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceProperties *sbuts = (SpaceProperties *)sl;
BLO_read_id_address(reader, parent_id->lib, &sbuts->pinid);
@ -927,7 +927,7 @@ static void buttons_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceL
}
}
static void buttons_blend_write(BlendWriter *writer, SpaceLink *sl)
static void buttons_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceProperties, sl);
}
@ -955,9 +955,9 @@ void ED_spacetype_buttons(void)
st->listener = buttons_area_listener;
st->context = buttons_context;
st->id_remap = buttons_id_remap;
st->blend_read_data = buttons_blend_read_data;
st->blend_read_lib = buttons_blend_read_lib;
st->blend_write = buttons_blend_write;
st->blend_read_data = buttons_space_blend_read_data;
st->blend_read_lib = buttons_space_blend_read_lib;
st->blend_write = buttons_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype buttons region");

View File

@ -1121,7 +1121,7 @@ static void clip_id_remap(ScrArea * /*area*/, SpaceLink *slink, const struct IDR
BKE_id_remapper_apply(mappings, (ID **)&sclip->mask_info.mask, ID_REMAP_APPLY_ENSURE_REAL);
}
static void clip_blend_read_data(BlendDataReader * /*reader*/, SpaceLink *sl)
static void clip_space_blend_read_data(BlendDataReader * /*reader*/, SpaceLink *sl)
{
SpaceClip *sclip = (SpaceClip *)sl;
@ -1130,14 +1130,14 @@ static void clip_blend_read_data(BlendDataReader * /*reader*/, SpaceLink *sl)
sclip->scopes.ok = 0;
}
static void clip_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void clip_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceClip *sclip = (SpaceClip *)sl;
BLO_read_id_address(reader, parent_id->lib, &sclip->clip);
BLO_read_id_address(reader, parent_id->lib, &sclip->mask_info.mask);
}
static void clip_blend_write(BlendWriter *writer, SpaceLink *sl)
static void clip_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceClip, sl);
}
@ -1162,9 +1162,9 @@ void ED_spacetype_clip(void)
st->dropboxes = clip_dropboxes;
st->refresh = clip_refresh;
st->id_remap = clip_id_remap;
st->blend_read_data = clip_blend_read_data;
st->blend_read_lib = clip_blend_read_lib;
st->blend_write = clip_blend_write;
st->blend_read_data = clip_space_blend_read_data;
st->blend_read_lib = clip_space_blend_read_lib;
st->blend_write = clip_space_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype clip region");

View File

@ -307,7 +307,7 @@ static void console_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
}
}
static void console_blend_write(BlendWriter *writer, SpaceLink *sl)
static void console_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceConsole *con = (SpaceConsole *)sl;
@ -335,7 +335,7 @@ void ED_spacetype_console(void)
st->keymap = console_keymap;
st->dropboxes = console_dropboxes;
st->blend_read_data = console_blend_read_data;
st->blend_write = console_blend_write;
st->blend_write = console_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype console region");

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

@ -926,7 +926,7 @@ static void file_id_remap(ScrArea *area, SpaceLink *sl, const struct IDRemapper
file_reset_filelist_showing_main_data(area, sfile);
}
static void file_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void file_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
SpaceFile *sfile = (SpaceFile *)sl;
@ -951,15 +951,15 @@ static void file_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
}
}
static void file_blend_read_lib(BlendLibReader *UNUSED(reader),
ID *UNUSED(parent_id),
SpaceLink *sl)
static void file_space_blend_read_lib(BlendLibReader *UNUSED(reader),
ID *UNUSED(parent_id),
SpaceLink *sl)
{
SpaceFile *sfile = (SpaceFile *)sl;
sfile->tags |= FILE_TAG_REBUILD_MAIN_FILES;
}
static void file_blend_write(BlendWriter *writer, SpaceLink *sl)
static void file_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceFile *sfile = (SpaceFile *)sl;
@ -995,9 +995,9 @@ void ED_spacetype_file(void)
st->space_subtype_set = file_space_subtype_set;
st->context = file_context;
st->id_remap = file_id_remap;
st->blend_read_data = file_blend_read_data;
st->blend_read_lib = file_blend_read_lib;
st->blend_write = file_blend_write;
st->blend_read_data = file_space_blend_read_data;
st->blend_read_lib = file_space_blend_read_lib;
st->blend_write = file_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");

View File

@ -87,6 +87,59 @@ typedef struct tBeztCopyData {
/** \name Utility Functions
* \{ */
/**
* Helper function that iterates over all FCurves and selected segments and applies the given
* function.
*/
static void apply_fcu_segment_function(bAnimContext *ac,
const float factor,
void (*segment_function)(FCurve *fcu,
FCurveSegment *segment,
const float factor))
{
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
segment_function(fcu, segment, factor);
}
ale->update |= ANIM_UPDATE_DEFAULT;
BLI_freelistN(&segments);
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void common_draw_status_header(bContext *C, tGraphSliderOp *gso, const char *operator_name)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_(operator_name));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
}
/**
* Construct a list with the original bezt arrays so we can restore them during modal operation.
* The data is stored on the struct that is passed.
@ -558,58 +611,16 @@ void GRAPH_OT_decimate(wmOperatorType *ot)
/** \name Blend to Neighbor Operator
* \{ */
static void blend_to_neighbor_graph_keys(bAnimContext *ac, float factor)
static void blend_to_neighbor_graph_keys(bAnimContext *ac, const float factor)
{
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
bAnimListElem *ale;
/* Loop through filtered data and blend keys. */
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
blend_to_neighbor_fcurve_segment(fcu, segment, factor);
}
BLI_freelistN(&segments);
ale->update |= ANIM_UPDATE_DEFAULT;
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void blend_to_neighbor_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_("Blend to Neighbor"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
apply_fcu_segment_function(ac, factor, blend_to_neighbor_fcurve_segment);
}
static void blend_to_neighbor_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
blend_to_neighbor_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Blend to Neighbor");
/* Reset keyframe data to the state at invoke. */
reset_bezts(gso);
@ -631,7 +642,7 @@ static int blend_to_neighbor_invoke(bContext *C, wmOperator *op, const wmEvent *
tGraphSliderOp *gso = op->customdata;
gso->modal_update = blend_to_neighbor_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
blend_to_neighbor_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Blend to Neighbor");
return invoke_result;
}
@ -689,54 +700,14 @@ void GRAPH_OT_blend_to_neighbor(wmOperatorType *ot)
static void breakdown_graph_keys(bAnimContext *ac, float factor)
{
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
bAnimListElem *ale;
for (ale = anim_data.first; ale; ale = ale->next) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
breakdown_fcurve_segment(fcu, segment, factor);
}
BLI_freelistN(&segments);
ale->update |= ANIM_UPDATE_DEFAULT;
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void breakdown_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_("Breakdown"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
apply_fcu_segment_function(ac, factor, breakdown_fcurve_segment);
}
static void breakdown_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
breakdown_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Breakdown");
/* Reset keyframe data to the state at invoke. */
reset_bezts(gso);
@ -756,7 +727,7 @@ static int breakdown_invoke(bContext *C, wmOperator *op, const wmEvent *event)
tGraphSliderOp *gso = op->customdata;
gso->modal_update = breakdown_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
breakdown_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Breakdown");
return invoke_result;
}
@ -836,35 +807,11 @@ static void blend_to_default_graph_keys(bAnimContext *ac, const float factor)
ANIM_animdata_freelist(&anim_data);
}
static void blend_to_default_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_("Blend to Default Value"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
}
static void blend_to_default_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
blend_to_default_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Blend to Default Value");
/* Set notifier that keyframes have changed. */
reset_bezts(gso);
@ -885,7 +832,7 @@ static int blend_to_default_invoke(bContext *C, wmOperator *op, const wmEvent *e
tGraphSliderOp *gso = op->customdata;
gso->modal_update = blend_to_default_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
blend_to_default_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Blend to Default Value");
return invoke_result;
}
@ -942,54 +889,14 @@ void GRAPH_OT_blend_to_default(wmOperatorType *ot)
static void ease_graph_keys(bAnimContext *ac, const float factor)
{
ListBase anim_data = {NULL, NULL};
ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype);
LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) {
FCurve *fcu = (FCurve *)ale->key_data;
ListBase segments = find_fcurve_segments(fcu);
LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
ease_fcurve_segment(fcu, segment, factor);
}
ale->update |= ANIM_UPDATE_DEFAULT;
BLI_freelistN(&segments);
}
ANIM_animdata_update(ac, &anim_data);
ANIM_animdata_freelist(&anim_data);
}
static void ease_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char mode_str[32];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
strcpy(mode_str, TIP_("Ease Keys"));
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
apply_fcu_segment_function(ac, factor, ease_fcurve_segment);
}
static void ease_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
ease_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Ease Keys");
/* Reset keyframes to the state at invoke. */
reset_bezts(gso);
@ -1009,7 +916,7 @@ static int ease_invoke(bContext *C, wmOperator *op, const wmEvent *event)
tGraphSliderOp *gso = op->customdata;
gso->modal_update = ease_modal_update;
gso->factor_prop = RNA_struct_find_property(op->ptr, "factor");
ease_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Ease Keys");
return invoke_result;
}
@ -1133,29 +1040,6 @@ static void gaussian_smooth_free_operator_data(void *operator_data)
MEM_freeN(gauss_data);
}
static void gaussian_smooth_draw_status_header(bContext *C, tGraphSliderOp *gso)
{
char status_str[UI_MAX_DRAW_STR];
char slider_string[UI_MAX_DRAW_STR];
ED_slider_status_string_get(gso->slider, slider_string, UI_MAX_DRAW_STR);
const char *mode_str = TIP_("Gaussian Smooth");
if (hasNumInput(&gso->num)) {
char str_ofs[NUM_STR_REP_LEN];
outputNumInput(&gso->num, str_ofs, &gso->scene->unit);
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_ofs);
}
else {
BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, slider_string);
}
ED_workspace_status_text(C, status_str);
}
static void gaussian_smooth_modal_update(bContext *C, wmOperator *op)
{
tGraphSliderOp *gso = op->customdata;
@ -1166,7 +1050,7 @@ static void gaussian_smooth_modal_update(bContext *C, wmOperator *op)
return;
}
gaussian_smooth_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Gaussian Smooth");
const float factor = slider_factor_get_and_remember(op);
tGaussOperatorData *operator_data = (tGaussOperatorData *)gso->operator_data;
@ -1209,7 +1093,7 @@ static int gaussian_smooth_invoke(bContext *C, wmOperator *op, const wmEvent *ev
ED_slider_allow_overshoot_set(gso->slider, false);
ED_slider_factor_set(gso->slider, 0.0f);
gaussian_smooth_draw_status_header(C, gso);
common_draw_status_header(C, gso, "Gaussian Smooth");
return invoke_result;
}

View File

@ -833,7 +833,7 @@ static void graph_space_subtype_item_extend(bContext *UNUSED(C),
RNA_enum_items_add(item, totitem, rna_enum_space_graph_mode_items);
}
static void graph_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void graph_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
SpaceGraph *sipo = (SpaceGraph *)sl;
@ -841,7 +841,7 @@ static void graph_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
memset(&sipo->runtime, 0x0, sizeof(sipo->runtime));
}
static void graph_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void graph_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceGraph *sipo = (SpaceGraph *)sl;
bDopeSheet *ads = sipo->ads;
@ -852,7 +852,7 @@ static void graph_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLin
}
}
static void graph_blend_write(BlendWriter *writer, SpaceLink *sl)
static void graph_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceGraph *sipo = (SpaceGraph *)sl;
ListBase tmpGhosts = sipo->runtime.ghost_curves;
@ -889,9 +889,9 @@ void ED_spacetype_ipo(void)
st->space_subtype_item_extend = graph_space_subtype_item_extend;
st->space_subtype_get = graph_space_subtype_get;
st->space_subtype_set = graph_space_subtype_set;
st->blend_read_data = graph_blend_read_data;
st->blend_read_lib = graph_blend_read_lib;
st->blend_write = graph_blend_write;
st->blend_read_data = graph_space_blend_read_data;
st->blend_read_lib = graph_space_blend_read_lib;
st->blend_write = graph_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype graphedit region");

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

@ -1038,7 +1038,7 @@ static void image_space_subtype_item_extend(bContext *UNUSED(C),
RNA_enum_items_add(item, totitem, rna_enum_space_image_mode_items);
}
static void image_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
static void image_space_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
{
SpaceImage *sima = (SpaceImage *)sl;
@ -1060,7 +1060,7 @@ static void image_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl
#endif
}
static void image_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void image_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceImage *sima = (SpaceImage *)sl;
@ -1073,7 +1073,7 @@ static void image_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLin
BLO_read_id_address(reader, parent_id->lib, &sima->gpd);
}
static void image_blend_write(BlendWriter *writer, SpaceLink *sl)
static void image_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceImage, sl);
}
@ -1103,9 +1103,9 @@ void ED_spacetype_image(void)
st->space_subtype_item_extend = image_space_subtype_item_extend;
st->space_subtype_get = image_space_subtype_get;
st->space_subtype_set = image_space_subtype_set;
st->blend_read_data = image_blend_read_data;
st->blend_read_lib = image_blend_read_lib;
st->blend_write = image_blend_write;
st->blend_read_data = image_space_blend_read_data;
st->blend_read_lib = image_space_blend_read_lib;
st->blend_write = image_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype image region");

View File

@ -248,7 +248,7 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
}
static void info_blend_write(BlendWriter *writer, SpaceLink *sl)
static void info_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceInfo, sl);
}
@ -267,7 +267,7 @@ void ED_spacetype_info(void)
st->duplicate = info_duplicate;
st->operatortypes = info_operatortypes;
st->keymap = info_keymap;
st->blend_write = info_blend_write;
st->blend_write = info_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype info region");

View File

@ -565,13 +565,13 @@ static void nla_id_remap(ScrArea *UNUSED(area),
BKE_id_remapper_apply(mappings, (ID **)&snla->ads->source, ID_REMAP_APPLY_DEFAULT);
}
static void nla_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void nla_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
SpaceNla *snla = (SpaceNla *)sl;
BLO_read_data_address(reader, &snla->ads);
}
static void nla_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void nla_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceNla *snla = (SpaceNla *)sl;
bDopeSheet *ads = snla->ads;
@ -582,7 +582,7 @@ static void nla_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink
}
}
static void nla_blend_write(BlendWriter *writer, SpaceLink *sl)
static void nla_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceNla *snla = (SpaceNla *)sl;
@ -608,9 +608,9 @@ void ED_spacetype_nla(void)
st->listener = nla_listener;
st->keymap = nla_keymap;
st->id_remap = nla_id_remap;
st->blend_read_data = nla_blend_read_data;
st->blend_read_lib = nla_blend_read_lib;
st->blend_write = nla_blend_write;
st->blend_read_data = nla_space_blend_read_data;
st->blend_read_lib = nla_space_blend_read_lib;
st->blend_write = nla_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype nla region");

View File

@ -1037,7 +1037,7 @@ static void node_space_subtype_item_extend(bContext *C, EnumPropertyItem **item,
}
}
static void node_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void node_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
SpaceNode *snode = (SpaceNode *)sl;
@ -1051,7 +1051,7 @@ static void node_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
snode->runtime = nullptr;
}
static void node_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void node_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceNode *snode = (SpaceNode *)sl;
@ -1102,7 +1102,7 @@ static void node_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink
}
}
static void node_blend_write(BlendWriter *writer, SpaceLink *sl)
static void node_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceNode *snode = (SpaceNode *)sl;
BLO_write_struct(writer, SpaceNode, snode);
@ -1139,9 +1139,9 @@ void ED_spacetype_node()
st->space_subtype_item_extend = node_space_subtype_item_extend;
st->space_subtype_get = node_space_subtype_get;
st->space_subtype_set = node_space_subtype_set;
st->blend_read_data = node_blend_read_data;
st->blend_read_lib = node_blend_read_lib;
st->blend_write = node_blend_write;
st->blend_read_data = node_space_blend_read_data;
st->blend_read_lib = node_space_blend_read_lib;
st->blend_write = node_space_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype node region");

View File

@ -447,7 +447,7 @@ static void outliner_deactivate(struct ScrArea *area)
ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW));
}
static void outliner_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void outliner_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
@ -478,7 +478,9 @@ static void outliner_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
space_outliner->runtime = nullptr;
}
static void outliner_blend_read_lib(BlendLibReader *reader, ID * /*parent_id*/, SpaceLink *sl)
static void outliner_space_blend_read_lib(BlendLibReader *reader,
ID * /*parent_id*/,
SpaceLink *sl)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
@ -552,7 +554,7 @@ static void write_space_outliner(BlendWriter *writer, const SpaceOutliner *space
}
}
static void outliner_blend_write(BlendWriter *writer, SpaceLink *sl)
static void outliner_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
write_space_outliner(writer, space_outliner);
@ -580,9 +582,9 @@ void ED_spacetype_outliner(void)
st->id_remap = outliner_id_remap;
st->deactivate = outliner_deactivate;
st->context = outliner_context;
st->blend_read_data = outliner_blend_read_data;
st->blend_read_lib = outliner_blend_read_lib;
st->blend_write = outliner_blend_write;
st->blend_read_data = outliner_space_blend_read_data;
st->blend_read_lib = outliner_space_blend_read_lib;
st->blend_write = outliner_space_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype outliner region");

View File

@ -146,7 +146,7 @@ static void script_main_region_listener(const wmRegionListenerParams *UNUSED(par
#endif
}
static void script_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void script_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceScript *scpt = (SpaceScript *)sl;
/*scpt->script = NULL; - 2.45 set to null, better re-run the script */
@ -158,7 +158,7 @@ static void script_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLi
}
}
static void script_blend_write(BlendWriter *writer, SpaceLink *sl)
static void script_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
SpaceScript *scr = (SpaceScript *)sl;
scr->but_refs = NULL;
@ -179,8 +179,8 @@ void ED_spacetype_script(void)
st->duplicate = script_duplicate;
st->operatortypes = script_operatortypes;
st->keymap = script_keymap;
st->blend_read_lib = script_blend_read_lib;
st->blend_write = script_blend_write;
st->blend_read_lib = script_space_blend_read_lib;
st->blend_write = script_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype script region");

View File

@ -952,7 +952,7 @@ static void sequencer_channel_region_draw(const bContext *C, ARegion *region)
draw_channels(C, region);
}
static void sequencer_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
static void sequencer_space_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
{
SpaceSeq *sseq = (SpaceSeq *)sl;
@ -978,7 +978,7 @@ static void sequencer_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink
memset(&sseq->runtime, 0x0, sizeof(sseq->runtime));
}
static void sequencer_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void sequencer_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceSeq *sseq = (SpaceSeq *)sl;
@ -988,7 +988,7 @@ static void sequencer_blend_read_lib(BlendLibReader *reader, ID *parent_id, Spac
BLO_read_id_address(reader, parent_id->lib, &sseq->gpd);
}
static void sequencer_blend_write(BlendWriter *writer, SpaceLink *sl)
static void sequencer_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceSeq, sl);
}
@ -1013,9 +1013,9 @@ void ED_spacetype_sequencer(void)
st->refresh = sequencer_refresh;
st->listener = sequencer_listener;
st->id_remap = sequencer_id_remap;
st->blend_read_data = sequencer_blend_read_data;
st->blend_read_lib = sequencer_blend_read_lib;
st->blend_write = sequencer_blend_write;
st->blend_read_data = sequencer_space_blend_read_data;
st->blend_read_lib = sequencer_space_blend_read_lib;
st->blend_write = sequencer_space_blend_write;
/* Create regions: */
/* Main window. */

View File

@ -124,7 +124,7 @@ static void statusbar_header_region_message_subscribe(const wmRegionMessageSubsc
WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw);
}
static void statusbar_blend_write(BlendWriter *writer, SpaceLink *sl)
static void statusbar_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceStatusBar, sl);
}
@ -143,7 +143,7 @@ void ED_spacetype_statusbar(void)
st->duplicate = statusbar_duplicate;
st->operatortypes = statusbar_operatortypes;
st->keymap = statusbar_keymap;
st->blend_write = statusbar_blend_write;
st->blend_write = statusbar_space_blend_write;
/* regions: header window */
art = MEM_callocN(sizeof(*art), "spacetype statusbar header region");

View File

@ -395,19 +395,19 @@ static void text_id_remap(ScrArea *UNUSED(area),
BKE_id_remapper_apply(mappings, (ID **)&stext->text, ID_REMAP_APPLY_ENSURE_REAL);
}
static void text_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
static void text_space_blend_read_data(BlendDataReader *UNUSED(reader), SpaceLink *sl)
{
SpaceText *st = (SpaceText *)sl;
memset(&st->runtime, 0x0, sizeof(st->runtime));
}
static void text_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void text_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
SpaceText *st = (SpaceText *)sl;
BLO_read_id_address(reader, parent_id->lib, &st->text);
}
static void text_blend_write(BlendWriter *writer, SpaceLink *sl)
static void text_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceText, sl);
}
@ -432,9 +432,9 @@ void ED_spacetype_text(void)
st->context = text_context;
st->dropboxes = text_dropboxes;
st->id_remap = text_id_remap;
st->blend_read_data = text_blend_read_data;
st->blend_read_lib = text_blend_read_lib;
st->blend_write = text_blend_write;
st->blend_read_data = text_space_blend_read_data;
st->blend_read_lib = text_space_blend_read_lib;
st->blend_write = text_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype text region");

View File

@ -276,7 +276,7 @@ static void undo_history_menu_register(void)
WM_menutype_add(mt);
}
static void topbar_blend_write(BlendWriter *writer, SpaceLink *sl)
static void topbar_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceTopBar, sl);
}
@ -295,7 +295,7 @@ void ED_spacetype_topbar(void)
st->duplicate = topbar_duplicate;
st->operatortypes = topbar_operatortypes;
st->keymap = topbar_keymap;
st->blend_write = topbar_blend_write;
st->blend_write = topbar_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype topbar main region");

View File

@ -177,7 +177,7 @@ static void userpref_navigation_region_listener(const wmRegionListenerParams *UN
static void userpref_execute_region_listener(const wmRegionListenerParams *UNUSED(params)) {}
static void userpref_blend_write(BlendWriter *writer, SpaceLink *sl)
static void userpref_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
BLO_write_struct(writer, SpaceUserPref, sl);
}
@ -196,7 +196,7 @@ void ED_spacetype_userpref(void)
st->duplicate = userpref_duplicate;
st->operatortypes = userpref_operatortypes;
st->keymap = userpref_keymap;
st->blend_write = userpref_blend_write;
st->blend_write = userpref_space_blend_write;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype userpref region");

View File

@ -2053,7 +2053,7 @@ static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRema
BKE_viewer_path_id_remap(&view3d->viewer_path, mappings);
}
static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
static void view3d_space_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
{
View3D *v3d = (View3D *)sl;
@ -2078,7 +2078,7 @@ static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl)
BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path);
}
static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
static void view3d_space_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl)
{
View3D *v3d = (View3D *)sl;
@ -2092,7 +2092,7 @@ static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLi
BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &v3d->viewer_path);
}
static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl)
static void view3d_space_blend_write(BlendWriter *writer, SpaceLink *sl)
{
View3D *v3d = (View3D *)sl;
BLO_write_struct(writer, View3D, v3d);
@ -2127,9 +2127,9 @@ void ED_spacetype_view3d()
st->gizmos = view3d_widgets;
st->context = view3d_context;
st->id_remap = view3d_id_remap;
st->blend_read_data = view3d_blend_read_data;
st->blend_read_lib = view3d_blend_read_lib;
st->blend_write = view3d_blend_write;
st->blend_read_data = view3d_space_blend_read_data;
st->blend_read_lib = view3d_space_blend_read_lib;
st->blend_write = view3d_space_blend_write;
/* regions: main window */
art = MEM_cnew<ARegionType>("spacetype view3d main region");

View File

@ -856,10 +856,12 @@ float ED_view3d_grid_scale(const Scene *scene, View3D *v3d, const char **r_grid_
}
#define STEPS_LEN 8
void ED_view3d_grid_steps(const Scene *scene,
View3D *v3d,
RegionView3D *rv3d,
float r_grid_steps[STEPS_LEN])
static void view3d_grid_steps_ex(const Scene *scene,
View3D *v3d,
RegionView3D *rv3d,
float r_grid_steps[STEPS_LEN],
void const **r_usys_pt,
int *r_len)
{
const void *usys;
int len;
@ -881,7 +883,7 @@ void ED_view3d_grid_steps(const Scene *scene,
}
for (; i < STEPS_LEN; i++) {
/* Fill last slots */
r_grid_steps[i] = 10.0f * r_grid_steps[i - 1];
r_grid_steps[i] = r_grid_steps[len - 1];
}
}
else {
@ -899,6 +901,20 @@ void ED_view3d_grid_steps(const Scene *scene,
subdiv *= v3d->gridsubdiv;
}
}
if (r_usys_pt) {
*r_usys_pt = usys;
}
if (r_len) {
*r_len = len;
}
}
void ED_view3d_grid_steps(const Scene *scene,
View3D *v3d,
RegionView3D *rv3d,
float r_grid_steps[STEPS_LEN])
{
view3d_grid_steps_ex(scene, v3d, rv3d, r_grid_steps, nullptr, nullptr);
}
float ED_view3d_grid_view_scale(Scene *scene,
@ -912,24 +928,20 @@ float ED_view3d_grid_view_scale(Scene *scene,
/* Decrease the distance between grid snap points depending on zoom. */
float dist = 12.0f / (region->sizex * rv3d->winmat[0][0]);
float grid_steps[STEPS_LEN];
ED_view3d_grid_steps(scene, v3d, rv3d, grid_steps);
/* Skip last item, in case the 'mid_dist' is greater than the largest unit. */
int i;
for (i = 0; i < ARRAY_SIZE(grid_steps) - 1; i++) {
const void *usys;
int grid_steps_len;
view3d_grid_steps_ex(scene, v3d, rv3d, grid_steps, &usys, &grid_steps_len);
int i = 0;
while (true) {
grid_scale = grid_steps[i];
if (grid_scale > dist) {
if (grid_scale > dist || i == (grid_steps_len - 1)) {
break;
}
i++;
}
if (r_grid_unit) {
const void *usys;
int len;
BKE_unit_system_get(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
if (usys) {
*r_grid_unit = IFACE_(BKE_unit_display_name_get(usys, len - i - 1));
}
if (r_grid_unit && usys) {
*r_grid_unit = IFACE_(BKE_unit_display_name_get(usys, grid_steps_len - i - 1));
}
}
else {

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

@ -178,7 +178,7 @@ class OptionalOutputsFunction : public MultiFunction {
{
if (params.single_output_is_required(0, "Out 1")) {
MutableSpan<int> values = params.uninitialized_single_output<int>(0, "Out 1");
values.fill_indices(mask, 5);
values.fill_indices(mask.indices(), 5);
}
MutableSpan<std::string> values = params.uninitialized_single_output<std::string>(1, "Out 2");
for (const int i : mask) {

View File

@ -518,8 +518,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
};
auto catmull_rom_to_nurbs = [&](IndexMask selection) {
dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
dst_curves.nurbs_orders_for_write().fill_indices(selection.indices(), 4);
dst_curves.nurbs_knots_modes_for_write().fill_indices(selection.indices(),
NURBS_KNOT_MODE_BEZIER);
fill_weights_if_necessary(selection);
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {
@ -544,7 +545,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
};
auto poly_to_nurbs = [&](IndexMask selection) {
dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
dst_curves.nurbs_orders_for_write().fill_indices(selection.indices(), 4);
bke::curves::copy_point_data(
src_points_by_curve, dst_points_by_curve, selection, src_positions, dst_positions);
fill_weights_if_necessary(selection);
@ -553,7 +554,7 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
* start/end. */
if (src_cyclic.is_single()) {
dst_curves.nurbs_knots_modes_for_write().fill_indices(
selection,
selection.indices(),
src_cyclic.get_internal_single() ? NURBS_KNOT_MODE_NORMAL : NURBS_KNOT_MODE_ENDPOINT);
}
else {
@ -576,8 +577,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(
const Span<float3> src_handles_l = src_curves.handle_positions_left();
const Span<float3> src_handles_r = src_curves.handle_positions_right();
dst_curves.nurbs_orders_for_write().fill_indices(selection, 4);
dst_curves.nurbs_knots_modes_for_write().fill_indices(selection, NURBS_KNOT_MODE_BEZIER);
dst_curves.nurbs_orders_for_write().fill_indices(selection.indices(), 4);
dst_curves.nurbs_knots_modes_for_write().fill_indices(selection.indices(),
NURBS_KNOT_MODE_BEZIER);
fill_weights_if_necessary(selection);
threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) {

View File

@ -1065,7 +1065,7 @@ bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves,
else {
/* Only trimmed curves are no longer cyclic. */
if (bke::SpanAttributeWriter cyclic = dst_attributes.lookup_for_write_span<bool>("cyclic")) {
cyclic.span.fill_indices(selection, false);
cyclic.span.fill_indices(selection.indices(), false);
cyclic.finish();
}

View File

@ -962,16 +962,18 @@ static void pack_island_xatlas(const Span<UVAABBIsland *> island_indices,
/**
* Pack islands using a mix of other strategies.
* \param islands: The islands to be packed. Will be modified with results.
* \param islands: The islands to be packed.
* \param scale: Scale islands by `scale` before packing.
* \param margin: Add `margin` units around islands before packing.
* \param params: Additional parameters. Scale and margin information is ignored.
* \param r_phis: Island layout information will be written here.
* \return Size of square covering the resulting packed UVs. The maximum `u` or `v` co-ordinate.
*/
static float pack_islands_scale_margin(const Span<PackIsland *> islands,
const float scale,
const float margin,
const UVPackIsland_Params &params)
const UVPackIsland_Params &params,
MutableSpan<uv_phi> r_phis)
{
/* #BLI_box_pack_2d produces layouts with high packing efficiency, but has `O(n^3)`
* time complexity, causing poor performance if there are lots of islands. See: #102843.
@ -988,8 +990,6 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
* - Call #pack_islands_alpaca_* on the remaining islands.
*/
blender::Array<uv_phi> phis(islands.size());
/* First, copy information from our input into the AABB structure. */
Array<UVAABBIsland *> aabbs(islands.size());
for (const int64_t i : islands.index_range()) {
@ -1056,7 +1056,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
scale,
margin,
params,
phis.as_mutable_span(),
r_phis,
&max_u,
&max_v);
break;
@ -1066,7 +1066,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
scale,
margin,
params.target_aspect_y,
phis.as_mutable_span(),
r_phis,
&max_u,
&max_v);
break;
@ -1076,25 +1076,11 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
/* Call Alpaca. */
if (params.rotate) {
pack_islands_alpaca_rotate(max_box_pack,
aabbs.as_mutable_span(),
params.target_aspect_y,
phis.as_mutable_span(),
&max_u,
&max_v);
pack_islands_alpaca_rotate(
max_box_pack, aabbs, params.target_aspect_y, r_phis, &max_u, &max_v);
}
else {
pack_islands_alpaca_turbo(max_box_pack,
aabbs.as_mutable_span(),
params.target_aspect_y,
phis.as_mutable_span(),
&max_u,
&max_v);
}
/* Write back UVs. */
for (int64_t i = 0; i < aabbs.size(); i++) {
islands[i]->place_(scale, phis[i]);
pack_islands_alpaca_turbo(max_box_pack, aabbs, params.target_aspect_y, r_phis, &max_u, &max_v);
}
return std::max(max_u / params.target_aspect_y, max_v);
@ -1103,7 +1089,7 @@ static float pack_islands_scale_margin(const Span<PackIsland *> islands,
/** Find the optimal scale to pack islands into the unit square.
* returns largest scale that will pack `islands` into the unit square.
*/
static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vector,
static float pack_islands_margin_fraction(const Span<PackIsland *> &islands,
const float margin_fraction,
const UVPackIsland_Params &params)
{
@ -1118,7 +1104,10 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
float value_low = 0.0f;
float scale_high = 0.0f;
float value_high = 0.0f;
float scale_last = 0.0f;
blender::Array<uv_phi> phis_a(islands.size());
blender::Array<uv_phi> phis_b(islands.size());
blender::Array<uv_phi> *phis_low = nullptr;
/* Scaling smaller than `min_scale_roundoff` is unlikely to fit and
* will destroy information in existing UVs. */
@ -1166,19 +1155,23 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
/* Modified binary-search to improve robustness. */
scale = sqrtf(scale * sqrtf(scale_low * scale_high));
}
BLI_assert(scale_low < scale);
BLI_assert(scale < scale_high);
}
scale = std::max(scale, min_scale_roundoff);
/* Evaluate our `f`. */
scale_last = scale;
blender::Array<uv_phi> *phis_target = (phis_low == &phis_a) ? &phis_b : &phis_a;
const float max_uv = pack_islands_scale_margin(
island_vector, scale_last, margin_fraction, params);
islands, scale, margin_fraction, params, *phis_target);
const float value = sqrtf(max_uv) - 1.0f;
if (value <= 0.0f) {
scale_low = scale;
value_low = value;
phis_low = phis_target;
}
else {
scale_high = scale;
@ -1188,28 +1181,25 @@ static float pack_islands_margin_fraction(const Span<PackIsland *> &island_vecto
scale_low = scale;
break;
}
if (!phis_low) {
phis_low = phis_target; /* May as well do "something", even if it's wrong. */
}
}
}
const bool flush = true;
if (flush) {
if (phis_low) {
/* Write back best pack as a side-effect. */
if (scale_last != scale_low) {
scale_last = scale_low;
const float max_uv = pack_islands_scale_margin(
island_vector, scale_last, margin_fraction, params);
BLI_assert(max_uv == value_low);
UNUSED_VARS(max_uv);
/* TODO (?): `if (max_uv < 1.0f) { scale_last /= max_uv; }` */
for (const int64_t i : islands.index_range()) {
islands[i]->place_(scale_low, (*phis_low)[i]);
}
}
return scale_last;
return scale_low;
}
static float calc_margin_from_aabb_length_sum(const Span<PackIsland *> &island_vector,
const UVPackIsland_Params &params)
{
/* Logic matches behavior from #geometry::uv_parametrizer_pack.
/* Logic matches previous behavior from #geometry::uv_parametrizer_pack.
* Attempt to give predictable results not dependent on current UV scale by using
* `aabb_length_sum` (was "`area`") to multiply the margin by the length (was "area"). */
double aabb_length_sum = 0.0f;
@ -1361,21 +1351,22 @@ void pack_islands(const Span<PackIsland *> &islands,
const UVPackIsland_Params &params,
float r_scale[2])
{
BLI_assert(0.0f <= params.margin);
BLI_assert(0.0f <= params.target_aspect_y);
if (islands.size() == 0) {
r_scale[0] = 1.0f;
r_scale[1] = 1.0f;
return; /* Nothing to do, just create a safe default. */
}
if (params.merge_overlap) {
return OverlapMerger::pack_islands_overlap(islands, params, r_scale);
}
finalize_geometry(islands, params);
if (params.margin == 0.0f) {
/* Special case for zero margin. Margin_method is ignored as all formulas give same result. */
const float max_uv = pack_islands_scale_margin(islands, 1.0f, 0.0f, params);
r_scale[0] = 1.0f / max_uv;
r_scale[1] = r_scale[0];
return;
}
if (params.margin_method == ED_UVPACK_MARGIN_FRACTION) {
if (params.margin_method == ED_UVPACK_MARGIN_FRACTION && params.margin > 0.0f) {
/* Uses a line search on scale. ~10x slower than other method. */
const float scale = pack_islands_margin_fraction(islands, params.margin, params);
r_scale[0] = scale;
@ -1390,14 +1381,20 @@ void pack_islands(const Span<PackIsland *> &islands,
case ED_UVPACK_MARGIN_SCALED: /* Default for Blender 3.3 and later. */
margin = calc_margin_from_aabb_length_sum(islands, params);
break;
case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */
BLI_assert_unreachable(); /* Handled above. */
case ED_UVPACK_MARGIN_FRACTION: /* Added as an option in Blender 3.4. */
BLI_assert(params.margin == 0.0f); /* Other (slower) cases are handled above. */
break;
default:
BLI_assert_unreachable();
}
const float max_uv = pack_islands_scale_margin(islands, 1.0f, margin, params);
blender::Array<uv_phi> phis(islands.size());
const float scale = 1.0f;
const float max_uv = pack_islands_scale_margin(islands, scale, margin, params, phis);
for (const int64_t i : islands.index_range()) {
islands[i]->place_(scale, phis[i]);
}
r_scale[0] = 1.0f / max_uv;
r_scale[1] = r_scale[0];
}

View File

@ -158,6 +158,18 @@ if(WITH_IMAGE_WEBP)
add_definitions(-DWITH_WEBP)
endif()
if(WITH_TBB)
add_definitions(-DWITH_TBB)
list(APPEND INC_SYS
${TBB_INCLUDE_DIRS}
)
list(APPEND LIB
${TBB_LIBRARIES}
)
endif()
list(APPEND INC
../../../intern/opencolorio
)

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

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

View File

@ -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

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

Some files were not shown because too many files have changed in this diff Show More