Add PropertyListHelper to Curves

This commit is contained in:
kobewi 2024-05-23 16:48:48 +02:00
parent 92a90a8e6f
commit e6ee72828f
4 changed files with 135 additions and 222 deletions

View file

@ -111,6 +111,7 @@ void PropertyListHelper::setup_for_instance(const PropertyListHelper &p_base, Ob
prefix = p_base.prefix;
array_length_getter = p_base.array_length_getter;
property_filter = p_base.property_filter;
property_list = p_base.property_list;
object = p_object;
}
@ -140,6 +141,16 @@ void PropertyListHelper::get_property_list(List<PropertyInfo> *p_list) const {
for (int i = 0; i < property_count; i++) {
for (const KeyValue<String, Property> &E : property_list) {
const Property &property = E.value;
if (property_filter) {
Callable::CallError ce;
Variant args[] = { property.info.name, i };
const Variant *argptrs[] = { &args[0], &args[1] };
bool property_valid = property_filter->call(object, argptrs, 2, ce);
if (!property_valid) {
continue;
}
}
PropertyInfo info = property.info;
if (!(info.usage & PROPERTY_USAGE_STORE_IF_NULL) && _call_getter(&property, i) == property.default_value) {
@ -192,6 +203,9 @@ bool PropertyListHelper::property_get_revert(const String &p_property, Variant &
void PropertyListHelper::clear() {
if (is_initialized()) {
memdelete(array_length_getter);
if (property_filter) {
memdelete(property_filter);
}
for (const KeyValue<String, Property> &E : property_list) {
if (E.value.setter) {

View file

@ -45,6 +45,7 @@ class PropertyListHelper {
String prefix;
MethodBind *array_length_getter = nullptr;
MethodBind *property_filter = nullptr;
HashMap<String, Property> property_list;
Object *object = nullptr;
@ -64,6 +65,10 @@ public:
void set_array_length_getter(G p_array_length_getter) {
array_length_getter = create_method_bind(p_array_length_getter);
}
template <typename F>
void set_property_filter(F p_property_filter) {
property_filter = create_method_bind(p_property_filter);
}
// Register property without setter/getter. Only use when you don't need PropertyListHelper for _set/_get logic.
void register_property(const PropertyInfo &p_info, const Variant &p_default);

View file

@ -36,6 +36,7 @@ const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
const char *Curve::SIGNAL_DOMAIN_CHANGED = "domain_changed";
Curve::Curve() {
property_helper.setup_for_instance(base_property_helper, this);
}
void Curve::set_point_count(int p_count) {
@ -231,6 +232,11 @@ void Curve::_remove_point(int p_index, bool p_mark_dirty) {
}
}
void Curve::_set_point_position(int p_index, const Vector2 &p_position) {
set_point_value(p_index, p_position.x);
set_point_offset(p_index, p_position.y);
}
void Curve::remove_point(int p_index) {
_remove_point(p_index);
notify_property_list_changed();
@ -432,6 +438,16 @@ real_t Curve::sample_local_nocheck(int p_index, real_t p_local_offset) const {
return y;
}
bool Curve::_filter_property(const String &p_name, int p_index) const {
if (p_index == 0) {
return p_name != "left_tangent" && p_name != "left_mode";
}
if (p_index == get_point_count() - 1) {
return p_name != "right_tangent" && p_name != "right_mode";
}
return true;
}
void Curve::mark_dirty() {
_baked_cache_dirty = true;
emit_changed();
@ -577,88 +593,6 @@ void Curve::ensure_default_setup(real_t p_min, real_t p_max) {
}
}
bool Curve::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
Vector2 position = p_value.operator Vector2();
set_point_offset(point_index, position.x);
set_point_value(point_index, position.y);
return true;
} else if (property == "left_tangent") {
set_point_left_tangent(point_index, p_value);
return true;
} else if (property == "left_mode") {
int mode = p_value;
set_point_left_mode(point_index, (TangentMode)mode);
return true;
} else if (property == "right_tangent") {
set_point_right_tangent(point_index, p_value);
return true;
} else if (property == "right_mode") {
int mode = p_value;
set_point_right_mode(point_index, (TangentMode)mode);
return true;
}
}
return false;
}
bool Curve::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
r_ret = get_point_position(point_index);
return true;
} else if (property == "left_tangent") {
r_ret = get_point_left_tangent(point_index);
return true;
} else if (property == "left_mode") {
r_ret = get_point_left_mode(point_index);
return true;
} else if (property == "right_tangent") {
r_ret = get_point_right_tangent(point_index);
return true;
} else if (property == "right_mode") {
r_ret = get_point_right_mode(point_index);
return true;
}
}
return false;
}
void Curve::_get_property_list(List<PropertyInfo> *p_list) const {
for (uint32_t i = 0; i < _points.size(); i++) {
PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
if (i != 0) {
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/left_tangent", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
pi = PropertyInfo(Variant::INT, vformat("point_%d/left_mode", i), PROPERTY_HINT_ENUM, "Free,Linear");
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
if (i != _points.size() - 1) {
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/right_tangent", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
pi = PropertyInfo(Variant::INT, vformat("point_%d/right_mode", i), PROPERTY_HINT_ENUM, "Free,Linear");
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
}
}
void Curve::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve::get_point_count);
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve::set_point_count);
@ -712,6 +646,19 @@ void Curve::_bind_methods() {
BIND_ENUM_CONSTANT(TANGENT_FREE);
BIND_ENUM_CONSTANT(TANGENT_LINEAR);
BIND_ENUM_CONSTANT(TANGENT_MODE_COUNT);
Point defaults;
const String mode_hint = "Free,Linear";
base_property_helper.set_prefix("point_");
base_property_helper.set_array_length_getter(&Curve::get_point_count);
base_property_helper.set_property_filter(&Curve::_filter_property);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR2, "position"), defaults.position, &Curve::_set_point_position, &Curve::get_point_position);
base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "left_tangent"), defaults.left_tangent, &Curve::set_point_left_tangent, &Curve::get_point_left_tangent);
base_property_helper.register_property(PropertyInfo(Variant::INT, "left_mode", PROPERTY_HINT_ENUM, mode_hint), defaults.left_mode, &Curve::set_point_left_mode, &Curve::get_point_left_mode);
base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "right_tangent"), defaults.right_tangent, &Curve::set_point_right_tangent, &Curve::get_point_right_tangent);
base_property_helper.register_property(PropertyInfo(Variant::INT, "right_mode", PROPERTY_HINT_ENUM, mode_hint), defaults.right_mode, &Curve::set_point_right_mode, &Curve::get_point_right_mode);
PropertyListHelper::register_base_helper(&base_property_helper);
}
int Curve2D::get_point_count() const {
@ -1309,6 +1256,16 @@ Vector<RBMap<real_t, Vector2>> Curve2D::_tessellate_even_length(int p_max_stages
return midpoints;
}
bool Curve2D::_filter_property(const String &p_name, int p_index) const {
if (p_index == 0) {
return p_name != "in";
}
if (p_index == get_point_count() - 1) {
return p_name != "out";
}
return true;
}
PackedVector2Array Curve2D::tessellate_even_length(int p_max_stages, real_t p_length) const {
PackedVector2Array tess;
@ -1341,64 +1298,6 @@ PackedVector2Array Curve2D::tessellate_even_length(int p_max_stages, real_t p_le
return tess;
}
bool Curve2D::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
set_point_position(point_index, p_value);
return true;
} else if (property == "in") {
set_point_in(point_index, p_value);
return true;
} else if (property == "out") {
set_point_out(point_index, p_value);
return true;
}
}
return false;
}
bool Curve2D::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
r_ret = get_point_position(point_index);
return true;
} else if (property == "in") {
r_ret = get_point_in(point_index);
return true;
} else if (property == "out") {
r_ret = get_point_out(point_index);
return true;
}
}
return false;
}
void Curve2D::_get_property_list(List<PropertyInfo> *p_list) const {
for (uint32_t i = 0; i < points.size(); i++) {
PropertyInfo pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/position", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
if (i != 0) {
pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/in", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
if (i != points.size() - 1) {
pi = PropertyInfo(Variant::VECTOR2, vformat("point_%d/out", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
}
}
void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve2D::get_point_count);
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve2D::set_point_count);
@ -1432,6 +1331,20 @@ void Curve2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_interval", PROPERTY_HINT_RANGE, "0.01,512,0.01"), "set_bake_interval", "get_bake_interval");
ADD_PROPERTY(PropertyInfo(Variant::INT, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data");
ADD_ARRAY_COUNT("Points", "point_count", "set_point_count", "get_point_count", "point_");
Point defaults;
base_property_helper.set_prefix("point_");
base_property_helper.set_array_length_getter(&Curve2D::get_point_count);
base_property_helper.set_property_filter(&Curve2D::_filter_property);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR2, "position"), defaults.position, &Curve2D::set_point_position, &Curve2D::get_point_position);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR2, "in"), defaults.in, &Curve2D::set_point_in, &Curve2D::get_point_in);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR2, "out"), defaults.out, &Curve2D::set_point_out, &Curve2D::get_point_out);
PropertyListHelper::register_base_helper(&base_property_helper);
}
Curve2D::Curve2D() {
property_helper.setup_for_instance(base_property_helper, this);
}
/***********************************************************************************/
@ -2366,6 +2279,16 @@ Vector<RBMap<real_t, Vector3>> Curve3D::_tessellate_even_length(int p_max_stages
return midpoints;
}
bool Curve3D::_filter_property(const String &p_name, int p_index) const {
if (p_index == 0) {
return p_name != "in";
}
if (p_index == get_point_count() - 1) {
return p_name != "out";
}
return true;
}
PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_length) const {
PackedVector3Array tess;
@ -2404,74 +2327,6 @@ PackedVector3Array Curve3D::tessellate_even_length(int p_max_stages, real_t p_le
return tess;
}
bool Curve3D::_set(const StringName &p_name, const Variant &p_value) {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
set_point_position(point_index, p_value);
return true;
} else if (property == "in") {
set_point_in(point_index, p_value);
return true;
} else if (property == "out") {
set_point_out(point_index, p_value);
return true;
} else if (property == "tilt") {
set_point_tilt(point_index, p_value);
return true;
}
}
return false;
}
bool Curve3D::_get(const StringName &p_name, Variant &r_ret) const {
Vector<String> components = String(p_name).split("/", true, 2);
if (components.size() >= 2 && components[0].begins_with("point_") && components[0].trim_prefix("point_").is_valid_int()) {
int point_index = components[0].trim_prefix("point_").to_int();
const String &property = components[1];
if (property == "position") {
r_ret = get_point_position(point_index);
return true;
} else if (property == "in") {
r_ret = get_point_in(point_index);
return true;
} else if (property == "out") {
r_ret = get_point_out(point_index);
return true;
} else if (property == "tilt") {
r_ret = get_point_tilt(point_index);
return true;
}
}
return false;
}
void Curve3D::_get_property_list(List<PropertyInfo> *p_list) const {
for (uint32_t i = 0; i < points.size(); i++) {
PropertyInfo pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/position", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
if (closed || i != 0) {
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/in", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
if (closed || i != points.size() - 1) {
pi = PropertyInfo(Variant::VECTOR3, vformat("point_%d/out", i));
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
pi = PropertyInfo(Variant::FLOAT, vformat("point_%d/tilt", i), PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians_as_degrees");
pi.usage &= ~PROPERTY_USAGE_STORAGE;
p_list->push_back(pi);
}
}
void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_point_count"), &Curve3D::get_point_count);
ClassDB::bind_method(D_METHOD("set_point_count", "count"), &Curve3D::set_point_count);
@ -2519,4 +2374,19 @@ void Curve3D::_bind_methods() {
ADD_GROUP("Up Vector", "up_vector_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "up_vector_enabled"), "set_up_vector_enabled", "is_up_vector_enabled");
Point defaults;
base_property_helper.set_prefix("point_");
base_property_helper.set_array_length_getter(&Curve3D::get_point_count);
base_property_helper.set_property_filter(&Curve3D::_filter_property);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR3, "position"), defaults.position, &Curve3D::set_point_position, &Curve3D::get_point_position);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR3, "in"), defaults.in, &Curve3D::set_point_in, &Curve3D::get_point_in);
base_property_helper.register_property(PropertyInfo(Variant::VECTOR3, "out"), defaults.out, &Curve3D::set_point_out, &Curve3D::get_point_out);
base_property_helper.register_property(PropertyInfo(Variant::FLOAT, "tilt"), defaults.tilt, &Curve3D::set_point_tilt, &Curve3D::get_point_tilt);
PropertyListHelper::register_base_helper(&base_property_helper);
}
Curve3D::Curve3D() {
property_helper.setup_for_instance(base_property_helper, this);
}

