summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/io/resource_loader.cpp2
-rw-r--r--core/math/math_2d.cpp9
-rw-r--r--core/math/math_defs.h4
-rw-r--r--core/math/math_funcs.h2
-rw-r--r--core/math/matrix3.cpp126
-rw-r--r--core/math/matrix3.h20
-rw-r--r--core/math/quat.cpp4
-rw-r--r--core/math/quat.h3
-rw-r--r--core/math/vector3.h7
-rw-r--r--core/script_language.h1
-rw-r--r--core/ustring.cpp4
-rw-r--r--core/variant_call.cpp18
-rw-r--r--doc/base/classes.xml62
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp7
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp8
-rw-r--r--drivers/gles3/shader_compiler_gles3.cpp1
-rw-r--r--drivers/pulseaudio/audio_driver_pulseaudio.cpp1
-rw-r--r--editor/editor_name_dialog.cpp6
-rw-r--r--editor/editor_name_dialog.h1
-rw-r--r--editor/editor_node.cpp7
-rw-r--r--editor/editor_settings.cpp5
-rw-r--r--editor/plugins/script_editor_plugin.cpp14
-rw-r--r--editor/plugins/script_text_editor.cpp15
-rw-r--r--editor/project_manager.cpp2
-rw-r--r--editor/script_create_dialog.cpp48
-rw-r--r--editor/script_create_dialog.h4
-rw-r--r--main/main.cpp2
-rw-r--r--main/tests/test_math.cpp2
-rw-r--r--modules/dds/texture_loader_dds.cpp10
-rw-r--r--modules/gdscript/gd_editor.cpp24
-rw-r--r--modules/gdscript/gd_parser.cpp7
-rw-r--r--modules/gdscript/gd_parser.h1
-rw-r--r--modules/gdscript/gd_script.cpp4
-rw-r--r--modules/gdscript/gd_script.h1
-rw-r--r--modules/multiscript/SCsub7
-rw-r--r--modules/multiscript/config.py8
-rw-r--r--modules/multiscript/multiscript.cpp750
-rw-r--r--modules/multiscript/multiscript.h200
-rw-r--r--modules/multiscript/register_types.cpp51
-rw-r--r--modules/multiscript/register_types.h31
-rw-r--r--modules/webp/image_loader_webp.cpp10
-rw-r--r--platform/x11/context_gl_x11.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp2
-rw-r--r--scene/3d/physics_body.cpp124
-rw-r--r--scene/3d/physics_body.h17
-rw-r--r--scene/3d/ray_cast.cpp86
-rw-r--r--scene/3d/ray_cast.h7
-rw-r--r--scene/gui/base_button.cpp2
-rw-r--r--scene/resources/animation.cpp4
-rw-r--r--scene/resources/texture.cpp16
-rw-r--r--servers/physics/body_sw.cpp4
51 files changed, 1646 insertions, 107 deletions
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 3b43492bc5..234d71cb68 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -179,10 +179,10 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, bool p
print_line("load resource: " + local_path);
bool found = false;
+ // Try all loaders and pick the first match for the type hint
for (int i = 0; i < loader_count; i++) {
if (!loader[i]->recognize_path(local_path, p_type_hint)) {
- print_line("path not recognized");
continue;
}
found = true;
diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp
index 20b916ee3b..962a42acb9 100644
--- a/core/math/math_2d.cpp
+++ b/core/math/math_2d.cpp
@@ -63,7 +63,8 @@ Vector2 Vector2::normalized() const {
}
bool Vector2::is_normalized() const {
- return Math::isequal_approx(length(), (real_t)1.0);
+ // use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
+ return Math::is_equal_approx(length_squared(), 1.0);
}
real_t Vector2::distance_to(const Vector2 &p_vector2) const {
@@ -281,7 +282,7 @@ Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, c
// slide returns the component of the vector along the given plane, specified by its normal vector.
Vector2 Vector2::slide(const Vector2 &p_n) const {
-#ifdef DEBUG_ENABLED
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(p_n.is_normalized() == false, Vector2());
#endif
return *this - p_n * this->dot(p_n);
@@ -292,7 +293,7 @@ Vector2 Vector2::bounce(const Vector2 &p_n) const {
}
Vector2 Vector2::reflect(const Vector2 &p_n) const {
-#ifdef DEBUG_ENABLED
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(p_n.is_normalized() == false, Vector2());
#endif
return 2.0 * p_n * this->dot(p_n) - *this;
@@ -439,7 +440,9 @@ Transform2D Transform2D::inverse() const {
void Transform2D::affine_invert() {
real_t det = basis_determinant();
+#ifdef MATH_CHECKS
ERR_FAIL_COND(det == 0);
+#endif
real_t idet = 1.0 / det;
SWAP(elements[0][0], elements[1][1]);
diff --git a/core/math/math_defs.h b/core/math/math_defs.h
index 1a5768e515..3d9eb63e11 100644
--- a/core/math/math_defs.h
+++ b/core/math/math_defs.h
@@ -35,6 +35,10 @@
#define CMP_NORMALIZE_TOLERANCE 0.000001
#define CMP_POINT_IN_PLANE_EPSILON 0.00001
+#ifdef DEBUG_ENABLED
+#define MATH_CHECKS
+#endif
+
#define USEC_TO_SEC(m_usec) ((m_usec) / 1000000.0)
/**
* "Real" is a type that will be translated to either floats or fixed depending
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index d71d9bd792..5bfbc1005f 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -168,7 +168,7 @@ public:
static float random(float from, float to);
static real_t random(int from, int to) { return (real_t)random((real_t)from, (real_t)to); }
- static _ALWAYS_INLINE_ bool isequal_approx(real_t a, real_t b) {
+ static _ALWAYS_INLINE_ bool is_equal_approx(real_t a, real_t b) {
// TODO: Comparing floats for approximate-equality is non-trivial.
// Using epsilon should cover the typical cases in Godot (where a == b is used to compare two reals), such as matrix and vector comparison operators.
// A proper implementation in terms of ULPs should eventually replace the contents of this function.
diff --git a/core/math/matrix3.cpp b/core/math/matrix3.cpp
index ef368009d1..c733251c3c 100644
--- a/core/math/matrix3.cpp
+++ b/core/math/matrix3.cpp
@@ -62,8 +62,9 @@ void Basis::invert() {
real_t det = elements[0][0] * co[0] +
elements[0][1] * co[1] +
elements[0][2] * co[2];
-
+#ifdef MATH_CHECKS
ERR_FAIL_COND(det == 0);
+#endif
real_t s = 1.0 / det;
set(co[0] * s, cofac(0, 2, 2, 1) * s, cofac(0, 1, 1, 2) * s,
@@ -72,8 +73,9 @@ void Basis::invert() {
}
void Basis::orthonormalize() {
+#ifdef MATH_CHECKS
ERR_FAIL_COND(determinant() == 0);
-
+#endif
// Gram-Schmidt Process
Vector3 x = get_axis(0);
@@ -102,20 +104,20 @@ bool Basis::is_orthogonal() const {
Basis id;
Basis m = (*this) * transposed();
- return isequal_approx(id, m);
+ return is_equal_approx(id, m);
}
bool Basis::is_rotation() const {
- return Math::isequal_approx(determinant(), 1) && is_orthogonal();
+ return Math::is_equal_approx(determinant(), 1) && is_orthogonal();
}
bool Basis::is_symmetric() const {
- if (Math::abs(elements[0][1] - elements[1][0]) > CMP_EPSILON)
+ if (!Math::is_equal_approx(elements[0][1], elements[1][0]))
return false;
- if (Math::abs(elements[0][2] - elements[2][0]) > CMP_EPSILON)
+ if (!Math::is_equal_approx(elements[0][2], elements[2][0]))
return false;
- if (Math::abs(elements[1][2] - elements[2][1]) > CMP_EPSILON)
+ if (!Math::is_equal_approx(elements[1][2], elements[2][1]))
return false;
return true;
@@ -123,11 +125,11 @@ bool Basis::is_symmetric() const {
Basis Basis::diagonalize() {
- //NOTE: only implemented for symmetric matrices
- //with the Jacobi iterative method method
-
+//NOTE: only implemented for symmetric matrices
+//with the Jacobi iterative method method
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(!is_symmetric(), Basis());
-
+#endif
const int ite_max = 1024;
real_t off_matrix_norm_2 = elements[0][1] * elements[0][1] + elements[0][2] * elements[0][2] + elements[1][2] * elements[1][2];
@@ -160,7 +162,7 @@ Basis Basis::diagonalize() {
// Compute the rotation angle
real_t angle;
- if (Math::abs(elements[j][j] - elements[i][i]) < CMP_EPSILON) {
+ if (Math::is_equal_approx(elements[j][j], elements[i][i])) {
angle = Math_PI / 4;
} else {
angle = 0.5 * Math::atan(2 * elements[i][j] / (elements[j][j] - elements[i][i]));
@@ -226,11 +228,25 @@ Basis Basis::scaled(const Vector3 &p_scale) const {
}
Vector3 Basis::get_scale() const {
- // We are assuming M = R.S, and performing a polar decomposition to extract R and S.
- // FIXME: We eventually need a proper polar decomposition.
- // As a cheap workaround until then, to ensure that R is a proper rotation matrix with determinant +1
- // (such that it can be represented by a Quat or Euler angles), we absorb the sign flip into the scaling matrix.
- // As such, it works in conjunction with get_rotation().
+ // FIXME: We are assuming M = R.S (R is rotation and S is scaling), and use polar decomposition to extract R and S.
+ // A polar decomposition is M = O.P, where O is an orthogonal matrix (meaning rotation and reflection) and
+ // P is a positive semi-definite matrix (meaning it contains absolute values of scaling along its diagonal).
+ //
+ // Despite being different from what we want to achieve, we can nevertheless make use of polar decomposition
+ // here as follows. We can split O into a rotation and a reflection as O = R.Q, and obtain M = R.S where
+ // we defined S = Q.P. Now, R is a proper rotation matrix and S is a (signed) scaling matrix,
+ // which can involve negative scalings. However, there is a catch: unlike the polar decomposition of M = O.P,
+ // the decomposition of O into a rotation and reflection matrix as O = R.Q is not unique.
+ // Therefore, we are going to do this decomposition by sticking to a particular convention.
+ // This may lead to confusion for some users though.
+ //
+ // The convention we use here is to absorb the sign flip into the scaling matrix.
+ // The same convention is also used in other similar functions such as set_scale,
+ // get_rotation_axis_angle, get_rotation, set_rotation_axis_angle, set_rotation_euler, ...
+ //
+ // A proper way to get rid of this issue would be to store the scaling values (or at least their signs)
+ // as a part of Basis. However, if we go that path, we need to disable direct (write) access to the
+ // matrix elements.
real_t det_sign = determinant() > 0 ? 1 : -1;
return det_sign * Vector3(
Vector3(elements[0][0], elements[1][0], elements[2][0]).length(),
@@ -238,6 +254,17 @@ Vector3 Basis::get_scale() const {
Vector3(elements[0][2], elements[1][2], elements[2][2]).length());
}
+// Sets scaling while preserving rotation.
+// This requires some care when working with matrices with negative determinant,
+// since we're using a particular convention for "polar" decomposition in get_scale and get_rotation.
+// For details, see the explanation in get_scale.
+void Basis::set_scale(const Vector3 &p_scale) {
+ Vector3 e = get_euler();
+ Basis(); // reset to identity
+ scale(p_scale);
+ rotate(e);
+}
+
// Multiplies the matrix from left by the rotation matrix: M -> R.M
// Note that this does *not* rotate the matrix itself.
//
@@ -260,6 +287,7 @@ void Basis::rotate(const Vector3 &p_euler) {
*this = rotated(p_euler);
}
+// TODO: rename this to get_rotation_euler
Vector3 Basis::get_rotation() const {
// Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
// and returns the Euler angles corresponding to the rotation part, complementing get_scale().
@@ -274,6 +302,42 @@ Vector3 Basis::get_rotation() const {
return m.get_euler();
}
+void Basis::get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const {
+ // Assumes that the matrix can be decomposed into a proper rotation and scaling matrix as M = R.S,
+ // and returns the Euler angles corresponding to the rotation part, complementing get_scale().
+ // See the comment in get_scale() for further information.
+ Basis m = orthonormalized();
+ real_t det = m.determinant();
+ if (det < 0) {
+ // Ensure that the determinant is 1, such that result is a proper rotation matrix which can be represented by Euler angles.
+ m.scale(Vector3(-1, -1, -1));
+ }
+
+ m.get_axis_angle(p_axis, p_angle);
+}
+
+// Sets rotation while preserving scaling.
+// This requires some care when working with matrices with negative determinant,
+// since we're using a particular convention for "polar" decomposition in get_scale and get_rotation.
+// For details, see the explanation in get_scale.
+void Basis::set_rotation_euler(const Vector3 &p_euler) {
+ Vector3 s = get_scale();
+ Basis(); // reset to identity
+ scale(s);
+ rotate(p_euler);
+}
+
+// Sets rotation while preserving scaling.
+// This requires some care when working with matrices with negative determinant,
+// since we're using a particular convention for "polar" decomposition in get_scale and get_rotation.
+// For details, see the explanation in get_scale.
+void Basis::set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle) {
+ Vector3 s = get_scale();
+ Basis(); // reset to identity
+ scale(s);
+ rotate(p_axis, p_angle);
+}
+
// get_euler returns a vector containing the Euler angles in the format
// (a1,a2,a3), where a3 is the angle of the first rotation, and a1 is the last
// (following the convention they are commonly defined in the literature).
@@ -294,9 +358,9 @@ Vector3 Basis::get_euler() const {
// -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy
Vector3 euler;
-
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(is_rotation() == false, euler);
-
+#endif
euler.y = Math::asin(elements[0][2]);
if (euler.y < Math_PI * 0.5) {
if (euler.y > -Math_PI * 0.5) {
@@ -340,11 +404,11 @@ void Basis::set_euler(const Vector3 &p_euler) {
*this = xmat * (ymat * zmat);
}
-bool Basis::isequal_approx(const Basis &a, const Basis &b) const {
+bool Basis::is_equal_approx(const Basis &a, const Basis &b) const {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
- if (Math::isequal_approx(a.elements[i][j], b.elements[i][j]) == false)
+ if (Math::is_equal_approx(a.elements[i][j], b.elements[i][j]) == false)
return false;
}
}
@@ -387,8 +451,9 @@ Basis::operator String() const {
}
Basis::operator Quat() const {
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(is_rotation() == false, Quat());
-
+#endif
real_t trace = elements[0][0] + elements[1][1] + elements[2][2];
real_t temp[4];
@@ -482,9 +547,10 @@ void Basis::set_orthogonal_index(int p_index) {
*this = _ortho_bases[p_index];
}
-void Basis::get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const {
+void Basis::get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
+#ifdef MATH_CHECKS
ERR_FAIL_COND(is_rotation() == false);
-
+#endif
real_t angle, x, y, z; // variables for result
real_t epsilon = 0.01; // margin to allow for rounding errors
real_t epsilon2 = 0.1; // margin to distinguish between 0 and 180 degrees
@@ -573,11 +639,11 @@ Basis::Basis(const Quat &p_quat) {
xz - wy, yz + wx, 1.0 - (xx + yy));
}
-Basis::Basis(const Vector3 &p_axis, real_t p_phi) {
- // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
-
+void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_phi) {
+// Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle
+#ifdef MATH_CHECKS
ERR_FAIL_COND(p_axis.is_normalized() == false);
-
+#endif
Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z);
real_t cosine = Math::cos(p_phi);
@@ -595,3 +661,7 @@ Basis::Basis(const Vector3 &p_axis, real_t p_phi) {
elements[2][1] = p_axis.y * p_axis.z * (1.0 - cosine) + p_axis.x * sine;
elements[2][2] = axis_sq.z + cosine * (1.0 - axis_sq.z);
}
+
+Basis::Basis(const Vector3 &p_axis, real_t p_phi) {
+ set_axis_angle(p_axis, p_phi);
+}
diff --git a/core/math/matrix3.h b/core/math/matrix3.h
index 08e963f56e..c3eeb1f705 100644
--- a/core/math/matrix3.h
+++ b/core/math/matrix3.h
@@ -77,15 +77,25 @@ public:
void rotate(const Vector3 &p_euler);
Basis rotated(const Vector3 &p_euler) const;
+
Vector3 get_rotation() const;
+ void get_rotation_axis_angle(Vector3 &p_axis, real_t &p_angle) const;
- void scale(const Vector3 &p_scale);
- Basis scaled(const Vector3 &p_scale) const;
- Vector3 get_scale() const;
+ void set_rotation_euler(const Vector3 &p_euler);
+ void set_rotation_axis_angle(const Vector3 &p_axis, real_t p_angle);
Vector3 get_euler() const;
void set_euler(const Vector3 &p_euler);
+ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const;
+ void set_axis_angle(const Vector3 &p_axis, real_t p_phi);
+
+ void scale(const Vector3 &p_scale);
+ Basis scaled(const Vector3 &p_scale) const;
+
+ Vector3 get_scale() const;
+ void set_scale(const Vector3 &p_scale);
+
// transposed dot products
_FORCE_INLINE_ real_t tdotx(const Vector3 &v) const {
return elements[0][0] * v[0] + elements[1][0] * v[1] + elements[2][0] * v[2];
@@ -97,7 +107,7 @@ public:
return elements[0][2] * v[0] + elements[1][2] * v[1] + elements[2][2] * v[2];
}
- bool isequal_approx(const Basis &a, const Basis &b) const;
+ bool is_equal_approx(const Basis &a, const Basis &b) const;
bool operator==(const Basis &p_matrix) const;
bool operator!=(const Basis &p_matrix) const;
@@ -121,8 +131,6 @@ public:
operator String() const;
- void get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const;
-
/* create / set */
_FORCE_INLINE_ void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) {
diff --git a/core/math/quat.cpp b/core/math/quat.cpp
index 9662542224..0bea97c2e8 100644
--- a/core/math/quat.cpp
+++ b/core/math/quat.cpp
@@ -92,6 +92,10 @@ Quat Quat::normalized() const {
return *this / length();
}
+bool Quat::is_normalized() const {
+ return Math::is_equal_approx(length(), 1.0);
+}
+
Quat Quat::inverse() const {
return Quat(-x, -y, -z, w);
}
diff --git a/core/math/quat.h b/core/math/quat.h
index 76b3cde2a3..f22275b457 100644
--- a/core/math/quat.h
+++ b/core/math/quat.h
@@ -48,6 +48,7 @@ public:
real_t length() const;
void normalize();
Quat normalized() const;
+ bool is_normalized() const;
Quat inverse() const;
_FORCE_INLINE_ real_t dot(const Quat &q) const;
void set_euler(const Vector3 &p_euler);
@@ -56,7 +57,7 @@ public:
Quat slerpni(const Quat &q, const real_t &t) const;
Quat cubic_slerp(const Quat &q, const Quat &prep, const Quat &postq, const real_t &t) const;
- _FORCE_INLINE_ void get_axis_and_angle(Vector3 &r_axis, real_t &r_angle) const {
+ _FORCE_INLINE_ void get_axis_angle(Vector3 &r_axis, real_t &r_angle) const {
r_angle = 2 * Math::acos(w);
r_axis.x = x / Math::sqrt(1 - w * w);
r_axis.y = y / Math::sqrt(1 - w * w);
diff --git a/core/math/vector3.h b/core/math/vector3.h
index a6bc20ccb2..5f4390fbd1 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -389,7 +389,8 @@ Vector3 Vector3::normalized() const {
}
bool Vector3::is_normalized() const {
- return Math::isequal_approx(length(), (real_t)1.0);
+ // use length_squared() instead of length() to avoid sqrt(), makes it more stringent.
+ return Math::is_equal_approx(length_squared(), 1.0);
}
Vector3 Vector3::inverse() const {
@@ -404,7 +405,7 @@ void Vector3::zero() {
// slide returns the component of the vector along the given plane, specified by its normal vector.
Vector3 Vector3::slide(const Vector3 &p_n) const {
-#ifdef DEBUG_ENABLED
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(p_n.is_normalized() == false, Vector3());
#endif
return *this - p_n * this->dot(p_n);
@@ -415,7 +416,7 @@ Vector3 Vector3::bounce(const Vector3 &p_n) const {
}
Vector3 Vector3::reflect(const Vector3 &p_n) const {
-#ifdef DEBUG_ENABLED
+#ifdef MATH_CHECKS
ERR_FAIL_COND_V(p_n.is_normalized() == false, Vector3());
#endif
return 2.0 * p_n * this->dot(p_n) - *this;
diff --git a/core/script_language.h b/core/script_language.h
index 1ec02f5845..115ab59dca 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -199,6 +199,7 @@ public:
virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const = 0;
virtual Script *create_script() const = 0;
virtual bool has_named_classes() const = 0;
+ virtual bool can_inherit_from_file() { return false; }
virtual int find_function(const String &p_function, const String &p_code) const = 0;
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const = 0;
virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; }
diff --git a/core/ustring.cpp b/core/ustring.cpp
index d2d4b6507f..b01f680dd6 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -513,14 +513,16 @@ String String::camelcase_to_underscore(bool lowercase) const {
for (size_t i = 1; i < this->size(); i++) {
bool is_upper = cstr[i] >= A && cstr[i] <= Z;
+ bool is_number = cstr[i] >= '0' && cstr[i] <= '9';
bool are_next_2_lower = false;
bool was_precedent_upper = cstr[i - 1] >= A && cstr[i - 1] <= Z;
+ bool was_precedent_number = cstr[i - 1] >= '0' && cstr[i - 1] <= '9';
if (i + 2 < this->size()) {
are_next_2_lower = cstr[i + 1] >= a && cstr[i + 1] <= z && cstr[i + 2] >= a && cstr[i + 2] <= z;
}
- bool should_split = ((is_upper && !was_precedent_upper) || (was_precedent_upper && is_upper && are_next_2_lower));
+ bool should_split = ((is_upper && !was_precedent_upper && !was_precedent_number) || (was_precedent_upper && is_upper && are_next_2_lower) || (is_number && !was_precedent_number));
if (should_split) {
new_string += this->substr(start_index, i - start_index) + "_";
start_index = i;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index e87dfd2768..beaee188eb 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -328,6 +328,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Vector2, normalized);
VCALL_LOCALMEM0R(Vector2, length);
VCALL_LOCALMEM0R(Vector2, length_squared);
+ VCALL_LOCALMEM0R(Vector2, is_normalized);
VCALL_LOCALMEM1R(Vector2, distance_to);
VCALL_LOCALMEM1R(Vector2, distance_squared_to);
VCALL_LOCALMEM1R(Vector2, angle_to);
@@ -362,6 +363,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Vector3, max_axis);
VCALL_LOCALMEM0R(Vector3, length);
VCALL_LOCALMEM0R(Vector3, length_squared);
+ VCALL_LOCALMEM0R(Vector3, is_normalized);
VCALL_LOCALMEM0R(Vector3, normalized);
VCALL_LOCALMEM0R(Vector3, inverse);
VCALL_LOCALMEM1R(Vector3, snapped);
@@ -418,6 +420,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Quat, length);
VCALL_LOCALMEM0R(Quat, length_squared);
VCALL_LOCALMEM0R(Quat, normalized);
+ VCALL_LOCALMEM0R(Quat, is_normalized);
VCALL_LOCALMEM0R(Quat, inverse);
VCALL_LOCALMEM1R(Quat, dot);
VCALL_LOCALMEM1R(Quat, xform);
@@ -704,6 +707,9 @@ struct _VariantCall {
VCALL_PTR1R(Basis, scaled);
VCALL_PTR0R(Basis, get_scale);
VCALL_PTR0R(Basis, get_euler);
+ VCALL_PTR1(Basis, set_scale);
+ VCALL_PTR1(Basis, set_rotation_euler);
+ VCALL_PTR2(Basis, set_rotation_axis_angle);
VCALL_PTR1R(Basis, tdotx);
VCALL_PTR1R(Basis, tdoty);
VCALL_PTR1R(Basis, tdotz);
@@ -875,6 +881,11 @@ struct _VariantCall {
r_ret = Basis(p_args[0]->operator Vector3(), p_args[1]->operator real_t());
}
+ static void Basis_init3(Variant &r_ret, const Variant **p_args) {
+
+ r_ret = Basis(p_args[0]->operator Vector3());
+ }
+
static void Transform_init1(Variant &r_ret, const Variant **p_args) {
Transform t;
@@ -1429,6 +1440,7 @@ void register_variant_methods() {
ADDFUNC0(VECTOR2, REAL, Vector2, length, varray());
ADDFUNC0(VECTOR2, REAL, Vector2, angle, varray());
ADDFUNC0(VECTOR2, REAL, Vector2, length_squared, varray());
+ ADDFUNC0(VECTOR2, BOOL, Vector2, is_normalized, varray());
ADDFUNC1(VECTOR2, REAL, Vector2, distance_to, VECTOR2, "to", varray());
ADDFUNC1(VECTOR2, REAL, Vector2, distance_squared_to, VECTOR2, "to", varray());
ADDFUNC1(VECTOR2, REAL, Vector2, angle_to, VECTOR2, "to", varray());
@@ -1462,6 +1474,7 @@ void register_variant_methods() {
ADDFUNC0(VECTOR3, INT, Vector3, max_axis, varray());
ADDFUNC0(VECTOR3, REAL, Vector3, length, varray());
ADDFUNC0(VECTOR3, REAL, Vector3, length_squared, varray());
+ ADDFUNC0(VECTOR3, BOOL, Vector3, is_normalized, varray());
ADDFUNC0(VECTOR3, VECTOR3, Vector3, normalized, varray());
ADDFUNC0(VECTOR3, VECTOR3, Vector3, inverse, varray());
ADDFUNC1(VECTOR3, VECTOR3, Vector3, snapped, REAL, "by", varray());
@@ -1497,6 +1510,7 @@ void register_variant_methods() {
ADDFUNC0(QUAT, REAL, Quat, length, varray());
ADDFUNC0(QUAT, REAL, Quat, length_squared, varray());
ADDFUNC0(QUAT, QUAT, Quat, normalized, varray());
+ ADDFUNC0(QUAT, BOOL, Quat, is_normalized, varray());
ADDFUNC0(QUAT, QUAT, Quat, inverse, varray());
ADDFUNC1(QUAT, REAL, Quat, dot, QUAT, "b", varray());
ADDFUNC1(QUAT, VECTOR3, Quat, xform, VECTOR3, "v", varray());
@@ -1692,6 +1706,9 @@ void register_variant_methods() {
ADDFUNC0(BASIS, REAL, Basis, determinant, varray());
ADDFUNC2(BASIS, BASIS, Basis, rotated, VECTOR3, "axis", REAL, "phi", varray());
ADDFUNC1(BASIS, BASIS, Basis, scaled, VECTOR3, "scale", varray());
+ ADDFUNC1(BASIS, NIL, Basis, set_scale, VECTOR3, "scale", varray());
+ ADDFUNC1(BASIS, NIL, Basis, set_rotation_euler, VECTOR3, "euler", varray());
+ ADDFUNC2(BASIS, NIL, Basis, set_rotation_axis_angle, VECTOR3, "axis", REAL, "angle", varray());
ADDFUNC0(BASIS, VECTOR3, Basis, get_scale, varray());
ADDFUNC0(BASIS, VECTOR3, Basis, get_euler, varray());
ADDFUNC1(BASIS, REAL, Basis, tdotx, VECTOR3, "with", varray());
@@ -1749,6 +1766,7 @@ void register_variant_methods() {
_VariantCall::add_constructor(_VariantCall::Basis_init1, Variant::BASIS, "x_axis", Variant::VECTOR3, "y_axis", Variant::VECTOR3, "z_axis", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Basis_init2, Variant::BASIS, "axis", Variant::VECTOR3, "phi", Variant::REAL);
+ _VariantCall::add_constructor(_VariantCall::Basis_init3, Variant::BASIS, "euler", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Transform_init1, Variant::TRANSFORM, "x_axis", Variant::VECTOR3, "y_axis", Variant::VECTOR3, "z_axis", Variant::VECTOR3, "origin", Variant::VECTOR3);
_VariantCall::add_constructor(_VariantCall::Transform_init2, Variant::TRANSFORM, "basis", Variant::BASIS, "origin", Variant::VECTOR3);
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index 9b1f8c788b..e0e88ef348 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -6868,6 +6868,15 @@
<method name="Basis">
<return type="Basis">
</return>
+ <argument index="0" name="euler" type="Vector3">
+ </argument>
+ <description>
+ Create a rotation matrix (in the XYZ convention: first Z, then Y, and X last) from the specified Euler angles, given in the vector format as (third,second,first).
+ </description>
+ </method>
+ <method name="Basis">
+ <return type="Basis">
+ </return>
<argument index="0" name="x_axis" type="Vector3">
</argument>
<argument index="1" name="y_axis" type="Vector3">
@@ -6889,8 +6898,7 @@
<return type="Vector3">
</return>
<description>
- Return Euler angles (in the XYZ convention: first Z, then Y, and X last) from the matrix. Returned vector contains the rotation angles in the format (third,second,first).
- This function only works if the matrix represents a proper rotation.
+ Assuming that the matrix is a proper rotation matrix (orthonormal matrix with determinant +1), return Euler angles (in the XYZ convention: first Z, then Y, and X last). Returned vector contains the rotation angles in the format (third,second,first).
</description>
</method>
<method name="get_orthogonal_index">
@@ -6932,6 +6940,26 @@
Introduce an additional rotation around the given axis by phi (radians). Only relevant when the matrix is being used as a part of [Transform]. The axis must be a normalized vector.
</description>
</method>
+ <method name="set_rotation_euler">
+ <return type="Basis">
+ </return>
+ <argument index="0" name="euler" type="Vector3">
+ </argument>
+ <description>
+ Changes only the rotation part of the [Basis] to a rotation corresponding to given Euler angles, while preserving the scaling part (as determined by get_scale).
+ </description>
+ </method>
+ <method name="set_rotation_axis_angle">
+ <return type="Basis">
+ </return>
+ <argument index="0" name="axis" type="Vector3">
+ </argument>
+ <argument index="1" name="phi" type="float">
+ </argument>
+ <description>
+ Changes only the rotation part of the [Basis] to a rotation around given axis by phi, while preserving the scaling part (as determined by get_scale).
+ </description>
+ </method>
<method name="scaled">
<return type="Basis">
</return>
@@ -6941,6 +6969,15 @@
Introduce an additional scaling specified by the given 3D scaling factor. Only relevant when the matrix is being used as a part of [Transform].
</description>
</method>
+ <method name="set_scale">
+ <return type="Basis">
+ </return>
+ <argument index="0" name="scale" type="Vector3">
+ </argument>
+ <description>
+ Changes only the scaling part of the Basis to the specified scaling, while preserving the rotation part (as determined by get_rotation).
+ </description>
+ </method>
<method name="tdotx">
<return type="float">
</return>
@@ -35148,6 +35185,13 @@
Returns a copy of the quaternion, normalized to unit length.
</description>
</method>
+ <method name="is_normalized">
+ <return type="bool">
+ </return>
+ <description>
+ Returns whether the quaternion is normalized or not.
+ </description>
+ </method>
<method name="slerp">
<return type="Quat">
</return>
@@ -48048,6 +48092,13 @@ do_property].
Returns a normalized vector to unit length.
</description>
</method>
+ <method name="is_normalized">
+ <return type="bool">
+ </return>
+ <description>
+ Returns whether the vector is normalized or not.
+ </description>
+ </method>
<method name="reflect">
<return type="Vector2">
</return>
@@ -48272,6 +48323,13 @@ do_property].
Return a copy of the normalized vector to unit length. This is the same as v / v.length().
</description>
</method>
+ <method name="is_normalized">
+ <return type="bool">
+ </return>
+ <description>
+ Returns whether the vector is normalized or not.
+ </description>
+ </method>
<method name="outer">
<return type="Basis">
</return>
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 9cb44349bf..a7996b09d3 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -125,14 +125,13 @@ void RasterizerSceneGLES3::shadow_atlas_set_size(RID p_atlas, int p_size) {
if (p_size == shadow_atlas->size)
return;
+ // erasing atlas
if (shadow_atlas->fbo) {
glDeleteTextures(1, &shadow_atlas->depth);
glDeleteFramebuffers(1, &shadow_atlas->fbo);
shadow_atlas->depth = 0;
shadow_atlas->fbo = 0;
-
- print_line("erasing atlas");
}
for (int i = 0; i < 4; i++) {
//clear subdivisions
@@ -4678,7 +4677,7 @@ void RasterizerSceneGLES3::initialize() {
const int ubo_light_size = 160;
state.ubo_light_size = ubo_light_size;
state.max_ubo_lights = MIN(RenderList::MAX_LIGHTS, max_ubo_size / ubo_light_size);
- print_line("max ubo light: " + itos(state.max_ubo_lights));
+ print_line("GLES3: max ubo light: " + itos(state.max_ubo_lights));
state.spot_array_tmp = (uint8_t *)memalloc(ubo_light_size * state.max_ubo_lights);
state.omni_array_tmp = (uint8_t *)memalloc(ubo_light_size * state.max_ubo_lights);
@@ -4704,7 +4703,7 @@ void RasterizerSceneGLES3::initialize() {
state.scene_shader.add_custom_define("#define MAX_FORWARD_LIGHTS " + itos(state.max_forward_lights_per_object) + "\n");
state.max_ubo_reflections = MIN(RenderList::MAX_REFLECTIONS, max_ubo_size / sizeof(ReflectionProbeDataUBO));
- print_line("max ubo reflections: " + itos(state.max_ubo_reflections) + " ubo size: " + itos(sizeof(ReflectionProbeDataUBO)));
+ print_line("GLES3: max ubo reflections: " + itos(state.max_ubo_reflections) + ", ubo size: " + itos(sizeof(ReflectionProbeDataUBO)));
state.reflection_array_tmp = (uint8_t *)memalloc(sizeof(ReflectionProbeDataUBO) * state.max_ubo_reflections);
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 9316b025eb..73547b5a16 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -825,7 +825,6 @@ Image RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_
if (!texture->images[p_cube_side].empty()) {
return texture->images[p_cube_side];
}
- print_line("GETTING FROM GL ");
#ifdef GLES_OVER_GL
@@ -842,7 +841,7 @@ Image RasterizerStorageGLES3::texture_get_data(RID p_texture, VS::CubeMapSide p_
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
- print_line("GET FORMAT: " + Image::get_format_name(texture->format) + " mipmaps: " + itos(texture->mipmaps));
+ //print_line("GET FORMAT: " + Image::get_format_name(texture->format) + " mipmaps: " + itos(texture->mipmaps));
for (int i = 0; i < texture->mipmaps; i++) {
@@ -4777,7 +4776,6 @@ RID RasterizerStorageGLES3::gi_probe_dynamic_data_create(int p_width, int p_heig
min_size = 4;
}
- print_line("dyndata create");
while (true) {
if (gipd->compression == GI_PROBE_S3TC) {
@@ -6270,7 +6268,6 @@ bool RasterizerStorageGLES3::free(RID p_rid) {
// delete the texture
GIProbeData *gi_probe_data = gi_probe_data_owner.get(p_rid);
- print_line("dyndata delete");
glDeleteTextures(1, &gi_probe_data->tex_id);
gi_probe_owner.free(p_rid);
memdelete(gi_probe_data);
@@ -6333,9 +6330,8 @@ void RasterizerStorageGLES3::initialize() {
{
int max_extensions = 0;
- print_line("getting extensions");
glGetIntegerv(GL_NUM_EXTENSIONS, &max_extensions);
- print_line("total " + itos(max_extensions));
+ print_line("GLES3: max extensions: " + itos(max_extensions));
for (int i = 0; i < max_extensions; i++) {
const GLubyte *s = glGetStringi(GL_EXTENSIONS, i);
if (!s)
diff --git a/drivers/gles3/shader_compiler_gles3.cpp b/drivers/gles3/shader_compiler_gles3.cpp
index 6aeb3af2cd..89b056df84 100644
--- a/drivers/gles3/shader_compiler_gles3.cpp
+++ b/drivers/gles3/shader_compiler_gles3.cpp
@@ -410,7 +410,6 @@ String ShaderCompilerGLES3::_dump_node_code(SL::Node *p_node, int p_level, Gener
if (fnode->name == "vertex") {
- print_line("vertex uses functions: " + itos(pnode->functions[i].uses_function.size()));
_dump_function_deps(pnode, fnode->name, function_code, r_gen_code.vertex_global, added_vtx);
r_gen_code.vertex = function_code["vertex"];
}
diff --git a/drivers/pulseaudio/audio_driver_pulseaudio.cpp b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
index 9aaf5c129b..45827ee4f7 100644
--- a/drivers/pulseaudio/audio_driver_pulseaudio.cpp
+++ b/drivers/pulseaudio/audio_driver_pulseaudio.cpp
@@ -102,7 +102,6 @@ float AudioDriverPulseAudio::get_latency() {
void AudioDriverPulseAudio::thread_func(void *p_udata) {
- print_line("thread");
AudioDriverPulseAudio *ad = (AudioDriverPulseAudio *)p_udata;
while (!ad->exit_thread) {
diff --git a/editor/editor_name_dialog.cpp b/editor/editor_name_dialog.cpp
index 972857fd88..29d5dda658 100644
--- a/editor/editor_name_dialog.cpp
+++ b/editor/editor_name_dialog.cpp
@@ -79,9 +79,11 @@ void EditorNameDialog::_bind_methods() {
}
EditorNameDialog::EditorNameDialog() {
+ makevb = memnew(VBoxContainer);
+ add_child(makevb);
name = memnew(LineEdit);
- add_child(name);
- move_child(name, get_label()->get_index() + 1);
+ makevb->add_child(name);
+ makevb->move_child(name, get_label()->get_index() + 1);
name->set_margin(MARGIN_TOP, 5);
name->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 5);
name->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 5);
diff --git a/editor/editor_name_dialog.h b/editor/editor_name_dialog.h
index a98db4ffe6..eeeee34d7e 100644
--- a/editor/editor_name_dialog.h
+++ b/editor/editor_name_dialog.h
@@ -38,6 +38,7 @@ class EditorNameDialog : public ConfirmationDialog {
GDCLASS(EditorNameDialog, ConfirmationDialog);
+ VBoxContainer *makevb;
LineEdit *name;
void _line_gui_input(const InputEvent &p_event);
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index cc7ee44902..04d369fed4 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -435,7 +435,7 @@ void EditorNode::_sources_changed(bool p_exist) {
if (defer_load_scene != "") {
- print_line("loading scene DEFERED");
+ print_line("loading scene DEFERRED");
load_scene(defer_load_scene);
defer_load_scene = "";
}
@@ -6051,7 +6051,10 @@ EditorNode::EditorNode() {
{
_initializing_addons = true;
- Vector<String> addons = GlobalConfig::get_singleton()->get("editor_plugins/enabled");
+ Vector<String> addons;
+ if (GlobalConfig::get_singleton()->has("editor_plugins/enabled")) {
+ addons = GlobalConfig::get_singleton()->get("editor_plugins/enabled");
+ }
for (int i = 0; i < addons.size(); i++) {
set_addon_plugin_enabled(addons[i], true);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 35373eb815..9beffa3c67 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -407,7 +407,7 @@ void EditorSettings::setup_network() {
IP::get_singleton()->get_local_addresses(&local_ip);
String lip;
String hint;
- String current = get("network/debug_host");
+ String current = has("network/debug_host") ? get("network/debug_host") : "";
for (List<IP_Address>::Element *E = local_ip.front(); E; E = E->next()) {
@@ -557,6 +557,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
hints["text_editor/theme/font"] = PropertyInfo(Variant::STRING, "text_editor/theme/font", PROPERTY_HINT_GLOBAL_FILE, "*.fnt");
set("text_editor/completion/auto_brace_complete", false);
set("text_editor/files/restore_scripts_on_load", true);
+ set("text_editor/completion/complete_file_paths", true);
//set("docks/scene_tree/display_old_action_buttons",false);
set("docks/scene_tree/start_create_dialog_fully_expanded", false);
@@ -591,9 +592,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
set("editors/2d/bone_color2", Color(0.75, 0.75, 0.75, 0.9));
set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.9));
set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.9));
-
set("editors/2d/keep_margins_when_changing_anchors", false);
-
set("editors/2d/warped_mouse_panning", true);
set("run/window_placement/rect", 0);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index a99cd7a2d6..cdcc0a0855 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1534,13 +1534,6 @@ void ScriptEditor::save_all_scripts() {
if (!se)
continue;
- if (!se->is_unsaved())
- continue;
-
- if (trim_trailing_whitespace_on_save) {
- se->trim_trailing_whitespace();
- }
-
if (convert_indent_on_save) {
if (use_space_indentation) {
se->convert_indent_to_spaces();
@@ -1549,6 +1542,13 @@ void ScriptEditor::save_all_scripts() {
}
}
+ if (trim_trailing_whitespace_on_save) {
+ se->trim_trailing_whitespace();
+ }
+
+ if (!se->is_unsaved())
+ continue;
+
Ref<Script> script = se->get_edited_script();
if (script.is_valid())
se->apply_code();
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 052c19f34e..e942d6cebd 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -285,6 +285,9 @@ void ScriptTextEditor::convert_indent_to_spaces() {
indent += " ";
}
+ int cursor_line = tx->cursor_get_line();
+ int cursor_column = tx->cursor_get_column();
+
bool changed_indentation = false;
for (int i = 0; i < tx->get_line_count(); i++) {
String line = tx->get_line(i);
@@ -300,6 +303,9 @@ void ScriptTextEditor::convert_indent_to_spaces() {
tx->begin_complex_operation();
changed_indentation = true;
}
+ if (cursor_line == i && cursor_column > j) {
+ cursor_column += indent_size - 1;
+ }
line = line.left(j) + indent + line.right(j + 1);
}
j++;
@@ -307,6 +313,7 @@ void ScriptTextEditor::convert_indent_to_spaces() {
tx->set_line(i, line);
}
if (changed_indentation) {
+ tx->cursor_set_column(cursor_column);
tx->end_complex_operation();
tx->update();
}
@@ -323,6 +330,9 @@ void ScriptTextEditor::convert_indent_to_tabs() {
int indent_size = EditorSettings::get_singleton()->get("text_editor/indent/size");
indent_size -= 1;
+ int cursor_line = tx->cursor_get_line();
+ int cursor_column = tx->cursor_get_column();
+
bool changed_indentation = false;
for (int i = 0; i < tx->get_line_count(); i++) {
String line = tx->get_line(i);
@@ -342,7 +352,9 @@ void ScriptTextEditor::convert_indent_to_tabs() {
tx->begin_complex_operation();
changed_indentation = true;
}
-
+ if (cursor_line == i && cursor_column > j) {
+ cursor_column -= indent_size;
+ }
line = line.left(j - indent_size) + "\t" + line.right(j + 1);
j = 0;
space_count = -1;
@@ -355,6 +367,7 @@ void ScriptTextEditor::convert_indent_to_tabs() {
tx->set_line(i, line);
}
if (changed_indentation) {
+ tx->cursor_set_column(cursor_column);
tx->end_complex_operation();
tx->update();
}
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 3bdc742354..1a4a36fa18 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1211,7 +1211,7 @@ ProjectManager::ProjectManager() {
}
}
- FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesytem/file_dialog/show_hidden_files"));
+ FileDialog::set_default_show_hidden_files(EditorSettings::get_singleton()->get("filesystem/file_dialog/show_hidden_files"));
set_area_as_parent_rect();
set_theme(create_editor_theme());
diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp
index 7808cae0cd..15c540e132 100644
--- a/editor/script_create_dialog.cpp
+++ b/editor/script_create_dialog.cpp
@@ -55,6 +55,8 @@ bool ScriptCreateDialog::_validate(const String &p_string) {
if (p_string.length() == 0)
return false;
+ String path_chars = "\"res://";
+ bool is_val_path = ScriptServer::get_language(language_menu->get_selected())->can_inherit_from_file();
for (int i = 0; i < p_string.length(); i++) {
if (i == 0) {
@@ -62,7 +64,17 @@ bool ScriptCreateDialog::_validate(const String &p_string) {
return false; // no start with number plz
}
- bool valid_char = (p_string[i] >= '0' && p_string[i] <= '9') || (p_string[i] >= 'a' && p_string[i] <= 'z') || (p_string[i] >= 'A' && p_string[i] <= 'Z') || p_string[i] == '_';
+ if (i == p_string.length() - 1 && is_val_path)
+ return p_string[i] == '\"';
+
+ if (is_val_path && i < path_chars.length()) {
+ if (p_string[i] != path_chars[i])
+ is_val_path = false;
+ else
+ continue;
+ }
+
+ bool valid_char = (p_string[i] >= '0' && p_string[i] <= '9') || (p_string[i] >= 'a' && p_string[i] <= 'z') || (p_string[i] >= 'A' && p_string[i] <= 'Z') || p_string[i] == '_' || (is_val_path && (p_string[i] == '/' || p_string[i] == '.'));
if (!valid_char)
return false;
@@ -74,7 +86,7 @@ bool ScriptCreateDialog::_validate(const String &p_string) {
void ScriptCreateDialog::_class_name_changed(const String &p_name) {
if (!_validate(parent_name->get_text())) {
- error_label->set_text(TTR("Invalid parent class name"));
+ error_label->set_text(TTR("Invalid parent class name or path"));
error_label->add_color_override("font_color", Color(1, 0.4, 0.0, 0.8));
} else if (class_name->is_editable()) {
if (class_name->get_text() == "") {
@@ -175,6 +187,12 @@ void ScriptCreateDialog::_lang_changed(int l) {
class_name->set_editable(false);
}
+ if (ScriptServer::get_language(l)->can_inherit_from_file()) {
+ parent_browse_button->show();
+ } else {
+ parent_browse_button->hide();
+ }
+
String selected_ext = "." + ScriptServer::get_language(l)->get_extension();
String path = file_path->get_text();
String extension = "";
@@ -215,7 +233,9 @@ void ScriptCreateDialog::_built_in_pressed() {
}
}
-void ScriptCreateDialog::_browse_path() {
+void ScriptCreateDialog::_browse_path(bool browse_parent) {
+
+ is_browsing_parent = browse_parent;
file_browse->set_mode(EditorFileDialog::MODE_SAVE_FILE);
file_browse->set_disable_overwrite_warning(true);
@@ -238,8 +258,13 @@ void ScriptCreateDialog::_browse_path() {
void ScriptCreateDialog::_file_selected(const String &p_file) {
String p = GlobalConfig::get_singleton()->localize_path(p_file);
- file_path->set_text(p);
- _path_changed(p);
+ if (is_browsing_parent) {
+ parent_name->set_text("\"" + p + "\"");
+ _class_name_changed("\"" + p + "\"");
+ } else {
+ file_path->set_text(p);
+ _path_changed(p);
+ }
}
void ScriptCreateDialog::_path_changed(const String &p_path) {
@@ -353,9 +378,18 @@ ScriptCreateDialog::ScriptCreateDialog() {
vb2->add_child(error_label);
vb->add_margin_child(TTR("Class Name:"), vb2);
+ HBoxContainer *hb1 = memnew(HBoxContainer);
parent_name = memnew(LineEdit);
- vb->add_margin_child(TTR("Inherits:"), parent_name);
parent_name->connect("text_changed", this, "_class_name_changed");
+ parent_name->set_h_size_flags(SIZE_EXPAND_FILL);
+ hb1->add_child(parent_name);
+ parent_browse_button = memnew(Button);
+ parent_browse_button->set_text(" .. ");
+ parent_browse_button->connect("pressed", this, "_browse_path", varray(true));
+ hb1->add_child(parent_browse_button);
+ parent_browse_button->hide();
+ vb->add_margin_child(TTR("Inherits:"), hb1);
+ is_browsing_parent = false;
language_menu = memnew(OptionButton);
vb->add_margin_child(TTR("Language"), language_menu);
@@ -398,7 +432,7 @@ ScriptCreateDialog::ScriptCreateDialog() {
file_path->set_h_size_flags(SIZE_EXPAND_FILL);
Button *b = memnew(Button);
b->set_text(" .. ");
- b->connect("pressed", this, "_browse_path");
+ b->connect("pressed", this, "_browse_path", varray(false));
hbc->add_child(b);
path_vb->add_child(hbc);
path_error_label = memnew(Label);
diff --git a/editor/script_create_dialog.h b/editor/script_create_dialog.h
index 499886facd..113d4a468c 100644
--- a/editor/script_create_dialog.h
+++ b/editor/script_create_dialog.h
@@ -44,6 +44,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
Label *error_label;
Label *path_error_label;
LineEdit *parent_name;
+ Button *parent_browse_button;
OptionButton *language_menu;
LineEdit *file_path;
EditorFileDialog *file_browse;
@@ -52,6 +53,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
AcceptDialog *alert;
bool path_valid;
bool create_new;
+ bool is_browsing_parent;
String initial_bp;
EditorSettings *editor_settings;
@@ -60,7 +62,7 @@ class ScriptCreateDialog : public ConfirmationDialog {
void _built_in_pressed();
bool _validate(const String &p_strin);
void _class_name_changed(const String &p_name);
- void _browse_path();
+ void _browse_path(bool browse_parent);
void _file_selected(const String &p_file);
virtual void ok_pressed();
void _create_new();
diff --git a/main/main.cpp b/main/main.cpp
index c294926045..377d15f5f4 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -697,7 +697,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
video_mode.width = globals->get("display/window/width");
if (!force_res && use_custom_res && globals->has("display/window/height"))
video_mode.height = globals->get("display/window/height");
- if (!editor && (!bool(globals->get("display/window/allow_hidpi")) || force_lowdpi)) {
+ if (!editor && ((globals->has("display/window/allow_hidpi") && !globals->get("display/window/allow_hidpi")) || force_lowdpi)) {
OS::get_singleton()->_allow_hidpi = false;
}
if (use_custom_res && globals->has("display/window/fullscreen"))
diff --git a/main/tests/test_math.cpp b/main/tests/test_math.cpp
index cb75dcec13..95a1672e67 100644
--- a/main/tests/test_math.cpp
+++ b/main/tests/test_math.cpp
@@ -602,7 +602,7 @@ MainLoop *test() {
print_line(q3);
print_line("before v: " + v + " a: " + rtos(a));
- q.get_axis_and_angle(v, a);
+ q.get_axis_angle(v, a);
print_line("after v: " + v + " a: " + rtos(a));
}
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index f80e6e501e..d79b7685d1 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -144,12 +144,14 @@ RES ResourceFormatDDS::load(const String &p_path, const String &p_original_path,
f->get_32();
f->get_32();
- /*print_line("DDS width: "+itos(width));
+ /*
+ print_line("DDS width: "+itos(width));
print_line("DDS height: "+itos(height));
- print_line("DDS mipmaps: "+itos(mipmaps));*/
+ print_line("DDS mipmaps: "+itos(mipmaps));
- //printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
- //printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+ printf("fourcc: %x fflags: %x, rgbbits: %x, fsize: %x\n",format_fourcc,format_flags,format_rgb_bits,format_size);
+ printf("rmask: %x gmask: %x, bmask: %x, amask: %x\n",format_red_mask,format_green_mask,format_blue_mask,format_alpha_mask);
+ */
//must avoid this later
while (f->get_pos() < 128)
diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp
index c2f14f5466..2a3015c185 100644
--- a/modules/gdscript/gd_editor.cpp
+++ b/modules/gdscript/gd_editor.cpp
@@ -32,6 +32,10 @@
#include "gd_script.h"
#include "global_config.h"
#include "os/file_access.h"
+#ifdef TOOLS_ENABLED
+#include "editor/editor_file_system.h"
+#include "editor/editor_settings.h"
+#endif
void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {
@@ -1406,6 +1410,17 @@ static void _make_function_hint(const GDParser::FunctionNode *p_func, int p_argi
arghint += ")";
}
+void get_directory_contents(EditorFileSystemDirectory *p_dir, Set<String> &r_list) {
+
+ for (int i = 0; i < p_dir->get_subdir_count(); i++) {
+ get_directory_contents(p_dir->get_subdir(i), r_list);
+ }
+
+ for (int i = 0; i < p_dir->get_file_count(); i++) {
+ r_list.insert("\"" + p_dir->get_file_path(i) + "\"");
+ }
+}
+
static void _find_type_arguments(GDCompletionContext &context, const GDParser::Node *p_node, int p_line, const StringName &p_method, const GDCompletionIdentifier &id, int p_argidx, Set<String> &result, String &arghint) {
//print_line("find type arguments?");
@@ -1754,6 +1769,10 @@ static void _find_call_arguments(GDCompletionContext &context, const GDParser::N
const GDParser::BuiltInFunctionNode *fn = static_cast<const GDParser::BuiltInFunctionNode *>(op->arguments[0]);
MethodInfo mi = GDFunctions::get_info(fn->function);
+ if (mi.name == "load" && bool(EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))) {
+ get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), result);
+ }
+
arghint = _get_visual_datatype(mi.return_val, false) + " " + GDFunctions::get_func_name(fn->function) + String("(");
for (int i = 0; i < mi.arguments.size(); i++) {
if (i > 0)
@@ -2375,6 +2394,11 @@ Error GDScriptLanguage::complete_code(const String &p_code, const String &p_base
}
} break;
+ case GDParser::COMPLETION_PRELOAD: {
+
+ if (EditorSettings::get_singleton()->get("text_editor/completion/complete_file_paths"))
+ get_directory_contents(EditorFileSystem::get_singleton()->get_filesystem(), options);
+ } break;
}
for (Set<String>::Element *E = options.front(); E; E = E->next()) {
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index cd16fef6b3..b02d7f713b 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -387,6 +387,13 @@ GDParser::Node *GDParser::_parse_expression(Node *p_parent, bool p_static, bool
_set_error("Expected '(' after 'preload'");
return NULL;
}
+ completion_cursor = StringName();
+ completion_type = COMPLETION_PRELOAD;
+ completion_class = current_class;
+ completion_function = current_function;
+ completion_line = tokenizer->get_token_line();
+ completion_block = current_block;
+ completion_found = true;
tokenizer->advance();
String path;
diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h
index 445ad7361c..4f3ca0dc5f 100644
--- a/modules/gdscript/gd_parser.h
+++ b/modules/gdscript/gd_parser.h
@@ -437,6 +437,7 @@ public:
COMPLETION_PARENT_FUNCTION,
COMPLETION_METHOD,
COMPLETION_CALL_ARGUMENTS,
+ COMPLETION_PRELOAD,
COMPLETION_INDEX,
COMPLETION_VIRTUAL_FUNC,
COMPLETION_YIELD,
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index 2367f60740..fe20a842cf 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -1696,9 +1696,9 @@ void GDScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_so
//same thing for placeholders
#ifdef TOOLS_ENABLED
- while (E->get()->placeholders.size()) {
+ for (Set<PlaceHolderScriptInstance *>::Element *P = E->get()->placeholders.front(); P; P = P->next()) {
- Object *obj = E->get()->placeholders.front()->get()->get_owner();
+ Object *obj = P->get()->get_owner();
//save instance info
List<Pair<StringName, Variant> > state;
if (obj->get_script_instance()) {
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index f92c11b9e0..00ae136790 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -384,6 +384,7 @@ public:
virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_functions = NULL) const;
virtual Script *create_script() const;
virtual bool has_named_classes() const;
+ virtual bool can_inherit_from_file() { return true; }
virtual int find_function(const String &p_function, const String &p_code) const;
virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
virtual Error complete_code(const String &p_code, const String &p_base_path, Object *p_owner, List<String> *r_options, String &r_call_hint);
diff --git a/modules/multiscript/SCsub b/modules/multiscript/SCsub
new file mode 100644
index 0000000000..0882406761
--- /dev/null
+++ b/modules/multiscript/SCsub
@@ -0,0 +1,7 @@
+#!/usr/bin/env python
+
+Import('env')
+
+env.add_source_files(env.modules_sources, "*.cpp")
+
+Export('env')
diff --git a/modules/multiscript/config.py b/modules/multiscript/config.py
new file mode 100644
index 0000000000..5698a37295
--- /dev/null
+++ b/modules/multiscript/config.py
@@ -0,0 +1,8 @@
+
+
+def can_build(platform):
+ return True
+
+
+def configure(env):
+ pass
diff --git a/modules/multiscript/multiscript.cpp b/modules/multiscript/multiscript.cpp
new file mode 100644
index 0000000000..b2633b7207
--- /dev/null
+++ b/modules/multiscript/multiscript.cpp
@@ -0,0 +1,750 @@
+/*************************************************************************/
+/* multiscript.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "multiscript.h"
+
+bool MultiScriptInstance::set(const StringName &p_name, const Variant &p_value) {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ bool found = sarr[i]->set(p_name, p_value);
+ if (found)
+ return true;
+ }
+
+ if (String(p_name).begins_with("script_")) {
+ bool valid;
+ owner->set(p_name, p_value, &valid);
+ return valid;
+ }
+ return false;
+}
+
+bool MultiScriptInstance::get(const StringName &p_name, Variant &r_ret) const {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ bool found = sarr[i]->get(p_name, r_ret);
+ if (found)
+ return true;
+ }
+ if (String(p_name).begins_with("script_")) {
+ bool valid;
+ r_ret = owner->get(p_name, &valid);
+ return valid;
+ }
+ return false;
+}
+void MultiScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ Set<String> existing;
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ List<PropertyInfo> pl;
+ sarr[i]->get_property_list(&pl);
+
+ for (List<PropertyInfo>::Element *E = pl.front(); E; E = E->next()) {
+
+ if (existing.has(E->get().name))
+ continue;
+
+ p_properties->push_back(E->get());
+ existing.insert(E->get().name);
+ }
+ }
+
+ p_properties->push_back(PropertyInfo(Variant::NIL, "Scripts", PROPERTY_HINT_NONE, String(), PROPERTY_USAGE_CATEGORY));
+
+ for (int i = 0; i < owner->scripts.size(); i++) {
+
+ p_properties->push_back(PropertyInfo(Variant::OBJECT, "script_" + String::chr('a' + i), PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_EDITOR));
+ }
+
+ if (owner->scripts.size() < 25) {
+
+ p_properties->push_back(PropertyInfo(Variant::OBJECT, "script_" + String::chr('a' + (owner->scripts.size())), PROPERTY_HINT_RESOURCE_TYPE, "Script", PROPERTY_USAGE_EDITOR));
+ }
+}
+
+void MultiScriptInstance::get_method_list(List<MethodInfo> *p_list) const {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ Set<StringName> existing;
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ List<MethodInfo> ml;
+ sarr[i]->get_method_list(&ml);
+
+ for (List<MethodInfo>::Element *E = ml.front(); E; E = E->next()) {
+
+ if (existing.has(E->get().name))
+ continue;
+
+ p_list->push_back(E->get());
+ existing.insert(E->get().name);
+ }
+ }
+}
+bool MultiScriptInstance::has_method(const StringName &p_method) const {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ if (sarr[i]->has_method(p_method))
+ return true;
+ }
+
+ return false;
+}
+
+Variant MultiScriptInstance::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ Variant r = sarr[i]->call(p_method, p_args, p_argcount, r_error);
+ if (r_error.error == Variant::CallError::CALL_OK)
+ return r;
+ else if (r_error.error != Variant::CallError::CALL_ERROR_INVALID_METHOD)
+ return r;
+ }
+
+ r_error.error = Variant::CallError::CALL_ERROR_INVALID_METHOD;
+ return Variant();
+}
+
+void MultiScriptInstance::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ sarr[i]->call_multilevel(p_method, p_args, p_argcount);
+ }
+}
+void MultiScriptInstance::notification(int p_notification) {
+
+ // ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ ScriptInstance *instance = instances[i];
+
+ if (!instance)
+ continue;
+
+ instance->notification(p_notification);
+ }
+}
+
+Ref<Script> MultiScriptInstance::get_script() const {
+
+ return owner;
+}
+
+ScriptLanguage *MultiScriptInstance::get_language() {
+
+ return MultiScriptLanguage::get_singleton();
+}
+
+MultiScriptInstance::~MultiScriptInstance() {
+
+ owner->remove_instance(object);
+}
+
+Variant::Type MultiScriptInstance::get_property_type(const StringName &p_name, bool *r_is_valid) const {
+ bool valid = false;
+ Variant::Type type;
+
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ type = sarr[i]->get_property_type(p_name, &valid);
+ if (valid) {
+ *r_is_valid = valid;
+ return type;
+ }
+ }
+ *r_is_valid = false;
+ return Variant::NIL;
+}
+
+ScriptInstance::RPCMode MultiScriptInstance::get_rpc_mode(const StringName &p_method) const {
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+ if (sarr[i]->has_method(p_method))
+ return sarr[i]->get_rpc_mode(p_method);
+ }
+ return RPC_MODE_DISABLED;
+}
+
+ScriptInstance::RPCMode MultiScriptInstance::get_rset_mode(const StringName &p_variable) const {
+ ScriptInstance **sarr = instances.ptr();
+ int sc = instances.size();
+
+ for (int i = 0; i < sc; i++) {
+
+ if (!sarr[i])
+ continue;
+
+ List<PropertyInfo> properties;
+ sarr[i]->get_property_list(&properties);
+
+ for (List<PropertyInfo>::Element *P = properties.front(); P; P = P->next()) {
+ if (P->get().name == p_variable) {
+ return sarr[i]->get_rset_mode(p_variable);
+ }
+ }
+ }
+ return RPC_MODE_DISABLED;
+}
+
+///////////////////
+
+bool MultiScript::is_tool() const {
+
+ for (int i = 0; i < scripts.size(); i++) {
+
+ if (scripts[i]->is_tool())
+ return true;
+ }
+
+ return false;
+}
+
+bool MultiScript::_set(const StringName &p_name, const Variant &p_value) {
+
+ _THREAD_SAFE_METHOD_
+
+ String s = String(p_name);
+ if (s.begins_with("script_")) {
+
+ int idx = s[7];
+ if (idx == 0)
+ return false;
+ idx -= 'a';
+
+ ERR_FAIL_COND_V(idx < 0, false);
+
+ Ref<Script> s = p_value;
+
+ if (idx < scripts.size()) {
+
+ if (s.is_null())
+ remove_script(idx);
+ else
+ set_script(idx, s);
+ } else if (idx == scripts.size()) {
+ if (s.is_null())
+ return false;
+ add_script(s);
+ } else
+ return false;
+
+ return true;
+ }
+
+ return false;
+}
+
+bool MultiScript::_get(const StringName &p_name, Variant &r_ret) const {
+
+ _THREAD_SAFE_METHOD_
+
+ String s = String(p_name);
+ if (s.begins_with("script_")) {
+
+ int idx = s[7];
+ if (idx == 0)
+ return false;
+ idx -= 'a';
+
+ ERR_FAIL_COND_V(idx < 0, false);
+
+ if (idx < scripts.size()) {
+
+ r_ret = get_script(idx);
+ return true;
+ } else if (idx == scripts.size()) {
+ r_ret = Ref<Script>();
+ return true;
+ }
+ }
+
+ return false;
+}
+void MultiScript::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ _THREAD_SAFE_METHOD_
+
+ for (int i = 0; i < scripts.size(); i++) {
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "script_" + String::chr('a' + i), PROPERTY_HINT_RESOURCE_TYPE, "Script"));
+ }
+
+ if (scripts.size() < 25) {
+
+ p_list->push_back(PropertyInfo(Variant::OBJECT, "script_" + String::chr('a' + (scripts.size())), PROPERTY_HINT_RESOURCE_TYPE, "Script"));
+ }
+}
+
+void MultiScript::set_script(int p_idx, const Ref<Script> &p_script) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_idx, scripts.size());
+ ERR_FAIL_COND(p_script.is_null());
+
+ scripts[p_idx] = p_script;
+ Ref<Script> s = p_script;
+
+ for (Map<Object *, MultiScriptInstance *>::Element *E = instances.front(); E; E = E->next()) {
+
+ MultiScriptInstance *msi = E->get();
+ ScriptInstance *si = msi->instances[p_idx];
+ if (si) {
+ msi->instances[p_idx] = NULL;
+ memdelete(si);
+ }
+
+ if (p_script->can_instance())
+ msi->instances[p_idx] = s->instance_create(msi->object);
+ }
+}
+
+Ref<Script> MultiScript::get_script(int p_idx) const {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX_V(p_idx, scripts.size(), Ref<Script>());
+
+ return scripts[p_idx];
+}
+void MultiScript::add_script(const Ref<Script> &p_script) {
+
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_COND(p_script.is_null());
+ Multi *script_owner = memnew(Multi);
+ script_instances.push_back(script_owner);
+ scripts.push_back(p_script);
+ Ref<Script> s = p_script;
+
+ for (Map<Object *, MultiScriptInstance *>::Element *E = instances.front(); E; E = E->next()) {
+
+ MultiScriptInstance *msi = E->get();
+
+ if (p_script->can_instance()) {
+ script_owner->real_owner = msi->object;
+ msi->instances.push_back(s->instance_create(script_owner));
+ } else {
+ msi->instances.push_back(NULL);
+ }
+
+ msi->object->_change_notify();
+ }
+
+ _change_notify();
+}
+
+void MultiScript::remove_script(int p_idx) {
+
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX(p_idx, scripts.size());
+
+ scripts.remove(p_idx);
+ script_instances.remove(p_idx);
+
+ for (Map<Object *, MultiScriptInstance *>::Element *E = instances.front(); E; E = E->next()) {
+
+ MultiScriptInstance *msi = E->get();
+ ScriptInstance *si = msi->instances[p_idx];
+ msi->instances.remove(p_idx);
+ if (si) {
+ memdelete(si);
+ }
+
+ msi->object->_change_notify();
+ }
+}
+
+void MultiScript::remove_instance(Object *p_object) {
+
+ _THREAD_SAFE_METHOD_
+ instances.erase(p_object);
+}
+
+bool MultiScript::can_instance() const {
+
+ return true;
+}
+
+StringName MultiScript::get_instance_base_type() const {
+
+ return StringName();
+}
+ScriptInstance *MultiScript::instance_create(Object *p_this) {
+
+ _THREAD_SAFE_METHOD_
+ MultiScriptInstance *msi = memnew(MultiScriptInstance);
+ msi->object = p_this;
+ msi->owner = this;
+
+ for (int i = 0; i < scripts.size(); i++) {
+
+ ScriptInstance *si;
+
+ if (scripts[i]->can_instance()) {
+ script_instances[i]->real_owner = p_this;
+ si = scripts[i]->instance_create(script_instances[i]);
+ } else {
+ si = NULL;
+ }
+
+ msi->instances.push_back(si);
+ }
+
+ instances[p_this] = msi;
+ p_this->_change_notify();
+ return msi;
+}
+bool MultiScript::instance_has(const Object *p_this) const {
+
+ _THREAD_SAFE_METHOD_
+ return instances.has((Object *)p_this);
+}
+
+bool MultiScript::has_source_code() const {
+
+ return false;
+}
+String MultiScript::get_source_code() const {
+
+ return "";
+}
+void MultiScript::set_source_code(const String &p_code) {
+}
+Error MultiScript::reload(bool p_keep_state) {
+
+ for (int i = 0; i < scripts.size(); i++)
+ scripts[i]->reload(p_keep_state);
+
+ return OK;
+}
+
+String MultiScript::get_node_type() const {
+
+ return "";
+}
+
+void MultiScript::_bind_methods() {
+}
+
+ScriptLanguage *MultiScript::get_language() const {
+
+ return MultiScriptLanguage::get_singleton();
+}
+
+///////////////
+
+MultiScript::MultiScript() {
+}
+
+MultiScript::~MultiScript() {
+ for (int i = 0; i < script_instances.size(); i++) {
+ memdelete(script_instances[i]);
+ }
+
+ script_instances.resize(0);
+}
+
+Ref<Script> MultiScript::get_base_script() const {
+ Ref<MultiScript> base_script;
+ return base_script;
+}
+
+bool MultiScript::has_method(const StringName &p_method) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ if (scripts[i]->has_method(p_method)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+MethodInfo MultiScript::get_method_info(const StringName &p_method) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ if (scripts[i]->has_method(p_method)) {
+ return scripts[i]->get_method_info(p_method);
+ }
+ }
+ return MethodInfo();
+}
+
+bool MultiScript::has_script_signal(const StringName &p_signal) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ if (scripts[i]->has_script_signal(p_signal)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MultiScript::get_script_signal_list(List<MethodInfo> *r_signals) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ scripts[i]->get_script_signal_list(r_signals);
+ }
+}
+
+bool MultiScript::get_property_default_value(const StringName &p_property, Variant &r_value) const {
+ for (int i = 0; i < scripts.size(); i++) {
+
+ if (scripts[i]->get_property_default_value(p_property, r_value)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void MultiScript::get_script_method_list(List<MethodInfo> *p_list) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ scripts[i]->get_script_method_list(p_list);
+ }
+}
+
+void MultiScript::get_script_property_list(List<PropertyInfo> *p_list) const {
+ for (int i = 0; i < scripts.size(); i++) {
+ scripts[i]->get_script_property_list(p_list);
+ }
+}
+
+void MultiScript::update_exports() {
+ for (int i = 0; i < scripts.size(); i++) {
+ scripts[i]->update_exports();
+ }
+}
+
+MultiScriptLanguage *MultiScriptLanguage::singleton = NULL;
+
+MultiScriptLanguage *MultiScriptLanguage::get_singleton() {
+ return singleton;
+}
+
+String MultiScriptLanguage::get_name() const {
+ return "MultiScript";
+}
+
+void MultiScriptLanguage::init() {}
+
+String MultiScriptLanguage::get_type() const {
+ return "MultiScript";
+}
+
+String MultiScriptLanguage::get_extension() const {
+ return "";
+}
+
+Error MultiScriptLanguage::execute_file(const String &p_path) {
+ return OK;
+}
+
+void MultiScriptLanguage::finish() {}
+
+void MultiScriptLanguage::get_reserved_words(List<String> *p_words) const {}
+
+void MultiScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const {}
+
+void MultiScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const {}
+
+Ref<Script> MultiScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const {
+ MultiScript *s = memnew(MultiScript);
+ s->base_class_name = p_base_class_name;
+ return Ref<MultiScript>(s);
+}
+
+bool MultiScriptLanguage::validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path, List<String> *r_fn) const {
+ return true;
+}
+
+Script *MultiScriptLanguage::create_script() const {
+ return memnew(MultiScript);
+}
+
+bool MultiScriptLanguage::has_named_classes() const {
+ return false;
+}
+
+int MultiScriptLanguage::find_function(const String &p_function, const String &p_code) const {
+ return -1;
+}
+
+String MultiScriptLanguage::make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const {
+ return "";
+}
+
+String MultiScriptLanguage::debug_get_error() const {
+ return "";
+}
+
+int MultiScriptLanguage::debug_get_stack_level_count() const {
+ return 0;
+}
+
+int MultiScriptLanguage::debug_get_stack_level_line(int p_level) const {
+ return 0;
+}
+
+String MultiScriptLanguage::debug_get_stack_level_function(int p_level) const {
+ return "";
+}
+
+String MultiScriptLanguage::debug_get_stack_level_source(int p_level) const {
+ return "";
+}
+
+void MultiScriptLanguage::debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
+
+void MultiScriptLanguage::debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
+
+void MultiScriptLanguage::debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems, int p_max_depth) {}
+
+String MultiScriptLanguage::debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems, int p_max_depth) {
+ return "";
+}
+
+void MultiScriptLanguage::get_recognized_extensions(List<String> *p_extensions) const {}
+
+void MultiScriptLanguage::get_public_functions(List<MethodInfo> *p_functions) const {}
+
+MultiScriptLanguage::MultiScriptLanguage() {
+ singleton = this;
+}
+
+MultiScriptLanguage::~MultiScriptLanguage() {}
+
+void MultiScriptLanguage::auto_indent_code(String &p_code, int p_from_line, int p_to_line) const {
+}
+
+void MultiScriptLanguage::add_global_constant(const StringName &p_variable, const Variant &p_value) {
+}
+
+void MultiScriptLanguage::reload_all_scripts() {
+}
+
+void MultiScriptLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) {
+}
+
+void MultiScriptLanguage::get_public_constants(List<Pair<String, Variant> > *p_constants) const {
+}
+
+void MultiScriptLanguage::profiling_start() {
+}
+
+void MultiScriptLanguage::profiling_stop() {
+}
+
+int MultiScriptLanguage::profiling_get_accumulated_data(ScriptLanguage::ProfilingInfo *p_info_arr, int p_info_max) {
+ return 0;
+}
+
+int MultiScriptLanguage::profiling_get_frame_data(ScriptLanguage::ProfilingInfo *p_info_arr, int p_info_max) {
+ return 0;
+}
+
+void Multi::_bind_methods() {
+ // ClassDB::bind_method("call", &Multi::call);
+ // ClassDB::bind_method("call_multilevel", &Multi::call_multilevel);
+ // ClassDB::bind_method("call_multilevel_reversed", &Multi::call_multilevel_reversed);
+}
+
+Variant Multi::call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error) {
+ if (real_owner)
+ return real_owner->call(p_method, p_args, p_argcount, r_error);
+ return Variant();
+}
+
+void Multi::call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount) {
+ if (real_owner)
+ real_owner->call_multilevel(p_method, p_args, p_argcount);
+}
+
+void Multi::call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount) {
+ if (real_owner)
+ real_owner->call_multilevel_reversed(p_method, p_args, p_argcount);
+}
diff --git a/modules/multiscript/multiscript.h b/modules/multiscript/multiscript.h
new file mode 100644
index 0000000000..7ec1d5402f
--- /dev/null
+++ b/modules/multiscript/multiscript.h
@@ -0,0 +1,200 @@
+/*************************************************************************/
+/* multiscript.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef MULTISCRIPT_H
+#define MULTISCRIPT_H
+
+#include "os/thread_safe.h"
+#include "script_language.h"
+
+class MultiScript;
+
+class Multi : public Object {
+ GDCLASS(Multi, Object)
+
+ friend class MultiScript;
+
+ Object *real_owner;
+
+public:
+ static void _bind_methods();
+
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
+ virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
+ virtual void call_multilevel_reversed(const StringName &p_method, const Variant **p_args, int p_argcount);
+};
+
+class MultiScriptInstance : public ScriptInstance {
+ friend class MultiScript;
+ mutable Vector<ScriptInstance *> instances;
+ Object *object;
+ mutable MultiScript *owner;
+
+public:
+ virtual bool set(const StringName &p_name, const Variant &p_value);
+ virtual bool get(const StringName &p_name, Variant &r_ret) const;
+ virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+
+ virtual void get_method_list(List<MethodInfo> *p_list) const;
+ virtual bool has_method(const StringName &p_method) const;
+ virtual Variant call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant::CallError &r_error);
+ virtual void call_multilevel(const StringName &p_method, const Variant **p_args, int p_argcount);
+ virtual void notification(int p_notification);
+
+ virtual Ref<Script> get_script() const;
+
+ virtual ScriptLanguage *get_language();
+ virtual ~MultiScriptInstance();
+
+ // ScriptInstance interface
+public:
+ Variant::Type get_property_type(const StringName &p_name, bool *r_is_valid) const;
+ RPCMode get_rpc_mode(const StringName &p_method) const;
+ RPCMode get_rset_mode(const StringName &p_variable) const;
+};
+
+class MultiScript : public Script {
+
+ _THREAD_SAFE_CLASS_
+ friend class MultiScriptInstance;
+ friend class MultiScriptLanguage;
+ GDCLASS(MultiScript, Script)
+
+ StringName base_class_name;
+
+ Vector<Ref<Script> > scripts;
+ Vector<Multi *> script_instances;
+
+ Map<Object *, MultiScriptInstance *> instances;
+
+protected:
+ 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;
+
+ static void _bind_methods();
+
+public:
+ void remove_instance(Object *p_object);
+ virtual bool can_instance() const;
+
+ virtual StringName get_instance_base_type() const;
+ virtual ScriptInstance *instance_create(Object *p_this);
+ virtual bool instance_has(const Object *p_this) const;
+
+ virtual bool has_source_code() const;
+ virtual String get_source_code() const;
+ virtual void set_source_code(const String &p_code);
+ virtual Error reload(bool p_keep_state = false);
+
+ virtual bool is_tool() const;
+
+ virtual String get_node_type() const;
+
+ void set_script(int p_idx, const Ref<Script> &p_script);
+ Ref<Script> get_script(int p_idx) const;
+ void remove_script(int p_idx);
+ void add_script(const Ref<Script> &p_script);
+
+ virtual ScriptLanguage *get_language() const;
+
+ MultiScript();
+ ~MultiScript();
+
+ virtual Ref<Script> get_base_script() const;
+ virtual bool has_method(const StringName &p_method) const;
+ virtual MethodInfo get_method_info(const StringName &p_method) const;
+ virtual bool has_script_signal(const StringName &p_signal) const;
+ virtual void get_script_signal_list(List<MethodInfo> *r_signals) const;
+ virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const;
+ virtual void get_script_method_list(List<MethodInfo> *p_list) const;
+ virtual void get_script_property_list(List<PropertyInfo> *p_list) const;
+ virtual void update_exports();
+};
+
+class MultiScriptLanguage : public ScriptLanguage {
+
+ static MultiScriptLanguage *singleton;
+
+public:
+ static _FORCE_INLINE_ MultiScriptLanguage *get_singleton();
+ virtual String get_name() const;
+
+ /* LANGUAGE FUNCTIONS */
+ virtual void init();
+ virtual String get_type() const;
+ virtual String get_extension() const;
+ virtual Error execute_file(const String &p_path);
+ virtual void finish();
+
+ /* EDITOR FUNCTIONS */
+ virtual void get_reserved_words(List<String> *p_words) const;
+ virtual void get_comment_delimiters(List<String> *p_delimiters) const;
+ virtual void get_string_delimiters(List<String> *p_delimiters) const;
+ virtual Ref<Script> get_template(const String &p_class_name, const String &p_base_class_name) const;
+ virtual bool validate(const String &p_script, int &r_line_error, int &r_col_error, String &r_test_error, const String &p_path = "", List<String> *r_fn = NULL) const;
+ virtual Script *create_script() const;
+ virtual bool has_named_classes() const;
+ virtual int find_function(const String &p_function, const String &p_code) const;
+ virtual String make_function(const String &p_class, const String &p_name, const PoolStringArray &p_args) const;
+
+ /* DEBUGGER FUNCTIONS */
+
+ virtual String debug_get_error() const;
+ virtual int debug_get_stack_level_count() const;
+ virtual int debug_get_stack_level_line(int p_level) const;
+ virtual String debug_get_stack_level_function(int p_level) const;
+ virtual String debug_get_stack_level_source(int p_level) const;
+ virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
+ virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
+ virtual void debug_get_globals(List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1);
+ virtual String debug_parse_stack_level_expression(int p_level, const String &p_expression, int p_max_subitems = -1, int p_max_depth = -1);
+
+ /* LOADER FUNCTIONS */
+
+ virtual void get_recognized_extensions(List<String> *p_extensions) const;
+ virtual void get_public_functions(List<MethodInfo> *p_functions) const;
+
+ MultiScriptLanguage();
+ virtual ~MultiScriptLanguage();
+
+ // ScriptLanguage interface
+public:
+ void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const;
+ void add_global_constant(const StringName &p_variable, const Variant &p_value);
+ void reload_all_scripts();
+ void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload);
+ void get_public_constants(List<Pair<String, Variant> > *p_constants) const;
+ void profiling_start();
+ void profiling_stop();
+ int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max);
+ int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max);
+};
+
+#endif // MULTISCRIPT_H
diff --git a/modules/multiscript/register_types.cpp b/modules/multiscript/register_types.cpp
new file mode 100644
index 0000000000..8170a2d9c1
--- /dev/null
+++ b/modules/multiscript/register_types.cpp
@@ -0,0 +1,51 @@
+/*************************************************************************/
+/* register_types.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "register_types.h"
+
+#include "multiscript.h"
+
+#include "core/script_language.h"
+
+static MultiScriptLanguage *script_multi_script = NULL;
+
+void register_multiscript_types() {
+ script_multi_script = memnew(MultiScriptLanguage);
+ ScriptServer::register_language(script_multi_script);
+ ClassDB::register_class<MultiScript>();
+
+ // ClassDB::register_class<Multi>();
+}
+
+void unregister_multiscript_types() {
+ if (script_multi_script) {
+ ScriptServer::unregister_language(script_multi_script);
+ memdelete(script_multi_script);
+ }
+}
diff --git a/modules/multiscript/register_types.h b/modules/multiscript/register_types.h
new file mode 100644
index 0000000000..b18d1adff2
--- /dev/null
+++ b/modules/multiscript/register_types.h
@@ -0,0 +1,31 @@
+/*************************************************************************/
+/* register_types.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2017 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+void register_multiscript_types();
+void unregister_multiscript_types();
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index 39c8f812ac..889eac71a7 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -87,9 +87,11 @@ static Image _webp_lossy_unpack(const PoolVector<uint8_t> &p_buffer) {
ERR_FAIL_V(Image());
}
- //print_line("width: "+itos(features.width));
- //print_line("height: "+itos(features.height));
- //print_line("alpha: "+itos(features.has_alpha));
+ /*
+ print_line("width: "+itos(features.width));
+ print_line("height: "+itos(features.height));
+ print_line("alpha: "+itos(features.has_alpha));
+ */
PoolVector<uint8_t> dst_image;
int datasize = features.width * features.height * (features.has_alpha ? 4 : 3);
@@ -130,9 +132,11 @@ Error ImageLoaderWEBP::load_image(Image *p_image, FileAccess *f) {
ERR_FAIL_V(ERR_FILE_CORRUPT);
}
+ /*
print_line("width: " + itos(features.width));
print_line("height: " + itos(features.height));
print_line("alpha: " + itos(features.has_alpha));
+ */
src_w = PoolVector<uint8_t>::Write();
diff --git a/platform/x11/context_gl_x11.cpp b/platform/x11/context_gl_x11.cpp
index 432aecb72e..ddf17481b1 100644
--- a/platform/x11/context_gl_x11.cpp
+++ b/platform/x11/context_gl_x11.cpp
@@ -152,8 +152,6 @@ Error ContextGL_X11::initialize() {
XSync(x11_display, False);
XSetErrorHandler(oldHandler);
- print_line("ALL IS GOOD");
-
glXMakeCurrent(x11_display, x11_window, p->glx_context);
/*
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index f752cbf6b6..626ea10515 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -1199,7 +1199,7 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const
revert_motion();
return Vector2();
}
- } else if (get_collision_normal().dot(-p_floor_direction) <= Math::cos(p_floor_max_angle)) { //ceiling
+ } else if (get_collision_normal().dot(-p_floor_direction) >= Math::cos(p_floor_max_angle)) { //ceiling
move_and_slide_on_ceiling = true;
} else {
move_and_slide_on_wall = true;
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 94f34d6fb3..98babedf0d 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -28,6 +28,7 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "physics_body.h"
+#include "method_bind_ext.inc"
#include "scene/scene_string_names.h"
void PhysicsBody::_notification(int p_what) {
@@ -907,6 +908,19 @@ bool KinematicBody::_ignores_mode(PhysicsServer::BodyMode p_mode) const {
return true;
}
+void KinematicBody::revert_motion() {
+
+ Transform gt = get_global_transform();
+ gt.origin -= travel; //I do hope this is correct.
+ travel = Vector3();
+ set_global_transform(gt);
+}
+
+Vector3 KinematicBody::get_travel() const {
+
+ return travel;
+}
+
Vector3 KinematicBody::move(const Vector3 &p_motion) {
//give me back regular physics engine logic
@@ -1097,10 +1111,111 @@ Vector3 KinematicBody::move(const Vector3 &p_motion) {
Transform gt = get_global_transform();
gt.origin += motion;
set_global_transform(gt);
+ travel = motion;
return p_motion - motion;
}
+Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, const Vector3 &p_ceil_direction, float p_slope_stop_min_velocity, int p_max_bounces, float p_floor_max_angle, float p_ceil_max_angle) {
+
+ /*
+ Things to note:
+ 1. This function is basically the KinematicBody2D function ported over.
+ 2. The 'travel' variable and stuff relating to it exists more or less for this function's sake.
+ 3. Someone is going to have to document this, so here's an example for them:
+ vel = move_and_slide(vel, Vector3(0, 1, 0), Vector3(0, -1, 0), 0.1);
+ Very useful for FPS controllers so long as you control horizontal motion properly - even for Quake-style AABB colliders.
+ The slope stop system is... rather weird, and it's correct operation depends on what scale your game is built on,
+ but as far as I can tell in theory it's suppposed to be a way of turning impassable slopes into invisible walls.
+ It can also be a pain, since there's a better-known way of defining such things: "let gravity do the work".
+ If you don't like it, set it to positive infinity.
+ 4. Might be a bug somewhere else in physics: When there are two CollisionShape nodes with a shared Shape, only one is considered, I think.
+ Test this further.
+ */
+
+ Vector3 motion = (move_and_slide_floor_velocity + p_linear_velocity) * get_fixed_process_delta_time();
+ Vector3 lv = p_linear_velocity;
+
+ move_and_slide_on_floor = false;
+ move_and_slide_on_ceiling = false;
+ move_and_slide_on_wall = false;
+ move_and_slide_colliders.clear();
+ move_and_slide_floor_velocity = Vector3();
+
+ while (p_max_bounces) {
+
+ motion = move(motion);
+
+ if (is_colliding()) {
+
+ bool hit_horizontal = false; //hit floor or ceiling
+
+ if (p_floor_direction != Vector3()) {
+ if (get_collision_normal().dot(p_floor_direction) >= Math::cos(p_floor_max_angle)) { //floor
+
+ hit_horizontal = true;
+ move_and_slide_on_floor = true;
+ move_and_slide_floor_velocity = get_collider_velocity();
+
+ //Note: These two lines are the only lines that really changed between 3D/2D, see if it can't be reused somehow???
+ Vector2 hz_velocity = Vector2(lv.x - move_and_slide_floor_velocity.x, lv.z - move_and_slide_floor_velocity.z);
+ if (get_travel().length() < 1 && hz_velocity.length() < p_slope_stop_min_velocity) {
+ revert_motion();
+ return Vector3();
+ }
+ }
+ }
+
+ if (p_ceil_direction != Vector3()) {
+ if (get_collision_normal().dot(p_ceil_direction) >= Math::cos(p_ceil_max_angle)) { //ceiling
+ hit_horizontal = true;
+ move_and_slide_on_ceiling = true;
+ }
+ }
+
+ //if it hit something but didn't hit a floor or ceiling, it is by default a wall
+ //(this imitates the pre-specifiable-ceiling logic more or less, except ceiling is optional)
+ if (!hit_horizontal) {
+ move_and_slide_on_wall = true;
+ }
+
+ Vector3 n = get_collision_normal();
+ motion = motion.slide(n);
+ lv = lv.slide(n);
+ Variant collider = _get_collider();
+ if (collider.get_type() != Variant::NIL) {
+ move_and_slide_colliders.push_back(collider);
+ }
+
+ } else {
+ break;
+ }
+
+ p_max_bounces--;
+ if (motion == Vector3())
+ break;
+ }
+
+ return lv;
+}
+
+bool KinematicBody::is_move_and_slide_on_floor() const {
+
+ return move_and_slide_on_floor;
+}
+bool KinematicBody::is_move_and_slide_on_wall() const {
+
+ return move_and_slide_on_wall;
+}
+bool KinematicBody::is_move_and_slide_on_ceiling() const {
+
+ return move_and_slide_on_ceiling;
+}
+Array KinematicBody::get_move_and_slide_colliders() const {
+
+ return move_and_slide_colliders;
+}
+
Vector3 KinematicBody::move_to(const Vector3 &p_position) {
return move(p_position - get_global_transform().origin);
@@ -1223,6 +1338,7 @@ void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("move", "rel_vec"), &KinematicBody::move);
ClassDB::bind_method(D_METHOD("move_to", "position"), &KinematicBody::move_to);
+ ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "ceil_normal", "slope_stop_min_velocity", "max_bounces", "floor_max_angle", "ceil_max_angle"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(Vector3(0, 0, 0)), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(Math::deg2rad((float)45)));
ClassDB::bind_method(D_METHOD("can_teleport_to", "position"), &KinematicBody::can_teleport_to);
@@ -1249,6 +1365,14 @@ void KinematicBody::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_collision_margin", "pixels"), &KinematicBody::set_collision_margin);
ClassDB::bind_method(D_METHOD("get_collision_margin", "pixels"), &KinematicBody::get_collision_margin);
+ ClassDB::bind_method(D_METHOD("get_travel"), &KinematicBody::get_travel);
+ ClassDB::bind_method(D_METHOD("revert_motion"), &KinematicBody::revert_motion);
+
+ ClassDB::bind_method(D_METHOD("get_move_and_slide_colliders"), &KinematicBody::get_move_and_slide_colliders);
+ ClassDB::bind_method(D_METHOD("is_move_and_slide_on_floor"), &KinematicBody::is_move_and_slide_on_floor);
+ ClassDB::bind_method(D_METHOD("is_move_and_slide_on_ceiling"), &KinematicBody::is_move_and_slide_on_ceiling);
+ ClassDB::bind_method(D_METHOD("is_move_and_slide_on_wall"), &KinematicBody::is_move_and_slide_on_wall);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with/static"), "set_collide_with_static_bodies", "can_collide_with_static_bodies");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with/kinematic"), "set_collide_with_kinematic_bodies", "can_collide_with_kinematic_bodies");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with/rigid"), "set_collide_with_rigid_bodies", "can_collide_with_rigid_bodies");
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index c62f6be13f..d13f84dc15 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -275,6 +275,13 @@ class KinematicBody : public PhysicsBody {
Vector3 collider_vel;
ObjectID collider;
int collider_shape;
+ Vector3 travel;
+
+ Vector3 move_and_slide_floor_velocity;
+ bool move_and_slide_on_floor;
+ bool move_and_slide_on_ceiling;
+ bool move_and_slide_on_wall;
+ Array move_and_slide_colliders;
Variant _get_collider() const;
@@ -295,6 +302,10 @@ public:
bool can_teleport_to(const Vector3 &p_position);
bool is_colliding() const;
+
+ Vector3 get_travel() const; // Set by move and others. Consider unreliable except immediately after a move call.
+ void revert_motion();
+
Vector3 get_collision_pos() const;
Vector3 get_collision_normal() const;
Vector3 get_collider_velocity() const;
@@ -316,6 +327,12 @@ public:
void set_collision_margin(float p_margin);
float get_collision_margin() const;
+ Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), const Vector3 &p_ceil_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 5, int p_max_bounces = 4, float p_floor_max_angle = Math::deg2rad((float)45), float p_ceil_max_angle = Math::deg2rad((float)45));
+ bool is_move_and_slide_on_floor() const;
+ bool is_move_and_slide_on_wall() const;
+ bool is_move_and_slide_on_ceiling() const;
+ Array get_move_and_slide_colliders() const;
+
KinematicBody();
~KinematicBody();
};
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index f984d8f06d..d3b4577c42 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -30,12 +30,15 @@
#include "ray_cast.h"
#include "collision_object.h"
+#include "mesh_instance.h"
#include "servers/physics_server.h"
void RayCast::set_cast_to(const Vector3 &p_point) {
cast_to = p_point;
if (is_inside_tree() && (get_tree()->is_editor_hint() || get_tree()->is_debugging_collisions_hint()))
update_gizmo();
+ if (is_inside_tree() && get_tree()->is_debugging_collisions_hint())
+ _update_debug_shape();
}
Vector3 RayCast::get_cast_to() const {
@@ -95,6 +98,13 @@ void RayCast::set_enabled(bool p_enabled) {
set_fixed_process(p_enabled);
if (!p_enabled)
collided = false;
+
+ if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
+ if (p_enabled)
+ _update_debug_shape();
+ else
+ _clear_debug_shape();
+ }
}
bool RayCast::is_enabled() const {
@@ -110,6 +120,9 @@ void RayCast::_notification(int p_what) {
if (enabled && !get_tree()->is_editor_hint()) {
set_fixed_process(true);
+
+ if (get_tree()->is_debugging_collisions_hint())
+ _update_debug_shape();
} else
set_fixed_process(false);
@@ -120,13 +133,23 @@ void RayCast::_notification(int p_what) {
set_fixed_process(false);
}
+ if (debug_shape)
+ _clear_debug_shape();
+
} break;
case NOTIFICATION_FIXED_PROCESS: {
if (!enabled)
break;
+ bool prev_collision_state = collided;
_update_raycast_state();
+ if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) {
+ if (debug_material.is_valid()) {
+ Ref<FixedSpatialMaterial> line_material = static_cast<Ref<FixedSpatialMaterial> >(debug_material);
+ line_material->set_albedo(collided ? Color(1.0, 0, 0) : Color(1.0, 0.8, 0.6));
+ }
+ }
} break;
}
@@ -232,6 +255,68 @@ void RayCast::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "type_mask", PROPERTY_HINT_FLAGS, "Static,Kinematic,Rigid,Character,Area"), "set_type_mask", "get_type_mask");
}
+void RayCast::_create_debug_shape() {
+
+ if (!debug_material.is_valid()) {
+ debug_material = Ref<FixedSpatialMaterial>(memnew(FixedSpatialMaterial));
+
+ Ref<FixedSpatialMaterial> line_material = static_cast<Ref<FixedSpatialMaterial> >(debug_material);
+ line_material->set_flag(FixedSpatialMaterial::FLAG_UNSHADED, true);
+ line_material->set_line_width(3.0);
+ line_material->set_albedo(Color(1.0, 0.8, 0.6));
+ }
+
+ Ref<Mesh> mesh = memnew(Mesh);
+
+ MeshInstance *mi = memnew(MeshInstance);
+ mi->set_mesh(mesh);
+
+ add_child(mi);
+ debug_shape = mi;
+}
+
+void RayCast::_update_debug_shape() {
+
+ if (!enabled)
+ return;
+
+ if (!debug_shape)
+ _create_debug_shape();
+
+ MeshInstance *mi = static_cast<MeshInstance *>(debug_shape);
+ if (!mi->get_mesh().is_valid())
+ return;
+
+ Ref<Mesh> mesh = mi->get_mesh();
+ if (mesh->get_surface_count() > 0)
+ mesh->surface_remove(0);
+
+ Array a;
+ a.resize(Mesh::ARRAY_MAX);
+
+ Vector<Vector3> verts;
+ verts.push_back(Vector3());
+ verts.push_back(cast_to);
+ a[Mesh::ARRAY_VERTEX] = verts;
+
+ mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a);
+ mesh->surface_set_material(0, debug_material);
+}
+
+void RayCast::_clear_debug_shape() {
+
+ if (!debug_shape)
+ return;
+
+ MeshInstance *mi = static_cast<MeshInstance *>(debug_shape);
+ if (mi->is_inside_tree())
+ mi->queue_delete();
+ else
+ memdelete(mi);
+
+ debug_shape = NULL;
+}
+
RayCast::RayCast() {
enabled = false;
@@ -241,4 +326,5 @@ RayCast::RayCast() {
layer_mask = 1;
type_mask = PhysicsDirectSpaceState::TYPE_MASK_COLLISION;
cast_to = Vector3(0, -1, 0);
+ debug_shape = NULL;
}
diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h
index 7ab7b57db3..63a53d724f 100644
--- a/scene/3d/ray_cast.h
+++ b/scene/3d/ray_cast.h
@@ -50,6 +50,13 @@ class RayCast : public Spatial {
uint32_t layer_mask;
uint32_t type_mask;
+ Node *debug_shape;
+ Ref<Material> debug_material;
+
+ void _create_debug_shape();
+ void _update_debug_shape();
+ void _clear_debug_shape();
+
protected:
void _notification(int p_what);
void _update_raycast_state();
diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp
index ee4f8736d7..839dcc3678 100644
--- a/scene/gui/base_button.cpp
+++ b/scene/gui/base_button.cpp
@@ -38,6 +38,8 @@ void BaseButton::_unpress_group() {
if (!button_group.is_valid())
return;
+ status.pressed = true;
+
for (Set<BaseButton *>::Element *E = button_group->buttons.front(); E; E = E->next()) {
if (E->get() == this)
continue;
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index cebec379e6..545c700354 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1761,8 +1761,8 @@ bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0, cons
Vector3 v02, v01;
real_t a02, a01;
- r02.get_axis_and_angle(v02, a02);
- r01.get_axis_and_angle(v01, a01);
+ r02.get_axis_angle(v02, a02);
+ r01.get_axis_angle(v01, a01);
if (Math::abs(a02) > p_max_optimizable_angle)
return false;
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 0889306bad..f0ac30a76e 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -459,25 +459,27 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla
flags = f->get_32(); //texture flags!
uint32_t df = f->get_32(); //data format
+ /*
print_line("width: " + itos(tw));
print_line("height: " + itos(th));
print_line("flags: " + itos(flags));
print_line("df: " + itos(df));
+ */
if (request_3d_callback && df & FORMAT_BIT_DETECT_3D) {
- print_line("request detect 3D at " + p_path);
+ //print_line("request detect 3D at " + p_path);
VS::get_singleton()->texture_set_detect_3d_callback(texture, _requested_3d, this);
} else {
- print_line("not requesting detect 3D at " + p_path);
+ //print_line("not requesting detect 3D at " + p_path);
VS::get_singleton()->texture_set_detect_3d_callback(texture, NULL, NULL);
}
if (request_srgb_callback && df & FORMAT_BIT_DETECT_SRGB) {
- print_line("request detect srgb at " + p_path);
+ //print_line("request detect srgb at " + p_path);
VS::get_singleton()->texture_set_detect_srgb_callback(texture, _requested_srgb, this);
} else {
+ //print_line("not requesting detect srgb at " + p_path);
VS::get_singleton()->texture_set_detect_srgb_callback(texture, NULL, NULL);
- print_line("not requesting detect srgb at " + p_path);
}
if (!(df & FORMAT_BIT_STREAM)) {
@@ -493,7 +495,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla
uint32_t mipmaps = f->get_32();
uint32_t size = f->get_32();
- print_line("mipmaps: " + itos(mipmaps));
+ //print_line("mipmaps: " + itos(mipmaps));
while (mipmaps > 1 && p_size_limit > 0 && (sw > p_size_limit || sh > p_size_limit)) {
@@ -539,7 +541,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla
mipmap_images.push_back(img);
}
- print_line("mipmap read total: " + itos(mipmap_images.size()));
+ //print_line("mipmap read total: " + itos(mipmap_images.size()));
memdelete(f); //no longer needed
@@ -626,7 +628,7 @@ Error StreamTexture::_load_data(const String &p_path, int &tw, int &th, int &fla
{
PoolVector<uint8_t>::Write w = img_data.write();
int bytes = f->get_buffer(w.ptr(), total_size - ofs);
- print_line("requested read: " + itos(total_size - ofs) + " but got: " + itos(bytes));
+ //print_line("requested read: " + itos(total_size - ofs) + " but got: " + itos(bytes));
memdelete(f);
diff --git a/servers/physics/body_sw.cpp b/servers/physics/body_sw.cpp
index 9def425f28..715f93c1c1 100644
--- a/servers/physics/body_sw.cpp
+++ b/servers/physics/body_sw.cpp
@@ -495,7 +495,7 @@ void BodySW::integrate_forces(real_t p_step) {
Vector3 axis;
real_t angle;
- rot.get_axis_and_angle(axis, angle);
+ rot.get_axis_angle(axis, angle);
axis.normalize();
angular_velocity = axis.normalized() * (angle / p_step);
@@ -638,7 +638,7 @@ void BodySW::simulate_motion(const Transform& p_xform,real_t p_step) {
Vector3 axis;
real_t angle;
- rot.get_axis_and_angle(axis,angle);
+ rot.get_axis_angle(axis,angle);
axis.normalize();
angular_velocity=axis.normalized() * (angle/p_step);
linear_velocity = (p_xform.origin - get_transform().origin)/p_step;