summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/extension/gdnative_interface.h2
-rw-r--r--core/math/basis.cpp12
-rw-r--r--core/math/basis.h24
-rw-r--r--core/math/expression.cpp38
-rw-r--r--core/math/expression.h4
-rw-r--r--core/math/math_funcs.h21
-rw-r--r--core/math/vector2.cpp7
-rw-r--r--core/math/vector2.h24
-rw-r--r--core/math/vector3.cpp8
-rw-r--r--core/math/vector3.h25
-rw-r--r--core/object/object.cpp49
-rw-r--r--core/object/object.h2
-rw-r--r--core/object/script_language.cpp5
-rw-r--r--core/object/script_language.h1
-rw-r--r--core/variant/callable.h1
-rw-r--r--core/variant/variant.cpp21
-rw-r--r--core/variant/variant.h2
-rw-r--r--core/variant/variant_call.cpp38
-rw-r--r--core/variant/variant_utility.cpp52
-rw-r--r--doc/classes/@GlobalScope.xml87
-rw-r--r--doc/classes/Expression.xml1
-rw-r--r--doc/classes/Vector2.xml10
-rw-r--r--doc/classes/Vector3.xml10
-rw-r--r--editor/animation_bezier_editor.cpp17
-rw-r--r--editor/editor_properties.cpp14
-rw-r--r--editor/editor_properties.h3
-rw-r--r--editor/editor_spin_slider.cpp2
-rw-r--r--editor/plugins/script_text_editor.cpp2
-rw-r--r--editor/property_editor.cpp4
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp3
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp12
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h2
-rw-r--r--modules/gdscript/gdscript_codegen.h2
-rw-r--r--modules/gdscript/gdscript_compiler.cpp66
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp8
-rw-r--r--modules/gdscript/gdscript_function.h1
-rw-r--r--modules/gdscript/gdscript_vm.cpp18
-rw-r--r--modules/visual_script/visual_script.cpp2
-rw-r--r--scene/3d/node_3d.cpp148
-rw-r--r--scene/3d/node_3d.h30
-rw-r--r--scene/gui/spin_box.cpp2
-rw-r--r--scene/resources/animation.cpp17
-rw-r--r--scene/resources/curve.cpp38
-rw-r--r--scene/resources/gradient.h12
45 files changed, 620 insertions, 228 deletions
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 67aa2bbbfb..24d8b0af6e 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -588,6 +588,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXPRESSION);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY);
diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h
index 095c7983ee..ccd6fb0f7e 100644
--- a/core/extension/gdnative_interface.h
+++ b/core/extension/gdnative_interface.h
@@ -153,7 +153,7 @@ typedef enum {
GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */
GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */
GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL,
-
+ GDNATIVE_CALL_ERROR_METHOD_NOT_CONST, /* used for const call */
} GDNativeCallErrorType;
typedef struct {
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 65353d8118..ce5e9aa9b3 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -365,12 +365,12 @@ Basis Basis::rotated_local(const Vector3 &p_axis, real_t p_angle) const {
return (*this) * Basis(p_axis, p_angle);
}
-Basis Basis::rotated(const Vector3 &p_euler) const {
- return Basis(p_euler) * (*this);
+Basis Basis::rotated(const Vector3 &p_euler, EulerOrder p_order) const {
+ return Basis::from_euler(p_euler, p_order) * (*this);
}
-void Basis::rotate(const Vector3 &p_euler) {
- *this = rotated(p_euler);
+void Basis::rotate(const Vector3 &p_euler, EulerOrder p_order) {
+ *this = rotated(p_euler, p_order);
}
Basis Basis::rotated(const Quaternion &p_quaternion) const {
@@ -935,9 +935,9 @@ void Basis::set_axis_angle_scale(const Vector3 &p_axis, real_t p_angle, const Ve
rotate(p_axis, p_angle);
}
-void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale) {
+void Basis::set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale, EulerOrder p_order) {
_set_diagonal(p_scale);
- rotate(p_euler);
+ rotate(p_euler, p_order);
}
void Basis::set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale) {
diff --git a/core/math/basis.h b/core/math/basis.h
index 9cce22510b..4be325cdd2 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -56,6 +56,15 @@ struct _NO_DISCARD_ Basis {
_FORCE_INLINE_ real_t determinant() const;
+ enum EulerOrder {
+ EULER_ORDER_XYZ,
+ EULER_ORDER_XZY,
+ EULER_ORDER_YXZ,
+ EULER_ORDER_YZX,
+ EULER_ORDER_ZXY,
+ EULER_ORDER_ZYX
+ };
+
void from_z(const Vector3 &p_z);
void rotate(const Vector3 &p_axis, real_t p_angle);
@@ -64,21 +73,12 @@ struct _NO_DISCARD_ Basis {
void rotate_local(const Vector3 &p_axis, real_t p_angle);
Basis rotated_local(const Vector3 &p_axis, real_t p_angle) const;
- void rotate(const Vector3 &p_euler);
- Basis rotated(const Vector3 &p_euler) const;
+ void rotate(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ);
+ Basis rotated(const Vector3 &p_euler, EulerOrder p_order = EULER_ORDER_YXZ) const;
void rotate(const Quaternion &p_quaternion);
Basis rotated(const Quaternion &p_quaternion) const;
- enum EulerOrder {
- EULER_ORDER_XYZ,
- EULER_ORDER_XZY,
- EULER_ORDER_YXZ,
- EULER_ORDER_YZX,
- EULER_ORDER_ZXY,
- EULER_ORDER_ZYX
- };
-
Vector3 get_euler_normalized(EulerOrder p_order = EULER_ORDER_YXZ) const;
void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
void get_rotation_axis_angle_local(Vector3 &p_axis, real_t &p_angle) const;
@@ -119,7 +119,7 @@ struct _NO_DISCARD_ Basis {
Vector3 get_scale_local() const;
void set_axis_angle_scale(const Vector3 &p_axis, real_t p_angle, const Vector3 &p_scale);
- void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale);
+ void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale, EulerOrder p_order = EULER_ORDER_YXZ);
void set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale);
// transposed dot products
diff --git a/core/math/expression.cpp b/core/math/expression.cpp
index 5a90f68b66..419056d7d6 100644
--- a/core/math/expression.cpp
+++ b/core/math/expression.cpp
@@ -1240,7 +1240,7 @@ bool Expression::_compile_expression() {
return false;
}
-bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str) {
+bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str) {
switch (p_node->type) {
case Expression::ENode::TYPE_INPUT: {
const Expression::InputNode *in = static_cast<const Expression::InputNode *>(p_node);
@@ -1266,7 +1266,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::OperatorNode *op = static_cast<const Expression::OperatorNode *>(p_node);
Variant a;
- bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, op->nodes[0], a, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1274,7 +1274,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
Variant b;
if (op->nodes[1]) {
- ret = _execute(p_inputs, p_instance, op->nodes[1], b, r_error_str);
+ ret = _execute(p_inputs, p_instance, op->nodes[1], b, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1292,14 +1292,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::IndexNode *index = static_cast<const Expression::IndexNode *>(p_node);
Variant base;
- bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Variant idx;
- ret = _execute(p_inputs, p_instance, index->index, idx, r_error_str);
+ ret = _execute(p_inputs, p_instance, index->index, idx, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1316,7 +1316,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::NamedIndexNode *index = static_cast<const Expression::NamedIndexNode *>(p_node);
Variant base;
- bool ret = _execute(p_inputs, p_instance, index->base, base, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, index->base, base, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1336,7 +1336,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
arr.resize(array->array.size());
for (int i = 0; i < array->array.size(); i++) {
Variant value;
- bool ret = _execute(p_inputs, p_instance, array->array[i], value, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, array->array[i], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
@@ -1353,14 +1353,14 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
Dictionary d;
for (int i = 0; i < dictionary->dict.size(); i += 2) {
Variant key;
- bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, dictionary->dict[i + 0], key, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
Variant value;
- ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, r_error_str);
+ ret = _execute(p_inputs, p_instance, dictionary->dict[i + 1], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1380,7 +1380,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
for (int i = 0; i < constructor->arguments.size(); i++) {
Variant value;
- bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, constructor->arguments[i], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
@@ -1408,7 +1408,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
for (int i = 0; i < bifunc->arguments.size(); i++) {
Variant value;
- bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, bifunc->arguments[i], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
}
@@ -1429,7 +1429,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
const Expression::CallNode *call = static_cast<const Expression::CallNode *>(p_node);
Variant base;
- bool ret = _execute(p_inputs, p_instance, call->base, base, r_error_str);
+ bool ret = _execute(p_inputs, p_instance, call->base, base, p_const_calls_only, r_error_str);
if (ret) {
return true;
@@ -1442,7 +1442,7 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
for (int i = 0; i < call->arguments.size(); i++) {
Variant value;
- ret = _execute(p_inputs, p_instance, call->arguments[i], value, r_error_str);
+ ret = _execute(p_inputs, p_instance, call->arguments[i], value, p_const_calls_only, r_error_str);
if (ret) {
return true;
@@ -1452,7 +1452,11 @@ bool Expression::_execute(const Array &p_inputs, Object *p_instance, Expression:
}
Callable::CallError ce;
- base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
+ if (p_const_calls_only) {
+ base.call_const(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
+ } else {
+ base.callp(call->method, (const Variant **)argp.ptr(), argp.size(), r_ret, ce);
+ }
if (ce.error != Callable::CallError::CALL_OK) {
r_error_str = vformat(RTR("On call to '%s':"), String(call->method));
@@ -1491,13 +1495,13 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu
return OK;
}
-Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error) {
+Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) {
ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + ".");
execution_error = false;
Variant output;
String error_txt;
- bool err = _execute(p_inputs, p_base, root, output, error_txt);
+ bool err = _execute(p_inputs, p_base, root, output, p_const_calls_only, error_txt);
if (err) {
execution_error = true;
error_str = error_txt;
@@ -1517,7 +1521,7 @@ String Expression::get_error_text() const {
void Expression::_bind_methods() {
ClassDB::bind_method(D_METHOD("parse", "expression", "input_names"), &Expression::parse, DEFVAL(Vector<String>()));
- ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true));
+ ClassDB::bind_method(D_METHOD("execute", "inputs", "base_instance", "show_error", "const_calls_only"), &Expression::execute, DEFVAL(Array()), DEFVAL(Variant()), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("has_execute_failed"), &Expression::has_execute_failed);
ClassDB::bind_method(D_METHOD("get_error_text"), &Expression::get_error_text);
}
diff --git a/core/math/expression.h b/core/math/expression.h
index 6ea3c1611f..2d58915996 100644
--- a/core/math/expression.h
+++ b/core/math/expression.h
@@ -257,14 +257,14 @@ private:
Vector<String> input_names;
bool execution_error = false;
- bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, String &r_error_str);
+ bool _execute(const Array &p_inputs, Object *p_instance, Expression::ENode *p_node, Variant &r_ret, bool p_const_calls_only, String &r_error_str);
protected:
static void _bind_methods();
public:
Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>());
- Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true);
+ Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true, bool p_const_calls_only = false);
bool has_execute_failed() const;
String get_error_text() const;
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index c8a55341aa..53deb9bd42 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -253,6 +253,27 @@ public:
(-p_pre + 3.0f * p_from - 3.0f * p_to + p_post) * (p_weight * p_weight * p_weight));
}
+ static _ALWAYS_INLINE_ double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ double omt = (1.0 - p_t);
+ double omt2 = omt * omt;
+ double omt3 = omt2 * omt;
+ double t2 = p_t * p_t;
+ double t3 = t2 * p_t;
+
+ return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
+ }
+ static _ALWAYS_INLINE_ float bezier_interpolate(float p_start, float p_control_1, float p_control_2, float p_end, float p_t) {
+ /* Formula from Wikipedia article on Bezier curves. */
+ float omt = (1.0f - p_t);
+ float omt2 = omt * omt;
+ float omt3 = omt2 * omt;
+ float t2 = p_t * p_t;
+ float t3 = t2 * p_t;
+
+ return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0f + p_control_2 * omt * t2 * 3.0f + p_end * t3;
+ }
+
static _ALWAYS_INLINE_ double lerp_angle(double p_from, double p_to, double p_weight) {
double difference = fmod(p_to - p_from, Math_TAU);
double distance = fmod(2.0 * difference, Math_TAU) - difference;
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index a27227905c..d9b5d55454 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -152,13 +152,6 @@ Vector2 Vector2::limit_length(const real_t p_len) const {
return v;
}
-Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
- Vector2 res = *this;
- res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
- res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
- return res;
-}
-
Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const {
Vector2 v = *this;
Vector2 vd = p_to - v;
diff --git a/core/math/vector2.h b/core/math/vector2.h
index bd67299f33..91d3d3a56b 100644
--- a/core/math/vector2.h
+++ b/core/math/vector2.h
@@ -113,7 +113,9 @@ struct _NO_DISCARD_ Vector2 {
_FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const;
_FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const;
- Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const;
+
Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const;
Vector2 slide(const Vector2 &p_normal) const;
@@ -261,6 +263,26 @@ Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const {
return rotated(angle * p_weight) * (result_length / start_length);
}
+Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const {
+ Vector2 res = *this;
+ res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
+ res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
+ return res;
+}
+
+Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const {
+ Vector2 res = *this;
+
+ /* Formula from Wikipedia article on Bezier curves. */
+ real_t omt = (1.0 - p_t);
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = p_t * p_t;
+ real_t t3 = t2 * p_t;
+
+ return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
+}
+
Vector2 Vector2::direction_to(const Vector2 &p_to) const {
Vector2 ret(p_to.x - x, p_to.y - y);
ret.normalize();
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index f94f39b7f2..d71d365053 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -85,14 +85,6 @@ Vector3 Vector3::limit_length(const real_t p_len) const {
return v;
}
-Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
- Vector3 res = *this;
- res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
- res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
- res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
- return res;
-}
-
Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const {
Vector3 v = *this;
Vector3 vd = p_to - v;
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 8891532f42..970416234d 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -104,7 +104,9 @@ struct _NO_DISCARD_ Vector3 {
_FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const;
_FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const;
- Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const;
+ _FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const;
+
Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const;
Vector2 octahedron_encode() const;
@@ -227,6 +229,27 @@ Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const {
return rotated(cross(p_to).normalized(), angle * p_weight) * (result_length / start_length);
}
+Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const {
+ Vector3 res = *this;
+ res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight);
+ res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight);
+ res.z = Math::cubic_interpolate(res.z, p_b.z, p_pre_a.z, p_post_b.z, p_weight);
+ return res;
+}
+
+Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const {
+ Vector3 res = *this;
+
+ /* Formula from Wikipedia article on Bezier curves. */
+ real_t omt = (1.0 - p_t);
+ real_t omt2 = omt * omt;
+ real_t omt3 = omt2 * omt;
+ real_t t2 = p_t * p_t;
+ real_t t3 = t2 * p_t;
+
+ return res * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
+}
+
real_t Vector3::distance_to(const Vector3 &p_to) const {
return (p_to - *this).length();
}
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 6585c3fa79..440da00c17 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -669,6 +669,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
return ret;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
}
@@ -688,6 +689,54 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
return ret;
}
+Variant Object::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ r_error.error = Callable::CallError::CALL_OK;
+
+ if (p_method == CoreStringNames::get_singleton()->_free) {
+ // Free is not const, so fail.
+ r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
+ return Variant();
+ }
+
+ Variant ret;
+ OBJ_DEBUG_LOCK
+
+ if (script_instance) {
+ ret = script_instance->call_const(p_method, p_args, p_argcount, r_error);
+ //force jumptable
+ switch (r_error.error) {
+ case Callable::CallError::CALL_OK:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INVALID_METHOD:
+ break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
+ break;
+ case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT:
+ case Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS:
+ case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
+ return ret;
+ case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL: {
+ }
+ }
+ }
+
+ //extension does not need this, because all methods are registered in MethodBind
+
+ MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+
+ if (method) {
+ if (!method->is_const()) {
+ r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
+ return ret;
+ }
+ ret = method->call(this, p_args, p_argcount, r_error);
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ }
+
+ return ret;
+}
+
void Object::notification(int p_notification, bool p_reversed) {
_notificationv(p_notification, p_reversed);
diff --git a/core/object/object.h b/core/object/object.h
index 7dac96bc2b..e065634000 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -67,6 +67,7 @@ enum PropertyHint {
PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed
PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type
PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines
+ PROPERTY_HINT_EXPRESSION, ///< used for string properties that can contain multiple lines
PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties
PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
PROPERTY_HINT_IMAGE_COMPRESS_LOSSY,
@@ -759,6 +760,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
+ virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
template <typename... VarArgs>
Variant call(const StringName &p_method, VarArgs... p_args) {
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 66c9a80193..4623d0e525 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -295,6 +295,11 @@ void ScriptServer::save_global_classes() {
}
////////////////////
+
+Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+ return callp(p_method, p_args, p_argcount, r_error);
+}
+
void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) {
List<PropertyInfo> pinfo;
get_property_list(&pinfo);
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 0a8e79a24e..776a9bfaab 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -190,6 +190,7 @@ public:
return callp(p_method, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), cerr);
}
+ virtual Variant call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error); // implement if language supports const functions
virtual void notification(int p_notification) = 0;
virtual String to_string(bool *r_valid) {
if (r_valid) {
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 6a760958d6..bbcf5427ba 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -61,6 +61,7 @@ public:
CALL_ERROR_TOO_MANY_ARGUMENTS, // expected is number of arguments
CALL_ERROR_TOO_FEW_ARGUMENTS, // expected is number of arguments
CALL_ERROR_INSTANCE_IS_NULL,
+ CALL_ERROR_METHOD_NOT_CONST,
};
Error error = Error::CALL_OK;
int argument = 0;
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 6007268e21..ae92d7b5c4 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -3327,13 +3327,20 @@ Vector<Variant> varray(const Variant &p_arg1, const Variant &p_arg2, const Varia
void Variant::static_assign(const Variant &p_variant) {
}
-bool Variant::is_shared() const {
- switch (type) {
+bool Variant::is_type_shared(Variant::Type p_type) {
+ switch (p_type) {
case OBJECT:
- return true;
case ARRAY:
- return true;
case DICTIONARY:
+ case PACKED_BYTE_ARRAY:
+ case PACKED_INT32_ARRAY:
+ case PACKED_INT64_ARRAY:
+ case PACKED_FLOAT32_ARRAY:
+ case PACKED_FLOAT64_ARRAY:
+ case PACKED_STRING_ARRAY:
+ case PACKED_VECTOR2_ARRAY:
+ case PACKED_VECTOR3_ARRAY:
+ case PACKED_COLOR_ARRAY:
return true;
default: {
}
@@ -3342,6 +3349,10 @@ bool Variant::is_shared() const {
return false;
}
+bool Variant::is_shared() const {
+ return is_type_shared(type);
+}
+
void Variant::_variant_call_error(const String &p_method, Callable::CallError &error) {
switch (error.error) {
case Callable::CallError::CALL_ERROR_INVALID_ARGUMENT: {
@@ -3395,6 +3406,8 @@ String Variant::get_call_error_text(Object *p_base, const StringName &p_method,
err_text = "Method not found.";
} else if (ce.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
err_text = "Instance is null";
+ } else if (ce.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ err_text = "Method not const in const instance";
} else if (ce.error == Callable::CallError::CALL_OK) {
return "Call OK";
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 992d9cad40..872b374b13 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -297,6 +297,7 @@ public:
static String get_type_name(Variant::Type p_type);
static bool can_convert(Type p_type_from, Type p_type_to);
static bool can_convert_strict(Type p_type_from, Type p_type_to);
+ static bool is_type_shared(Variant::Type p_type);
bool is_ref_counted() const;
_FORCE_INLINE_ bool is_num() const {
@@ -555,6 +556,7 @@ public:
return ret;
}
+ void call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error);
static String get_call_error_text(const StringName &p_method, const Variant **p_argptrs, int p_argcount, const Callable::CallError &ce);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 8e420ecf04..a4bb7630d6 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1031,6 +1031,37 @@ void Variant::callp(const StringName &p_method, const Variant **p_args, int p_ar
#endif
r_ret = _get_obj().obj->callp(p_method, p_args, p_argcount, r_error);
+ } else {
+ r_error.error = Callable::CallError::CALL_OK;
+
+ const VariantBuiltInMethodInfo *imf = builtin_method_info[type].lookup_ptr(p_method);
+
+ if (!imf) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
+ return;
+ }
+
+ imf->call(this, p_args, p_argcount, r_ret, imf->default_arguments, r_error);
+ }
+}
+
+void Variant::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
+ if (type == Variant::OBJECT) {
+ //call object
+ Object *obj = _get_obj().obj;
+ if (!obj) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+#ifdef DEBUG_ENABLED
+ if (EngineDebugger::is_active() && !_get_obj().id.is_ref_counted() && ObjectDB::get_instance(_get_obj().id) == nullptr) {
+ r_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ return;
+ }
+
+#endif
+ r_ret = _get_obj().obj->call_const(p_method, p_args, p_argcount, r_error);
+
//else if (type==Variant::METHOD) {
} else {
r_error.error = Callable::CallError::CALL_OK;
@@ -1042,6 +1073,11 @@ void Variant::callp(const StringName &p_method, const Variant **p_args, int p_ar
return;
}
+ if (!imf->is_const) {
+ r_error.error = Callable::CallError::CALL_ERROR_METHOD_NOT_CONST;
+ return;
+ }
+
imf->call(this, p_args, p_argcount, r_ret, imf->default_arguments, r_error);
}
}
@@ -1556,6 +1592,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, lerp, sarray("to", "weight"), varray());
bind_method(Vector2, slerp, sarray("to", "weight"), varray());
bind_method(Vector2, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
+ bind_method(Vector2, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray());
bind_method(Vector2, max_axis_index, sarray(), varray());
bind_method(Vector2, min_axis_index, sarray(), varray());
bind_method(Vector2, move_toward, sarray("to", "delta"), varray());
@@ -1643,6 +1680,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, lerp, sarray("to", "weight"), varray());
bind_method(Vector3, slerp, sarray("to", "weight"), varray());
bind_method(Vector3, cubic_interpolate, sarray("b", "pre_a", "post_b", "weight"), varray());
+ bind_method(Vector3, bezier_interpolate, sarray("control_1", "control_2", "end", "t"), varray());
bind_method(Vector3, move_toward, sarray("to", "delta"), varray());
bind_method(Vector3, dot, sarray("with"), varray());
bind_method(Vector3, cross, sarray("with"), varray());
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 7fabdcbc82..d1b5e285d2 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -231,6 +231,10 @@ struct VariantUtilityFunctions {
return Math::cubic_interpolate(from, to, pre, post, weight);
}
+ static inline double bezier_interpolate(double p_start, double p_control_1, double p_control_2, double p_end, double p_t) {
+ return Math::bezier_interpolate(p_start, p_control_1, p_control_2, p_end, p_t);
+ }
+
static inline double lerp_angle(double from, double to, double weight) {
return Math::lerp_angle(from, to, weight);
}
@@ -267,6 +271,52 @@ struct VariantUtilityFunctions {
return Math::db2linear(db);
}
+ static inline Variant wrap(const Variant &p_x, const Variant &p_min, const Variant &p_max, Callable::CallError &r_error) {
+ Variant::Type x_type = p_x.get_type();
+ if (x_type != Variant::INT && x_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = x_type;
+ return Variant();
+ }
+
+ Variant::Type min_type = p_min.get_type();
+ if (min_type != Variant::INT && min_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = x_type;
+ return Variant();
+ }
+
+ Variant::Type max_type = p_max.get_type();
+ if (max_type != Variant::INT && max_type != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 2;
+ r_error.expected = x_type;
+ return Variant();
+ }
+
+ Variant value;
+
+ switch (x_type) {
+ case Variant::INT: {
+ if (x_type != min_type || x_type != max_type) {
+ value = wrapf((double)p_x, (double)p_min, (double)p_max);
+ } else {
+ value = wrapi((int)p_x, (int)p_min, (int)p_max);
+ }
+ } break;
+ case Variant::FLOAT: {
+ value = wrapf((double)p_x, (double)p_min, (double)p_max);
+ } break;
+ default:
+ break;
+ }
+
+ r_error.error = Callable::CallError::CALL_OK;
+ return value;
+ }
+
static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) {
return Math::wrapi(value, min, max);
}
@@ -1204,6 +1254,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(cubic_interpolate, sarray("from", "to", "pre", "post", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDR(bezier_interpolate, sarray("start", "control_1", "control_2", "end", "t"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(lerp_angle, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(inverse_lerp, sarray("from", "to", "weight"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(range_lerp, sarray("value", "istart", "istop", "ostart", "ostop"), Variant::UTILITY_FUNC_TYPE_MATH);
@@ -1216,6 +1267,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH);
+ FUNCBINDVR3(wrap, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 863ea899ee..1a0253a28b 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -106,6 +106,17 @@
[/codeblock]
</description>
</method>
+ <method name="bezier_interpolate">
+ <return type="float" />
+ <argument index="0" name="start" type="float" />
+ <argument index="1" name="control_1" type="float" />
+ <argument index="2" name="control_2" type="float" />
+ <argument index="3" name="end" type="float" />
+ <argument index="4" name="t" type="float" />
+ <description>
+ Returns the point at the given [code]t[/code] on a one-dimnesional [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
+ </description>
+ </method>
<method name="bytes2var">
<return type="Variant" />
<argument index="0" name="bytes" type="PackedByteArray" />
@@ -1040,6 +1051,27 @@
A weak reference to an object is not enough to keep the object alive: when the only remaining references to a referent are weak references, garbage collection is free to destroy the referent and reuse its memory for something else. However, until the object is actually destroyed the weak reference may return the object even if there are no strong references to it.
</description>
</method>
+ <method name="wrap">
+ <return type="Variant" />
+ <argument index="0" name="value" type="Variant" />
+ <argument index="1" name="min" type="Variant" />
+ <argument index="2" name="max" type="Variant" />
+ <description>
+ Wraps the [Variant] [code]value[/code] between [code]min[/code] and [code]max[/code].
+ Usable for creating loop-alike behavior or infinite surfaces.
+ Variant types [int] and [float] (real) are supported. If any of the argument is [float] the result will be [float], otherwise it is [int].
+ [codeblock]
+ var a = wrap(4, 5, 10)
+ # a is 9 (int)
+
+ var a = wrap(7, 5, 10)
+ # a is 7 (int)
+
+ var a = wrap(10.5, 5, 10)
+ # a is 5.5 (float)
+ [/codeblock]
+ </description>
+ </method>
<method name="wrapf">
<return type="float" />
<argument index="0" name="value" type="float" />
@@ -2521,21 +2553,24 @@
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="20" enum="PropertyHint">
Hints that a string property is text with line breaks. Editing it will show a text input field where line breaks can be typed.
</constant>
- <constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="21" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_EXPRESSION" value="21" enum="PropertyHint">
+ Hints that a string property is an [Expression].
+ </constant>
+ <constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="22" enum="PropertyHint">
Hints that a string property should have a placeholder text visible on its input field, whenever the property is empty. The hint string is the placeholder text to use.
</constant>
- <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="22" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="23" enum="PropertyHint">
Hints that a color property should be edited without changing its alpha component, i.e. only R, G and B channels are edited.
</constant>
- <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="23" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="24" enum="PropertyHint">
Hints that an image is compressed using lossy compression.
</constant>
- <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="24" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="25" enum="PropertyHint">
Hints that an image is compressed using lossless compression.
</constant>
- <constant name="PROPERTY_HINT_OBJECT_ID" value="25" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_OBJECT_ID" value="26" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_TYPE_STRING" value="26" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_TYPE_STRING" value="27" enum="PropertyHint">
Hint that a property represents a particular type. If a property is [constant TYPE_STRING], allows to set a type from the create dialog. If you need to create an [Array] to contain elements of a specific type, the [code]hint_string[/code] must encode nested types using [code]":"[/code] and [code]"/"[/code] for specifying [Resource] types. For instance:
[codeblock]
hint_string = "%s:" % [TYPE_INT] # Array of inteters.
@@ -2545,47 +2580,47 @@
[/codeblock]
[b]Note:[/b] The final colon is required to specify for properly detecting built-in types.
</constant>
- <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="27" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE" value="28" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="28" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_METHOD_OF_VARIANT_TYPE" value="29" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="29" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_METHOD_OF_BASE_TYPE" value="30" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="30" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_METHOD_OF_INSTANCE" value="31" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="31" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_METHOD_OF_SCRIPT" value="32" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="32" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE" value="33" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="33" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_PROPERTY_OF_BASE_TYPE" value="34" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="34" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_PROPERTY_OF_INSTANCE" value="35" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="35" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_PROPERTY_OF_SCRIPT" value="36" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="36" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_OBJECT_TOO_BIG" value="37" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="37" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_NODE_PATH_VALID_TYPES" value="38" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_SAVE_FILE" value="38" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_SAVE_FILE" value="39" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="39" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_GLOBAL_SAVE_FILE" value="40" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="40" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_OBJECTID" value="41" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_INT_IS_POINTER" value="42" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_INT_IS_POINTER" value="43" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_ARRAY_TYPE" value="41" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_ARRAY_TYPE" value="42" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_LOCALE_ID" value="43" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALE_ID" value="44" enum="PropertyHint">
Hints that a string property is a locale code. Editing it will show a locale dialog for picking language and country.
</constant>
- <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="44" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_LOCALIZABLE_STRING" value="45" enum="PropertyHint">
Hints that a dictionary property is string translation map. Dictionary keys are locale codes and, values are translated strings.
</constant>
- <constant name="PROPERTY_HINT_NODE_TYPE" value="45" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_NODE_TYPE" value="46" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_MAX" value="46" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_MAX" value="47" enum="PropertyHint">
</constant>
<constant name="PROPERTY_USAGE_NONE" value="0" enum="PropertyUsageFlags">
</constant>
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
index b37de09e04..50979c9b68 100644
--- a/doc/classes/Expression.xml
+++ b/doc/classes/Expression.xml
@@ -56,6 +56,7 @@
<argument index="0" name="inputs" type="Array" default="[]" />
<argument index="1" name="base_instance" type="Object" default="null" />
<argument index="2" name="show_error" type="bool" default="true" />
+ <argument index="3" name="const_calls_only" type="bool" default="false" />
<description>
Executes the expression that was previously parsed by [method parse] and returns the result. Before you use the returned object, you should check if the method failed by calling [method has_execute_failed].
If you defined input variables in [method parse], you can specify their values in the inputs array, in the same order.
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index 6ccc0fc6a6..454db51919 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -85,6 +85,16 @@
Returns the aspect ratio of this vector, the ratio of [member x] to [member y].
</description>
</method>
+ <method name="bezier_interpolate" qualifiers="const">
+ <return type="Vector2" />
+ <argument index="0" name="control_1" type="Vector2" />
+ <argument index="1" name="control_2" type="Vector2" />
+ <argument index="2" name="end" type="Vector2" />
+ <argument index="3" name="t" type="float" />
+ <description>
+ Returns the point at the given [code]t[/code] on the [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by this vector and the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
+ </description>
+ </method>
<method name="bounce" qualifiers="const">
<return type="Vector2" />
<argument index="0" name="n" type="Vector2" />
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index d907ceb52b..c181720a66 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -61,6 +61,16 @@
Returns the unsigned minimum angle to the given vector, in radians.
</description>
</method>
+ <method name="bezier_interpolate" qualifiers="const">
+ <return type="Vector3" />
+ <argument index="0" name="control_1" type="Vector3" />
+ <argument index="1" name="control_2" type="Vector3" />
+ <argument index="2" name="end" type="Vector3" />
+ <argument index="3" name="t" type="float" />
+ <description>
+ Returns the point at the given [code]t[/code] on the [url=https://en.wikipedia.org/wiki/B%C3%A9zier_curve]Bezier curve[/url] defined by this vector and the given [code]control_1[/code], [code]control_2[/code], and [code]end[/code] points.
+ </description>
+ </method>
<method name="bounce" qualifiers="const">
<return type="Vector3" />
<argument index="0" name="n" type="Vector3" />
diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp
index f0650ee446..391cd009f1 100644
--- a/editor/animation_bezier_editor.cpp
+++ b/editor/animation_bezier_editor.cpp
@@ -44,17 +44,6 @@ float AnimationBezierTrackEdit::_bezier_h_to_pixel(float p_h) {
return h;
}
-static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
- /* Formula from Wikipedia article on Bezier curves. */
- real_t omt = (1.0 - t);
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
-
- return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
-}
-
void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
float scale = timeline->get_zoom_scale();
@@ -151,7 +140,7 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
for (int k = 0; k < iterations; k++) {
float middle = (low + high) / 2;
- Vector2 interp = _bezier_interp(middle, start, out_handle, in_handle, end);
+ Vector2 interp = start.bezier_interpolate(out_handle, in_handle, end, middle);
if (interp.x < t) {
low = middle;
@@ -161,8 +150,8 @@ void AnimationBezierTrackEdit::_draw_track(int p_track, const Color &p_color) {
}
//interpolate the result:
- Vector2 low_pos = _bezier_interp(low, start, out_handle, in_handle, end);
- Vector2 high_pos = _bezier_interp(high, start, out_handle, in_handle, end);
+ Vector2 low_pos = start.bezier_interpolate(out_handle, in_handle, end, low);
+ Vector2 high_pos = start.bezier_interpolate(out_handle, in_handle, end, high);
float c = (t - low_pos.x) / (high_pos.x - low_pos.x);
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index 7bc01e36f2..aff328bba7 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -165,6 +165,9 @@ void EditorPropertyMultilineText::_notification(int p_what) {
Ref<Font> font = get_theme_font(SNAME("font"), SNAME("Label"));
int font_size = get_theme_font_size(SNAME("font_size"), SNAME("Label"));
text->set_custom_minimum_size(Vector2(0, font->get_height(font_size) * 6));
+ text->add_theme_font_override("font", get_theme_font("expression", "EditorFonts"));
+ text->add_theme_font_size_override("font_size", get_theme_font_size("expression_size", "EditorFonts"));
+
} break;
}
}
@@ -172,7 +175,7 @@ void EditorPropertyMultilineText::_notification(int p_what) {
void EditorPropertyMultilineText::_bind_methods() {
}
-EditorPropertyMultilineText::EditorPropertyMultilineText() {
+EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override("separation", 0);
add_child(hb);
@@ -189,6 +192,12 @@ EditorPropertyMultilineText::EditorPropertyMultilineText() {
hb->add_child(open_big_text);
big_text_dialog = nullptr;
big_text = nullptr;
+ if (p_expression) {
+ expression = true;
+ Ref<EditorStandardSyntaxHighlighter> highlighter;
+ highlighter.instantiate();
+ text->set_syntax_highlighter(highlighter);
+ }
}
///////////////////// TEXT ENUM /////////////////////////
@@ -3775,6 +3784,9 @@ EditorProperty *EditorInspectorDefaultPlugin::get_editor_for_property(Object *p_
} else if (p_hint == PROPERTY_HINT_MULTILINE_TEXT) {
EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText);
return editor;
+ } else if (p_hint == PROPERTY_HINT_EXPRESSION) {
+ EditorPropertyMultilineText *editor = memnew(EditorPropertyMultilineText(true));
+ return editor;
} else if (p_hint == PROPERTY_HINT_TYPE_STRING) {
EditorPropertyClassName *editor = memnew(EditorPropertyClassName);
editor->setup("Object", p_hint_text);
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index 72b2b0b283..7cd6ea4f6b 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -81,6 +81,7 @@ class EditorPropertyMultilineText : public EditorProperty {
void _big_text_changed();
void _text_changed();
void _open_big_text();
+ bool expression = false;
protected:
virtual void _set_read_only(bool p_read_only) override;
@@ -89,7 +90,7 @@ protected:
public:
virtual void update_property() override;
- EditorPropertyMultilineText();
+ EditorPropertyMultilineText(bool p_expression = false);
};
class EditorPropertyTextEnum : public EditorProperty {
diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp
index a0c818ba84..f23f0cf758 100644
--- a/editor/editor_spin_slider.cpp
+++ b/editor/editor_spin_slider.cpp
@@ -530,7 +530,7 @@ void EditorSpinSlider::_evaluate_input_text() {
return;
}
- Variant v = expr->execute(Array(), nullptr, false);
+ Variant v = expr->execute(Array(), nullptr, false, true);
if (v.get_type() == Variant::NIL) {
return;
}
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 05c707c065..7d4ffd1a25 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1196,7 +1196,7 @@ void ScriptTextEditor::_edit_option(int p_op) {
String whitespace = line.substr(0, line.size() - line.strip_edges(true, false).size()); //extract the whitespace at the beginning
if (expression.parse(line) == OK) {
- Variant result = expression.execute(Array(), Variant(), false);
+ Variant result = expression.execute(Array(), Variant(), false, true);
if (expression.get_error_text().is_empty()) {
results.push_back(whitespace + result.get_construct_string());
} else {
diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp
index 771d34d841..d936e821df 100644
--- a/editor/property_editor.cpp
+++ b/editor/property_editor.cpp
@@ -1474,7 +1474,7 @@ void CustomPropertyEditor::_modified(String p_string) {
v = value_editor[0]->get_text().to_int();
return;
} else {
- v = expr->execute(Array(), nullptr, false);
+ v = expr->execute(Array(), nullptr, false, false);
}
if (v != prev_v) {
@@ -1650,7 +1650,7 @@ real_t CustomPropertyEditor::_parse_real_expression(String text) {
if (err != OK) {
out = value_editor[0]->get_text().to_float();
} else {
- out = expr->execute(Array(), nullptr, false);
+ out = expr->execute(Array(), nullptr, false, true);
}
return out;
}
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 42b02ce3b9..ea994654bf 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -2278,6 +2278,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
push_error(vformat(R"(Too few arguments for %s constructor. Received %d but expected %d.)", Variant::get_type_name(builtin_type), p_call->arguments.size(), err.expected), p_call);
break;
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
p_call->is_constant = true;
@@ -2380,6 +2381,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
@@ -2422,6 +2424,7 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool p_is_a
case Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS:
push_error(vformat(R"*(Too few arguments for "%s()" call. Expected at least %d but received %d.)*", function_name, err.expected, p_call->arguments.size()), p_call);
break;
+ case Callable::CallError::CALL_ERROR_METHOD_NOT_CONST:
case Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL:
break; // Can't happen in a builtin constructor.
case Callable::CallError::CALL_OK:
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index 3d5a39bf38..6a1effd680 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -1336,6 +1336,18 @@ void GDScriptByteCodeGenerator::write_endif() {
if_jmp_addrs.pop_back();
}
+void GDScriptByteCodeGenerator::write_jump_if_shared(const Address &p_value) {
+ append(GDScriptFunction::OPCODE_JUMP_IF_SHARED, 1);
+ append(p_value);
+ if_jmp_addrs.push_back(opcodes.size());
+ append(0); // Jump destination, will be patched.
+}
+
+void GDScriptByteCodeGenerator::write_end_jump_if_shared() {
+ patch_jump(if_jmp_addrs.back()->get());
+ if_jmp_addrs.pop_back();
+}
+
void GDScriptByteCodeGenerator::start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) {
Address counter(Address::LOCAL_VARIABLE, add_local("@counter_pos", p_iterator_type), p_iterator_type);
Address container(Address::LOCAL_VARIABLE, add_local("@container_pos", p_list_type), p_list_type);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index 6ee8fda533..f4b402fc96 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -479,6 +479,8 @@ public:
virtual void write_if(const Address &p_condition) override;
virtual void write_else() override;
virtual void write_endif() override;
+ virtual void write_jump_if_shared(const Address &p_value) override;
+ virtual void write_end_jump_if_shared() override;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) override;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) override;
virtual void write_for() override;
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 326b66a295..81fa265aca 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -140,6 +140,8 @@ public:
virtual void write_if(const Address &p_condition) = 0;
virtual void write_else() = 0;
virtual void write_endif() = 0;
+ virtual void write_jump_if_shared(const Address &p_value) = 0;
+ virtual void write_end_jump_if_shared() = 0;
virtual void start_for(const GDScriptDataType &p_iterator_type, const GDScriptDataType &p_list_type) = 0;
virtual void write_for_assignment(const Address &p_variable, const Address &p_list) = 0;
virtual void write_for() = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 16a0b17d61..1daf8ff7a9 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1056,13 +1056,25 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
// Set back the values into their bases.
for (const ChainInfo &info : set_chain) {
- if (!info.is_named) {
- gen->write_set(info.base, info.key, assigned);
- if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
- gen->pop_temporary();
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type) {
+ // Jump shared values since they are already updated in-place.
+ gen->write_jump_if_shared(assigned);
+ }
+ if (known_type && !is_shared) {
+ if (!info.is_named) {
+ gen->write_set(info.base, info.key, assigned);
+ if (info.key.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
+ gen->pop_temporary();
+ }
+ } else {
+ gen->write_set_named(info.base, info.name, assigned);
}
- } else {
- gen->write_set_named(info.base, info.name, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
}
if (assigned.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
gen->pop_temporary();
@@ -1070,19 +1082,35 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
assigned = info.base;
}
- // If this is a class member property, also assign to it.
- // This allow things like: position.x += 2.0
- if (assign_class_member_property != StringName()) {
- gen->write_set_member(assigned, assign_class_member_property);
- }
- // Same as above but for members
- if (is_member_property) {
- if (member_property_has_setter && !member_property_is_in_setter) {
- Vector<GDScriptCodeGenerator::Address> args;
- args.push_back(assigned);
- gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
- } else {
- gen->write_assign(target_member_property, assigned);
+ bool known_type = assigned.type.has_type;
+ bool is_shared = Variant::is_type_shared(assigned.type.builtin_type);
+
+ if (!known_type || !is_shared) {
+ // If this is a class member property, also assign to it.
+ // This allow things like: position.x += 2.0
+ if (assign_class_member_property != StringName()) {
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ gen->write_set_member(assigned, assign_class_member_property);
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
+ } else if (is_member_property) {
+ // Same as above but for script members.
+ if (!known_type) {
+ gen->write_jump_if_shared(assigned);
+ }
+ if (member_property_has_setter && !member_property_is_in_setter) {
+ Vector<GDScriptCodeGenerator::Address> args;
+ args.push_back(assigned);
+ gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), member_property_setter_function, args);
+ } else {
+ gen->write_assign(target_member_property, assigned);
+ }
+ if (!known_type) {
+ gen->write_end_jump_if_shared();
+ }
}
}
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index dc114f2eff..726f0efe2b 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -838,6 +838,14 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
incr = 1;
} break;
+ case OPCODE_JUMP_IF_SHARED: {
+ text += "jump-if-shared ";
+ text += DADDR(1);
+ text += " to ";
+ text += itos(_code_ptr[ip + 2]);
+
+ incr = 3;
+ } break;
case OPCODE_RETURN: {
text += "return ";
text += DADDR(1);
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index d2ca795977..3f1265679b 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -304,6 +304,7 @@ public:
OPCODE_JUMP_IF,
OPCODE_JUMP_IF_NOT,
OPCODE_JUMP_TO_DEF_ARGUMENT,
+ OPCODE_JUMP_IF_SHARED,
OPCODE_RETURN,
OPCODE_RETURN_TYPED_BUILTIN,
OPCODE_RETURN_TYPED_ARRAY,
diff --git a/modules/gdscript/gdscript_vm.cpp b/modules/gdscript/gdscript_vm.cpp
index 20b8d29ec3..16a8e728e4 100644
--- a/modules/gdscript/gdscript_vm.cpp
+++ b/modules/gdscript/gdscript_vm.cpp
@@ -177,6 +177,8 @@ String GDScriptFunction::_get_call_error(const Callable::CallError &p_err, const
err_text = "Invalid call. Nonexistent " + p_where + ".";
} else if (p_err.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
err_text = "Attempt to call " + p_where + " on a null instance.";
+ } else if (p_err.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ err_text = "Attempt to call " + p_where + " on a const instance.";
} else {
err_text = "Bug, call error: #" + itos(p_err.error);
}
@@ -311,6 +313,7 @@ void (*type_init_function_table[])(Variant *) = {
&&OPCODE_JUMP_IF, \
&&OPCODE_JUMP_IF_NOT, \
&&OPCODE_JUMP_TO_DEF_ARGUMENT, \
+ &&OPCODE_JUMP_IF_SHARED, \
&&OPCODE_RETURN, \
&&OPCODE_RETURN_TYPED_BUILTIN, \
&&OPCODE_RETURN_TYPED_ARRAY, \
@@ -2361,6 +2364,21 @@ Variant GDScriptFunction::call(GDScriptInstance *p_instance, const Variant **p_a
}
DISPATCH_OPCODE;
+ OPCODE(OPCODE_JUMP_IF_SHARED) {
+ CHECK_SPACE(3);
+
+ GET_INSTRUCTION_ARG(val, 0);
+
+ if (val->is_shared()) {
+ int to = _code_ptr[ip + 2];
+ GD_ERR_BREAK(to < 0 || to > _code_size);
+ ip = to;
+ } else {
+ ip += 3;
+ }
+ }
+ DISPATCH_OPCODE;
+
OPCODE(OPCODE_RETURN) {
CHECK_SPACE(2);
GET_INSTRUCTION_ARG(r, 0);
diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp
index c4fafb6676..c5bcf23c8e 100644
--- a/modules/visual_script/visual_script.cpp
+++ b/modules/visual_script/visual_script.cpp
@@ -1655,6 +1655,8 @@ Variant VisualScriptInstance::_call_internal(const StringName &p_method, void *p
error_str += "Expected " + itos(r_error.argument) + " arguments.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INVALID_METHOD) {
error_str += "Invalid Call.";
+ } else if (r_error.error == Callable::CallError::CALL_ERROR_METHOD_NOT_CONST) {
+ error_str += "Method not const in a const instance.";
} else if (r_error.error == Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL) {
error_str += "Base Instance is null";
}
diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp
index 60858b00b1..4c00250162 100644
--- a/scene/3d/node_3d.cpp
+++ b/scene/3d/node_3d.cpp
@@ -85,12 +85,20 @@ void Node3D::_notify_dirty() {
}
void Node3D::_update_local_transform() const {
- if (this->get_rotation_edit_mode() != ROTATION_EDIT_MODE_BASIS) {
- data.local_transform = data.local_transform.orthogonalized();
- }
- data.local_transform.basis.set_euler_scale(data.rotation, data.scale);
+ // This function is called when the local transform (data.local_transform) is dirty and the right value is contained in the Euler rotation and scale.
+
+ data.local_transform.basis.set_euler_scale(data.euler_rotation, data.scale, data.euler_rotation_order);
- data.dirty &= ~DIRTY_LOCAL;
+ data.dirty &= ~DIRTY_LOCAL_TRANSFORM;
+}
+
+void Node3D::_update_rotation_and_scale() const {
+ // This function is called when the Euler rotation (data.euler_rotation) is dirty and the right value is contained in the local transform
+
+ data.scale = data.local_transform.basis.get_scale();
+ data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order);
+
+ data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE;
}
void Node3D::_propagate_transform_changed(Node3D *p_origin) {
@@ -113,7 +121,7 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) {
#endif
get_tree()->xform_change_list.add(&xform_change);
}
- data.dirty |= DIRTY_GLOBAL;
+ data.dirty |= DIRTY_GLOBAL_TRANSFORM;
data.children_lock--;
}
@@ -137,12 +145,12 @@ void Node3D::_notification(int p_what) {
if (data.top_level && !Engine::get_singleton()->is_editor_hint()) {
if (data.parent) {
data.local_transform = data.parent->get_global_transform() * get_transform();
- data.dirty = DIRTY_VECTORS; //global is always dirty upon entering a scene
+ data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // As local transform was updated, rot/scale should be dirty.
}
data.top_level_active = true;
}
- data.dirty |= DIRTY_GLOBAL; //global is always dirty upon entering a scene
+ data.dirty |= DIRTY_GLOBAL_TRANSFORM; // Global is always dirty upon entering a scene.
_notify_dirty();
notification(NOTIFICATION_ENTER_WORLD);
@@ -212,12 +220,27 @@ void Node3D::set_basis(const Basis &p_basis) {
set_transform(Transform3D(p_basis, data.local_transform.origin));
}
void Node3D::set_quaternion(const Quaternion &p_quaternion) {
- set_transform(Transform3D(Basis(p_quaternion), data.local_transform.origin));
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ // We need the scale part, so if these are dirty, update it
+ data.scale = data.local_transform.basis.get_scale();
+ data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE;
+ }
+ data.local_transform.basis = Basis(p_quaternion, data.scale);
+ // Rotscale should not be marked dirty because that would cause precision loss issues with the scale. Instead reconstruct rotation now.
+ data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order);
+
+ data.dirty = DIRTY_NONE;
+
+ _propagate_transform_changed(this);
+ if (data.notify_local_transform) {
+ notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+ }
}
void Node3D::set_transform(const Transform3D &p_transform) {
data.local_transform = p_transform;
- data.dirty |= DIRTY_VECTORS;
+ data.dirty = DIRTY_EULER_ROTATION_AND_SCALE; // Make rot/scale dirty.
+
_propagate_transform_changed(this);
if (data.notify_local_transform) {
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
@@ -227,8 +250,13 @@ void Node3D::set_transform(const Transform3D &p_transform) {
Basis Node3D::get_basis() const {
return get_transform().basis;
}
+
Quaternion Node3D::get_quaternion() const {
- return Quaternion(get_transform().basis);
+ if (data.dirty & DIRTY_LOCAL_TRANSFORM) {
+ _update_local_transform();
+ }
+
+ return data.local_transform.basis.get_rotation_quaternion();
}
void Node3D::set_global_transform(const Transform3D &p_transform) {
@@ -240,7 +268,7 @@ void Node3D::set_global_transform(const Transform3D &p_transform) {
}
Transform3D Node3D::get_transform() const {
- if (data.dirty & DIRTY_LOCAL) {
+ if (data.dirty & DIRTY_LOCAL_TRANSFORM) {
_update_local_transform();
}
@@ -249,8 +277,8 @@ Transform3D Node3D::get_transform() const {
Transform3D Node3D::get_global_transform() const {
ERR_FAIL_COND_V(!is_inside_tree(), Transform3D());
- if (data.dirty & DIRTY_GLOBAL) {
- if (data.dirty & DIRTY_LOCAL) {
+ if (data.dirty & DIRTY_GLOBAL_TRANSFORM) {
+ if (data.dirty & DIRTY_LOCAL_TRANSFORM) {
_update_local_transform();
}
@@ -264,7 +292,7 @@ Transform3D Node3D::get_global_transform() const {
data.global_transform.basis.orthonormalize();
}
- data.dirty &= ~DIRTY_GLOBAL;
+ data.dirty &= ~DIRTY_GLOBAL_TRANSFORM;
}
return data.global_transform;
@@ -314,13 +342,27 @@ void Node3D::set_rotation_edit_mode(RotationEditMode p_mode) {
if (data.rotation_edit_mode == p_mode) {
return;
}
+
+ bool transform_changed = false;
+ if (data.rotation_edit_mode == ROTATION_EDIT_MODE_BASIS && !(data.dirty & DIRTY_LOCAL_TRANSFORM)) {
+ data.local_transform.orthogonalize();
+ transform_changed = true;
+ }
+
data.rotation_edit_mode = p_mode;
- // Shearing is not allowed except in ROTATION_EDIT_MODE_BASIS.
- data.dirty |= DIRTY_LOCAL;
- _propagate_transform_changed(this);
- if (data.notify_local_transform) {
- notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+ if (p_mode == ROTATION_EDIT_MODE_EULER && (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE)) {
+ // If going to Euler mode, ensure that vectors are _not_ dirty, else the retrieved value may be wrong.
+ // Otherwise keep what is there, so switching back and forth between modes does not break the vectors.
+
+ _update_rotation_and_scale();
+ }
+
+ if (transform_changed) {
+ _propagate_transform_changed(this);
+ if (data.notify_local_transform) {
+ notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+ }
}
notify_property_list_changed();
@@ -333,38 +375,47 @@ Node3D::RotationEditMode Node3D::get_rotation_edit_mode() const {
void Node3D::set_rotation_order(RotationOrder p_order) {
Basis::EulerOrder order = Basis::EulerOrder(p_order);
- if (data.rotation_order == order) {
+ if (data.euler_rotation_order == order) {
return;
}
ERR_FAIL_INDEX(int32_t(order), 6);
+ bool transform_changed = false;
- if (data.dirty & DIRTY_VECTORS) {
- data.rotation = data.local_transform.basis.get_euler_normalized(order);
- data.scale = data.local_transform.basis.get_scale();
- data.dirty &= ~DIRTY_VECTORS;
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ _update_rotation_and_scale();
+ } else if (data.dirty & DIRTY_LOCAL_TRANSFORM) {
+ data.euler_rotation = Basis::from_euler(data.euler_rotation, data.euler_rotation_order).get_euler_normalized(order);
+ transform_changed = true;
} else {
- data.rotation = Basis::from_euler(data.rotation, data.rotation_order).get_euler_normalized(order);
+ data.dirty |= DIRTY_LOCAL_TRANSFORM;
+ transform_changed = true;
}
- data.rotation_order = order;
- //changing rotation order should not affect transform
+ data.euler_rotation_order = order;
- notify_property_list_changed(); //will change rotation
+ if (transform_changed) {
+ _propagate_transform_changed(this);
+ if (data.notify_local_transform) {
+ notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
+ }
+ }
+ notify_property_list_changed(); // Will change the rotation property.
}
Node3D::RotationOrder Node3D::get_rotation_order() const {
- return RotationOrder(data.rotation_order);
+ return RotationOrder(data.euler_rotation_order);
}
void Node3D::set_rotation(const Vector3 &p_euler_rad) {
- if (data.dirty & DIRTY_VECTORS) {
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ // Update scale only if rotation and scale are dirty, as rotation will be overridden.
data.scale = data.local_transform.basis.get_scale();
- data.dirty &= ~DIRTY_VECTORS;
+ data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE;
}
- data.rotation = p_euler_rad;
- data.dirty |= DIRTY_LOCAL;
+ data.euler_rotation = p_euler_rad;
+ data.dirty = DIRTY_LOCAL_TRANSFORM;
_propagate_transform_changed(this);
if (data.notify_local_transform) {
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
@@ -372,13 +423,14 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) {
}
void Node3D::set_scale(const Vector3 &p_scale) {
- if (data.dirty & DIRTY_VECTORS) {
- data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
- data.dirty &= ~DIRTY_VECTORS;
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ // Update rotation only if rotation and scale are dirty, as scale will be overridden.
+ data.euler_rotation = data.local_transform.basis.get_euler_normalized(data.euler_rotation_order);
+ data.dirty &= ~DIRTY_EULER_ROTATION_AND_SCALE;
}
data.scale = p_scale;
- data.dirty |= DIRTY_LOCAL;
+ data.dirty = DIRTY_LOCAL_TRANSFORM;
_propagate_transform_changed(this);
if (data.notify_local_transform) {
notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED);
@@ -390,22 +442,16 @@ Vector3 Node3D::get_position() const {
}
Vector3 Node3D::get_rotation() const {
- if (data.dirty & DIRTY_VECTORS) {
- data.scale = data.local_transform.basis.get_scale();
- data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
-
- data.dirty &= ~DIRTY_VECTORS;
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ _update_rotation_and_scale();
}
- return data.rotation;
+ return data.euler_rotation;
}
Vector3 Node3D::get_scale() const {
- if (data.dirty & DIRTY_VECTORS) {
- data.scale = data.local_transform.basis.get_scale();
- data.rotation = data.local_transform.basis.get_euler_normalized(data.rotation_order);
-
- data.dirty &= ~DIRTY_VECTORS;
+ if (data.dirty & DIRTY_EULER_ROTATION_AND_SCALE) {
+ _update_rotation_and_scale();
}
return data.scale;
@@ -865,14 +911,14 @@ Variant Node3D::property_get_revert(const String &p_name) {
} else if (p_name == "quaternion") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
- r_ret = Quaternion(Transform3D(variant).get_basis());
+ r_ret = Quaternion(Transform3D(variant).get_basis().get_rotation_quaternion());
} else {
return Quaternion();
}
} else if (p_name == "rotation") {
Variant variant = PropertyUtils::get_property_default_value(this, "transform", &valid);
if (valid && variant.get_type() == Variant::Type::TRANSFORM3D) {
- r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.rotation_order);
+ r_ret = Transform3D(variant).get_basis().get_euler_normalized(data.euler_rotation_order);
} else {
return Vector3();
}
diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h
index 6d857a83ea..cfd88585e4 100644
--- a/scene/3d/node_3d.h
+++ b/scene/3d/node_3d.h
@@ -52,6 +52,9 @@ class Node3D : public Node {
GDCLASS(Node3D, Node);
public:
+ // Edit mode for the rotation.
+ // THIS MODE ONLY AFFECTS HOW DATA IS EDITED AND SAVED
+ // IT DOES _NOT_ AFFECT THE TRANSFORM LOGIC (see comment in TransformDirty).
enum RotationEditMode {
ROTATION_EDIT_MODE_EULER,
ROTATION_EDIT_MODE_QUATERNION,
@@ -68,11 +71,27 @@ public:
};
private:
+ // For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaterinon/Scale and Euler Rotation/Scale.
+ // Transform and Quaterinon are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand).
+ // Euler needs to be kept separate because converting to Basis and back may result in a different vector (which is troublesome for users
+ // editing in the inspector, not only because of the numerical precision loss but because they expect these rotations to be consistent, or support
+ // "redundant" rotations for animation interpolation, like going from 0 to 720 degrees).
+ //
+ // As such, the system works in a way where if the local transform is set (via transform/basis/quaternion), the EULER rotation and scale becomes dirty.
+ // It will remain dirty until reading back is attempted (for performance reasons). Likewise, if the Euler rotation scale are set, the local transform
+ // will become dirty (and again, will not become valid again until read).
+ //
+ // All this is transparent from outside the Node3D API, which allows everything to works by calling these functions in exchange.
+ //
+ // Additionally, setting either transform, quaternion, Euler rotation or scale makes the global transform dirty, which will be updated when read again.
+ //
+ // NOTE: Again, RotationEditMode is _independent_ of this mechanism, it is only meant to expose the right set of properties for editing (editor) and saving
+ // (to scene, in order to keep the same values and avoid data loss on conversions). It has zero influence in the logic described above.
enum TransformDirty {
DIRTY_NONE = 0,
- DIRTY_VECTORS = 1,
- DIRTY_LOCAL = 2,
- DIRTY_GLOBAL = 4
+ DIRTY_EULER_ROTATION_AND_SCALE = 1,
+ DIRTY_LOCAL_TRANSFORM = 2,
+ DIRTY_GLOBAL_TRANSFORM = 4
};
mutable SelfList<Node> xform_change;
@@ -80,8 +99,8 @@ private:
struct Data {
mutable Transform3D global_transform;
mutable Transform3D local_transform;
- mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ;
- mutable Vector3 rotation;
+ mutable Basis::EulerOrder euler_rotation_order = Basis::EULER_ORDER_YXZ;
+ mutable Vector3 euler_rotation;
mutable Vector3 scale = Vector3(1, 1, 1);
mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
@@ -131,6 +150,7 @@ protected:
_FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; }
_FORCE_INLINE_ void _update_local_transform() const;
+ _FORCE_INLINE_ void _update_rotation_and_scale() const;
void _notification(int p_what);
static void _bind_methods();
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index e50d7e765c..890e349afb 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -62,7 +62,7 @@ void SpinBox::_text_submitted(const String &p_string) {
return;
}
- Variant value = expr->execute(Array(), nullptr, false);
+ Variant value = expr->execute(Array(), nullptr, false, true);
if (value.get_type() != Variant::NIL) {
set_value(value);
}
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 8ae4872d14..7183accc66 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -3379,17 +3379,6 @@ Vector2 Animation::bezier_track_get_key_out_handle(int p_track, int p_index) con
return bt->values[p_index].value.out_handle;
}
-static _FORCE_INLINE_ Vector2 _bezier_interp(real_t t, const Vector2 &start, const Vector2 &control_1, const Vector2 &control_2, const Vector2 &end) {
- /* Formula from Wikipedia article on Bezier curves. */
- real_t omt = (1.0 - t);
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = t * t;
- real_t t3 = t2 * t;
-
- return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
-}
-
real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
//this uses a different interpolation scheme
ERR_FAIL_INDEX_V(p_track, tracks.size(), 0);
@@ -3438,7 +3427,7 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
for (int i = 0; i < iterations; i++) {
real_t middle = (low + high) / 2;
- Vector2 interp = _bezier_interp(middle, start, start_out, end_in, end);
+ Vector2 interp = start.bezier_interpolate(start_out, end_in, end, middle);
if (interp.x < t) {
low = middle;
@@ -3448,8 +3437,8 @@ real_t Animation::bezier_track_interpolate(int p_track, double p_time) const {
}
//interpolate the result:
- Vector2 low_pos = _bezier_interp(low, start, start_out, end_in, end);
- Vector2 high_pos = _bezier_interp(high, start, start_out, end_in, end);
+ Vector2 low_pos = start.bezier_interpolate(start_out, end_in, end, low);
+ Vector2 high_pos = start.bezier_interpolate(start_out, end_in, end, high);
real_t c = (t - low_pos.x) / (high_pos.x - low_pos.x);
return low_pos.lerp(high_pos, c).y;
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 96cf7bb708..da26a0261f 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -32,18 +32,6 @@
#include "core/core_string_names.h"
-template <class T>
-static _FORCE_INLINE_ T _bezier_interp(real_t p_t, T p_start, T p_control_1, T p_control_2, T p_end) {
- /* Formula from Wikipedia article on Bezier curves. */
- real_t omt = (1.0 - p_t);
- real_t omt2 = omt * omt;
- real_t omt3 = omt2 * omt;
- real_t t2 = p_t * p_t;
- real_t t3 = t2 * p_t;
-
- return p_start * omt3 + p_control_1 * omt2 * p_t * 3.0 + p_control_2 * omt * t2 * 3.0 + p_end * t3;
-}
-
const char *Curve::SIGNAL_RANGE_CHANGED = "range_changed";
Curve::Curve() {
@@ -376,7 +364,7 @@ real_t Curve::interpolate_local_nocheck(int p_index, real_t p_local_offset) cons
real_t yac = a.position.y + d * a.right_tangent;
real_t ybc = b.position.y - d * b.left_tangent;
- real_t y = _bezier_interp(p_local_offset, a.position.y, yac, ybc, b.position.y);
+ real_t y = Math::bezier_interpolate(a.position.y, yac, ybc, b.position.y, p_local_offset);
return y;
}
@@ -747,7 +735,7 @@ Vector2 Curve2D::interpolate(int p_index, const real_t p_offset) const {
Vector2 p3 = points[p_index + 1].position;
Vector2 p2 = p3 + points[p_index + 1].in;
- return _bezier_interp(p_offset, p0, p1, p2, p3);
+ return p0.bezier_interpolate(p1, p2, p3, p_offset);
}
Vector2 Curve2D::interpolatef(real_t p_findex) const {
@@ -767,9 +755,9 @@ void Curve2D::mark_dirty() {
void Curve2D::_bake_segment2d(RBMap<real_t, Vector2> &r_bake, real_t p_begin, real_t p_end, const Vector2 &p_a, const Vector2 &p_out, const Vector2 &p_b, const Vector2 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
real_t mp = p_begin + (p_end - p_begin) * 0.5;
- Vector2 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
- Vector2 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
- Vector2 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
+ Vector2 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin);
+ Vector2 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp);
+ Vector2 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end);
Vector2 na = (mid - beg).normalized();
Vector2 nb = (end - mid).normalized();
@@ -828,7 +816,7 @@ void Curve2D::_bake() const {
np = 1.0;
}
- Vector2 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ Vector2 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np);
real_t d = position.distance_to(npp);
if (d > bake_interval) {
@@ -841,7 +829,7 @@ void Curve2D::_bake() const {
real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
- npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid);
d = position.distance_to(npp);
if (bake_interval < d) {
@@ -1336,7 +1324,7 @@ Vector3 Curve3D::interpolate(int p_index, real_t p_offset) const {
Vector3 p3 = points[p_index + 1].position;
Vector3 p2 = p3 + points[p_index + 1].in;
- return _bezier_interp(p_offset, p0, p1, p2, p3);
+ return p0.bezier_interpolate(p1, p2, p3, p_offset);
}
Vector3 Curve3D::interpolatef(real_t p_findex) const {
@@ -1356,9 +1344,9 @@ void Curve3D::mark_dirty() {
void Curve3D::_bake_segment3d(RBMap<real_t, Vector3> &r_bake, real_t p_begin, real_t p_end, const Vector3 &p_a, const Vector3 &p_out, const Vector3 &p_b, const Vector3 &p_in, int p_depth, int p_max_depth, real_t p_tol) const {
real_t mp = p_begin + (p_end - p_begin) * 0.5;
- Vector3 beg = _bezier_interp(p_begin, p_a, p_a + p_out, p_b + p_in, p_b);
- Vector3 mid = _bezier_interp(mp, p_a, p_a + p_out, p_b + p_in, p_b);
- Vector3 end = _bezier_interp(p_end, p_a, p_a + p_out, p_b + p_in, p_b);
+ Vector3 beg = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_begin);
+ Vector3 mid = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, mp);
+ Vector3 end = p_a.bezier_interpolate(p_a + p_out, p_b + p_in, p_b, p_end);
Vector3 na = (mid - beg).normalized();
Vector3 nb = (end - mid).normalized();
@@ -1426,7 +1414,7 @@ void Curve3D::_bake() const {
np = 1.0;
}
- Vector3 npp = _bezier_interp(np, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ Vector3 npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, np);
real_t d = position.distance_to(npp);
if (d > bake_interval) {
@@ -1439,7 +1427,7 @@ void Curve3D::_bake() const {
real_t mid = low + (hi - low) * 0.5;
for (int j = 0; j < iterations; j++) {
- npp = _bezier_interp(mid, points[i].position, points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position);
+ npp = points[i].position.bezier_interpolate(points[i].position + points[i].out, points[i + 1].position + points[i + 1].in, points[i + 1].position, mid);
d = position.distance_to(npp);
if (bake_interval < d) {
diff --git a/scene/resources/gradient.h b/scene/resources/gradient.h
index a3d3449099..2b04ead0af 100644
--- a/scene/resources/gradient.h
+++ b/scene/resources/gradient.h
@@ -92,10 +92,6 @@ public:
void set_interpolation_mode(InterpolationMode p_interp_mode);
InterpolationMode get_interpolation_mode();
- _FORCE_INLINE_ float cubic_interpolate(float p0, float p1, float p2, float p3, float x) {
- return p1 + 0.5 * x * (p2 - p0 + x * (2.0 * p0 - 5.0 * p1 + 4.0 * p2 - p3 + x * (3.0 * (p1 - p2) + p3 - p0)));
- }
-
_FORCE_INLINE_ Color get_color_at_offset(float p_offset) {
if (points.is_empty()) {
return Color(0, 0, 0, 1);
@@ -161,10 +157,10 @@ public:
const Point &pointP3 = points[p3];
float x = (p_offset - pointFirst.offset) / (pointSecond.offset - pointFirst.offset);
- float r = cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
- float g = cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
- float b = cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
- float a = cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
+ float r = Math::cubic_interpolate(pointP0.color.r, pointFirst.color.r, pointSecond.color.r, pointP3.color.r, x);
+ float g = Math::cubic_interpolate(pointP0.color.g, pointFirst.color.g, pointSecond.color.g, pointP3.color.g, x);
+ float b = Math::cubic_interpolate(pointP0.color.b, pointFirst.color.b, pointSecond.color.b, pointP3.color.b, x);
+ float a = Math::cubic_interpolate(pointP0.color.a, pointFirst.color.a, pointSecond.color.a, pointP3.color.a, x);
return Color(r, g, b, a);
} break;