View file

@ -32,6 +32,7 @@
#include "core/io/resource.h"
#include "core/templates/rb_map.h"
#include "scene/property_list_helper.h"
// y(x) curve
class Curve : public Resource {
@ -54,8 +55,7 @@ public:
TangentMode left_mode = TANGENT_FREE;
TangentMode right_mode = TANGENT_FREE;
Point() {
}
Point() {}
Point(const Vector2 &p_position,
real_t p_left = 0.0,
@ -70,6 +70,9 @@ public:
}
};
static inline PropertyListHelper base_property_helper;
PropertyListHelper property_helper;
Curve();
int get_point_count() const { return _points.size(); }
@ -140,14 +143,18 @@ public:
void ensure_default_setup(real_t p_min, real_t p_max);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
protected:
bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); }
bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); }
void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list); }
bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); }
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); }
static void _bind_methods();
private:
bool _filter_property(const String &p_name, int p_index) const;
void mark_dirty();
int _add_point(Vector2 p_position,
real_t p_left_tangent = 0,
@ -156,6 +163,7 @@ private:
TangentMode p_right_mode = TANGENT_FREE,
bool p_mark_dirty = true);
void _remove_point(int p_index, bool p_mark_dirty = true);
void _set_point_position(int p_index, const Vector2 &p_position);
LocalVector<Point> _points;
mutable bool _baked_cache_dirty = false;
@ -178,6 +186,9 @@ class Curve2D : public Resource {
Vector2 position;
};
static inline PropertyListHelper base_property_helper;
PropertyListHelper property_helper;
LocalVector<Point> points;
struct BakedPoint {
@ -211,16 +222,19 @@ class Curve2D : public Resource {
Dictionary _get_data() const;
void _set_data(const Dictionary &p_data);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _add_point(const Vector2 &p_position, const Vector2 &p_in = Vector2(), const Vector2 &p_out = Vector2(), int p_atpos = -1);
void _remove_point(int p_index);
Vector<RBMap<real_t, Vector2>> _tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const;
bool _filter_property(const String &p_name, int p_index) const;
protected:
bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); }
bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); }
void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list); }
bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); }
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); }
static void _bind_methods();
public:
@ -252,6 +266,8 @@ public:
PackedVector2Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; //useful for display
PackedVector2Array tessellate_even_length(int p_max_stages = 5, real_t p_length = 20.0) const; // Useful for baking.
Curve2D();
};
class Curve3D : public Resource {
@ -264,6 +280,9 @@ class Curve3D : public Resource {
real_t tilt = 0.0;
};
static inline PropertyListHelper base_property_helper;
PropertyListHelper property_helper;
LocalVector<Point> points;
#ifdef TOOLS_ENABLED
// For Path3DGizmo.
@ -303,16 +322,19 @@ class Curve3D : public Resource {
Dictionary _get_data() const;
void _set_data(const Dictionary &p_data);
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
void _get_property_list(List<PropertyInfo> *p_list) const;
void _add_point(const Vector3 &p_position, const Vector3 &p_in = Vector3(), const Vector3 &p_out = Vector3(), int p_atpos = -1);
void _remove_point(int p_index);
Vector<RBMap<real_t, Vector3>> _tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const;
bool _filter_property(const String &p_name, int p_index) const;
protected:
bool _set(const StringName &p_name, const Variant &p_value) { return property_helper.property_set_value(p_name, p_value); }
bool _get(const StringName &p_name, Variant &r_ret) const { return property_helper.property_get_value(p_name, r_ret); }
void _get_property_list(List<PropertyInfo> *p_list) const { property_helper.get_property_list(p_list); }
bool _property_can_revert(const StringName &p_name) const { return property_helper.property_can_revert(p_name); }
bool _property_get_revert(const StringName &p_name, Variant &r_property) const { return property_helper.property_get_revert(p_name, r_property); }
static void _bind_methods();
public:
@ -360,4 +382,6 @@ public:
PackedVector3Array tessellate(int p_max_stages = 5, real_t p_tolerance = 4) const; // Useful for display.
PackedVector3Array tessellate_even_length(int p_max_stages = 5, real_t p_length = 0.2) const; // Useful for baking.
Curve3D();
};