summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS.md3
-rw-r--r--DONORS.md42
-rw-r--r--core/image.cpp3
-rw-r--r--core/input_map.cpp8
-rw-r--r--core/input_map.h1
-rw-r--r--core/math/math_2d.cpp10
-rw-r--r--core/math/math_2d.h2
-rw-r--r--core/math/vector3.h6
-rw-r--r--core/variant.h1
-rw-r--r--core/variant_call.cpp6
-rw-r--r--doc/classes/AudioStreamPlayer.xml1
-rw-r--r--doc/classes/AudioStreamPlayer2D.xml1
-rw-r--r--doc/classes/AudioStreamPlayer3D.xml1
-rw-r--r--doc/classes/Engine.xml6
-rw-r--r--doc/classes/RichTextLabel.xml8
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp27
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h1
-rw-r--r--drivers/gles3/shaders/canvas.glsl64
-rw-r--r--editor/editor_settings.cpp4
-rw-r--r--editor/editor_themes.cpp4
-rw-r--r--editor/filesystem_dock.cpp15
-rw-r--r--editor/filesystem_dock.h3
-rw-r--r--editor/icons/icon_bone_2_d.svg61
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp227
-rw-r--r--editor/plugins/canvas_item_editor_plugin.h25
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp147
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h4
-rw-r--r--platform/windows/os_windows.cpp6
-rw-r--r--scene/2d/skeleton_2d.cpp15
-rw-r--r--scene/2d/skeleton_2d.h4
-rw-r--r--scene/gui/rich_text_label.cpp64
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/resources/curve.cpp166
-rw-r--r--scene/resources/curve.h4
-rw-r--r--scene/resources/default_theme/default_theme.cpp6
-rw-r--r--scene/resources/primitive_meshes.cpp16
-rw-r--r--scene/resources/primitive_meshes.h4
37 files changed, 776 insertions, 192 deletions
diff --git a/AUTHORS.md b/AUTHORS.md
index a5ef24e825..67563298f2 100644
--- a/AUTHORS.md
+++ b/AUTHORS.md
@@ -97,6 +97,7 @@ name is available.
Ramesh Ravone (RameshRavone)
Ray Koopa (RayKoopa)
Rémi Verschelde (akien-mga)
+ Roberto F. Arroyo (robfram)
Ruslan Mustakov (endragor)
Saniko (sanikoyes)
SaracenOne
@@ -108,6 +109,7 @@ name is available.
Wilhem Barbier (nounoursheureux)
Will Nations (willnationsdev)
Wilson E. Alvarez (Rubonnek)
+ Xavier Cho (mysticfall)
Yuri Roubinski (Chaosus)
Zher Huei Lee (leezh)
ZuBsPaCe
@@ -117,7 +119,6 @@ name is available.
m4nu3lf
marynate
mrezai
- robfram
romulox-x
rraallvv
sersoong
diff --git a/DONORS.md b/DONORS.md
index 1a129e36d7..c0ab24390a 100644
--- a/DONORS.md
+++ b/DONORS.md
@@ -27,7 +27,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Christian Uldall Pedersen
Christopher Igoe
Christoph Woinke
- codetrotter
+ Claudiu Dumitru
E Hewert
Hein-Pieter van Braam
Igors Vaitkus
@@ -61,10 +61,10 @@ generous deed immortalized in the next stable release of Godot Engine.
Allen Schade
Andreas Schüle
Austen McRae
- Benjamin Botwin
Bernhard Liebl
Catalin Moldovan
DeepSquid
+ Duane Johnson
Florian Breisch
Gary Oberbrunner
Johannes Wuensch
@@ -72,13 +72,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Joshua Lesperance
Libre-Dépanne
Matthew Bennett
+ Paul LaMotte
Ranoller
- Rob Messick
Svenne Krap
Timothy Hagberg
BanjoNode2D
- Brandon
+ Beliar
Chris Serino
Conrad Curry
Craig Smith
@@ -87,18 +87,17 @@ generous deed immortalized in the next stable release of Godot Engine.
Dexter Miguel
Garrett Dockins
Guilherme Felipe de C. G. da Silva
- Harman Bains
John
Justo Delgado Baudí
- Karsten Bock
Laurence Bannister
Rami
Robert Willes
Robin Arys
- Rufus Sasparilla
+ Rufus Xavier Sarsaparilla
ScottMakesGames
Testus Maximus
Thomas Bjarnelöf
+ William Connell
Wojciech Chojnacki
Xavier Tan
Zaq Poi
@@ -107,15 +106,19 @@ generous deed immortalized in the next stable release of Godot Engine.
Alexey Dyadchenko
Amanda Haldy
Chris Brown
+ Chris Petrich
Chris Wilson
Cody Parker
D
Daniel Eliasinski
+ E.G.
Eric Monson
flesk
François Cantin
G Barnes
GGGames.org
+ Giovanni Solimeno
+ Hasen Judy
Heath Hayes
Jeppe Zapp
Jeremi Biernacki
@@ -126,18 +129,18 @@ generous deed immortalized in the next stable release of Godot Engine.
Justin Arnold
Leandro Voltolino
Lisandro Lorea
- Marco Andrew Cafolla
Markus Wiesner
Marvin
+ Mohammad Taleb
Nick Nikitin
Pablo Cholaky
Patrick Schnorbus
Pete Goodwin
Phyronnaz
+ SeokHui Lee
Simon De Greve
Sofox
Ted
- Travis Womack
Trent McPheron
Vladimir
@@ -149,8 +152,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Alder Stefano
Alessandro Senese
Álvaro Domínguez López
- Andrea Badii
- Andrew Thomas
+ Anders Jensen-Urstad
Anthony Bongiovanni
Arda Erol
Arthur S. Muszynski
@@ -161,22 +163,24 @@ generous deed immortalized in the next stable release of Godot Engine.
Benjamin Beshara
Ben Vercammen
Blair Allen
- Bryanna M
+ Brandon
Bryan Stevenson
Carwyn Edwards
Casey Foote
Chris Chapin
Christian Baune
Christian Winter
+ Christopher Schmitt
Collin Shooltz
Daniel Egger
Daniel Kaplan
- Daniel Langegger
+ DanielMaximiano
Daniel Mircea
David
David Cravens
David May
Dominik Wetzel
+ Edward Herbert
Eric Martini
Fabian Becker
fengjiongmax
@@ -193,11 +197,13 @@ generous deed immortalized in the next stable release of Godot Engine.
Hunter Jones
ialex32x
Ivan Vodopiviz
+ Jahn Johansen
Jaime Ruiz-Borau Vizárraga
Jed
Jeff Hungerford
Joel Fivat
Johan Lindberg
+ Jonas Rudlang
Jonas Yamazaki
Jonathan Martin
Jonathan Nieto
@@ -212,6 +218,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Kevin Boyer
Kevin Kamper Meejach Petersen
Klavdij Voncina
+ Krzysztof Jankowski
Lars pfeffer
Linus Lind Lundgren
Macil
@@ -231,12 +238,15 @@ generous deed immortalized in the next stable release of Godot Engine.
Moritz Laass
nee
Neil Blakey-Milner
+ Nick Pavlica
Niclas Eriksen
- Nik Lee
+ Nicolás Montaña
+ Nicolas SAN AGUSTIN
Niko Leopold
nivardus
Noi Sek
Oleg Tyshchenko
+ Oleksandr Yemets
Pablo Seibelt
Pan Ip
Pat LaBine
@@ -252,6 +262,7 @@ generous deed immortalized in the next stable release of Godot Engine.
Roger Smith
Roman Tinkov
Ryan Whited
+ Samuel El-Borai
Sasori Olkof
Scott D. Yelich
Sootstone
@@ -260,11 +271,12 @@ generous deed immortalized in the next stable release of Godot Engine.
Thomas Bell
Thomas Herzog & Xananax
Thomas Kurz
+ Tomasz Wacławek
Tom Larrow
Tyler Stafos
UltyX
+ Victor
Victor Gonzalez Fernandez
- Victor Holt
Viktor Ferenczi
werner mendizabal
Wout Standaert
diff --git a/core/image.cpp b/core/image.cpp
index 58f49d69e6..8cd2562bcb 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -1161,6 +1161,9 @@ PoolVector<uint8_t> Image::get_data() const {
void Image::create(int p_width, int p_height, bool p_use_mipmaps, Format p_format) {
+ ERR_FAIL_INDEX(p_width - 1, MAX_WIDTH);
+ ERR_FAIL_INDEX(p_height - 1, MAX_HEIGHT);
+
int mm = 0;
int size = _get_dst_image_size(p_width, p_height, p_format, mm, p_use_mipmaps ? -1 : 0);
data.resize(size);
diff --git a/core/input_map.cpp b/core/input_map.cpp
index 67c0e4eb04..d33f40cbcf 100644
--- a/core/input_map.cpp
+++ b/core/input_map.cpp
@@ -48,6 +48,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
+ ClassDB::bind_method(D_METHOD("action_erase_events", "action"), &InputMap::action_erase_events);
ClassDB::bind_method(D_METHOD("get_action_list", "action"), &InputMap::_get_action_list);
ClassDB::bind_method(D_METHOD("event_is_action", "event", "action"), &InputMap::event_is_action);
ClassDB::bind_method(D_METHOD("load_from_globals"), &InputMap::load_from_globals);
@@ -155,6 +156,13 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
input_map[p_action].inputs.erase(E);
}
+void InputMap::action_erase_events(const StringName &p_action) {
+
+ ERR_FAIL_COND(!input_map.has(p_action));
+
+ input_map[p_action].inputs.clear();
+}
+
Array InputMap::_get_action_list(const StringName &p_action) {
Array ret;
diff --git a/core/input_map.h b/core/input_map.h
index f497a2b86e..bdec75c65b 100644
--- a/core/input_map.h
+++ b/core/input_map.h
@@ -75,6 +75,7 @@ public:
void action_add_event(const StringName &p_action, const Ref<InputEvent> &p_event);
bool action_has_event(const StringName &p_action, const Ref<InputEvent> &p_event);
void action_erase_event(const StringName &p_action, const Ref<InputEvent> &p_event);
+ void action_erase_events(const StringName &p_action);
const List<Ref<InputEvent> > *get_action_list(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action) const;
diff --git a/core/math/math_2d.cpp b/core/math/math_2d.cpp
index 3767d298a1..a053ffbd93 100644
--- a/core/math/math_2d.cpp
+++ b/core/math/math_2d.cpp
@@ -103,6 +103,16 @@ Vector2 Vector2::floor() const {
return Vector2(Math::floor(x), Math::floor(y));
}
+Vector2 Vector2::ceil() const {
+
+ return Vector2(Math::ceil(x), Math::ceil(y));
+}
+
+Vector2 Vector2::round() const {
+
+ return Vector2(Math::round(x), Math::round(y));
+}
+
Vector2 Vector2::rotated(real_t p_by) const {
Vector2 v;
diff --git a/core/math/math_2d.h b/core/math/math_2d.h
index e7188da85b..611d47e3ff 100644
--- a/core/math/math_2d.h
+++ b/core/math/math_2d.h
@@ -162,6 +162,8 @@ struct Vector2 {
}
Vector2 floor() const;
+ Vector2 ceil() const;
+ Vector2 round() const;
Vector2 snapped(const Vector2 &p_by) const;
real_t aspect() const { return width / height; }
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 10ec4f5641..3bbfd7627c 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -103,6 +103,7 @@ struct Vector3 {
_FORCE_INLINE_ Vector3 floor() const;
_FORCE_INLINE_ Vector3 sign() const;
_FORCE_INLINE_ Vector3 ceil() const;
+ _FORCE_INLINE_ Vector3 round() const;
_FORCE_INLINE_ real_t distance_to(const Vector3 &p_b) const;
_FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_b) const;
@@ -204,6 +205,11 @@ Vector3 Vector3::ceil() const {
return Vector3(Math::ceil(x), Math::ceil(y), Math::ceil(z));
}
+Vector3 Vector3::round() const {
+
+ return Vector3(Math::round(x), Math::round(y), Math::round(z));
+}
+
Vector3 Vector3::linear_interpolate(const Vector3 &p_b, real_t p_t) const {
return Vector3(
diff --git a/core/variant.h b/core/variant.h
index 2cdb5c9ab6..f227e4bfdb 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -140,7 +140,6 @@ private:
::AABB *_aabb;
Basis *_basis;
Transform *_transform;
- RefPtr *_resource;
void *_ptr; //generic pointer
uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)];
} _data;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 1b938e3f41..bd1cde5a82 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -344,6 +344,8 @@ struct _VariantCall {
VCALL_LOCALMEM1R(Vector2, rotated);
VCALL_LOCALMEM0R(Vector2, tangent);
VCALL_LOCALMEM0R(Vector2, floor);
+ VCALL_LOCALMEM0R(Vector2, ceil);
+ VCALL_LOCALMEM0R(Vector2, round);
VCALL_LOCALMEM1R(Vector2, snapped);
VCALL_LOCALMEM0R(Vector2, aspect);
VCALL_LOCALMEM1R(Vector2, dot);
@@ -386,6 +388,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(Vector3, abs);
VCALL_LOCALMEM0R(Vector3, floor);
VCALL_LOCALMEM0R(Vector3, ceil);
+ VCALL_LOCALMEM0R(Vector3, round);
VCALL_LOCALMEM1R(Vector3, distance_to);
VCALL_LOCALMEM1R(Vector3, distance_squared_to);
VCALL_LOCALMEM1R(Vector3, angle_to);
@@ -1519,6 +1522,8 @@ void register_variant_methods() {
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, rotated, REAL, "phi", varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, tangent, varray());
ADDFUNC0R(VECTOR2, VECTOR2, Vector2, floor, varray());
+ ADDFUNC0R(VECTOR2, VECTOR2, Vector2, ceil, varray());
+ ADDFUNC0R(VECTOR2, VECTOR2, Vector2, round, varray());
ADDFUNC1R(VECTOR2, VECTOR2, Vector2, snapped, VECTOR2, "by", varray());
ADDFUNC0R(VECTOR2, REAL, Vector2, aspect, varray());
ADDFUNC1R(VECTOR2, REAL, Vector2, dot, VECTOR2, "with", varray());
@@ -1560,6 +1565,7 @@ void register_variant_methods() {
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, abs, varray());
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, floor, varray());
ADDFUNC0R(VECTOR3, VECTOR3, Vector3, ceil, varray());
+ ADDFUNC0R(VECTOR3, VECTOR3, Vector3, round, varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, distance_to, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, distance_squared_to, VECTOR3, "b", varray());
ADDFUNC1R(VECTOR3, REAL, Vector3, angle_to, VECTOR3, "to", varray());
diff --git a/doc/classes/AudioStreamPlayer.xml b/doc/classes/AudioStreamPlayer.xml
index 4a69695ae5..7d9fcf3e01 100644
--- a/doc/classes/AudioStreamPlayer.xml
+++ b/doc/classes/AudioStreamPlayer.xml
@@ -57,6 +57,7 @@
If the audio configuration has more than two speakers, this sets the target channels. See [code]MIX_TARGET_*[/code] constants.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code] audio is playing.
diff --git a/doc/classes/AudioStreamPlayer2D.xml b/doc/classes/AudioStreamPlayer2D.xml
index 495d37e6b8..81beab245d 100644
--- a/doc/classes/AudioStreamPlayer2D.xml
+++ b/doc/classes/AudioStreamPlayer2D.xml
@@ -63,6 +63,7 @@
Maximum distance from which audio is still hearable.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code] audio is playing.
diff --git a/doc/classes/AudioStreamPlayer3D.xml b/doc/classes/AudioStreamPlayer3D.xml
index 3418ef7a26..311efb7495 100644
--- a/doc/classes/AudioStreamPlayer3D.xml
+++ b/doc/classes/AudioStreamPlayer3D.xml
@@ -87,6 +87,7 @@
Decides if audio should pause when source is outside of 'max_distance' range.
</member>
<member name="pitch_scale" type="float" setter="set_pitch_scale" getter="get_pitch_scale">
+ Changes the pitch and the tempo of the audio.
</member>
<member name="playing" type="bool" setter="_set_playing" getter="is_playing">
If [code]true[/code], audio is playing.
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index ea3c404346..6384b4d0fd 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -46,9 +46,9 @@
<description>
Returns the current engine version information in a Dictionary.
- "major" - Holds the major version number as a String
- "minor" - Holds the minor version number as a String
- "patch" - Holds the patch version number as a String
+ "major" - Holds the major version number as an int
+ "minor" - Holds the minor version number as an int
+ "patch" - Holds the patch version number as an int
"status" - Holds the status (e.g. "beta", "rc1", "rc2", ... "stable") as a String
"build" - Holds the build name (e.g. "custom-build") as a String
"string" - major + minor + patch + status + build in a single String
diff --git a/doc/classes/RichTextLabel.xml b/doc/classes/RichTextLabel.xml
index 49bb1aed45..99d3a9a45f 100644
--- a/doc/classes/RichTextLabel.xml
+++ b/doc/classes/RichTextLabel.xml
@@ -319,6 +319,14 @@
</theme_item>
<theme_item name="font_color_selected" type="Color">
</theme_item>
+ <theme_item name="font_color_shadow" type="Color">
+ </theme_item>
+ <theme_item name="shadow_as_outline" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_x" type="int">
+ </theme_item>
+ <theme_item name="shadow_offset_y" type="int">
+ </theme_item>
<theme_item name="italics_font" type="Font">
</theme_item>
<theme_item name="line_separation" type="int">
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 12da7351bd..b221a41893 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -287,7 +287,8 @@ void RasterizerCanvasGLES3::_set_texture_rect_mode(bool p_enable, bool p_ninepat
state.canvas_shader.set_uniform(CanvasShaderGLES3::MODELVIEW_MATRIX, state.final_transform);
state.canvas_shader.set_uniform(CanvasShaderGLES3::EXTRA_MATRIX, state.extra_matrix);
if (state.using_skeleton) {
- state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TO_OBJECT_LOCAL_MATRIX, state.skeleton_transform);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM, state.skeleton_transform);
+ state.canvas_shader.set_uniform(CanvasShaderGLES3::SKELETON_TRANSFORM_INVERSE, state.skeleton_transform_inverse);
}
if (storage->frame.current_rt) {
state.canvas_shader.set_uniform(CanvasShaderGLES3::SCREEN_PIXEL_SIZE, Vector2(1.0 / storage->frame.current_rt->width, 1.0 / storage->frame.current_rt->height));
@@ -306,11 +307,17 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
uint32_t buffer_ofs = 0;
//vertex
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_vertices);
glEnableVertexAttribArray(VS::ARRAY_VERTEX);
glVertexAttribPointer(VS::ARRAY_VERTEX, 2, GL_FLOAT, false, sizeof(Vector2), ((uint8_t *)0) + buffer_ofs);
buffer_ofs += sizeof(Vector2) * p_vertex_count;
//color
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
if (p_singlecolor) {
glDisableVertexAttribArray(VS::ARRAY_COLOR);
@@ -327,6 +334,10 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
buffer_ofs += sizeof(Color) * p_vertex_count;
}
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
if (p_uvs) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(Vector2) * p_vertex_count, p_uvs);
@@ -338,11 +349,16 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
glDisableVertexAttribArray(VS::ARRAY_TEX_UV);
}
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
if (p_bones && p_weights) {
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(int) * 4 * p_vertex_count, p_bones);
glEnableVertexAttribArray(VS::ARRAY_BONES);
- glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, false, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs);
+ //glVertexAttribPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, false, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs);
+ glVertexAttribIPointer(VS::ARRAY_BONES, 4, GL_UNSIGNED_INT, sizeof(int) * 4, ((uint8_t *)0) + buffer_ofs);
buffer_ofs += sizeof(int) * 4 * p_vertex_count;
glBufferSubData(GL_ARRAY_BUFFER, buffer_ofs, sizeof(float) * 4 * p_vertex_count, p_weights);
@@ -355,6 +371,10 @@ void RasterizerCanvasGLES3::_draw_polygon(const int *p_indices, int p_index_coun
glVertexAttrib4f(VS::ARRAY_WEIGHTS, 0, 0, 0, 0);
}
+#ifdef DEBUG_ENABLED
+ ERR_FAIL_COND(buffer_ofs > data.polygon_buffer_size);
+#endif
+
//bind the indices buffer.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.polygon_index_buffer);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, sizeof(int) * p_index_count, p_indices);
@@ -1145,7 +1165,8 @@ void RasterizerCanvasGLES3::canvas_render_items(Item *p_item_list, int p_z, cons
if (!skeleton->use_2d) {
skeleton = NULL;
} else {
- state.skeleton_transform = ci->final_transform.affine_inverse() * (p_transform * skeleton->base_transform_2d);
+ state.skeleton_transform = p_transform * skeleton->base_transform_2d;
+ state.skeleton_transform_inverse = state.skeleton_transform.affine_inverse();
}
}
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 73a0f85bc9..bfaf1fdb4b 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -86,6 +86,7 @@ public:
Transform2D final_transform;
bool using_skeleton;
Transform2D skeleton_transform;
+ Transform2D skeleton_transform_inverse;
} state;
diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl
index 910867c9b1..326aab4c7c 100644
--- a/drivers/gles3/shaders/canvas.glsl
+++ b/drivers/gles3/shaders/canvas.glsl
@@ -58,7 +58,8 @@ out highp vec2 pixel_size_interp;
#ifdef USE_SKELETON
uniform mediump sampler2D skeleton_texture; // texunit:-1
-uniform mat4 skeleton_to_object_local_matrix;
+uniform highp mat4 skeleton_transform;
+uniform highp mat4 skeleton_transform_inverse;
#endif
#ifdef USE_LIGHTING
@@ -156,6 +157,36 @@ void main() {
#endif
+
+#define extra_matrix extra_matrix2
+
+{
+
+VERTEX_SHADER_CODE
+
+}
+
+
+#ifdef USE_NINEPATCH
+
+ pixel_size_interp=abs(dst_rect.zw) * vertex;
+#endif
+
+#if !defined(SKIP_TRANSFORM_USED)
+ outvec = extra_matrix * outvec;
+ outvec = modelview_matrix * outvec;
+#endif
+
+#undef extra_matrix
+
+ color_interp = color;
+
+#ifdef USE_PIXEL_SNAP
+
+ outvec.xy=floor(outvec+0.5).xy;
+#endif
+
+
#ifdef USE_SKELETON
if (bone_weights!=vec4(0.0)){ //must be a valid bone
@@ -192,42 +223,13 @@ void main() {
texelFetch(skeleton_texture,tex_ofs+ivec2(0,1),0)
) * bone_weights.w;
- mat4 bone_matrix = /*skeleton_to_object_local_matrix */ transpose(mat4(m[0],m[1],vec4(0.0,0.0,1.0,0.0),vec4(0.0,0.0,0.0,1.0)));
+ mat4 bone_matrix = skeleton_transform * transpose(mat4(m[0],m[1],vec4(0.0,0.0,1.0,0.0),vec4(0.0,0.0,0.0,1.0))) * skeleton_transform_inverse;
outvec = bone_matrix * outvec;
}
#endif
-#define extra_matrix extra_matrix2
-
-{
-
-VERTEX_SHADER_CODE
-
-}
-
-
-#ifdef USE_NINEPATCH
-
- pixel_size_interp=abs(dst_rect.zw) * vertex;
-#endif
-
-#if !defined(SKIP_TRANSFORM_USED)
- outvec = extra_matrix * outvec;
- outvec = modelview_matrix * outvec;
-#endif
-
-#undef extra_matrix
-
- color_interp = color;
-
-#ifdef USE_PIXEL_SNAP
-
- outvec.xy=floor(outvec+0.5).xy;
-#endif
-
-
gl_Position = projection_matrix * outvec;
#ifdef USE_LIGHTING
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 15d3b28da8..9f55ae32b0 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -454,9 +454,11 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8));
_initial_set("editors/2d/bone_width", 5);
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9));
- _initial_set("editors/2d/bone_color2", Color(0.75, 0.75, 0.75, 0.9));
+ _initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.9));
_initial_set("editors/2d/bone_selected_color", Color(0.9, 0.45, 0.45, 0.9));
_initial_set("editors/2d/bone_ik_color", Color(0.9, 0.9, 0.45, 0.9));
+ _initial_set("editors/2d/bone_outline_color", Color(0.35, 0.35, 0.35));
+ _initial_set("editors/2d/bone_outline_size", 2);
_initial_set("editors/2d/keep_margins_when_changing_anchors", false);
_initial_set("editors/2d/warped_mouse_panning", true);
_initial_set("editors/2d/simple_spacebar_panning", false);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 3582379e34..e93df835e4 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -891,6 +891,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
//RichTextLabel
theme->set_color("default_color", "RichTextLabel", font_color);
+ theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+ theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * EDSCALE);
+ theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * EDSCALE);
+ theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * EDSCALE);
theme->set_stylebox("focus", "RichTextLabel", make_empty_stylebox());
theme->set_stylebox("normal", "RichTextLabel", style_tree_bg);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index c07e12b67d..e7741c7926 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1237,6 +1237,15 @@ void FileSystemDock::_file_option(int p_option) {
make_dir_dialog->popup_centered_minsize(Size2(250, 80) * EDSCALE);
make_dir_dialog_text->grab_focus();
} break;
+ case FILE_NEW_SCRIPT: {
+ String tarDir = path;
+ if (tarDir != "res://" && !tarDir.ends_with("/")) {
+ tarDir += "/";
+ }
+
+ make_script_dialog_text->config("Node", tarDir + "new_script.gd");
+ make_script_dialog_text->popup_centered(Size2(300, 300) * EDSCALE);
+ } break;
case FILE_COPY_PATH: {
int idx = files->get_current();
if (idx < 0 || idx >= files->get_item_count())
@@ -1662,6 +1671,7 @@ void FileSystemDock::_files_list_rmb_select(int p_item, const Vector2 &p_pos) {
}
file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
file_options->set_position(files->get_global_position() + p_pos);
@@ -1673,6 +1683,7 @@ void FileSystemDock::_rmb_pressed(const Vector2 &p_pos) {
file_options->set_size(Size2(1, 1));
file_options->add_item(TTR("New Folder..."), FILE_NEW_FOLDER);
+ file_options->add_item(TTR("New Script..."), FILE_NEW_SCRIPT);
file_options->add_item(TTR("Show In File Manager"), FILE_SHOW_IN_EXPLORER);
file_options->set_position(files->get_global_position() + p_pos);
file_options->popup();
@@ -1988,6 +1999,10 @@ FileSystemDock::FileSystemDock(EditorNode *p_editor) {
make_dir_dialog->register_text_enter(make_dir_dialog_text);
make_dir_dialog->connect("confirmed", this, "_make_dir_confirm");
+ make_script_dialog_text = memnew(ScriptCreateDialog);
+ make_script_dialog_text->set_title(TTR("Create Script"));
+ add_child(make_script_dialog_text);
+
updating_tree = false;
initialized = false;
import_dock_needs_update = false;
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index c8448a1022..e59d4c96e1 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -50,6 +50,7 @@
#include "dependency_editor.h"
#include "editor_dir_dialog.h"
#include "editor_file_system.h"
+#include "script_create_dialog.h"
class EditorNode;
@@ -75,6 +76,7 @@ private:
FILE_REIMPORT,
FILE_INFO,
FILE_NEW_FOLDER,
+ FILE_NEW_SCRIPT,
FILE_SHOW_IN_EXPLORER,
FILE_COPY_PATH
};
@@ -126,6 +128,7 @@ private:
LineEdit *duplicate_dialog_text;
ConfirmationDialog *make_dir_dialog;
LineEdit *make_dir_dialog_text;
+ ScriptCreateDialog *make_script_dialog_text;
class FileOrFolder {
public:
diff --git a/editor/icons/icon_bone_2_d.svg b/editor/icons/icon_bone_2_d.svg
new file mode 100644
index 0000000000..efcbc17e13
--- /dev/null
+++ b/editor/icons/icon_bone_2_d.svg
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewBox="0 0 16 16"
+ id="svg6"
+ sodipodi:docname="icon_bone_2d.svg"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)">
+ <metadata
+ id="metadata12">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs10" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="836"
+ inkscape:window-height="480"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="67"
+ inkscape:window-y="27"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg6" />
+ <g
+ transform="translate(0 -1036.4)"
+ id="g4"
+ style="fill:#a5b7f3;fill-opacity:1">
+ <path
+ d="m10.478 1037.4a2.4664 2.4663 0 0 0 -1.7804 0.7205 2.4664 2.4663 0 0 0 -0.31408 3.1041l-3.559 3.5608a2.4664 2.4663 0 0 0 -3.1023 0.3121 2.4664 2.4663 0 0 0 0 3.4876 2.4664 2.4663 0 0 0 1.397 0.6955 2.4664 2.4663 0 0 0 0.69561 1.397 2.4664 2.4663 0 0 0 3.4877 0 2.4664 2.4663 0 0 0 0.31408 -3.1041l3.5609-3.5608a2.4664 2.4663 0 0 0 3.1004 -0.3102 2.4664 2.4663 0 0 0 0 -3.4875 2.4664 2.4663 0 0 0 -1.397 -0.6974 2.4664 2.4663 0 0 0 -0.69561 -1.3971 2.4664 2.4663 0 0 0 -1.7072 -0.7205z"
+ fill="#fc9c9c"
+ id="path2"
+ style="fill:#a5b7f3;fill-opacity:1" />
+ </g>
+</svg>
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index ff38e12250..8cad40c9ce 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -44,6 +44,7 @@
#include "scene/2d/particles_2d.h"
#include "scene/2d/polygon_2d.h"
#include "scene/2d/screen_button.h"
+#include "scene/2d/skeleton_2d.h"
#include "scene/2d/sprite.h"
#include "scene/gui/grid_container.h"
#include "scene/gui/nine_patch_rect.h"
@@ -2586,43 +2587,58 @@ void CanvasItemEditor::_draw_bones() {
Color bone_color1 = EditorSettings::get_singleton()->get("editors/2d/bone_color1");
Color bone_color2 = EditorSettings::get_singleton()->get("editors/2d/bone_color2");
Color bone_ik_color = EditorSettings::get_singleton()->get("editors/2d/bone_ik_color");
+ Color bone_outline_color = EditorSettings::get_singleton()->get("editors/2d/bone_outline_color");
Color bone_selected_color = EditorSettings::get_singleton()->get("editors/2d/bone_selected_color");
+ int bone_outline_size = EditorSettings::get_singleton()->get("editors/2d/bone_outline_size");
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
E->get().from = Vector2();
E->get().to = Vector2();
- Node2D *n2d = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key()));
- if (!n2d)
- continue;
+ Node2D *from_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().from));
+ Node2D *to_node = Object::cast_to<Node2D>(ObjectDB::get_instance(E->key().to));
- if (!n2d->get_parent())
+ if (!from_node->is_inside_tree())
+ continue; //may have been removed
+ if (!from_node)
continue;
- CanvasItem *pi = n2d->get_parent_item();
-
- Node2D *pn2d = Object::cast_to<Node2D>(n2d->get_parent());
-
- if (!pn2d)
+ if (!to_node && E->get().length == 0)
continue;
- Vector2 from = transform.xform(pn2d->get_global_position());
- Vector2 to = transform.xform(n2d->get_global_position());
+ Vector2 from = transform.xform(from_node->get_global_position());
+ Vector2 to;
+
+ if (to_node)
+ to = transform.xform(to_node->get_global_position());
+ else
+ to = transform.xform(from_node->get_global_transform().xform(Vector2(E->get().length, 0)));
E->get().from = from;
E->get().to = to;
Vector2 rel = to - from;
Vector2 relt = rel.tangent().normalized() * bone_width;
+ Vector2 reln = rel.normalized();
+ Vector2 reltn = relt.normalized();
Vector<Vector2> bone_shape;
bone_shape.push_back(from);
bone_shape.push_back(from + rel * 0.2 + relt);
bone_shape.push_back(to);
bone_shape.push_back(from + rel * 0.2 - relt);
+
+ Vector<Vector2> bone_shape_outline;
+ bone_shape_outline.push_back(from + (-reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + (-reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 + relt + reltn * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln + reltn) * bone_outline_size);
+ bone_shape_outline.push_back(to + (reln - reltn) * bone_outline_size);
+ bone_shape_outline.push_back(from + rel * 0.2 - relt - reltn * bone_outline_size);
+
Vector<Color> colors;
- if (pi->has_meta("_edit_ik_")) {
+ if (from_node->has_meta("_edit_ik_")) {
colors.push_back(bone_ik_color);
colors.push_back(bone_ik_color);
@@ -2635,14 +2651,26 @@ void CanvasItemEditor::_draw_bones() {
colors.push_back(bone_color2);
}
- VisualServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector<Vector2>(), RID());
-
- if (editor_selection->is_selected(pi)) {
- for (int i = 0; i < bone_shape.size(); i++) {
+ Vector<Color> outline_colors;
- VisualServer::get_singleton()->canvas_item_add_line(ci, bone_shape[i], bone_shape[(i + 1) % bone_shape.size()], bone_selected_color, 2);
- }
+ if (editor_selection->is_selected(from_node)) {
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ outline_colors.push_back(bone_selected_color);
+ } else {
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
+ outline_colors.push_back(bone_outline_color);
}
+
+ VisualServer::get_singleton()->canvas_item_add_polygon(ci, bone_shape_outline, outline_colors);
+ VisualServer::get_singleton()->canvas_item_add_primitive(ci, bone_shape, colors, Vector<Vector2>(), RID());
}
}
}
@@ -2766,26 +2794,75 @@ void CanvasItemEditor::_draw_locks_and_groups(Node *p_node, const Transform2D &p
}
}
-void CanvasItemEditor::_build_bones_list(Node *p_node) {
- ERR_FAIL_COND(!p_node);
+bool CanvasItemEditor::_build_bones_list(Node *p_node) {
+ ERR_FAIL_COND_V(!p_node, false);
+
+ bool has_child_bones = false;
for (int i = 0; i < p_node->get_child_count(); i++) {
- _build_bones_list(p_node->get_child(i));
+ if (_build_bones_list(p_node->get_child(i))) {
+ has_child_bones = true;
+ }
}
CanvasItem *c = Object::cast_to<CanvasItem>(p_node);
- if (c && c->is_visible_in_tree()) {
- if (c->has_meta("_edit_bone_")) {
+ if (!c) {
+ return false;
+ }
+
+ Node *p = c->get_parent();
+ if (!p) {
+ return false;
+ }
+
+ if (!c->is_visible()) {
+ return false;
+ }
+
+ if (Object::cast_to<Bone2D>(c)) {
- ObjectID id = c->get_instance_id();
- if (!bone_list.has(id)) {
- BoneList bone;
- bone_list[id] = bone;
+ if (Object::cast_to<Bone2D>(p)) {
+ //add as bone->parent relationship
+ BoneKey bk;
+ bk.from = p->get_instance_id();
+ bk.to = c->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
}
- bone_list[id].last_pass = bone_last_frame;
+ bone_list[bk].last_pass = bone_last_frame;
+ }
+
+ if (!has_child_bones) {
+ BoneKey bk;
+ bk.from = c->get_instance_id();
+ bk.to = 0;
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
+ }
+ bone_list[bk].last_pass = bone_last_frame;
+ }
+
+ return true;
+ }
+ if (c->has_meta("_edit_bone_")) {
+
+ BoneKey bk;
+ bk.from = c->get_parent()->get_instance_id();
+ bk.to = c->get_instance_id();
+ if (!bone_list.has(bk)) {
+ BoneList b;
+ b.length = 0;
+ bone_list[bk] = b;
}
+ bone_list[bk].last_pass = bone_last_frame;
}
+
+ return false;
}
void CanvasItemEditor::_draw_viewport() {
@@ -2912,9 +2989,9 @@ void CanvasItemEditor::_notification(int p_what) {
// Show / Hide the layout button
presets_menu->set_visible(nb_control > 0 && nb_control == selection.size());
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- Object *b = ObjectDB::get_instance(E->key());
+ Object *b = ObjectDB::get_instance(E->key().from);
if (!b) {
viewport->update();
@@ -2926,9 +3003,18 @@ void CanvasItemEditor::_notification(int p_what) {
continue;
}
- if (b2->get_global_transform() != E->get().xform) {
+ Transform2D global_xform = b2->get_global_transform();
+
+ if (global_xform != E->get().xform) {
- E->get().xform = b2->get_global_transform();
+ E->get().xform = global_xform;
+ viewport->update();
+ }
+
+ Bone2D *bone = Object::cast_to<Bone2D>(b);
+ if (bone && bone->get_default_length() != E->get().length) {
+
+ E->get().length = bone->get_default_length();
viewport->update();
}
}
@@ -2944,12 +3030,19 @@ void CanvasItemEditor::_notification(int p_what) {
AnimationPlayerEditor::singleton->get_key_editor()->connect("visibility_changed", this, "_keying_changed");
_keying_changed();
+ get_tree()->connect("node_added", this, "_tree_changed", varray());
+ get_tree()->connect("node_removed", this, "_tree_changed", varray());
} else if (p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_sb->set_texture(get_icon("EditorRect2D", "EditorIcons"));
}
+ if (p_what == NOTIFICATION_EXIT_TREE) {
+ get_tree()->disconnect("node_added", this, "_tree_changed");
+ get_tree()->disconnect("node_removed", this, "_tree_changed");
+ }
+
if (p_what == NOTIFICATION_ENTER_TREE || p_what == EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED) {
select_button->set_icon(get_icon("ToolSelect", "EditorIcons"));
list_select_button->set_icon(get_icon("ListSelect", "EditorIcons"));
@@ -3034,6 +3127,47 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) {
editor_selection->add_node(p_canvas_item);
}
+void CanvasItemEditor::_queue_update_bone_list() {
+
+ if (bone_list_dirty)
+ return;
+
+ call_deferred("_update_bone_list");
+ bone_list_dirty = true;
+}
+
+void CanvasItemEditor::_update_bone_list() {
+
+ bone_last_frame++;
+
+ if (editor->get_edited_scene()) {
+ _build_bones_list(editor->get_edited_scene());
+ }
+
+ List<Map<BoneKey, BoneList>::Element *> bone_to_erase;
+ for (Map<BoneKey, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
+ if (E->get().last_pass != bone_last_frame) {
+ bone_to_erase.push_back(E);
+ continue;
+ }
+
+ Node *node = Object::cast_to<Node>(ObjectDB::get_instance(E->key().from));
+ if (!node || !node->is_inside_tree() || (node != get_tree()->get_edited_scene_root() && !get_tree()->get_edited_scene_root()->is_a_parent_of(node))) {
+ bone_to_erase.push_back(E);
+ continue;
+ }
+ }
+ while (bone_to_erase.size()) {
+ bone_list.erase(bone_to_erase.front()->get());
+ bone_to_erase.pop_front();
+ }
+ bone_list_dirty = false;
+}
+
+void CanvasItemEditor::_tree_changed(Node *) {
+ _queue_update_bone_list();
+}
+
void CanvasItemEditor::_update_scrollbars() {
updating_scroll = true;
@@ -3058,22 +3192,7 @@ void CanvasItemEditor::_update_scrollbars() {
Size2 screen_rect = Size2(ProjectSettings::get_singleton()->get("display/window/size/width"), ProjectSettings::get_singleton()->get("display/window/size/height"));
Rect2 local_rect = Rect2(Point2(), viewport->get_size() - Size2(vmin.width, hmin.height));
- bone_last_frame++;
-
- if (editor->get_edited_scene()) {
- _build_bones_list(editor->get_edited_scene());
- }
-
- List<Map<ObjectID, BoneList>::Element *> bone_to_erase;
- for (Map<ObjectID, BoneList>::Element *E = bone_list.front(); E; E = E->next()) {
- if (E->get().last_pass != bone_last_frame) {
- bone_to_erase.push_back(E);
- }
- }
- while (bone_to_erase.size()) {
- bone_list.erase(bone_to_erase.front()->get());
- bone_to_erase.pop_front();
- }
+ _queue_update_bone_list();
// Calculate scrollable area
Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
@@ -3837,6 +3956,9 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_draw_viewport", &CanvasItemEditor::_draw_viewport);
ClassDB::bind_method("_gui_input_viewport", &CanvasItemEditor::_gui_input_viewport);
ClassDB::bind_method("_snap_changed", &CanvasItemEditor::_snap_changed);
+ ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
+ ClassDB::bind_method("_tree_changed", &CanvasItemEditor::_tree_changed);
+
ClassDB::bind_method(D_METHOD("_selection_result_pressed"), &CanvasItemEditor::_selection_result_pressed);
ClassDB::bind_method(D_METHOD("_selection_menu_hide"), &CanvasItemEditor::_selection_menu_hide);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
@@ -4040,6 +4162,7 @@ void CanvasItemEditor::focus_selection() {
CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
+ bone_list_dirty = false;
tool = TOOL_SELECT;
undo_redo = p_editor->get_undo_redo();
editor = p_editor;
@@ -4224,13 +4347,13 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p = skeleton_menu->get_popup();
p->set_hide_on_checkable_item_selection(false);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Bones"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
- p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Bones")), SKELETON_CLEAR_BONES);
- p->add_separator();
p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_show_bones", TTR("Show Bones")), SKELETON_SHOW_BONES);
p->add_separator();
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_set_ik_chain", TTR("Make IK Chain")), SKELETON_SET_IK_CHAIN);
p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_ik_chain", TTR("Clear IK Chain")), SKELETON_CLEAR_IK_CHAIN);
+ p->add_separator();
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_make_bones", TTR("Make Custom Bone(s) from Node(s)"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_B), SKELETON_MAKE_BONES);
+ p->add_shortcut(ED_SHORTCUT("canvas_item_editor/skeleton_clear_bones", TTR("Clear Custom Bones")), SKELETON_CLEAR_BONES);
p->connect("id_pressed", this, "_popup_callback");
hb->add_child(memnew(VSeparator));
diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h
index 373a4b799e..eb3595cae6 100644
--- a/editor/plugins/canvas_item_editor_plugin.h
+++ b/editor/plugins/canvas_item_editor_plugin.h
@@ -264,12 +264,26 @@ class CanvasItemEditor : public VBoxContainer {
struct BoneList {
Transform2D xform;
+ float length;
+ uint64_t last_pass;
Vector2 from;
Vector2 to;
- uint64_t last_pass;
};
+
uint64_t bone_last_frame;
- Map<ObjectID, BoneList> bone_list;
+
+ struct BoneKey {
+ ObjectID from;
+ ObjectID to;
+ _FORCE_INLINE_ bool operator<(const BoneKey &p_key) const {
+ if (from == p_key.from)
+ return to < p_key.to;
+ else
+ return from < p_key.from;
+ }
+ };
+
+ Map<BoneKey, BoneList> bone_list;
struct PoseClipboard {
Vector2 pos;
@@ -366,7 +380,7 @@ class CanvasItemEditor : public VBoxContainer {
void _selection_menu_hide();
UndoRedo *undo_redo;
- void _build_bones_list(Node *p_node);
+ bool _build_bones_list(Node *p_node);
List<CanvasItem *> _get_edited_canvas_items(bool retreive_locked = false, bool remove_canvas_item_if_parent_in_selection = true);
Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list);
@@ -433,6 +447,11 @@ class CanvasItemEditor : public VBoxContainer {
HSplitContainer *palette_split;
VSplitContainer *bottom_split;
+ bool bone_list_dirty;
+ void _queue_update_bone_list();
+ void _update_bone_list();
+ void _tree_changed(Node *);
+
friend class CanvasItemEditorPlugin;
protected:
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 14d2550000..f04e0a801c 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -380,23 +380,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_drag_from = Vector2(mb->get_position().x, mb->get_position().y);
uv_drag = true;
- uv_prev = node->get_uv();
+ points_prev = node->get_uv();
if (uv_edit_mode[0]->is_pressed()) { //edit uv
- uv_prev = node->get_uv();
+ points_prev = node->get_uv();
} else { //edit polygon
- uv_prev = node->get_polygon();
+ points_prev = node->get_polygon();
}
uv_move_current = uv_mode;
if (uv_move_current == UV_MODE_CREATE) {
if (!uv_create) {
- uv_prev.resize(0);
+ points_prev.resize(0);
Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
- uv_prev.push_back(tuv);
+ points_prev.push_back(tuv);
uv_create_to = tuv;
- uv_drag_index = 0;
+ point_drag_index = 0;
uv_drag_from = tuv;
uv_drag = true;
uv_create = true;
@@ -404,18 +404,18 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create_poly_prev = node->get_polygon();
uv_create_bones_prev = node->call("_get_bones");
splits_prev = node->get_splits();
- node->set_polygon(uv_prev);
- node->set_uv(uv_prev);
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
} else {
Vector2 tuv = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
- if (uv_prev.size() > 3 && tuv.distance_to(uv_prev[0]) < 8) {
+ if (points_prev.size() > 3 && tuv.distance_to(points_prev[0]) < 8) {
undo_redo->create_action(TTR("Create Polygon & UV"));
undo_redo->add_do_method(node, "set_uv", node->get_uv());
- undo_redo->add_undo_method(node, "set_uv", uv_prev);
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
- undo_redo->add_undo_method(node, "set_polygon", uv_prev);
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
undo_redo->add_do_method(node, "clear_bones");
undo_redo->add_undo_method(node, "_set_bones", node->call("_get_bones"));
undo_redo->add_do_method(uv_edit_draw, "update");
@@ -425,12 +425,12 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_create = false;
_uv_mode(UV_MODE_EDIT_POINT);
} else {
- uv_prev.push_back(tuv);
- uv_drag_index = uv_prev.size() - 1;
+ points_prev.push_back(tuv);
+ point_drag_index = points_prev.size() - 1;
uv_drag_from = tuv;
}
- node->set_polygon(uv_prev);
- node->set_uv(uv_prev);
+ node->set_polygon(points_prev);
+ node->set_uv(points_prev);
}
}
@@ -446,34 +446,34 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_EDIT_POINT) {
- uv_drag_index = -1;
- for (int i = 0; i < uv_prev.size(); i++) {
+ point_drag_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
- Vector2 tuv = mtx.xform(uv_prev[i]);
+ Vector2 tuv = mtx.xform(points_prev[i]);
if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
uv_drag_from = tuv;
- uv_drag_index = i;
+ point_drag_index = i;
}
}
- if (uv_drag_index == -1) {
+ if (point_drag_index == -1) {
uv_drag = false;
}
}
if (uv_move_current == UV_MODE_ADD_SPLIT) {
- int drag_index = -1;
- drag_index = -1;
- for (int i = 0; i < uv_prev.size(); i++) {
+ int split_to_index = -1;
+ split_to_index = -1;
+ for (int i = 0; i < points_prev.size(); i++) {
- Vector2 tuv = mtx.xform(uv_prev[i]);
+ Vector2 tuv = mtx.xform(points_prev[i]);
if (tuv.distance_to(Vector2(mb->get_position().x, mb->get_position().y)) < 8) {
- drag_index = i;
+ split_to_index = i;
}
}
- if (drag_index == -1) {
+ if (split_to_index == -1) {
split_create = false;
return;
}
@@ -481,41 +481,65 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (split_create) {
split_create = false;
- if (drag_index < uv_drag_index) {
- SWAP(drag_index, uv_drag_index);
+ if (split_to_index < point_drag_index) {
+ SWAP(split_to_index, point_drag_index);
}
bool valid = true;
- if (drag_index == uv_drag_index) {
+ String split_error;
+ if (split_to_index == point_drag_index) {
+ split_error = TTR("Split point with itself.");
valid = false;
}
- if (drag_index + 1 == uv_drag_index) {
+ if (split_to_index + 1 == point_drag_index) {
//not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
valid = false;
}
- if (drag_index == uv_prev.size() - 1 && uv_drag_index == 0) {
+ if (split_to_index == points_prev.size() - 1 && point_drag_index == 0) {
//not a split,goes along the edge
+ split_error = TTR("Split can't form an existing edge.");
valid = false;
}
+
for (int i = 0; i < splits_prev.size(); i += 2) {
- if (splits_prev[i] == uv_drag_index && splits_prev[i + 1] == drag_index) {
+
+ if (splits_prev[i] == point_drag_index && splits_prev[i + 1] == split_to_index) {
//already exists
+ split_error = TTR("Split already exists.");
valid = false;
+ break;
}
- if (splits_prev[i] > uv_drag_index && splits_prev[i + 1] > drag_index) {
- //crossing
- valid = false;
+
+ int a_state; //-1, outside split, 0 split point, +1, inside split
+ if (point_drag_index == splits_prev[i] || point_drag_index == splits_prev[i + 1]) {
+ a_state = 0;
+ } else if (point_drag_index < splits_prev[i] || point_drag_index > splits_prev[i + 1]) {
+ a_state = -1;
+ } else {
+ a_state = 1;
}
- if (splits_prev[i] < uv_drag_index && splits_prev[i + 1] < drag_index) {
- //crossing opposite direction
+ int b_state; //-1, outside split, 0 split point, +1, inside split
+ if (split_to_index == splits_prev[i] || split_to_index == splits_prev[i + 1]) {
+ b_state = 0;
+ } else if (split_to_index < splits_prev[i] || split_to_index > splits_prev[i + 1]) {
+ b_state = -1;
+ } else {
+ b_state = 1;
+ }
+
+ if (b_state * a_state < 0) {
+ //crossing
+ split_error = "Split crosses another split.";
valid = false;
+ break;
}
}
if (valid) {
- splits_prev.push_back(uv_drag_index);
- splits_prev.push_back(drag_index);
+ splits_prev.push_back(point_drag_index);
+ splits_prev.push_back(split_to_index);
undo_redo->create_action(TTR("Add Split"));
@@ -525,13 +549,14 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
undo_redo->add_undo_method(uv_edit_draw, "update");
undo_redo->commit_action();
} else {
- error->set_text(TTR("Invalid Split"));
+ error->set_text(TTR("Invalid Split: ") + split_error);
error->popup_centered_minsize();
}
} else {
- uv_drag_index = drag_index;
+ point_drag_index = split_to_index;
split_create = true;
+ splits_prev = node->get_splits();
uv_create_to = mtx.affine_inverse().xform(Vector2(mb->get_position().x, mb->get_position().y));
}
}
@@ -539,11 +564,11 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_move_current == UV_MODE_REMOVE_SPLIT) {
for (int i = 0; i < splits_prev.size(); i += 2) {
- if (splits_prev[i] < 0 || splits_prev[i] >= uv_prev.size())
+ if (splits_prev[i] < 0 || splits_prev[i] >= points_prev.size())
continue;
- if (splits_prev[i + 1] < 0 || splits_prev[i] >= uv_prev.size())
+ if (splits_prev[i + 1] < 0 || splits_prev[i] >= points_prev.size())
continue;
- Vector2 e[2] = { mtx.xform(uv_prev[splits_prev[i]]), mtx.xform(uv_prev[splits_prev[i + 1]]) };
+ Vector2 e[2] = { mtx.xform(points_prev[splits_prev[i]]), mtx.xform(points_prev[splits_prev[i + 1]]) };
Vector2 mp = Vector2(mb->get_position().x, mb->get_position().y);
Vector2 cp = Geometry::get_closest_point_to_segment_2d(mp, e);
if (cp.distance_to(mp) < 8) {
@@ -574,7 +599,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
}
- if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == uv_prev.size()) {
+ if (bone_selected != -1 && node->get_bone_weights(bone_selected).size() == points_prev.size()) {
prev_weights = node->get_bone_weights(bone_selected);
bone_painting = true;
@@ -588,10 +613,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
if (uv_edit_mode[0]->is_pressed()) { //edit uv
undo_redo->add_do_method(node, "set_uv", node->get_uv());
- undo_redo->add_undo_method(node, "set_uv", uv_prev);
+ undo_redo->add_undo_method(node, "set_uv", points_prev);
} else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
undo_redo->add_do_method(node, "set_polygon", node->get_polygon());
- undo_redo->add_undo_method(node, "set_polygon", uv_prev);
+ undo_redo->add_undo_method(node, "set_polygon", points_prev);
}
undo_redo->add_do_method(uv_edit_draw, "update");
undo_redo->add_undo_method(uv_edit_draw, "update");
@@ -624,9 +649,9 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_drag = false;
if (uv_edit_mode[0]->is_pressed()) { //edit uv
- node->set_uv(uv_prev);
+ node->set_uv(points_prev);
} else if (uv_edit_mode[1]->is_pressed()) { //edit polygon
- node->set_polygon(uv_prev);
+ node->set_polygon(points_prev);
}
uv_edit_draw->update();
} else if (split_create) {
@@ -669,8 +694,8 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_EDIT_POINT: {
- PoolVector<Vector2> uv_new = uv_prev;
- uv_new.set(uv_drag_index, uv_new[uv_drag_index] + drag);
+ PoolVector<Vector2> uv_new = points_prev;
+ uv_new.set(point_drag_index, uv_new[point_drag_index] + drag);
if (uv_edit_mode[0]->is_pressed()) { //edit uv
node->set_uv(uv_new);
@@ -680,7 +705,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
} break;
case UV_MODE_MOVE: {
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
uv_new.set(i, uv_new[i] + drag);
@@ -694,16 +719,16 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
case UV_MODE_ROTATE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float angle = (uv_drag_from - mtx.xform(center)).normalized().angle_to((uv_drag_to - mtx.xform(center)).normalized());
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel.rotated(angle);
uv_new.set(i, center + rel);
}
@@ -718,10 +743,10 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
case UV_MODE_SCALE: {
Vector2 center;
- PoolVector<Vector2> uv_new = uv_prev;
+ PoolVector<Vector2> uv_new = points_prev;
for (int i = 0; i < uv_new.size(); i++)
- center += uv_prev[i];
+ center += points_prev[i];
center /= uv_new.size();
float from_dist = uv_drag_from.distance_to(mtx.xform(center));
@@ -732,7 +757,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
float scale = to_dist / from_dist;
for (int i = 0; i < uv_new.size(); i++) {
- Vector2 rel = uv_prev[i] - center;
+ Vector2 rel = points_prev[i] - center;
rel = rel * scale;
uv_new.set(i, center + rel);
}
@@ -760,7 +785,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
PoolVector<float>::Write w = painted_weights.write();
PoolVector<float>::Read r = prev_weights.read();
- PoolVector<Vector2>::Read rv = uv_prev.read();
+ PoolVector<Vector2>::Read rv = points_prev.read();
for (int i = 0; i < pc; i++) {
if (mtx.xform(rv[i]).distance_to(bone_paint_pos) < radius) {
@@ -899,7 +924,7 @@ void Polygon2DEditor::_uv_draw() {
}
if (split_create) {
- Vector2 from = uvs[uv_drag_index];
+ Vector2 from = uvs[point_drag_index];
Vector2 to = uv_create_to;
uv_edit_draw->draw_line(mtx.xform(from), mtx.xform(to), Color(0.9, 0.5, 0.5), 2);
}
@@ -1232,7 +1257,7 @@ Polygon2DEditor::Polygon2DEditor(EditorNode *p_editor) :
uv_edit_draw->connect("draw", this, "_uv_draw");
uv_edit_draw->connect("gui_input", this, "_uv_input");
uv_draw_zoom = 1.0;
- uv_drag_index = -1;
+ point_drag_index = -1;
uv_drag = false;
uv_create = false;
updating_uv_scroll = false;
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
index cf529b39f7..f9b42a21c2 100644
--- a/editor/plugins/polygon_2d_editor_plugin.h
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -99,14 +99,14 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
Vector2 uv_draw_ofs;
float uv_draw_zoom;
- PoolVector<Vector2> uv_prev;
+ PoolVector<Vector2> points_prev;
PoolVector<Vector2> uv_create_uv_prev;
PoolVector<Vector2> uv_create_poly_prev;
Array uv_create_bones_prev;
PoolVector<int> splits_prev;
Vector2 uv_create_to;
- int uv_drag_index;
+ int point_drag_index;
bool uv_drag;
bool uv_create;
bool split_create;
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 363f54fc7b..bed5781ae4 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -188,6 +188,10 @@ void OS_Windows::initialize_core() {
ticks_start = 0;
ticks_start = get_ticks_usec();
+ // set minimum resolution for periodic timers, otherwise Sleep(n) may wait at least as
+ // long as the windows scheduler resolution (~16-30ms) even for calls like Sleep(1)
+ timeBeginPeriod(1);
+
process_map = memnew((Map<ProcessID, ProcessInfo>));
IP_Unix::make_default();
@@ -1252,6 +1256,8 @@ void OS_Windows::finalize() {
void OS_Windows::finalize_core() {
+ timeEndPeriod(1);
+
memdelete(process_map);
TCPServerWinsock::cleanup();
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 9197127235..2363c791fa 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -56,7 +56,11 @@ void Bone2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_skeleton_rest"), &Bone2D::get_skeleton_rest);
ClassDB::bind_method(D_METHOD("get_index_in_skeleton"), &Bone2D::get_index_in_skeleton);
+ ClassDB::bind_method(D_METHOD("set_default_length", "default_length"), &Bone2D::set_default_length);
+ ClassDB::bind_method(D_METHOD("get_default_length"), &Bone2D::get_default_length);
+
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D,"rest"),"set_rest","get_rest");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"default_length",PROPERTY_HINT_RANGE,"1,1024,1"),"set_default_length","get_default_length");
}
void Bone2D::set_rest(const Transform2D &p_rest) {
@@ -84,6 +88,16 @@ void Bone2D::apply_rest() {
set_transform(rest);
}
+void Bone2D::set_default_length(float p_length) {
+
+ default_length=p_length;
+
+}
+
+float Bone2D::get_default_length() const {
+ return default_length;
+}
+
int Bone2D::get_index_in_skeleton() const {
ERR_FAIL_COND_V(!skeleton,-1);
skeleton->_update_bone_setup();
@@ -118,6 +132,7 @@ Bone2D::Bone2D() {
skeleton = NULL;
parent_bone = NULL;
skeleton_index=-1;
+ default_length=16;
set_notify_local_transform(true);
//this is a clever hack so the bone knows no rest has been set yet, allowing to show an error.
for(int i=0;i<3;i++) {
diff --git a/scene/2d/skeleton_2d.h b/scene/2d/skeleton_2d.h
index 9ae74b56d3..cd270dac85 100644
--- a/scene/2d/skeleton_2d.h
+++ b/scene/2d/skeleton_2d.h
@@ -11,6 +11,7 @@ class Bone2D : public Node2D {
Bone2D *parent_bone;
Skeleton2D *skeleton;
Transform2D rest;
+ float default_length;
friend class Skeleton2D;
int skeleton_index;
@@ -27,6 +28,9 @@ public:
String get_configuration_warning() const;
+ void set_default_length(float p_length);
+ float get_default_length() const;
+
int get_index_in_skeleton() const;
Bone2D();
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 1fcde9e9a8..ffc8a72164 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -90,7 +90,7 @@ Rect2 RichTextLabel::_get_text_rect() {
Ref<StyleBox> style = get_stylebox("normal");
return Rect2(style->get_offset(), get_size() - style->get_minimum_size());
}
-int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
+int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos, Item **r_click_item, int *r_click_char, bool *r_outside, int p_char_count) {
RID ci;
if (r_outside)
@@ -269,10 +269,12 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int descent = font->get_descent();
Color color;
+ Color font_color_shadow;
bool underline = false;
if (p_mode == PROCESS_DRAW) {
color = _find_color(text, p_base_color);
+ font_color_shadow = _find_color(text, p_font_color_shadow);
underline = _find_underline(text);
if (_find_meta(text, &meta)) {
@@ -284,7 +286,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
}
rchar = 0;
-
while (*c) {
int end = 0;
@@ -297,7 +298,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
line_ascent = line < l.ascent_caches.size() ? l.ascent_caches[line] : 1;
line_descent = line < l.descent_caches.size() ? l.descent_caches[line] : 1;
}
-
while (c[end] != 0 && !(end && c[end - 1] == ' ' && c[end] != ' ')) {
int cw = font->get_char_size(c[end], c[end + 1]).width;
@@ -314,7 +314,6 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
end++;
}
-
CHECK_HEIGHT(fh);
ENSURE_WIDTH(w);
@@ -376,16 +375,30 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (c[i] == '\t')
visible = false;
- if (selected) {
+ if (visible) {
+ if (selected) {
+ cw = font->get_char_size(c[i], c[i + 1]).x;
+ draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
+ }
- cw = font->get_char_size(c[i], c[i + 1]).x;
- draw_rect(Rect2(p_ofs.x + pofs, p_ofs.y + y, cw, lh), selection_bg);
- if (visible)
- font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ if (p_font_color_shadow.a > 0) {
+ float x_ofs_shadow = align_ofs + pofs;
+ float y_ofs_shadow = y + lh - line_descent;
+ float move = font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + shadow_ofs, c[i], c[i + 1], p_font_color_shadow);
+
+ if (p_shadow_as_outline) {
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ font->draw_char(ci, Point2(x_ofs_shadow, y_ofs_shadow) + Vector2(-shadow_ofs.x, -shadow_ofs.y), c[i], c[i + 1], p_font_color_shadow);
+ }
+ x_ofs_shadow += move;
+ }
- } else {
- if (visible)
+ if (selected) {
+ font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], override_selected_font_color ? selection_fg : color);
+ } else {
cw = font->draw_char(ci, p_ofs + Point2(align_ofs + pofs, y + lh - line_descent), c[i], c[i + 1], color);
+ }
}
p_char_count++;
@@ -464,6 +477,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
int vseparation = get_constant("table_vseparation");
Color ccolor = _find_color(table, p_base_color);
Vector2 draw_ofs = Point2(wofs, y);
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
if (p_mode == PROCESS_CACHE) {
@@ -487,7 +503,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
- _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, available_width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
table->columns[column].min_width = MAX(table->columns[column].min_width, frame->lines[i].minimum_width);
table->columns[column].max_width = MAX(table->columns[column].max_width, frame->lines[i].maximum_width);
}
@@ -560,7 +576,7 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
for (int i = 0; i < frame->lines.size(); i++) {
int ly = 0;
- _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color());
+ _process_line(frame, Point2(), ly, table->columns[column].width, i, PROCESS_CACHE, cfont, Color(), font_color_shadow, use_outline, shadow_ofs);
frame->lines[i].height_cache = ly; //actual height
frame->lines[i].height_accum_cache = ly; //actual height
}
@@ -593,9 +609,9 @@ int RichTextLabel::_process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &
if (visible) {
if (p_mode == PROCESS_DRAW) {
- nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor);
+ nonblank_line_count += _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_DRAW, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs);
} else if (p_mode == PROCESS_POINTER) {
- _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, p_click_pos, r_click_item, r_click_char, r_outside);
+ _process_line(frame, p_ofs + offset + draw_ofs + Vector2(0, yofs), ly, table->columns[column].width, i, PROCESS_POINTER, cfont, ccolor, font_color_shadow, use_outline, shadow_ofs, p_click_pos, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item) {
RETURN; // exit early
}
@@ -767,12 +783,18 @@ void RichTextLabel::_notification(int p_what) {
int y = (main->lines[from_line].height_accum_cache - main->lines[from_line].height_cache) - ofs;
Ref<Font> base_font = get_font("normal_font");
Color base_color = get_color("default_color");
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
+
+ float x_ofs = 0;
visible_line_count = 0;
while (y < size.height && from_line < main->lines.size()) {
- visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, Point2i(), NULL, NULL, NULL, total_chars);
+ visible_line_count += _process_line(main, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_DRAW, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, Point2i(), NULL, NULL, NULL, total_chars);
total_chars += main->lines[from_line].char_count;
+
from_line++;
}
}
@@ -787,6 +809,9 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
int ofs = vscroll->get_value();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
//todo, change to binary search
int from_line = 0;
@@ -807,7 +832,7 @@ void RichTextLabel::_find_click(ItemFrame *p_frame, const Point2i &p_click, Item
while (y < text_rect.get_size().height && from_line < p_frame->lines.size()) {
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, p_click, r_click_item, r_click_char, r_outside);
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, from_line, PROCESS_POINTER, base_font, base_color, font_color_shadow, use_outline, shadow_ofs, p_click, r_click_item, r_click_char, r_outside);
if (r_click_item && *r_click_item)
return;
from_line++;
@@ -1182,13 +1207,16 @@ void RichTextLabel::_validate_line_caches(ItemFrame *p_frame) {
//validate invalid lines
Size2 size = get_size();
Rect2 text_rect = _get_text_rect();
+ Color font_color_shadow = get_color("font_color_shadow");
+ bool use_outline = get_constant("shadow_as_outline");
+ Point2 shadow_ofs(get_constant("shadow_offset_x"), get_constant("shadow_offset_y"));
Ref<Font> base_font = get_font("normal_font");
for (int i = p_frame->first_invalid_line; i < p_frame->lines.size(); i++) {
int y = 0;
- _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color());
+ _process_line(p_frame, text_rect.get_position(), y, text_rect.get_size().width - scroll_w, i, PROCESS_CACHE, base_font, Color(), font_color_shadow, use_outline, shadow_ofs);
p_frame->lines[i].height_cache = y;
p_frame->lines[i].height_accum_cache = y;
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index 83938cff61..850638bff3 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -270,7 +270,7 @@ private:
int visible_characters;
float percent_visible;
- int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
+ int _process_line(ItemFrame *p_frame, const Vector2 &p_ofs, int &y, int p_width, int p_line, ProcessMode p_mode, const Ref<Font> &p_base_font, const Color &p_base_color, const Color &p_font_color_shadow, bool p_shadow_as_outline, const Point2 &shadow_ofs, const Point2i &p_click_pos = Point2i(), Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL, int p_char_count = 0);
void _find_click(ItemFrame *p_frame, const Point2i &p_click, Item **r_click_item = NULL, int *r_click_char = NULL, bool *r_outside = NULL);
Ref<Font> _find_font(Item *p_item);
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index 5fd6f6c74d..4ec1e8973d 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -806,6 +806,87 @@ float Curve2D::get_bake_interval() const {
return bake_interval;
}
+Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, Vector2());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ Vector2 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve2D::get_closest_offset(const Vector2 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve2D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector2Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector2 origin = r[i];
+ Vector2 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector2 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
Dictionary Curve2D::_get_data() const {
Dictionary dc;
@@ -909,6 +990,8 @@ void Curve2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_baked_length"), &Curve2D::get_baked_length);
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve2D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve2D::get_baked_points);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve2D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve2D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve2D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve2D::_get_data);
@@ -1276,6 +1359,87 @@ PoolRealArray Curve3D::get_baked_tilts() const {
return baked_tilt_cache;
}
+Vector3 Curve3D::get_closest_point(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, Vector3());
+ }
+
+ if (pc == 1)
+ return baked_point_cache.get(0);
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ Vector3 nearest;
+ float nearest_dist = -1.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = proj;
+ nearest_dist = dist;
+ }
+ }
+
+ return nearest;
+}
+
+float Curve3D::get_closest_offset(const Vector3 &p_to_point) const {
+ // Brute force method
+
+ if (baked_cache_dirty)
+ _bake();
+
+ //validate//
+ int pc = baked_point_cache.size();
+ if (pc == 0) {
+ ERR_EXPLAIN("No points in Curve3D");
+ ERR_FAIL_COND_V(pc == 0, 0.0f);
+ }
+
+ if (pc == 1)
+ return 0.0f;
+
+ PoolVector3Array::Read r = baked_point_cache.read();
+
+ float nearest = 0.0f;
+ float nearest_dist = -1.0f;
+ float offset = 0.0f;
+
+ for (int i = 0; i < pc - 1; i++) {
+ Vector3 origin = r[i];
+ Vector3 direction = (r[i + 1] - origin) / bake_interval;
+
+ float d = CLAMP((p_to_point - origin).dot(direction), 0.0f, bake_interval);
+ Vector3 proj = origin + direction * d;
+
+ float dist = proj.distance_squared_to(p_to_point);
+
+ if (nearest_dist < 0.0f || dist < nearest_dist) {
+ nearest = offset + d;
+ nearest_dist = dist;
+ }
+
+ offset += bake_interval;
+ }
+
+ return nearest;
+}
+
void Curve3D::set_bake_interval(float p_tolerance) {
bake_interval = p_tolerance;
@@ -1404,6 +1568,8 @@ void Curve3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("interpolate_baked", "offset", "cubic"), &Curve3D::interpolate_baked, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_baked_points"), &Curve3D::get_baked_points);
ClassDB::bind_method(D_METHOD("get_baked_tilts"), &Curve3D::get_baked_tilts);
+ ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Curve3D::get_closest_point);
+ ClassDB::bind_method(D_METHOD("get_closest_offset", "to_point"), &Curve3D::get_closest_offset);
ClassDB::bind_method(D_METHOD("tessellate", "max_stages", "tolerance_degrees"), &Curve3D::tessellate, DEFVAL(5), DEFVAL(4));
ClassDB::bind_method(D_METHOD("_get_data"), &Curve3D::_get_data);
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index 4f55e4055c..492eb05d1e 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -199,6 +199,8 @@ public:
float get_baked_length() const;
Vector2 interpolate_baked(float p_offset, bool p_cubic = false) const;
PoolVector2Array get_baked_points() const; //useful for going through
+ Vector2 get_closest_point(const Vector2 &p_to_point) const;
+ float get_closest_offset(const Vector2 &p_to_point) const;
PoolVector2Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
@@ -268,6 +270,8 @@ public:
float interpolate_baked_tilt(float p_offset) const;
PoolVector3Array get_baked_points() const; //useful for going through
PoolRealArray get_baked_tilts() const; //useful for going through
+ Vector3 get_closest_point(const Vector3 &p_to_point) const;
+ float get_closest_offset(const Vector3 &p_to_point) const;
PoolVector3Array tessellate(int p_max_stages = 5, float p_tolerance = 4) const; //useful for display
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index ea70797530..2e652a00f9 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -818,6 +818,12 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("font_color_selected", "RichTextLabel", font_color_selection);
theme->set_color("selection_color", "RichTextLabel", Color(0.1, 0.1, 1, 0.8));
+ theme->set_color("font_color_shadow", "RichTextLabel", Color(0, 0, 0, 0));
+
+ theme->set_constant("shadow_offset_x", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_offset_y", "RichTextLabel", 1 * scale);
+ theme->set_constant("shadow_as_outline", "RichTextLabel", 0 * scale);
+
theme->set_constant("line_separation", "RichTextLabel", 1 * scale);
theme->set_constant("table_hseparation", "RichTextLabel", 3 * scale);
theme->set_constant("table_vseparation", "RichTextLabel", 3 * scale);
diff --git a/scene/resources/primitive_meshes.cpp b/scene/resources/primitive_meshes.cpp
index 056ac2772b..ad63422aad 100644
--- a/scene/resources/primitive_meshes.cpp
+++ b/scene/resources/primitive_meshes.cpp
@@ -166,7 +166,11 @@ void PrimitiveMesh::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mesh_arrays"), &PrimitiveMesh::get_mesh_arrays);
+ ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &PrimitiveMesh::set_custom_aabb);
+ ClassDB::bind_method(D_METHOD("get_custom_aabb"), &PrimitiveMesh::get_custom_aabb);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "SpatialMaterial,ShaderMaterial"), "set_material", "get_material");
+ ADD_PROPERTY(PropertyInfo(Variant::AABB, "custom_aabb", PROPERTY_HINT_NONE, ""), "set_custom_aabb", "get_custom_aabb");
}
void PrimitiveMesh::set_material(const Ref<Material> &p_material) {
@@ -187,6 +191,18 @@ Array PrimitiveMesh::get_mesh_arrays() const {
return surface_get_arrays(0);
}
+void PrimitiveMesh::set_custom_aabb(const AABB &p_custom) {
+
+ custom_aabb = p_custom;
+ VS::get_singleton()->mesh_set_custom_aabb(mesh, custom_aabb);
+ emit_changed();
+}
+
+AABB PrimitiveMesh::get_custom_aabb() const {
+
+ return custom_aabb;
+}
+
PrimitiveMesh::PrimitiveMesh() {
// defaults
mesh = VisualServer::get_singleton()->mesh_create();
diff --git a/scene/resources/primitive_meshes.h b/scene/resources/primitive_meshes.h
index 94a7a055a3..23d1671d5c 100644
--- a/scene/resources/primitive_meshes.h
+++ b/scene/resources/primitive_meshes.h
@@ -48,6 +48,7 @@ class PrimitiveMesh : public Mesh {
private:
RID mesh;
mutable AABB aabb;
+ AABB custom_aabb;
Ref<Material> material;
@@ -81,6 +82,9 @@ public:
Array get_mesh_arrays() const;
+ void set_custom_aabb(const AABB &p_custom);
+ AABB get_custom_aabb() const;
+
PrimitiveMesh();
~PrimitiveMesh();
};