summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/os/os.cpp1
-rw-r--r--core/os/os.h3
-rw-r--r--core/path_db.cpp31
-rw-r--r--core/path_db.h5
-rw-r--r--core/resource.h2
-rw-r--r--demos/2d/platformer/coin.gd8
-rw-r--r--doc/base/classes.xml108
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp4
-rw-r--r--main/main.cpp1
-rw-r--r--modules/gdscript/gd_parser.cpp46
-rw-r--r--platform/osx/os_osx.h2
-rw-r--r--platform/osx/os_osx.mm5
-rw-r--r--scene/2d/animated_sprite.cpp5
-rw-r--r--scene/2d/sprite.cpp7
-rw-r--r--scene/main/instance_placeholder.cpp74
-rw-r--r--scene/main/instance_placeholder.h37
-rw-r--r--scene/main/node.cpp76
-rw-r--r--scene/main/node.h37
-rw-r--r--scene/register_scene_types.cpp13
-rw-r--r--scene/resources/packed_scene.cpp1144
-rw-r--r--scene/resources/packed_scene.h112
-rw-r--r--scene/resources/scene_format_text.cpp792
-rw-r--r--scene/resources/scene_format_text.h46
-rw-r--r--tools/editor/editor_node.cpp19
-rw-r--r--tools/editor/editor_node.h3
-rw-r--r--tools/editor/property_editor.cpp151
-rw-r--r--tools/editor/property_editor.h7
-rw-r--r--tools/editor/reparent_dialog.cpp1
-rw-r--r--tools/editor/scene_tree_dock.cpp51
-rw-r--r--tools/editor/scene_tree_editor.cpp123
-rw-r--r--tools/editor/scene_tree_editor.h8
31 files changed, 2636 insertions, 286 deletions
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 2db926e556..ee9f12b79d 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -516,6 +516,7 @@ OS::OS() {
_target_fps=0;
_render_thread_mode=RENDER_THREAD_SAFE;
_time_scale=1.0;
+ _pixel_snap=false;
Math::seed(1234567);
}
diff --git a/core/os/os.h b/core/os/os.h
index d89734d7d3..e5338b4a02 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -58,6 +58,7 @@ class OS {
float _fps;
int _target_fps;
float _time_scale;
+ bool _pixel_snap;
char *last_error;
@@ -393,7 +394,7 @@ public:
void set_time_scale(float p_scale);
float get_time_scale() const;
-
+ _FORCE_INLINE_ bool get_use_pixel_snap() const { return _pixel_snap; }
OS();
virtual ~OS();
diff --git a/core/path_db.cpp b/core/path_db.cpp
index d3dc3aceb8..c6ea25d966 100644
--- a/core/path_db.cpp
+++ b/core/path_db.cpp
@@ -286,6 +286,37 @@ NodePath::NodePath(const Vector<StringName>& p_path,const Vector<StringName>& p_
data->property=p_property;
}
+
+void NodePath::simplify() {
+
+ if (!data)
+ return;
+ for(int i=0;i<data->path.size();i++) {
+ if (data->path.size()==1)
+ break;
+ if (data->path[i].operator String()==".") {
+ data->path.remove(i);
+ i--;
+ } else if (data->path[i].operator String()==".." && i>0 && data->path[i-1].operator String()!="." && data->path[i-1].operator String()!="..") {
+ //remove both
+ data->path.remove(i-1);
+ data->path.remove(i-1);
+ i-=2;
+ if (data->path.size()==0) {
+ data->path.push_back(".");
+ break;
+ }
+ }
+ }
+}
+
+NodePath NodePath::simplified() const {
+
+ NodePath np=*this;
+ np.simplify();
+ return np;
+}
+
NodePath::NodePath(const String& p_path) {
data=NULL;
diff --git a/core/path_db.h b/core/path_db.h
index b4f13d50be..de84216006 100644
--- a/core/path_db.h
+++ b/core/path_db.h
@@ -84,7 +84,10 @@ public:
bool operator==(const NodePath& p_path) const;
bool operator!=(const NodePath& p_path) const;
void operator=(const NodePath& p_path);
-
+
+ void simplify();
+ NodePath simplified() const;
+
NodePath(const Vector<StringName>& p_path,bool p_absolute,const String& p_property="");
NodePath(const Vector<StringName>& p_path,const Vector<StringName>& p_subpath,bool p_absolute,const String& p_property="");
NodePath(const NodePath& p_path);
diff --git a/core/resource.h b/core/resource.h
index 9d9c445e1d..3596abe673 100644
--- a/core/resource.h
+++ b/core/resource.h
@@ -130,7 +130,7 @@ public:
void set_name(const String& p_name);
String get_name() const;
- void set_path(const String& p_path,bool p_take_over=false);
+ virtual void set_path(const String& p_path,bool p_take_over=false);
String get_path() const;
void set_subindex(int p_sub_index);
diff --git a/demos/2d/platformer/coin.gd b/demos/2d/platformer/coin.gd
index 3e31a395ae..983cd46d88 100644
--- a/demos/2d/platformer/coin.gd
+++ b/demos/2d/platformer/coin.gd
@@ -18,3 +18,11 @@ func _ready():
# Initalization here
pass
+
+
+func _on_coin_area_enter( area ):
+ pass # replace with function body
+
+
+func _on_coin_area_enter_shape( area_id, area, area_shape, area_shape ):
+ pass # replace with function body
diff --git a/doc/base/classes.xml b/doc/base/classes.xml
index d83997ad8a..157d49fbc8 100644
--- a/doc/base/classes.xml
+++ b/doc/base/classes.xml
@@ -5905,7 +5905,7 @@
<description>
Base class of anything 2D. Canvas items are laid out in a tree and children inherit and extend the transform of their parent. CanvasItem is extended by [Control], for anything GUI related, and by [Node2D] for anything 2D engine related.
Any CanvasItem can draw. For this, the "update" function must be called, then NOTIFICATION_DRAW will be received on idle time to request redraw. Because of this, canvas items don't need to be redraw on every frame, improving the performance significan'tly. Several functions for drawing on the CanvasItem are provided (see draw_* functions). They can only be used inside the notification, signal or _draw() overrided function, though.
- Canvas items are draw in tree order. By default, children are on top of their parents so a root CanvasItem will be drawn behind everything (this can be changed per item though).
+ Canvas items are draw in tree order. By default, children are on top of their parents so a root CanvasItem will be drawn behind everything (this can be changed per item though).
Canvas items can also be hidden (hiding also their subtree). They provide many means for changing standard parameters such as opacity (for it and the subtree) and self opacity, blend mode.
Ultimately, a transform notification can be requested, which will notify the node that its global position changed in case the parent tree changed.
</description>
@@ -11556,8 +11556,11 @@
</class>
<class name="GraphEdit" inherits="Control" category="Core">
<brief_description>
+ GraphEdit is an area capable of showing various GraphNodes. It manages connection events between them.
</brief_description>
<description>
+ GraphEdit manages the showing of GraphNodes it contains, as well as connections an disconnections between them. Signals are sent for each of these two events. Disconnection between GraphNodes slots is disabled by default.
+ It is greatly advised to enable low processor usage mode [OS.set_low_processor_usage_mode()] when using GraphEdits.
</description>
<methods>
<method name="connect_node">
@@ -11572,6 +11575,7 @@
<argument index="3" name="to_port" type="int">
</argument>
<description>
+ Create a connection between 'from_port' slot of 'from' GraphNode and 'to_port' slot of 'to' GraphNode. If the connection already exists, no connection is created.
</description>
</method>
<method name="is_node_connected">
@@ -11586,6 +11590,7 @@
<argument index="3" name="to_port" type="int">
</argument>
<description>
+ Return true if the 'from_port' slot of 'from' GraphNode is connected to the 'to_port' slot of 'to' GraphNode.
</description>
</method>
<method name="disconnect_node">
@@ -11598,24 +11603,28 @@
<argument index="3" name="to_port" type="int">
</argument>
<description>
+ Remove the connection between 'from_port' slot of 'from' GraphNode and 'to_port' slot of 'to' GraphNode, if connection exists.
</description>
</method>
<method name="get_connection_list" qualifiers="const">
<return type="Array">
</return>
<description>
+ Return an Array containing the list of connections. A connection consists in a structure of the form {from_slot: 0, from: "GraphNode name 0", to_slot: 1, to: "GraphNode name 1" }
</description>
</method>
<method name="set_right_disconnects">
<argument index="0" name="enable" type="bool">
</argument>
<description>
+ Enable the disconnection of existing connections in the visual GraphEdit by left-clicking a connection and releasing into the void.
</description>
</method>
<method name="is_right_disconnects_enabled" qualifiers="const">
<return type="bool">
</return>
<description>
+ Return true is the disconnection of connections is enable in the visual GraphEdit. False otherwise.
</description>
</method>
</methods>
@@ -11630,6 +11639,7 @@
<argument index="3" name="to_slot" type="int">
</argument>
<description>
+ Signal sent to the GraphEdit when the connection between 'from_slot' slot of 'from' GraphNode and 'to_slot' slot of 'to' GraphNode is attempted to be removed.
</description>
</signal>
<signal name="connection_request">
@@ -11642,6 +11652,7 @@
<argument index="3" name="to_slot" type="int">
</argument>
<description>
+ Signal sent to the GraphEdit when the connection between 'from_slot' slot of 'from' GraphNode and 'to_slot' slot of 'to' GraphNode is attempted to be created.
</description>
</signal>
</signals>
@@ -11650,20 +11661,24 @@
</class>
<class name="GraphNode" inherits="Container" category="Core">
<brief_description>
+ A GraphNode is a container with several input and output slots allowing connections between GraphNodes. Slots can have different, incompatible types.
</brief_description>
<description>
+ A GraphNode is a container defined by a title. It can have 1 or more input and output slots, which can be enabled (shown) or disabled (not shown) and have different (incompatible) types. Colors can also be assigned to slots. A tuple of input and output slots is defined for each GUI element included in the GraphNode. Input and output connections are left and right slots, but only enabled slots are counted as connections.
</description>
<methods>
<method name="set_title">
<argument index="0" name="title" type="String">
</argument>
<description>
+ Set the title of the GraphNode.
</description>
</method>
<method name="get_title" qualifiers="const">
<return type="String">
</return>
<description>
+ Return the title of the GraphNode.
</description>
</method>
<method name="set_slot">
@@ -11682,16 +11697,19 @@
<argument index="6" name="color_right" type="Color">
</argument>
<description>
+ Set the tuple of input/output slots defined by 'idx' ID. 'left' slots are input, 'right' are output. 'type' is an integer defining the type of the slot. Refer to description for the compatibility between slot types.
</description>
</method>
<method name="clear_slot">
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Disable input and ouput slot whose index is 'idx'.
</description>
</method>
<method name="clear_all_slots">
<description>
+ Disable all input and output slots of the GraphNode.
</description>
</method>
<method name="is_slot_enabled_left" qualifiers="const">
@@ -11700,6 +11718,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return true if left (input) slot 'idx' is enabled. False otherwise.
</description>
</method>
<method name="get_slot_type_left" qualifiers="const">
@@ -11708,6 +11727,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the (integer) type of left (input) 'idx' slot.
</description>
</method>
<method name="get_slot_color_left" qualifiers="const">
@@ -11716,6 +11736,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the color set to 'idx' left (input) slot.
</description>
</method>
<method name="is_slot_enabled_right" qualifiers="const">
@@ -11724,6 +11745,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return true if right (output) slot 'idx' is enabled. False otherwise.
</description>
</method>
<method name="get_slot_type_right" qualifiers="const">
@@ -11732,6 +11754,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the (integer) type of right (output) 'idx' slot.
</description>
</method>
<method name="get_slot_color_right" qualifiers="const">
@@ -11740,30 +11763,35 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the color set to 'idx' right (output) slot.
</description>
</method>
<method name="set_offset">
<argument index="0" name="offset" type="Vector2">
</argument>
<description>
+ Set the offset of the GraphNode.
</description>
</method>
<method name="get_offset" qualifiers="const">
<return type="Vector2">
</return>
<description>
+ Return the offset of the GraphNode.
</description>
</method>
<method name="get_connection_output_count">
<return type="int">
</return>
<description>
+ Return the number of enabled output slots (connections) of the GraphNode.
</description>
</method>
<method name="get_connection_input_count">
<return type="int">
</return>
<description>
+ Return the number of enabled input slots (connections) to the GraphNode.
</description>
</method>
<method name="get_connection_output_pos">
@@ -11772,6 +11800,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the position of the output connection 'idx'.
</description>
</method>
<method name="get_connection_output_type">
@@ -11780,6 +11809,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the type of the output connection 'idx'.
</description>
</method>
<method name="get_connection_output_color">
@@ -11788,6 +11818,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the color of the output connection 'idx'.
</description>
</method>
<method name="get_connection_input_pos">
@@ -11796,6 +11827,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the position of the input connection 'idx'.
</description>
</method>
<method name="get_connection_input_type">
@@ -11804,6 +11836,7 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the type of the input connection 'idx'.
</description>
</method>
<method name="get_connection_input_color">
@@ -11812,28 +11845,33 @@
<argument index="0" name="idx" type="int">
</argument>
<description>
+ Return the color of the input connection 'idx'.
</description>
</method>
<method name="set_show_close_button">
<argument index="0" name="show" type="bool">
</argument>
<description>
+ Show the close button on the GraphNode if 'show' is true (disabled by default). If enabled, a connection on the signal close_request is needed for the close button to work.
</description>
</method>
<method name="is_close_button_visible" qualifiers="const">
<return type="bool">
</return>
<description>
+ Returns true if the close button is shown. False otherwise.
</description>
</method>
</methods>
<signals>
<signal name="raise_request">
<description>
+ Signal sent when the GraphNode is requested to be displayed over other ones. Happens on focusing (clicking into) the GraphNode.
</description>
</signal>
<signal name="close_request">
<description>
+ Signal sent on closing the GraphNode.
</description>
</signal>
<signal name="dragged">
@@ -11842,10 +11880,12 @@
<argument index="1" name="to" type="Vector2">
</argument>
<description>
+ Signal sent when the GraphNode is dragged.
</description>
</signal>
<signal name="offset_changed">
<description>
+ Signal sent when the GraphNode is moved.
</description>
</signal>
</signals>
@@ -17934,7 +17974,7 @@
</brief_description>
<description>
Nodes can be set as children of other nodes, resulting in a tree arrangement. Any tree of nodes is called a "Scene".
- Scenes can be saved to disk, and then instanced into other scenes. This allows for very high flexibility in the architecture and data model of the projects.
+ Scenes can be saved to disk, and then instanced into other scenes. This allows for very high flexibility in the architecture and data model of the projects.
[SceneMainLoop] contains the "active" tree of nodes, and a node becomes active (receinving NOTIFICATION_ENTER_SCENE) when added to that tree.
A node can contain any number of nodes as a children (but there is only one tree root) with the requirement that no two childrens with the same name can exist.
Nodes can, optionally, be added to groups. This makes it easy to reach a number of nodes from the code (for example an "enemies" group).
@@ -18684,11 +18724,11 @@
<description>
Operating System functions. OS Wraps the most common functionality to communicate with the host Operating System, such as:
-Mouse Grabbing
- -Mouse Cursors
+ -Mouse Cursors
-Clipboard
-Video Mode
-Date " Time
- -Timers
+ -Timers
-Environment Variables
-Execution of Binaries
-Command Line
@@ -27567,7 +27607,7 @@
Rigid body 2D node.
</brief_description>
<description>
- Rigid body 2D node. This node is used for placing rigid bodies in the scene. It can contain a number of shapes, and also shift state between regular Rigid Body to Character or even Static.
+ Rigid body 2D node. This node is used for placing rigid bodies in the scene. It can contain a number of shapes, and also shift state between regular Rigid Body to Character or even Static.
Character mode forbids the node from being rotated. This node can have a custom force integrator function, for writing complex physics motion behavior per node.
As a warning, don't change this node position every frame or very often. Sporadic changes work fine, but physics runs at a different granularity (fixed hz) than usual rendering (process callback) and maybe even in a separate thread, so changing this from a process loop will yield strange behavior.
</description>
@@ -37719,6 +37759,18 @@
<description>
</description>
</method>
+ <method name="set_audio_track">
+ <argument index="0" name="track" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_audio_track" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_stream_name" qualifiers="const">
<return type="String">
</return>
@@ -37755,6 +37807,18 @@
<description>
</description>
</method>
+ <method name="set_buffering_msec">
+ <argument index="0" name="msec" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_buffering_msec" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
</methods>
<constants>
</constants>
@@ -37765,30 +37829,16 @@
<description>
</description>
<methods>
- <method name="get_pending_frame_count" qualifiers="const">
- <return type="int">
- </return>
- <description>
- </description>
- </method>
- <method name="pop_frame">
- <argument index="0" name="arg0" type="Object">
- </argument>
- <description>
- </description>
- </method>
- <method name="peek_frame" qualifiers="const">
- <return type="Image">
- </return>
- <description>
- </description>
- </method>
- <method name="set_audio_track">
- <argument index="0" name="idx" type="int">
- </argument>
- <description>
- </description>
- </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
+<class name="VideoStreamTheora" inherits="VideoStream" category="Core">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <methods>
</methods>
<constants>
</constants>
diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp
index a3c742577a..3d75ed29f3 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -4145,7 +4145,7 @@ void RasterizerGLES2::begin_frame() {
//fragment_lighting=Globals::get_singleton()->get("rasterizer/use_fragment_lighting");
#ifdef TOOLS_ENABLED
- canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP,GLOBAL_DEF("rasterizer/use_pixel_snap",false));
+ canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP,GLOBAL_DEF("display/use_2d_pixel_snap",false));
shadow_filter=ShadowFilterTechnique(int(Globals::get_singleton()->get("rasterizer/shadow_filter")));
#endif
@@ -10807,7 +10807,7 @@ void RasterizerGLES2::init() {
copy_shader.set_conditional(CopyShaderGLES2::USE_8BIT_HDR,!use_fp16_fb);
canvas_shader.set_conditional(CanvasShaderGLES2::USE_DEPTH_SHADOWS,read_depth_supported);
- canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP,GLOBAL_DEF("rasterizer/use_pixel_snap",false));
+ canvas_shader.set_conditional(CanvasShaderGLES2::USE_PIXEL_SNAP,GLOBAL_DEF("display/use_2d_pixel_snap",false));
npo2_textures_available=true;
//fragment_lighting=false;
diff --git a/main/main.cpp b/main/main.cpp
index 84f7c3a88e..e1bc3b9fd9 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -701,6 +701,7 @@ Error Main::setup(const char *execpath,int argc, char *argv[],bool p_second_phas
GLOBAL_DEF("display/resizable",video_mode.resizable);
GLOBAL_DEF("display/test_width",0);
GLOBAL_DEF("display/test_height",0);
+ OS::get_singleton()->_pixel_snap=GLOBAL_DEF("display/use_2d_pixel_snap",false);
if (rtm==-1) {
rtm=GLOBAL_DEF("render/thread_model",OS::RENDER_THREAD_SAFE);
if (rtm>=1) //hack for now
diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp
index 9c39051b7f..313fb57d0e 100644
--- a/modules/gdscript/gd_parser.cpp
+++ b/modules/gdscript/gd_parser.cpp
@@ -2421,6 +2421,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
}; //fallthrough to use the same
case Variant::REAL: {
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="EASE") {
+ current_export.hint=PROPERTY_HINT_EXP_EASING;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
+
float sign=1.0;
if (tokenizer->get_token()==GDTokenizer::TK_OP_SUB) {
@@ -2571,6 +2581,17 @@ void GDParser::_parse_class(ClassNode *p_class) {
}
break;
}
+
+ if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER && tokenizer->get_token_identifier()=="MULTILINE") {
+
+ current_export.hint=PROPERTY_HINT_MULTILINE_TEXT;
+ tokenizer->advance();
+ if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')' in hint.");
+ return;
+ }
+ break;
+ }
} break;
case Variant::COLOR: {
@@ -2607,23 +2628,16 @@ void GDParser::_parse_class(ClassNode *p_class) {
} else if (tokenizer->get_token()==GDTokenizer::TK_IDENTIFIER) {
String identifier = tokenizer->get_token_identifier();
- if (identifier == "flag") {
- current_export.type=Variant::INT;
- current_export.hint=PROPERTY_HINT_ALL_FLAGS;
- }else if (identifier == "multiline"){
- current_export.type=Variant::STRING;
- current_export.hint=PROPERTY_HINT_MULTILINE_TEXT;
- } else {
- if (!ObjectTypeDB::is_type(identifier,"Resource")) {
-
- current_export=PropertyInfo();
- _set_error("Export hint not a type or resource.");
- }
-
- current_export.type=Variant::OBJECT;
- current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
- current_export.hint_string=identifier;
+ if (!ObjectTypeDB::is_type(identifier,"Resource")) {
+
+ current_export=PropertyInfo();
+ _set_error("Export hint not a type or resource.");
}
+
+ current_export.type=Variant::OBJECT;
+ current_export.hint=PROPERTY_HINT_RESOURCE_TYPE;
+ current_export.hint_string=identifier;
+
tokenizer->advance();
}
diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h
index 49fa6c0862..e8277688ac 100644
--- a/platform/osx/os_osx.h
+++ b/platform/osx/os_osx.h
@@ -157,6 +157,8 @@ public:
Error shell_open(String p_uri);
void push_input(const InputEvent& p_event);
+ String get_locale() const;
+
virtual void set_video_mode(const VideoMode& p_video_mode,int p_screen=0);
virtual VideoMode get_video_mode(int p_screen=0) const;
virtual void get_fullscreen_mode_list(List<VideoMode> *p_list,int p_screen=0) const;
diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm
index b0ce37cecf..4990d04ab6 100644
--- a/platform/osx/os_osx.mm
+++ b/platform/osx/os_osx.mm
@@ -1254,6 +1254,11 @@ Error OS_OSX::shell_open(String p_uri) {
return OK;
}
+String OS_OSX::get_locale() const {
+ NSString* preferredLang = [[NSLocale preferredLanguages] objectAtIndex:0];
+ return [preferredLang UTF8String];
+}
+
void OS_OSX::swap_buffers() {
[context flushBuffer];
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
index 0b00ac9560..342b86b4c1 100644
--- a/scene/2d/animated_sprite.cpp
+++ b/scene/2d/animated_sprite.cpp
@@ -28,6 +28,8 @@
/*************************************************************************/
#include "animated_sprite.h"
#include "scene/scene_string_names.h"
+#include "os/os.h"
+
void AnimatedSprite::edit_set_pivot(const Point2& p_pivot) {
set_offset(p_pivot);
@@ -153,6 +155,9 @@ void AnimatedSprite::_notification(int p_what) {
if (centered)
ofs-=s/2;
+ if (OS::get_singleton()->get_use_pixel_snap()) {
+ ofs=ofs.floor();
+ }
Rect2 dst_rect(ofs,s);
if (hflip)
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
index 87697fc073..89d9966958 100644
--- a/scene/2d/sprite.cpp
+++ b/scene/2d/sprite.cpp
@@ -30,6 +30,7 @@
#include "core/core_string_names.h"
#include "scene/scene_string_names.h"
#include "scene/main/viewport.h"
+#include "os/os.h"
void Sprite::edit_set_pivot(const Point2& p_pivot) {
@@ -85,6 +86,9 @@ void Sprite::_notification(int p_what) {
Point2 ofs=offset;
if (centered)
ofs-=s/2;
+ if (OS::get_singleton()->get_use_pixel_snap()) {
+ ofs=ofs.floor();
+ }
Rect2 dst_rect(ofs,s);
@@ -422,6 +426,9 @@ void ViewportSprite::_notification(int p_what) {
if (centered)
ofs-=s/2;
+ if (OS::get_singleton()->get_use_pixel_snap()) {
+ ofs=ofs.floor();
+ }
Rect2 dst_rect(ofs,s);
texture->draw_rect_region(ci,dst_rect,src_rect,modulate);
diff --git a/scene/main/instance_placeholder.cpp b/scene/main/instance_placeholder.cpp
new file mode 100644
index 0000000000..370eb1e74a
--- /dev/null
+++ b/scene/main/instance_placeholder.cpp
@@ -0,0 +1,74 @@
+#include "instance_placeholder.h"
+
+#include "scene/resources/packed_scene.h"
+#include "io/resource_loader.h"
+
+bool InstancePlaceholder::_set(const StringName& p_name, const Variant& p_value) {
+
+ PropSet ps;
+ ps.name=p_name;
+ ps.value=p_value;
+ stored_values.push_back(ps);
+ return true;
+}
+
+bool InstancePlaceholder::_get(const StringName& p_name,Variant &r_ret) const{
+
+ return false;
+}
+void InstancePlaceholder::_get_property_list( List<PropertyInfo> *p_list) const{
+
+
+}
+
+
+void InstancePlaceholder::set_path(const String& p_name) {
+
+ path=p_name;
+}
+
+String InstancePlaceholder::get_path() const {
+
+ return path;
+}
+void InstancePlaceholder::replace_by_instance(const Ref<PackedScene> &p_custom_scene){
+
+ ERR_FAIL_COND(!is_inside_tree());
+
+ Node *base = get_parent();
+ if (!base)
+ return;
+
+ Ref<PackedScene> ps;
+ if (p_custom_scene.is_valid())
+ ps = p_custom_scene;
+ else
+ ps = ResourceLoader::load(path,"PackedScene");
+
+ if (!ps.is_valid())
+ return;
+ Node *scene = ps->instance();
+ scene->set_name(get_name());
+ int pos = get_position_in_parent();
+
+ for(List<PropSet>::Element *E=stored_values.front();E;E=E->next()) {
+ scene->set(E->get().name,E->get().value);
+ }
+
+ queue_delete();
+
+ base->remove_child(this);
+ base->add_child(scene);
+ base->move_child(scene,pos);
+
+}
+
+void InstancePlaceholder::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("replace_by_instance","custom_scene:PackedScene"),&InstancePlaceholder::replace_by_instance,DEFVAL(Variant()));
+}
+
+InstancePlaceholder::InstancePlaceholder() {
+
+
+}
diff --git a/scene/main/instance_placeholder.h b/scene/main/instance_placeholder.h
new file mode 100644
index 0000000000..e9e76e7a2d
--- /dev/null
+++ b/scene/main/instance_placeholder.h
@@ -0,0 +1,37 @@
+#ifndef INSTANCE_PLACEHOLDER_H
+#define INSTANCE_PLACEHOLDER_H
+
+#include "scene/main/node.h"
+
+class PackedScene;
+
+class InstancePlaceholder : public Node {
+
+ OBJ_TYPE(InstancePlaceholder,Node);
+
+ String path;
+ struct PropSet {
+ StringName name;
+ Variant value;
+ };
+
+ List<PropSet> stored_values;
+
+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 set_path(const String& p_name);
+ String get_path() const;
+
+ void replace_by_instance(const Ref<PackedScene>& p_custom_scene=Ref<PackedScene>());
+
+ InstancePlaceholder();
+};
+
+#endif // INSTANCE_PLACEHOLDER_H
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 8336ce35f6..dd4587c19f 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -841,6 +841,20 @@ Node *Node::get_child(int p_index) const {
return data.children[p_index];
}
+
+Node *Node::_get_child_by_name(const StringName& p_name) const {
+
+ int cc=data.children.size();
+ Node* const* cd=data.children.ptr();
+
+ for(int i=0;i<cc;i++){
+ if (cd[i]->data.name==p_name)
+ return cd[i];
+ }
+
+ return NULL;
+}
+
Node *Node::_get_node(const NodePath& p_path) const {
ERR_FAIL_COND_V( !data.inside_tree && p_path.is_absolute(), NULL );
@@ -906,8 +920,10 @@ Node *Node::_get_node(const NodePath& p_path) const {
Node *Node::get_node(const NodePath& p_path) const {
Node *node = _get_node(p_path);
- ERR_EXPLAIN("Node not found: "+p_path);
- ERR_FAIL_COND_V(!node,NULL);
+ if (!node) {
+ ERR_EXPLAIN("Node not found: "+p_path);
+ ERR_FAIL_COND_V(!node,NULL);
+ }
return node;
}
@@ -1036,6 +1052,7 @@ void Node::get_owned_by(Node *p_by,List<Node*> *p_owned) {
void Node::_set_owner_nocheck(Node* p_owner) {
+ ERR_FAIL_COND(data.owner);
data.owner=p_owner;
data.owner->data.owned.push_back( this );
data.OW = data.owner->data.owned.back();
@@ -1332,7 +1349,29 @@ String Node::get_filename() const {
return data.filename;
}
+void Node::set_editable_instance(Node* p_node,bool p_editable) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_COND(!is_a_parent_of(p_node));
+ NodePath p = get_path_to(p_node);
+ if (!p_editable)
+ data.editable_instances.erase(p);
+ else
+ data.editable_instances[p]=true;
+
+}
+
+bool Node::is_editable_instance(Node *p_node) const {
+
+ if (!p_node)
+ return false; //easier, null is never editable :)
+ ERR_FAIL_COND_V(!is_a_parent_of(p_node),false);
+ NodePath p = get_path_to(p_node);
+ return data.editable_instances.has(p);
+}
+
+#if 0
void Node::generate_instance_state() {
@@ -1383,13 +1422,36 @@ Dictionary Node::get_instance_state() const {
return data.instance_state;
}
-Vector<StringName> Node::get_instance_groups() const {
+#endif
+
+void Node::set_scene_instance_state(const Ref<SceneState>& p_state) {
+
+ data.instance_state=p_state;
+}
+
+Ref<SceneState> Node::get_scene_instance_state() const{
+
+ return data.instance_state;
+}
+
+void Node::set_scene_inherited_state(const Ref<SceneState>& p_state) {
+
+ data.inherited_state=p_state;
+}
+
+Ref<SceneState> Node::get_scene_inherited_state() const{
- return data.instance_groups;
+ return data.inherited_state;
}
-Vector<Node::Connection> Node::get_instance_connections() const{
- return data.instance_connections;
+void Node::set_scene_instance_load_placeholder(bool p_enable) {
+
+ data.use_placeholder=p_enable;
+}
+
+bool Node::get_scene_instance_load_placeholder() const{
+
+ return data.use_placeholder;
}
int Node::get_position_in_parent() const {
@@ -2049,6 +2111,7 @@ Node::Node() {
data.parent_owned=false;
data.in_constructor=true;
data.viewport=NULL;
+ data.use_placeholder=false;
}
Node::~Node() {
@@ -2065,3 +2128,4 @@ Node::~Node() {
}
+////////////////////////////////
diff --git a/scene/main/node.h b/scene/main/node.h
index a6d5bfbd9f..87fa4dd6ca 100644
--- a/scene/main/node.h
+++ b/scene/main/node.h
@@ -38,6 +38,7 @@
class Viewport;
+class SceneState;
class Node : public Object {
OBJ_TYPE( Node, Object );
@@ -69,9 +70,10 @@ private:
struct Data {
String filename;
- Dictionary instance_state;
- Vector<StringName> instance_groups;
- Vector<Connection> instance_connections;
+ Ref<SceneState> instance_state;
+ Ref<SceneState> inherited_state;
+
+ HashMap<NodePath,int> editable_instances;
Node *parent;
Node *owner;
@@ -96,6 +98,7 @@ private:
PauseMode pause_mode;
Node *pause_owner;
// variables used to properly sort the node when processing, ignored otherwise
+ //should move all the stuff below to bits
bool fixed_process;
bool idle_process;
@@ -105,6 +108,9 @@ private:
bool parent_owned;
bool in_constructor;
+ bool use_placeholder;
+
+
} data;
@@ -112,6 +118,7 @@ private:
virtual bool _use_builtin_script() const { return true; }
Node *_get_node(const NodePath& p_path) const;
+ Node *_get_child_by_name(const StringName& p_name) const;
@@ -151,7 +158,7 @@ protected:
static void _bind_methods();
-friend class PackedScene;
+friend class SceneState;
void _add_child_nocheck(Node* p_child,const StringName& p_name);
void _set_owner_nocheck(Node* p_owner);
@@ -208,7 +215,7 @@ public:
struct GroupInfo {
- String name;
+ StringName name;
bool persistent;
};
@@ -229,7 +236,11 @@ public:
void set_filename(const String& p_filename);
String get_filename() const;
-
+
+ void set_editable_instance(Node* p_node,bool p_editable);
+ bool is_editable_instance(Node* p_node) const;
+
+
/* NOTIFICATIONS */
void propagate_notification(int p_notification);
@@ -261,10 +272,14 @@ public:
//Node *clone_tree() const;
// used by editors, to save what has changed only
- void generate_instance_state();
- Dictionary get_instance_state() const;
- Vector<StringName> get_instance_groups() const;
- Vector<Connection> get_instance_connections() const;
+ void set_scene_instance_state(const Ref<SceneState>& p_state);
+ Ref<SceneState> get_scene_instance_state() const;
+
+ void set_scene_inherited_state(const Ref<SceneState>& p_state);
+ Ref<SceneState> get_scene_inherited_state() const;
+
+ void set_scene_instance_load_placeholder(bool p_enable);
+ bool get_scene_instance_load_placeholder() const;
static Vector<Variant> make_binds(VARIANT_ARG_LIST);
@@ -307,6 +322,4 @@ public:
typedef Set<Node*,Node::Comparator> NodeSet;
-
-
#endif
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 851de4a89f..fe6b192d78 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -36,6 +36,7 @@
#include "resources/default_theme/default_theme.h"
#include "object_type_db.h"
#include "scene/main/canvas_layer.h"
+#include "scene/main/instance_placeholder.h"
#include "scene/main/viewport.h"
#include "scene/gui/control.h"
#include "scene/gui/texture_progress.h"
@@ -216,7 +217,7 @@
#include "scene/3d/collision_polygon.h"
#endif
-
+#include "scene/resources/scene_format_text.h"
static ResourceFormatLoaderImage *resource_loader_image=NULL;
static ResourceFormatLoaderWAV *resource_loader_wav=NULL;
@@ -229,6 +230,8 @@ static ResourceFormatLoaderBitMap *resource_loader_bitmap=NULL;
static ResourceFormatLoaderTheme *resource_loader_theme=NULL;
static ResourceFormatLoaderShader *resource_loader_shader=NULL;
+static ResourceFormatSaverText *resource_saver_text=NULL;
+
//static SceneStringNames *string_names;
void register_scene_types() {
@@ -267,6 +270,7 @@ void register_scene_types() {
ObjectTypeDB::register_type<Object>();
ObjectTypeDB::register_type<Node>();
+ ObjectTypeDB::register_virtual_type<InstancePlaceholder>();
ObjectTypeDB::register_type<Viewport>();
ObjectTypeDB::register_virtual_type<RenderTargetTexture>();
@@ -612,6 +616,9 @@ void register_scene_types() {
OS::get_singleton()->yield(); //may take time to init
+ resource_saver_text = memnew( ResourceFormatSaverText );
+ ResourceSaver::add_resource_format_saver(resource_saver_text);
+
}
void unregister_scene_types() {
@@ -629,5 +636,9 @@ void unregister_scene_types() {
memdelete( resource_loader_theme );
memdelete( resource_loader_shader );
+
+ if (resource_saver_text) {
+ memdelete(resource_saver_text);
+ }
SceneStringNames::free();
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index b6082c3a76..a9dc0e8bfb 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -32,13 +32,30 @@
#include "scene/3d/spatial.h"
#include "scene/gui/control.h"
#include "scene/2d/node_2d.h"
+#include "scene/main/instance_placeholder.h"
-bool PackedScene::can_instance() const {
+#define PACK_VERSION 2
+
+bool SceneState::can_instance() const {
return nodes.size()>0;
}
-Node *PackedScene::instance(bool p_gen_edit_state) const {
+
+Node *SceneState::instance(bool p_gen_edit_state) const {
+
+ // nodes where instancing failed (because something is missing)
+ List<Node*> stray_instances;
+
+#define NODE_FROM_ID(p_name,p_id)\
+ Node *p_name;\
+ if (p_id&FLAG_ID_IS_PATH) {\
+ NodePath np=node_paths[p_id&FLAG_MASK];\
+ p_name=ret_nodes[0]->_get_node(np);\
+ } else {\
+ ERR_FAIL_INDEX_V(p_id&FLAG_MASK,nc,NULL);\
+ p_name=ret_nodes[p_id&FLAG_MASK];\
+ }
int nc = nodes.size();
ERR_FAIL_COND_V(nc==0,NULL);
@@ -59,29 +76,80 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
Node **ret_nodes=(Node**)alloca( sizeof(Node*)*nc );
+ bool gen_node_path_cache=p_gen_edit_state && node_path_cache.empty();
for(int i=0;i<nc;i++) {
const NodeData &n=nd[i];
- if (!ObjectTypeDB::is_type_enabled(snames[n.type])) {
- ret_nodes[i]=NULL;
- continue;
+ Node *parent=NULL;
+
+ if (i>0) {
+
+ NODE_FROM_ID(nparent,n.parent);
+#ifdef DEBUG_ENABLED
+ if (!nparent && n.parent&FLAG_ID_IS_PATH) {
+
+ WARN_PRINT(String("Parent path '"+String(node_paths[n.parent&FLAG_MASK])+"' for node '"+String(snames[n.name])+"' has vanished when instancing: '"+get_path()+"'.").ascii().get_data());
+
+ }
+#endif
+ parent=nparent;
}
Node *node=NULL;
- if (n.instance>=0) {
- //instance existing
- Ref<PackedScene> sdata = props[ n.instance ];
+ if (i==0 && base_scene_idx>=0) {
+ //scene inheritance on root node
+ print_line("scene inherit");
+ Ref<PackedScene> sdata = props[ base_scene_idx ];
ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
- node = sdata->instance();
+ node = sdata->instance(p_gen_edit_state);
ERR_FAIL_COND_V(!node,NULL);
- if (p_gen_edit_state)
- node->generate_instance_state();
+ if (p_gen_edit_state) {
+ node->set_scene_inherited_state(sdata->get_state());
+ }
- } else {
- //create anew
+ } else if (n.instance>=0) {
+ //instance a scene into this node
+ print_line("instance");
+ if (n.instance&FLAG_INSTANCE_IS_PLACEHOLDER) {
+
+ String path = props[n.instance&FLAG_MASK];
+ if (disable_placeholders) {
+
+ Ref<PackedScene> sdata = ResourceLoader::load(path,"PackedScene");
+ ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
+ node = sdata->instance(p_gen_edit_state);
+ ERR_FAIL_COND_V(!node,NULL);
+ } else {
+ InstancePlaceholder *ip = memnew( InstancePlaceholder );
+ ip->set_path(path);
+ node=ip;
+ }
+ node->set_scene_instance_load_placeholder(true);
+ } else {
+ Ref<PackedScene> sdata = props[ n.instance&FLAG_MASK ];
+ ERR_FAIL_COND_V( !sdata.is_valid(), NULL);
+ node = sdata->instance(p_gen_edit_state);
+ ERR_FAIL_COND_V(!node,NULL);
+
+ }
+
+ } else if (n.type==TYPE_INSTANCED) {
+ //print_line("instanced");
+ //get the node from somewhere, it likely already exists from another instance
+ if (parent) {
+ node=parent->_get_child_by_name(snames[n.name]);
+#ifdef DEBUG_ENABLED
+ if (!node) {
+ WARN_PRINT(String("Node '"+String(ret_nodes[0]->get_path_to(parent))+"/"+String(snames[n.name])+"' was modified from inside a instance, but it has vanished.").ascii().get_data());
+ }
+#endif
+ }
+ } else if (ObjectTypeDB::is_type_enabled(snames[n.type])) {
+ print_line("created");
+ //node belongs to this scene and must be created
Object * obj = ObjectTypeDB::instance(snames[ n.type ]);
if (!obj || !obj->cast_to<Node>()) {
if (obj) {
@@ -109,49 +177,68 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
}
- //properties
- int nprop_count=n.properties.size();
- if (nprop_count) {
+ if (node) {
+ // may not have found the node (part of instanced scene and removed)
+ // if found all is good, otherwise ignore
+
+ //properties
+ int nprop_count=n.properties.size();
+ if (nprop_count) {
- const NodeData::Property* nprops=&n.properties[0];
+ const NodeData::Property* nprops=&n.properties[0];
- for(int j=0;j<nprop_count;j++) {
+ for(int j=0;j<nprop_count;j++) {
- bool valid;
- ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL );
- ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL );
+ bool valid;
+ ERR_FAIL_INDEX_V( nprops[j].name, sname_count, NULL );
+ ERR_FAIL_INDEX_V( nprops[j].value, prop_count, NULL );
- node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
+ node->set(snames[ nprops[j].name ],props[ nprops[j].value ],&valid);
+ }
}
- }
- //name
+ //name
- //groups
- for(int j=0;j<n.groups.size();j++) {
+ //groups
+ for(int j=0;j<n.groups.size();j++) {
- ERR_FAIL_INDEX_V( n.groups[j], sname_count, NULL );
- node->add_to_group( snames[ n.groups[j] ], true );
- }
+ ERR_FAIL_INDEX_V( n.groups[j], sname_count, NULL );
+ node->add_to_group( snames[ n.groups[j] ], true );
+ }
+ if (n.instance>=0 || n.type!=TYPE_INSTANCED) {
+ //if node was not part of instance, must set it's name, parenthood and ownership
+ if (i>0) {
+ if (parent) {
+ parent->_add_child_nocheck(node,snames[n.name]);
+ } else {
+ //it may be possible that an instanced scene has changed
+ //and the node has nowhere to go anymore
+ stray_instances.push_back(node); //can't be added, go to stray list
+ }
+ } else {
+ node->_set_name_nocheck( snames[ n.name ] );
+ }
- ret_nodes[i]=node;
+ }
- if (i>0) {
- ERR_FAIL_INDEX_V(n.parent,i,NULL);
- ERR_FAIL_COND_V(!ret_nodes[n.parent],NULL);
- ret_nodes[n.parent]->_add_child_nocheck(node,snames[n.name]);
- } else {
- node->_set_name_nocheck( snames[ n.name ] );
- }
+ if (n.owner>=0) {
+ NODE_FROM_ID(owner,n.owner);
+ if (owner)
+ node->_set_owner_nocheck(owner);
+ }
- if (n.owner>=0) {
- ERR_FAIL_INDEX_V(n.owner,i,NULL);
- node->_set_owner_nocheck(ret_nodes[n.owner]);
}
+
+ ret_nodes[i]=node;
+
+ if (node && gen_node_path_cache && ret_nodes[0]) {
+ NodePath n = ret_nodes[0]->get_path_to(node);
+ node_path_cache[n]=i;
+ }
}
@@ -163,8 +250,14 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
for(int i=0;i<cc;i++) {
const ConnectionData &c=cdata[i];
- ERR_FAIL_INDEX_V( c.from, nc, NULL );
- ERR_FAIL_INDEX_V( c.to, nc, NULL );
+ //ERR_FAIL_INDEX_V( c.from, nc, NULL );
+ //ERR_FAIL_INDEX_V( c.to, nc, NULL );
+
+ NODE_FROM_ID(cfrom,c.from);
+ NODE_FROM_ID(cto,c.to);
+
+ if (!cfrom || !cto)
+ continue;
Vector<Variant> binds;
if (c.binds.size()) {
@@ -173,17 +266,25 @@ Node *PackedScene::instance(bool p_gen_edit_state) const {
binds[j]=props[ c.binds[j] ];
}
- if (!ret_nodes[c.from] || !ret_nodes[c.to])
- continue;
- ret_nodes[c.from]->connect( snames[ c.signal], ret_nodes[ c.to ], snames[ c.method], binds,CONNECT_PERSIST|c.flags );
+
+ cfrom->connect( snames[ c.signal], cto, snames[ c.method], binds,CONNECT_PERSIST|c.flags );
}
- Node *s = ret_nodes[0];
+ //Node *s = ret_nodes[0];
- if (get_path()!="" && get_path().find("::")==-1)
- s->set_filename(get_path());
+ //remove nodes that could not be added, likely as a result that
+ while(stray_instances.size()) {
+ memdelete(stray_instances.front()->get());
+ stray_instances.pop_front();;
+ }
+
+ for(int i=0;i<editable_instances.size();i++) {
+ Node *ei = ret_nodes[0]->_get_node(editable_instances[i]);
+ if (ei) {
+ ret_nodes[0]->set_editable_instance(ei,true);
+ }
+ }
- s->notification(Node::NOTIFICATION_INSTANCED);
return ret_nodes[0];
}
@@ -209,44 +310,157 @@ static int _vm_get_variant(const Variant& p_variant, HashMap<Variant,int,Variant
return idx;
}
-Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) {
+Error SceneState::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
+
- if (p_node!=p_owner && (p_node->get_owner()!=p_owner))
- return OK; //nothing to do with this node, may either belong to another scene or be onowned
+ // this function handles all the work related to properly packing scenes, be it
+ // instanced or inherited.
+ // given the complexity of this process, an attempt will be made to properly
+ // document it. if you fail to understand something, please ask!
+
+ //discard nodes that do not belong to be processed
+ if (p_node!=p_owner && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
+ return OK;
+
+ // save the child instanced scenes that are chosen as editable, so they can be restored
+ // upon load back
+ if (p_node!=p_owner && p_node->get_filename()!=String() && p_owner->is_editable_instance(p_node))
+ editable_instances.push_back(p_owner->get_path_to(p_node));
NodeData nd;
nd.name=_nm_get_string(p_node->get_name(),name_map);
- nd.type=_nm_get_string(p_node->get_type(),name_map);
- nd.parent=p_parent_idx;
+ nd.instance=-1; //not instanced by default
+
+ // if this node is part of an instanced scene or sub-instanced scene
+ // we need to get the corresponding instance states.
+ // with the instance states, we can query for identical properties/groups
+ // and only save what has changed
+
+ List<PackState> pack_state_stack;
+
+ bool instanced_by_owner=true;
+
+ {
+ Node *n=p_node;
+
+ while(n) {
+
+ if (n==p_owner) {
+
+ Ref<SceneState> state = n->get_scene_inherited_state();
+ if (state.is_valid()) {
+ int node = state->find_node_by_path(n->get_path_to(p_node));
+ if (node>=0) {
+ //this one has state for this node, save
+ PackState ps;
+ ps.node=node;
+ ps.state=state;
+ pack_state_stack.push_front(ps);
+ instanced_by_owner=false;
+ }
+ }
+
+ if (p_node->get_filename()!=String() && p_node->get_owner()==p_owner && instanced_by_owner) {
+
+ if (p_node->get_scene_instance_load_placeholder()) {
+ //it's a placeholder, use the placeholder path
+ nd.instance=_vm_get_variant(p_node->get_filename(),variant_map);
+ nd.instance|=FLAG_INSTANCE_IS_PLACEHOLDER;
+ } else {
+ //must instance ourselves
+ Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
+ if (!instance.is_valid()) {
+ return ERR_CANT_OPEN;
+ }
+ nd.instance=_vm_get_variant(instance,variant_map);
+ }
+ }
+ n=NULL;
+ } else {
+ if (n->get_filename()!=String()) {
+ //is an instance
+ Ref<SceneState> state = n->get_scene_instance_state();
+ if (state.is_valid()) {
+ int node = state->find_node_by_path(n->get_path_to(p_node));
+ if (node>=0) {
+ //this one has state for this node, save
+ PackState ps;
+ ps.node=node;
+ ps.state=state;
+ pack_state_stack.push_back(ps);
+ }
+ }
+
+ }
+ n=n->get_owner();
+ }
+ }
+ }
+
+#if 0
+
+ Ref<SceneState> base_scene = p_node->get_scene_inherited_state(); //for inheritance
+ Ref<SceneState> instance_state;
+ int instance_state_node=-1;
+
+ if (base_scene.is_valid() && (p_node==p_owner || p_node->get_owner()==p_owner)) {
+ //scene inheritance in use, see if this node is actually inherited
+ NodePath path = p_owner->get_path_to(p_node);
+ instance_state_node = base_scene->find_node_by_path(path);
+ if (instance_state_node>=0) {
+ instance_state=base_scene;
+ }
+ }
- Dictionary instance_state;
- Set<StringName> instance_groups;
+ // check that this is a directly instanced scene from the scene being packed, if so
+ // this information must be saved. Of course, if using scene instancing and this node
+ // does belong to base scene, ignore.
+ if (instance_state.is_null() && p_node!=p_owner && p_node->get_owner()==p_owner && p_node->get_filename()!="") {
- if (p_node!=p_owner && p_node->get_filename()!="") {
- //instanced
+ //instanced, only direct sub-scnes are supported of course
Ref<PackedScene> instance = ResourceLoader::load(p_node->get_filename());
if (!instance.is_valid()) {
return ERR_CANT_OPEN;
}
nd.instance=_vm_get_variant(instance,variant_map);
- instance_state = p_node->get_instance_state();
- Vector<StringName> ig = p_node->get_instance_groups();
- for(int i=0;i<ig.size();i++)
- instance_groups.insert(ig[i]);
+
} else {
nd.instance=-1;
}
+ // finally, if this does not belong to scene inheritance, check
+ // if it belongs to scene instancing
+
+ if (instance_state.is_null() && p_node!=p_owner) {
+ //if not affected by scene inheritance, this may be
+ if (p_node->get_owner()==p_owner && p_node->get_filename()!=String()) {
+ instance_state=p_node->get_scene_instance_state();
+ if (instance_state.is_valid()) {
+ instance_state_node=instance_state->find_node_by_path(p_node->get_path_to(p_node));
+ }
- //instance state makes sure that only changes to instance are saved
+ } else if (p_node->get_owner()!=p_owner && p_owner->is_editable_instance(p_node->get_owner())) {
+ instance_state=p_node->get_owner()->get_scene_instance_state();
+ if (instance_state.is_valid()) {
+ instance_state_node=instance_state->find_node_by_path(p_node->get_owner()->get_path_to(p_node));
+ }
+ }
+ }
+#endif
+ int subscene_prop_search_from=0;
+
+ // all setup, we then proceed to check all properties for the node
+ // and save the ones that are worth saving
List<PropertyInfo> plist;
p_node->get_property_list(&plist);
+
+
for (List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
@@ -257,34 +471,63 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
String name = E->get().name;
Variant value = p_node->get( E->get().name );
- if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) {
- continue;
- }
- print_line("PASSED!");
- print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE));
- print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one()));
+ bool isdefault = ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one());
+
+// if (nd.instance<0 && ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONZERO) && value.is_zero()) || ((E->get().usage & PROPERTY_USAGE_STORE_IF_NONONE) && value.is_one())) {
+// continue;
+// }
- if (nd.instance>=0) {
- //only save changed properties in instance
- /*
- // this was commented because it would not save properties created from within script
- // done with _get_property_list, that are not in the original node.
- // if some property is not saved, check again
- if (!instance_state.has(name)) {
- print_line("skip not in instance");
+
+ //print_line("PASSED!");
+ //print_line("at: "+String(p_node->get_name())+"::"+name+": - nz: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO)+" no: "+itos(E->get().usage&PROPERTY_USAGE_STORE_IF_NONONE));
+ //print_line("value: "+String(value)+" is zero: "+itos(value.is_zero())+" is one" +itos(value.is_one()));
+
+ if (pack_state_stack.size()) {
+ // we are on part of an instanced subscene
+ // or part of instanced scene.
+ // only save what has been changed
+ // only save changed properties in instance
+
+ if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE || E->get().name=="__meta__") {
+ //property has requested that no instance state is saved, sorry
+ //also, meta won't be overriden or saved
continue;
- }*/
+ }
+
+ bool exists=false;
+ Variant original;
+
+ for (List<PackState>::Element *F=pack_state_stack.back();F;F=F->prev()) {
+ //check all levels of pack to see if the property exists somewhere
+ const PackState &ps=F->get();
- if (E->get().usage & PROPERTY_USAGE_NO_INSTANCE_STATE) {
+ original = ps.state->get_property_value(ps.node,E->get().name,exists);
+ if (exists) {
+ break;
+ }
+ }
+
+
+ if (exists && bool(Variant::evaluate(Variant::OP_EQUAL,value,original))) {
+ //exists and did not change
continue;
}
- if (instance_state.has(name) && instance_state[name]==value) {
+ if (!exists && isdefault) {
+ //does not exist in original node, but it's the default value
+ //so safe to skip too.
continue;
}
+
+ } else {
+
+ if (isdefault) {
+ //it's the default value, no point in saving it
+ continue;
+ }
}
NodeData::Property prop;
@@ -295,6 +538,9 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
}
+ // save the groups this node is into
+ // discard groups that come from the original scene
+
List<Node::GroupInfo> groups;
p_node->get_groups(&groups);
for(List<Node::GroupInfo>::Element *E=groups.front();E;E=E->next()) {
@@ -302,27 +548,123 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
if (!gi.persistent)
continue;
- if (nd.instance>=0 && instance_groups.has(gi.name))
- continue; //group was instanced, don't add here
+// if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name))
+// continue; //group was instanced, don't add here
+
+ bool skip=false;
+ for (List<PackState>::Element *F=pack_state_stack.front();F;F=F->next()) {
+ //check all levels of pack to see if the group was added somewhere
+ const PackState &ps=F->get();
+ if (ps.state->is_node_in_group(ps.node,gi.name)) {
+ skip=true;
+ break;
+ }
+ }
+
+ if (skip)
+ continue;
nd.groups.push_back(_nm_get_string(gi.name,name_map));
}
- if (node_map.has(p_node->get_owner()))
- nd.owner=node_map[p_node->get_owner()];
- else
+
+ // save the right owner
+ // for the saved scene root this is -1
+ // for nodes of the saved scene this is 0
+ // for nodes of instanced scenes this is >0
+
+ if (p_node==p_owner) {
+ //saved scene root
+ nd.owner=-1;
+ } else if (p_node->get_owner()==p_owner) {
+ //part of saved scene
+ nd.owner=0;
+ } else {
+
nd.owner=-1;
+#if 0
+ // this is pointless, if this was instanced by something else,
+ // the owner will already be set.
+
+ if (node_map.has(p_node->get_owner())) {
+ //maybe an existing saved node
+ nd.owner=node_map[p_node->get_owner()];
+ } else {
+ //not saved, use nodepath map
+ int sidx;
+ if (nodepath_map.has(p_node->get_owner())) {
+ sidx=nodepath_map[p_node->get_owner()];
+ } else {
+ sidx=nodepath_map.size();
+ nodepath_map[p_node->get_owner()]=sidx;
+ }
+
+ nd.owner=FLAG_ID_IS_PATH|sidx;
+
+ }
+#endif
+
+
+ }
+
+ // Save the right type. If this node was created by an instance
+ // then flag that the node should not be created but reused
+ if (pack_state_stack.empty()) {
+ //this node is not part of an instancing process, so save the type
+ nd.type=_nm_get_string(p_node->get_type(),name_map);
+ } else {
+ // this node is part of an instanced process, so do not save the type.
+ // instead, save that it was instanced
+ nd.type=TYPE_INSTANCED;
+ }
+
+
+ // determine whether to save this node or not
+ // if this node is part of an instanced sub-scene, we can skip storing it if basically
+ // no properties changed and no groups were added to it.
+ // below condition is true for all nodes of the scene being saved, and ones in subscenes
+ // that hold changes
+
+ bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist
+ save_node = save_node || p_node==p_owner; // owner is always saved
+ save_node = save_node || (p_node->get_owner()==p_owner && instanced_by_owner); //part of scene and not instanced
+
int idx = nodes.size();
- node_map[p_node]=idx;
- nodes.push_back(nd);
+ int parent_node=NO_PARENT_SAVED;
+
+ if (save_node) {
+ //don't save the node if nothing and subscene
+
+ node_map[p_node]=idx;
+
+ //ok validate parent node
+ if (p_parent_idx==NO_PARENT_SAVED) {
+
+ int sidx;
+ if (nodepath_map.has(p_node->get_parent())) {
+ sidx=nodepath_map[p_node->get_parent()];
+ } else {
+ sidx=nodepath_map.size();
+ nodepath_map[p_node->get_parent()]=sidx;
+ }
+
+ nd.parent=FLAG_ID_IS_PATH|sidx;
+ } else {
+ nd.parent=p_parent_idx;
+ }
+
+ parent_node=idx;
+ nodes.push_back(nd);
+
+ }
for(int i=0;i<p_node->get_child_count();i++) {
Node *c=p_node->get_child(i);
- Error err = _parse_node(p_owner,c,idx,name_map,variant_map,node_map);
+ Error err = _parse_node(p_owner,c,parent_node,name_map,variant_map,node_map,nodepath_map);
if (err)
return err;
}
@@ -331,53 +673,135 @@ Error PackedScene::_parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<
}
-Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map) {
-
- if (p_node!=p_owner && (p_node->get_owner()!=p_owner))
- return OK; //nothing to do with this node, may either belong to another scene or be onowned
-
- List<MethodInfo> signals;
- p_node->get_signal_list(&signals);
+Error SceneState::_parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map) {
- ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
- NodeData &nd = nodes[node_map[p_node]];
- Set<Connection> instance_connections;
+ if (p_node!=p_owner && p_node->get_owner() && p_node->get_owner()!=p_owner && !p_owner->is_editable_instance(p_node->get_owner()))
+ return OK;
- if (nd.instance>=0) {
- Vector<Connection> iconns = p_node->get_instance_connections();
- for(int i=0;i<iconns.size();i++) {
+ List<MethodInfo> _signals;
+ p_node->get_signal_list(&_signals);
- instance_connections.insert(iconns[i]);
- }
- }
+ //ERR_FAIL_COND_V( !node_map.has(p_node), ERR_BUG);
+ //NodeData &nd = nodes[node_map[p_node]];
- for(List<MethodInfo>::Element *E=signals.front();E;E=E->next()) {
+ for(List<MethodInfo>::Element *E=_signals.front();E;E=E->next()) {
List<Node::Connection> conns;
p_node->get_signal_connection_list(E->get().name,&conns);
for(List<Node::Connection>::Element *F=conns.front();F;F=F->next()) {
const Node::Connection &c = F->get();
- if (!(c.flags&CONNECT_PERSIST))
+
+ if (!(c.flags&CONNECT_PERSIST)) //only persistent connections get saved
continue;
- if (nd.instance>=0 && instance_connections.has(c))
- continue; //came from instance, don't save!
+ // only connections that originate or end into main saved scene are saved
+ // everything else is discarded
Node *n=c.target->cast_to<Node>();
- if (!n)
+ if (!n) {
continue;
+ }
+
+ //source node is outside saved scene?
+ bool src_is_out = p_node!=p_owner && (p_node->get_filename()!=String() || p_node->get_owner()!=p_owner);
+ //target node is outside saved scene?
+ bool dst_is_out = n!=p_owner && (n->get_filename()!=String() || n->get_owner()!=p_owner);
- if (!node_map.has(n)) {
- WARN_PRINT("Connection to node outside scene??")
+ //if both are out, ignore connection
+ if (src_is_out && dst_is_out) {
continue;
}
+
+ {
+ Node *nl=p_node;
+
+ bool exists=false;
+
+ while(nl) {
+
+ if (nl==p_owner) {
+
+ Ref<SceneState> state = nl->get_scene_inherited_state();
+ if (state.is_valid()) {
+ int from_node = state->find_node_by_path(nl->get_path_to(p_node));
+ int to_node = state->find_node_by_path(nl->get_path_to(n));
+
+ if (from_node>=0 && to_node>=0) {
+ //this one has state for this node, save
+ if (state->is_connection(from_node,c.signal,to_node,c.method)) {
+ exists=true;
+ break;
+ }
+ }
+ }
+
+ nl=NULL;
+ } else {
+ if (nl->get_filename()!=String()) {
+ //is an instance
+ Ref<SceneState> state = nl->get_scene_instance_state();
+ if (state.is_valid()) {
+ int from_node = state->find_node_by_path(nl->get_path_to(p_node));
+ int to_node = state->find_node_by_path(nl->get_path_to(n));
+
+ if (from_node>=0 && to_node>=0) {
+ //this one has state for this node, save
+ if (state->is_connection(from_node,c.signal,to_node,c.method)) {
+ exists=true;
+ break;
+ }
+ }
+ }
+
+ }
+ nl=nl->get_owner();
+ }
+ }
+
+ if (exists) {
+ continue;
+ }
+
+ }
+
+
+ int src_id;
+
+ if (node_map.has(p_node)) {
+ src_id=node_map[p_node];
+ } else {
+ if (nodepath_map.has(p_node)) {
+ src_id=FLAG_ID_IS_PATH|nodepath_map[p_node];
+ } else {
+ int sidx=nodepath_map.size();
+ nodepath_map[p_node]=sidx;
+ src_id=FLAG_ID_IS_PATH|sidx;
+ }
+ }
+
+
+
+ int target_id;
+
+ if (node_map.has(n)) {
+ target_id=node_map[n];
+ } else {
+ if (nodepath_map.has(n)) {
+ target_id=FLAG_ID_IS_PATH|nodepath_map[n];
+ } else {
+ int sidx=nodepath_map.size();
+ nodepath_map[n]=sidx;
+ target_id=FLAG_ID_IS_PATH|sidx;
+ }
+ }
+
ConnectionData cd;
- cd.from=node_map[p_node];
- cd.to=node_map[n];
+ cd.from=src_id;
+ cd.to=target_id;
cd.method=_nm_get_string(c.method,name_map);
cd.signal=_nm_get_string(c.signal,name_map);
cd.flags=c.flags;
@@ -392,7 +816,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName
for(int i=0;i<p_node->get_child_count();i++) {
Node *c=p_node->get_child(i);
- Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map);
+ Error err = _parse_connections(p_owner,c,name_map,variant_map,node_map,nodepath_map);
if (err)
return err;
}
@@ -401,7 +825,7 @@ Error PackedScene::_parse_connections(Node *p_owner,Node *p_node, Map<StringName
}
-Error PackedScene::pack(Node *p_scene) {
+Error SceneState::pack(Node *p_scene) {
ERR_FAIL_NULL_V( p_scene, ERR_INVALID_PARAMETER );
@@ -412,14 +836,27 @@ Error PackedScene::pack(Node *p_scene) {
Map<StringName,int> name_map;
HashMap<Variant,int,VariantHasher> variant_map;
Map<Node*,int> node_map;
+ Map<Node*,int> nodepath_map;
+
+ //if using scene inheritance, pack the scene it inherits from
+ if (scene->get_scene_inherited_state().is_valid()) {
+ String path = scene->get_scene_inherited_state()->get_path();
+ Ref<PackedScene> instance = ResourceLoader::load(path);
+ if (instance.is_valid()) {
+
+ base_scene_idx=_vm_get_variant(instance,variant_map);
+ }
+ }
+ //instanced, only direct sub-scnes are supported of course
+
- Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map);
+ Error err = _parse_node(scene,scene,-1,name_map,variant_map,node_map,nodepath_map);
if (err) {
clear();
ERR_FAIL_V(err);
}
- err = _parse_connections(scene,scene,name_map,variant_map,node_map);
+ err = _parse_connections(scene,scene,name_map,variant_map,node_map,nodepath_map);
if (err) {
clear();
ERR_FAIL_V(err);
@@ -440,19 +877,177 @@ Error PackedScene::pack(Node *p_scene) {
variants[idx]=*K;
}
+ node_paths.resize(nodepath_map.size());
+ for(Map<Node*,int>::Element *E=nodepath_map.front();E;E=E->next()) {
+
+ node_paths[E->get()]=scene->get_path_to(E->key());
+ }
+
+
return OK;
}
-void PackedScene::clear() {
+void SceneState::set_path(const String &p_path) {
+
+ path=p_path;
+}
+
+String SceneState::get_path() const{
+
+ return path;
+}
+
+void SceneState::clear() {
names.clear();
variants.clear();
nodes.clear();
connections.clear();
+ node_path_cache.clear();
+ node_paths.clear();
+ editable_instances.clear();
+ base_scene_idx=-1;
}
-void PackedScene::_set_bundled_scene(const Dictionary& d) {
+Ref<SceneState> SceneState::_get_base_scene_state() const {
+
+ if (base_scene_idx>=0) {
+
+ Ref<PackedScene> ps = variants[base_scene_idx];
+ if (ps.is_valid()) {
+ return ps->get_state();
+ }
+ }
+
+ return Ref<SceneState>();
+}
+
+int SceneState::find_node_by_path(const NodePath& p_node) const {
+
+ if (!node_path_cache.has(p_node)) {
+ if (_get_base_scene_state().is_valid()) {
+ int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ if (idx>=0) {
+ if (!base_scene_node_remap.has(idx)) {
+ int ridx = nodes.size() + base_scene_node_remap.size();
+ base_scene_node_remap[ridx]=idx;
+ }
+
+ return base_scene_node_remap[idx];
+ }
+ }
+ return -1;
+ }
+
+ int nid = node_path_cache[p_node];
+
+ if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) {
+ //for nodes that _do_ exist in current scene, still try to look for
+ //the node in the instanced scene, as a property may be missing
+ //from the local one
+ int idx = _get_base_scene_state()->find_node_by_path(p_node);
+ base_scene_node_remap[nid]=idx;
+
+ }
+
+ return nid;
+}
+Variant SceneState::get_property_value(int p_node, const StringName& p_property, bool &found) const {
+
+ found=false;
+
+ ERR_FAIL_COND_V(p_node<0,Variant());
+
+ if (p_node<nodes.size()) {
+ //find in built-in nodes
+ int pc = nodes[p_node].properties.size();
+ const StringName* namep = names.ptr();
+
+ const NodeData::Property *p=nodes[p_node].properties.ptr();
+ for(int i=0;i<pc;i++) {
+ if (p_property==namep[p[i].name]) {
+ found=true;
+ return variants[p[i].value];
+ }
+ }
+ }
+
+ //property not found, try on instance
+
+ if (base_scene_node_remap.has(p_node)) {
+ return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node],p_property,found);
+ }
+
+ return Variant();
+}
+
+bool SceneState::is_node_in_group(int p_node,const StringName& p_group) const {
+
+ ERR_FAIL_COND_V(p_node<0,false);
+
+ if (p_node<nodes.size()) {
+ const StringName* namep = names.ptr();
+ for(int i=0;i<nodes[p_node].groups.size();i++) {
+ if (namep[nodes[p_node].groups[i]]==p_group)
+ return true;
+ }
+ }
+
+ if (base_scene_node_remap.has(p_node)) {
+ return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node],p_group);
+ }
+
+ return false;
+}
+
+bool SceneState::disable_placeholders=false;
+
+void SceneState::set_disable_placeholders(bool p_disable) {
+
+ disable_placeholders=p_disable;
+}
+
+bool SceneState::is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const {
+
+ ERR_FAIL_COND_V(p_node<0,false);
+ ERR_FAIL_COND_V(p_to_node<0,false);
+
+ if (p_node<nodes.size() && p_to_node<nodes.size()) {
+
+ int signal_idx=-1;
+ int method_idx=-1;
+ for(int i=0;i<names.size();i++) {
+ if (names[i]==p_signal) {
+ signal_idx=i;
+ } else if (names[i]==p_to_method) {
+ method_idx=i;
+ }
+ }
+
+ if (signal_idx>=0 && method_idx>=0) {
+ //signal and method strings are stored..
+
+ for(int i=0;i<connections.size();i++) {
+
+ if (connections[i].from==p_node && connections[i].to==p_to_node && connections[i].signal==signal_idx && connections[i].method==method_idx) {
+
+ return true;
+ }
+ }
+ }
+ }
+
+ if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) {
+ return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node],p_signal,base_scene_node_remap[p_to_node],p_to_method);
+ }
+
+ return false;
+
+}
+
+
+void SceneState::set_bundled_scene(const Dictionary& d) {
ERR_FAIL_COND( !d.has("names"));
@@ -463,6 +1058,15 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) {
ERR_FAIL_COND( !d.has("conns"));
// ERR_FAIL_COND( !d.has("path"));
+ int version=1;
+ if (d.has("version"))
+ version=d["version"];
+
+ if (version>PACK_VERSION) {
+ ERR_EXPLAIN("Save format version too new!");
+ ERR_FAIL();
+ }
+
DVector<String> snames = d["names"];
if (snames.size()) {
@@ -540,11 +1144,34 @@ void PackedScene::_set_bundled_scene(const Dictionary& d) {
}
+ Array np;
+ if (d.has("node_paths")) {
+ np=d["node_paths"];
+ }
+ node_paths.resize(np.size());
+ for(int i=0;i<np.size();i++) {
+ node_paths[i]=np[i];
+ }
+
+ Array ei;
+ if (d.has("editable_instances")) {
+ ei=d["editable_instances"];
+ }
+
+ if (d.has("base_scene")) {
+ base_scene_idx=d["base_scene"];
+ }
+
+ editable_instances.resize(ei.size());
+ for(int i=0;i<editable_instances.size();i++) {
+ editable_instances[i]=ei[i];
+ }
+
// path=d["path"];
}
-Dictionary PackedScene::_get_bundled_scene() const {
+Dictionary SceneState::get_bundled_scene() const {
DVector<String> rnames;
rnames.resize(names.size());
@@ -605,7 +1232,25 @@ Dictionary PackedScene::_get_bundled_scene() const {
}
d["conns"]=rconns;
- d["version"]=1;
+
+ Array rnode_paths;
+ rnode_paths.resize(node_paths.size());
+ for(int i=0;i<node_paths.size();i++) {
+ rnode_paths[i]=node_paths[i];
+ }
+ d["node_paths"]=rnode_paths;
+
+ Array reditable_instances;
+ reditable_instances.resize(editable_instances.size());
+ for(int i=0;i<editable_instances.size();i++) {
+ reditable_instances[i]=editable_instances[i];
+ }
+ d["editable_instances"]=reditable_instances;
+ if (base_scene_idx>=0) {
+ d["base_scene"]=base_scene_idx;
+ }
+
+ d["version"]=PACK_VERSION;
// d["path"]=path;
@@ -614,10 +1259,264 @@ Dictionary PackedScene::_get_bundled_scene() const {
}
+int SceneState::get_node_count() const {
+
+ return nodes.size();
+}
+
+StringName SceneState::get_node_type(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ if (nodes[p_idx].type==TYPE_INSTANCED)
+ return StringName();
+ return names[nodes[p_idx].type];
+}
+
+StringName SceneState::get_node_name(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ return names[nodes[p_idx].name];
+}
+
+Ref<PackedScene> SceneState::get_node_instance(int p_idx) const {
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Ref<PackedScene>());
+ if (nodes[p_idx].instance>=0) {
+ return variants[nodes[p_idx].instance];
+ } else if (nodes[p_idx].parent<=0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
+
+ if (base_scene_idx>=0) {
+ return variants[base_scene_idx];
+ }
+ }
+
+ return Ref<PackedScene>();
+
+
+}
+Vector<StringName> SceneState::get_node_groups(int p_idx) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Vector<StringName>());
+ Vector<StringName> groups;
+ for(int i=0;i<nodes[p_idx].groups.size();i++) {
+ groups.push_back(names[nodes[p_idx].groups[i]]);
+ }
+ return groups;
+}
+
+
+NodePath SceneState::get_node_path(int p_idx,bool p_for_parent) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
+
+ if (nodes[p_idx].parent<0 || nodes[p_idx].parent==NO_PARENT_SAVED) {
+ if (p_for_parent) {
+ return NodePath();
+ } else {
+ return NodePath(".");
+ }
+ }
+
+ Vector<StringName> sub_path;
+ NodePath base_path;
+ int nidx=p_idx;
+ while(true) {
+ if (nodes[nidx].parent==NO_PARENT_SAVED || nodes[nidx].parent<0) {
+
+ sub_path.insert(0,".");
+ break;
+ }
+
+ if (!p_for_parent || p_idx!=nidx) {
+ sub_path.insert(0,names[nodes[nidx].name]);
+ }
+
+ if (nodes[nidx].parent&FLAG_ID_IS_PATH) {
+ base_path=node_paths[nodes[nidx].parent&FLAG_MASK];
+ break;
+ } else {
+ nidx=nodes[nidx].parent&FLAG_MASK;
+ }
+ }
+
+ for(int i=0;i<base_path.get_name_count();i++) {
+ StringName sn = base_path.get_name(i);
+ sub_path.insert(0,base_path.get_name(i));
+ }
+
+ if (sub_path.empty()) {
+ return NodePath(".");
+ }
+
+ return NodePath(sub_path,false);
+
+}
+
+int SceneState::get_node_property_count(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),-1);
+ return nodes[p_idx].properties.size();
+
+}
+StringName SceneState::get_node_property_name(int p_idx,int p_prop) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),StringName());
+ ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),StringName());
+ return names[nodes[p_idx].properties[p_prop].name];
+
+}
+Variant SceneState::get_node_property_value(int p_idx,int p_prop) const{
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),Variant());
+ ERR_FAIL_INDEX_V(p_prop,nodes[p_idx].properties.size(),Variant());
+
+ return variants[nodes[p_idx].properties[p_prop].value];
+}
+
+
+NodePath SceneState::get_node_owner_path(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,nodes.size(),NodePath());
+ if (nodes[p_idx].owner<0 || nodes[p_idx].owner==NO_PARENT_SAVED)
+ return NodePath(); //root likely
+ if (nodes[p_idx].owner&FLAG_ID_IS_PATH) {
+ return node_paths[nodes[p_idx].owner&FLAG_MASK];
+ } else {
+ return get_node_path(nodes[p_idx].owner&FLAG_MASK);
+ }
+}
+
+int SceneState::get_connection_count() const {
+
+ return connections.size();
+}
+NodePath SceneState::get_connection_source(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
+ if (connections[p_idx].from&FLAG_ID_IS_PATH) {
+ return node_paths[connections[p_idx].from&FLAG_MASK];
+ } else {
+ return get_node_path(connections[p_idx].from&FLAG_MASK);
+ }
+
+}
+
+StringName SceneState::get_connection_signal(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
+ return names[connections[p_idx].signal];
+
+}
+NodePath SceneState::get_connection_target(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),NodePath());
+ if (connections[p_idx].to&FLAG_ID_IS_PATH) {
+ return node_paths[connections[p_idx].to&FLAG_MASK];
+ } else {
+ return get_node_path(connections[p_idx].to&FLAG_MASK);
+ }
+
+}
+StringName SceneState::get_connection_method(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),StringName());
+ return names[connections[p_idx].method];
+
+}
+int SceneState::get_connection_flags(int p_idx) const{
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
+ return connections[p_idx].flags;
+}
+
+Array SceneState::get_connection_binds(int p_idx) const {
+
+ ERR_FAIL_INDEX_V(p_idx,connections.size(),-1);
+ Array binds;
+ for(int i=0;i<connections[p_idx].binds.size();i++) {
+ binds.push_back(variants[connections[p_idx].binds[i]]);
+ }
+ return binds;
+}
+
+Vector<NodePath> SceneState::get_editable_instances() const {
+ return editable_instances;
+}
+
+
+SceneState::SceneState() {
+
+ base_scene_idx=-1;
+}
+
+
+////////////////
+
+
+
+void PackedScene::_set_bundled_scene(const Dictionary& d) {
+
+ state->set_bundled_scene(d);
+}
+
+Dictionary PackedScene::_get_bundled_scene() const {
+
+ return state->get_bundled_scene();
+}
+
+
+Error PackedScene::pack(Node *p_scene) {
+
+ return state->pack(p_scene);
+}
+
+void PackedScene::clear() {
+
+ state->clear();
+}
+
+bool PackedScene::can_instance() const {
+
+ return state->can_instance();
+}
+
+Node *PackedScene::instance(bool p_gen_edit_state) const {
+
+#ifndef TOOLS_ENABLED
+ if (p_gen_edit_state) {
+ ERR_EXPLAIN("Edit state is only for editors, does not work without tools compiled");
+ ERR_FAIL_COND_V(p_gen_edit_state,NULL);
+ }
+#endif
+
+ Node *s = state->instance(p_gen_edit_state);
+ if (!s)
+ return NULL;
+
+ if (p_gen_edit_state) {
+ s->set_scene_instance_state(state);
+ }
+
+ if (get_path()!="" && get_path().find("::")==-1)
+ s->set_filename(get_path());
+
+
+ s->notification(Node::NOTIFICATION_INSTANCED);
+
+ return s;
+}
+
+Ref<SceneState> PackedScene::get_state() {
+
+ return state;
+}
+
+void PackedScene::set_path(const String& p_path,bool p_take_over) {
+
+ state->set_path(p_path);
+ Resource::set_path(p_path,p_take_over);
+}
+
void PackedScene::_bind_methods() {
ObjectTypeDB::bind_method(_MD("pack","path:Node"),&PackedScene::pack);
- ObjectTypeDB::bind_method(_MD("instance:Node"),&PackedScene::instance,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("instance:Node","gen_edit_state"),&PackedScene::instance,DEFVAL(false));
ObjectTypeDB::bind_method(_MD("can_instance"),&PackedScene::can_instance);
ObjectTypeDB::bind_method(_MD("_set_bundled_scene"),&PackedScene::_set_bundled_scene);
ObjectTypeDB::bind_method(_MD("_get_bundled_scene"),&PackedScene::_get_bundled_scene);
@@ -628,5 +1527,6 @@ void PackedScene::_bind_methods() {
PackedScene::PackedScene() {
+ state = Ref<SceneState>( memnew( SceneState ));
}
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 0546addd3e..3956d2abe4 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -32,26 +32,27 @@
#include "resource.h"
#include "scene/main/node.h"
-//changes:
-//1-make the InstanceState a reference inside the resource that can be shared
-//2-make the instance "editable" with a flag, save here and load here, no need for property
-//3-properly save modifications in sub-scene
-//4-add scene inheritance
-//5-chain of instance states in editor? (to check what was modified)
-//6-saving will be hell
+class SceneState : public Reference {
-class PackedScene : public Resource {
-
- OBJ_TYPE( PackedScene, Resource );
- RES_BASE_EXTENSION("scn");
+ OBJ_TYPE( SceneState, Reference );
Vector<StringName> names;
Vector<Variant> variants;
+ Vector<NodePath> node_paths;
+ Vector<NodePath> editable_instances;
+ mutable HashMap<NodePath,int> node_path_cache;
+ mutable Map<int,int> base_scene_node_remap;
- //missing - instances
- //missing groups
- //missing - owner
- //missing - override names and values
+ int base_scene_idx;
+
+ enum {
+ FLAG_ID_IS_PATH=(1<<30),
+ FLAG_INSTANCE_IS_PLACEHOLDER=(1<<30),
+ FLAG_MASK=(1<<24)-1,
+ NO_PARENT_SAVED=0x7FFFFFFF,
+ TYPE_INSTANCED=0x7FFFFFFF,
+
+ };
struct NodeData {
@@ -68,9 +69,15 @@ class PackedScene : public Resource {
};
Vector<Property> properties;
- Vector<int> groups;
+ Vector<int> groups;
+
};
+ struct PackState {
+ Ref<SceneState> state;
+ int node;
+ PackState() { node=-1; }
+ };
Vector<NodeData> nodes;
@@ -86,16 +93,78 @@ class PackedScene : public Resource {
Vector<ConnectionData> connections;
- Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map);
- Error _parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map);
+ Error _parse_node(Node *p_owner,Node *p_node,int p_parent_idx, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map);
+ Error _parse_connections(Node *p_owner,Node *p_node, Map<StringName,int> &name_map,HashMap<Variant,int,VariantHasher> &variant_map,Map<Node*,int> &node_map,Map<Node*,int> &nodepath_map);
+
+ String path;
+
+ _FORCE_INLINE_ Ref<SceneState> _get_base_scene_state() const;
+
+ static bool disable_placeholders;
+public:
+
+ static void set_disable_placeholders(bool p_disable);
+
+ int find_node_by_path(const NodePath& p_node) const;
+ Variant get_property_value(int p_node,const StringName& p_property,bool &found) const;
+ bool is_node_in_group(int p_node,const StringName& p_group) const;
+ bool is_connection(int p_node,const StringName& p_signal,int p_to_node,const StringName& p_to_method) const;
+
+
+ void set_bundled_scene(const Dictionary& p_dictionary);
+ Dictionary get_bundled_scene() const;
+
+ Error pack(Node *p_scene);
+
+ void set_path(const String &p_path);
+ String get_path() const;
+
+ void clear();
+
+ bool can_instance() const;
+ Node *instance(bool p_gen_edit_state=false) const;
+
+
+ //build-unbuild API
+
+ int get_node_count() const;
+ StringName get_node_type(int p_idx) const;
+ StringName get_node_name(int p_idx) const;
+ NodePath get_node_path(int p_idx,bool p_for_parent=false) const;
+ NodePath get_node_owner_path(int p_idx) const;
+ Ref<PackedScene> get_node_instance(int p_idx) const;
+ Vector<StringName> get_node_groups(int p_idx) const;
+
+ int get_node_property_count(int p_idx) const;
+ StringName get_node_property_name(int p_idx,int p_prop) const;
+ Variant get_node_property_value(int p_idx,int p_prop) const;
+
+ int get_connection_count() const;
+ NodePath get_connection_source(int p_idx) const;
+ StringName get_connection_signal(int p_idx) const;
+ NodePath get_connection_target(int p_idx) const;
+ StringName get_connection_method(int p_idx) const;
+ int get_connection_flags(int p_idx) const;
+ Array get_connection_binds(int p_idx) const;
+
+ Vector<NodePath> get_editable_instances() const;
+
+ SceneState();
+};
+
+class PackedScene : public Resource {
+
+ OBJ_TYPE(PackedScene, Resource );
+ RES_BASE_EXTENSION("scn");
+
+ Ref<SceneState> state;
void _set_bundled_scene(const Dictionary& p_scene);
Dictionary _get_bundled_scene() const;
protected:
-
static void _bind_methods();
public:
@@ -107,7 +176,12 @@ public:
bool can_instance() const;
Node *instance(bool p_gen_edit_state=false) const;
+ virtual void set_path(const String& p_path,bool p_take_over=false);
+
+ Ref<SceneState> get_state();
+
PackedScene();
+
};
#endif // SCENE_PRELOADER_H
diff --git a/scene/resources/scene_format_text.cpp b/scene/resources/scene_format_text.cpp
new file mode 100644
index 0000000000..8403c06ad1
--- /dev/null
+++ b/scene/resources/scene_format_text.cpp
@@ -0,0 +1,792 @@
+#include "scene_format_text.h"
+
+#include "globals.h"
+#include "version.h"
+#include "os/dir_access.h"
+
+#define FORMAT_VERSION 1
+
+void ResourceFormatSaverTextInstance::write_property(const String& p_name,const Variant& p_property,bool *r_ok) {
+
+ if (r_ok)
+ *r_ok=false;
+
+ if (p_name!=String()) {
+ f->store_string(p_name+" = ");
+ }
+
+ switch( p_property.get_type() ) {
+
+ case Variant::NIL: {
+ f->store_string("null");
+ } break;
+ case Variant::BOOL: {
+
+ f->store_string(p_property.operator bool() ? "true":"false" );
+ } break;
+ case Variant::INT: {
+
+ f->store_string( itos(p_property.operator int()) );
+ } break;
+ case Variant::REAL: {
+
+ f->store_string( rtoss(p_property.operator real_t()) );
+ } break;
+ case Variant::STRING: {
+
+ String str=p_property;
+
+ str="\""+str.c_escape()+"\"";
+ f->store_string( str );
+ } break;
+ case Variant::VECTOR2: {
+
+ Vector2 v = p_property;
+ f->store_string("Vector2( "+rtoss(v.x) +", "+rtoss(v.y)+" )" );
+ } break;
+ case Variant::RECT2: {
+
+ Rect2 aabb = p_property;
+ f->store_string("Rect2( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y)+" )" );
+
+ } break;
+ case Variant::VECTOR3: {
+
+ Vector3 v = p_property;
+ f->store_string("Vector3( "+rtoss(v.x) +", "+rtoss(v.y)+", "+rtoss(v.z)+" )");
+ } break;
+ case Variant::PLANE: {
+
+ Plane p = p_property;
+ f->store_string("Plane( "+rtoss(p.normal.x) +", "+rtoss(p.normal.y)+", "+rtoss(p.normal.z)+", "+rtoss(p.d)+" )" );
+
+ } break;
+ case Variant::_AABB: {
+
+ AABB aabb = p_property;
+ f->store_string("AABB( "+rtoss(aabb.pos.x) +", "+rtoss(aabb.pos.y) +", "+rtoss(aabb.pos.z) +", "+rtoss(aabb.size.x) +", "+rtoss(aabb.size.y) +", "+rtoss(aabb.size.z)+" )" );
+
+ } break;
+ case Variant::QUAT: {
+
+ Quat quat = p_property;
+ f->store_string("Quat( "+rtoss(quat.x)+", "+rtoss(quat.y)+", "+rtoss(quat.z)+", "+rtoss(quat.w)+" )");
+
+ } break;
+ case Variant::MATRIX32: {
+
+ String s="Matrix32( ";
+ Matrix32 m3 = p_property;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<2;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ f->store_string(s+" )");
+
+ } break;
+ case Variant::MATRIX3: {
+
+ String s="Matrix3( ";
+ Matrix3 m3 = p_property;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<3;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ f->store_string(s+" )");
+
+ } break;
+ case Variant::TRANSFORM: {
+
+ String s="Transform( ";
+ Transform t = p_property;
+ Matrix3 &m3 = t.basis;
+ for (int i=0;i<3;i++) {
+ for (int j=0;j<3;j++) {
+
+ if (i!=0 || j!=0)
+ s+=", ";
+ s+=rtoss( m3.elements[i][j] );
+ }
+ }
+
+ s=s+", "+rtoss(t.origin.x) +", "+rtoss(t.origin.y)+", "+rtoss(t.origin.z);
+
+ f->store_string(s+" )");
+ } break;
+
+ // misc types
+ case Variant::COLOR: {
+
+ Color c = p_property;
+ f->store_string("Color( "+rtoss(c.r) +", "+rtoss(c.g)+", "+rtoss(c.b)+", "+rtoss(c.a)+" )");
+
+ } break;
+ case Variant::IMAGE: {
+
+
+ Image img=p_property;
+
+ if (img.empty()) {
+ f->store_string("RawImage()");
+ break;
+ }
+
+ String imgstr="RawImage( ";
+ imgstr+=itos(img.get_width());
+ imgstr+=", "+itos(img.get_height());
+ imgstr+=", "+itos(img.get_mipmaps());
+ imgstr+=", ";
+
+ switch(img.get_format()) {
+
+ case Image::FORMAT_GRAYSCALE: imgstr+="GRAYSCALE"; break;
+ case Image::FORMAT_INTENSITY: imgstr+="INTENSITY"; break;
+ case Image::FORMAT_GRAYSCALE_ALPHA: imgstr+="GRAYSCALE_ALPHA"; break;
+ case Image::FORMAT_RGB: imgstr+="RGB"; break;
+ case Image::FORMAT_RGBA: imgstr+="RGBA"; break;
+ case Image::FORMAT_INDEXED : imgstr+="INDEXED"; break;
+ case Image::FORMAT_INDEXED_ALPHA: imgstr+="INDEXED_ALPHA"; break;
+ case Image::FORMAT_BC1: imgstr+="BC1"; break;
+ case Image::FORMAT_BC2: imgstr+="BC2"; break;
+ case Image::FORMAT_BC3: imgstr+="BC3"; break;
+ case Image::FORMAT_BC4: imgstr+="BC4"; break;
+ case Image::FORMAT_BC5: imgstr+="BC5"; break;
+ case Image::FORMAT_PVRTC2: imgstr+="PVRTC2"; break;
+ case Image::FORMAT_PVRTC2_ALPHA: imgstr+="PVRTC2_ALPHA"; break;
+ case Image::FORMAT_PVRTC4: imgstr+="PVRTC4"; break;
+ case Image::FORMAT_PVRTC4_ALPHA: imgstr+="PVRTC4_ALPHA"; break;
+ case Image::FORMAT_ETC: imgstr+="ETC"; break;
+ case Image::FORMAT_ATC: imgstr+="ATC"; break;
+ case Image::FORMAT_ATC_ALPHA_EXPLICIT: imgstr+="ATC_ALPHA_EXPLICIT"; break;
+ case Image::FORMAT_ATC_ALPHA_INTERPOLATED: imgstr+="ATC_ALPHA_INTERPOLATED"; break;
+ case Image::FORMAT_CUSTOM: imgstr+="CUSTOM"; break;
+ default: {}
+ }
+
+
+ String s;
+
+ DVector<uint8_t> data = img.get_data();
+ int len = data.size();
+ DVector<uint8_t>::Read r = data.read();
+ const uint8_t *ptr=r.ptr();;
+ for (int i=0;i<len;i++) {
+
+ uint8_t byte = ptr[i];
+ const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char str[3]={ hex[byte>>4], hex[byte&0xF], 0};
+ s+=str;
+ }
+
+ imgstr+=", ";
+ f->store_string(imgstr);
+ f->store_string(s);
+ f->store_string(" )");
+ } break;
+ case Variant::NODE_PATH: {
+
+ String str=p_property;
+
+ str="NodePath(\""+str.c_escape()+"\")";
+ f->store_string(str);
+
+ } break;
+
+ case Variant::OBJECT: {
+
+ RES res = p_property;
+ if (res.is_null()) {
+ f->store_string("null");
+ if (r_ok)
+ *r_ok=true;
+
+ break; // don't save it
+ }
+
+ if (external_resources.has(res)) {
+
+ f->store_string("ExtResource( "+itos(external_resources[res]+1)+" )");
+ } else {
+
+ if (internal_resources.has(res)) {
+ f->store_string("SubResource( "+itos(internal_resources[res])+" )");
+ } else if (res->get_path().length() && res->get_path().find("::")==-1) {
+
+ //external resource
+ String path=relative_paths?local_path.path_to_file(res->get_path()):res->get_path();
+ f->store_string("Resource( \""+path+"\" )");
+ } else {
+ f->store_string("null");
+ ERR_EXPLAIN("Resource was not pre cached for the resource section, bug?");
+ ERR_BREAK(true);
+ //internal resource
+ }
+ }
+
+ } break;
+ case Variant::INPUT_EVENT: {
+
+ f->store_string("InputEvent()"); //will be added later
+ } break;
+ case Variant::DICTIONARY: {
+
+ Dictionary dict = p_property;
+
+ List<Variant> keys;
+ dict.get_key_list(&keys);
+ keys.sort();
+
+ f->store_string("{ ");
+ for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
+
+ //if (!_check_type(dict[E->get()]))
+ // continue;
+ bool ok;
+ write_property("",E->get(),&ok);
+ ERR_CONTINUE(!ok);
+
+ f->store_string(":");
+ write_property("",dict[E->get()],&ok);
+ if (!ok)
+ write_property("",Variant()); //at least make the file consistent..
+ if (E->next())
+ f->store_string(", ");
+ }
+
+
+ f->store_string(" }");
+
+
+ } break;
+ case Variant::ARRAY: {
+
+ f->store_string("[ ");
+ Array array = p_property;
+ int len=array.size();
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ write_property("",array[i]);
+
+
+ }
+ f->store_string(" ]");
+
+ } break;
+
+ case Variant::RAW_ARRAY: {
+
+ f->store_string("RawArray( ");
+ String s;
+ DVector<uint8_t> data = p_property;
+ int len = data.size();
+ DVector<uint8_t>::Read r = data.read();
+ const uint8_t *ptr=r.ptr();;
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ uint8_t byte = ptr[i];
+ const char hex[16]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
+ char str[3]={ hex[byte>>4], hex[byte&0xF], 0};
+ f->store_string(str);
+
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::INT_ARRAY: {
+
+ f->store_string("IntArray( ");
+ DVector<int> data = p_property;
+ int len = data.size();
+ DVector<int>::Read r = data.read();
+ const int *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+
+ f->store_string(itos(ptr[i]));
+ }
+
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::REAL_ARRAY: {
+
+ f->store_string("FloatArray( ");
+ DVector<real_t> data = p_property;
+ int len = data.size();
+ DVector<real_t>::Read r = data.read();
+ const real_t *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i]));
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::STRING_ARRAY: {
+
+ f->store_string("StringArray( ");
+ DVector<String> data = p_property;
+ int len = data.size();
+ DVector<String>::Read r = data.read();
+ const String *ptr=r.ptr();;
+ String s;
+ //write_string("\n");
+
+
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ String str=ptr[i];
+ f->store_string(""+str.c_escape()+"\"");
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::VECTOR2_ARRAY: {
+
+ f->store_string("Vector2Array( ");
+ DVector<Vector2> data = p_property;
+ int len = data.size();
+ DVector<Vector2>::Read r = data.read();
+ const Vector2 *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y) );
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::VECTOR3_ARRAY: {
+
+ f->store_string("Vector3Array( ");
+ DVector<Vector3> data = p_property;
+ int len = data.size();
+ DVector<Vector3>::Read r = data.read();
+ const Vector3 *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+ f->store_string(rtoss(ptr[i].x)+", "+rtoss(ptr[i].y)+", "+rtoss(ptr[i].z) );
+ }
+
+ f->store_string(" )");
+
+ } break;
+ case Variant::COLOR_ARRAY: {
+
+ f->store_string("ColorArray( ");
+
+ DVector<Color> data = p_property;
+ int len = data.size();
+ DVector<Color>::Read r = data.read();
+ const Color *ptr=r.ptr();;
+
+ for (int i=0;i<len;i++) {
+
+ if (i>0)
+ f->store_string(", ");
+
+ f->store_string(rtoss(ptr[i].r)+", "+rtoss(ptr[i].g)+", "+rtoss(ptr[i].b)+", "+rtoss(ptr[i].a) );
+
+ }
+ f->store_string(" )");
+
+ } break;
+ default: {}
+
+ }
+
+ if (r_ok)
+ *r_ok=true;
+
+}
+
+
+void ResourceFormatSaverTextInstance::_find_resources(const Variant& p_variant,bool p_main) {
+
+
+ switch(p_variant.get_type()) {
+ case Variant::OBJECT: {
+
+
+ RES res = p_variant.operator RefPtr();
+
+ if (res.is_null() || external_resources.has(res))
+ return;
+
+ if (!p_main && (!bundle_resources ) && res->get_path().length() && res->get_path().find("::") == -1 ) {
+ int index = external_resources.size();
+ external_resources[res]=index;
+ return;
+ }
+
+ if (resource_set.has(res))
+ return;
+
+ List<PropertyInfo> property_list;
+
+ res->get_property_list( &property_list );
+ property_list.sort();
+
+ List<PropertyInfo>::Element *I=property_list.front();
+
+ while(I) {
+
+ PropertyInfo pi=I->get();
+
+ if (pi.usage&PROPERTY_USAGE_STORAGE || (bundle_resources && pi.usage&PROPERTY_USAGE_BUNDLE)) {
+
+ Variant v=res->get(I->get().name);
+ _find_resources(v);
+ }
+
+ I=I->next();
+ }
+
+ resource_set.insert( res ); //saved after, so the childs it needs are available when loaded
+ saved_resources.push_back(res);
+
+ } break;
+ case Variant::ARRAY: {
+
+ Array varray=p_variant;
+ int len=varray.size();
+ for(int i=0;i<len;i++) {
+
+ Variant v=varray.get(i);
+ _find_resources(v);
+ }
+
+ } break;
+ case Variant::DICTIONARY: {
+
+ Dictionary d=p_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for(List<Variant>::Element *E=keys.front();E;E=E->next()) {
+
+ Variant v = d[E->get()];
+ _find_resources(v);
+ }
+ } break;
+ default: {}
+ }
+
+}
+
+
+
+Error ResourceFormatSaverTextInstance::save(const String &p_path,const RES& p_resource,uint32_t p_flags) {
+
+ if (p_path.ends_with(".tscn")) {
+ packed_scene=p_resource;
+ }
+
+ Error err;
+ f = FileAccess::open(p_path, FileAccess::WRITE,&err);
+ ERR_FAIL_COND_V( err, ERR_CANT_OPEN );
+ FileAccessRef _fref(f);
+
+ local_path = Globals::get_singleton()->localize_path(p_path);
+
+ relative_paths=p_flags&ResourceSaver::FLAG_RELATIVE_PATHS;
+ skip_editor=p_flags&ResourceSaver::FLAG_OMIT_EDITOR_PROPERTIES;
+ bundle_resources=p_flags&ResourceSaver::FLAG_BUNDLE_RESOURCES;
+ takeover_paths=p_flags&ResourceSaver::FLAG_REPLACE_SUBRESOURCE_PATHS;
+ if (!p_path.begins_with("res://")) {
+ takeover_paths=false;
+ }
+
+ // save resources
+ _find_resources(p_resource,true);
+
+ if (packed_scene.is_valid()) {
+ //add instances to external resources if saving a packed scene
+ for(int i=0;i<packed_scene->get_state()->get_node_count();i++) {
+ Ref<PackedScene> instance=packed_scene->get_state()->get_node_instance(i);
+ if (instance.is_valid() && !external_resources.has(instance)) {
+ int index = external_resources.size();
+ external_resources[instance]=index;
+ }
+ }
+ }
+
+
+ ERR_FAIL_COND_V(err!=OK,err);
+
+ {
+ String title=packed_scene.is_valid()?"[gd_scene ":"[gd_resource ";
+ if (packed_scene.is_null())
+ title+="type=\""+p_resource->get_type()+"\" ";
+ int load_steps=saved_resources.size()+external_resources.size();
+ //if (packed_scene.is_valid()) {
+ // load_steps+=packed_scene->get_node_count();
+ //}
+ //no, better to not use load steps from nodes, no point to that
+
+ if (load_steps>1) {
+ title+="load_steps="+itos(load_steps)+" ";
+ }
+ title+="format="+itos(FORMAT_VERSION)+"";
+ //title+="engine_version=\""+itos(VERSION_MAJOR)+"."+itos(VERSION_MINOR)+"\"";
+
+ f->store_string(title);
+ f->store_line("]\n"); //one empty line
+ }
+
+
+ for(Map<RES,int>::Element *E=external_resources.front();E;E=E->next()) {
+
+ String p = E->key()->get_path();
+
+ f->store_string("[ext_resource path=\""+p+"\" type=\""+E->key()->get_save_type()+"\" id="+itos(E->get()+1)+"]\n"); //bundled
+ }
+
+ if (external_resources.size())
+ f->store_line(String()); //separate
+
+ Set<int> used_indices;
+
+ for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) {
+
+ RES res = E->get();
+ if (E->next() && (res->get_path()=="" || res->get_path().find("::") != -1 )) {
+
+ if (res->get_subindex()!=0) {
+ if (used_indices.has(res->get_subindex())) {
+ res->set_subindex(0); //repeated
+ } else {
+ used_indices.insert(res->get_subindex());
+ }
+ }
+ }
+ }
+
+ for(List<RES>::Element *E=saved_resources.front();E;E=E->next()) {
+
+ RES res = E->get();
+ ERR_CONTINUE(!resource_set.has(res));
+ bool main = (E->next()==NULL);
+
+ if (main && packed_scene.is_valid())
+ break; //save as a scene
+
+ if (main) {
+ f->store_line("[resource]\n");
+ } else {
+ String line="[sub_resource ";
+ if (res->get_subindex()==0) {
+ int new_subindex=1;
+ if (used_indices.size()) {
+ new_subindex=used_indices.back()->get()+1;
+ }
+
+ res->set_subindex(new_subindex);
+ used_indices.insert(new_subindex);
+ }
+
+ int idx = res->get_subindex();
+ line+="type=\""+res->get_type()+"\" id="+itos(idx);
+ f->store_line(line+"]\n");
+ if (takeover_paths) {
+ res->set_path(p_path+"::"+itos(idx),true);
+ }
+
+ internal_resources[res]=idx;
+
+ }
+
+
+ List<PropertyInfo> property_list;
+ res->get_property_list(&property_list);
+// property_list.sort();
+ for(List<PropertyInfo>::Element *PE = property_list.front();PE;PE=PE->next()) {
+
+
+ if (skip_editor && PE->get().name.begins_with("__editor"))
+ continue;
+
+ if (PE->get().usage&PROPERTY_USAGE_STORAGE || (bundle_resources && PE->get().usage&PROPERTY_USAGE_BUNDLE)) {
+
+ String name = PE->get().name;
+ Variant value = res->get(name);
+
+
+ if ((PE->get().usage&PROPERTY_USAGE_STORE_IF_NONZERO && value.is_zero())||(PE->get().usage&PROPERTY_USAGE_STORE_IF_NONONE && value.is_one()) )
+ continue;
+
+ if (PE->get().type==Variant::OBJECT && value.is_zero())
+ continue;
+
+ write_property(name,value);
+ f->store_string("\n");
+ }
+
+
+ }
+
+ f->store_string("\n");
+
+ }
+
+ if (packed_scene.is_valid()) {
+ //if this is a scene, save nodes and connections!
+ Ref<SceneState> state = packed_scene->get_state();
+ for(int i=0;i<state->get_node_count();i++) {
+
+ StringName type = state->get_node_type(i);
+ StringName name = state->get_node_name(i);
+ NodePath path = state->get_node_path(i,true);
+ NodePath owner = state->get_node_owner_path(i);
+ Ref<PackedScene> instance = state->get_node_instance(i);
+ Vector<StringName> groups = state->get_node_groups(i);
+
+ String header="[node";
+ header+=" name=\""+String(name)+"\"";
+ if (type!=StringName()) {
+ header+=" type=\""+String(type)+"\"";
+ }
+ if (path!=NodePath()) {
+ header+=" parent=\""+String(path.simplified())+"\"";
+ }
+ if (owner!=NodePath() && owner!=NodePath(".")) {
+ header+=" owner=\""+String(owner.simplified())+"\"";
+ }
+
+ if (groups.size()) {
+ String sgroups=" groups=[ ";
+ for(int j=0;j<groups.size();j++) {
+ if (j>0)
+ sgroups+=", ";
+ sgroups+="\""+groups[i].operator String().c_escape()+"\"";
+ }
+ sgroups+=" ]";
+ header+=sgroups;
+ }
+
+ f->store_string(header);
+
+ if (instance.is_valid()) {
+ f->store_string(" instance=");
+ write_property("",instance);
+ }
+
+ f->store_line("]\n");
+
+ for(int j=0;j<state->get_node_property_count(i);j++) {
+
+ write_property(state->get_node_property_name(i,j),state->get_node_property_value(i,j));
+ f->store_line(String());
+
+ }
+
+ if (state->get_node_property_count(i)) {
+ //add space
+ f->store_line(String());
+ }
+
+ }
+
+ for(int i=0;i<state->get_connection_count();i++) {
+
+ String connstr="[connection";
+ connstr+=" signal=\""+String(state->get_connection_signal(i))+"\"";
+ connstr+=" from=\""+String(state->get_connection_source(i).simplified())+"\"";
+ connstr+=" to=\""+String(state->get_connection_target(i).simplified())+"\"";
+ connstr+=" method=\""+String(state->get_connection_method(i))+"\"";
+ int flags = state->get_connection_flags(i);
+ if (flags!=Object::CONNECT_PERSIST) {
+ connstr+=" flags="+itos(flags);
+ }
+
+ Array binds=state->get_connection_binds(i);
+ f->store_string(connstr);
+ if (binds.size()) {
+ f->store_string(" binds=");
+ write_property("",binds);
+ }
+
+ f->store_line("]\n");
+ }
+
+ f->store_line(String());
+
+ Vector<NodePath> editable_instances = state->get_editable_instances();
+ for(int i=0;i<editable_instances.size();i++) {
+ f->store_line("[editable path=\""+editable_instances[i].operator String()+"\"]");
+ }
+ }
+
+ if (f->get_error()!=OK && f->get_error()!=ERR_FILE_EOF) {
+ f->close();
+ return ERR_CANT_CREATE;
+ }
+
+ f->close();
+ //memdelete(f);
+
+ return OK;
+}
+
+
+
+Error ResourceFormatSaverText::save(const String &p_path,const RES& p_resource,uint32_t p_flags) {
+
+ if (p_path.ends_with(".sct") && p_resource->get_type()!="PackedScene") {
+ return ERR_FILE_UNRECOGNIZED;
+ }
+
+ ResourceFormatSaverTextInstance saver;
+ return saver.save(p_path,p_resource,p_flags);
+
+}
+
+bool ResourceFormatSaverText::recognize(const RES& p_resource) const {
+
+
+ return true; // all recognized!
+}
+void ResourceFormatSaverText::get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const {
+
+ p_extensions->push_back("tres"); //text resource
+ if (p_resource->get_type()=="PackedScene")
+ p_extensions->push_back("tscn"); //text scene
+
+}
+
+ResourceFormatSaverText* ResourceFormatSaverText::singleton=NULL;
+ResourceFormatSaverText::ResourceFormatSaverText() {
+ singleton=this;
+}
diff --git a/scene/resources/scene_format_text.h b/scene/resources/scene_format_text.h
new file mode 100644
index 0000000000..576a78d183
--- /dev/null
+++ b/scene/resources/scene_format_text.h
@@ -0,0 +1,46 @@
+#ifndef SCENE_FORMAT_TEXT_H
+#define SCENE_FORMAT_TEXT_H
+
+#include "io/resource_loader.h"
+#include "io/resource_saver.h"
+#include "os/file_access.h"
+#include "scene/resources/packed_scene.h"
+
+class ResourceFormatSaverTextInstance {
+
+ String local_path;
+
+ Ref<PackedScene> packed_scene;
+
+ bool takeover_paths;
+ bool relative_paths;
+ bool bundle_resources;
+ bool skip_editor;
+ FileAccess *f;
+ Set<RES> resource_set;
+ List<RES> saved_resources;
+ Map<RES,int> external_resources;
+ Map<RES,int> internal_resources;
+
+ void _find_resources(const Variant& p_variant,bool p_main=false);
+ void write_property(const String& p_name,const Variant& p_property,bool *r_ok=NULL);
+
+public:
+
+ Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
+
+
+};
+
+class ResourceFormatSaverText : public ResourceFormatSaver {
+public:
+ static ResourceFormatSaverText* singleton;
+ virtual Error save(const String &p_path,const RES& p_resource,uint32_t p_flags=0);
+ virtual bool recognize(const RES& p_resource) const;
+ virtual void get_recognized_extensions(const RES& p_resource,List<String> *p_extensions) const;
+
+ ResourceFormatSaverText();
+};
+
+
+#endif // SCENE_FORMAT_TEXT_H
diff --git a/tools/editor/editor_node.cpp b/tools/editor/editor_node.cpp
index 4f5755bd3d..9137503e1b 100644
--- a/tools/editor/editor_node.cpp
+++ b/tools/editor/editor_node.cpp
@@ -1108,6 +1108,11 @@ void EditorNode::_dialog_action(String p_file) {
push_item(res.operator->() );
} break;
+ case FILE_NEW_INHERITED_SCENE: {
+
+
+ load_scene(p_file,false,true);
+ } break;
case FILE_OPEN_SCENE: {
@@ -1931,6 +1936,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
} break;
+ case FILE_NEW_INHERITED_SCENE:
case FILE_OPEN_SCENE: {
@@ -1951,7 +1957,7 @@ void EditorNode::_menu_option_confirm(int p_option,bool p_confirmed) {
if (scene) {
file->set_current_path(scene->get_filename());
};
- file->set_title("Open Scene");
+ file->set_title(p_option==FILE_OPEN_SCENE?"Open Scene":"Open Base Scene");
file->popup_centered_ratio();
} break;
@@ -3312,7 +3318,7 @@ void EditorNode::fix_dependencies(const String& p_for_file) {
dependency_fixer->edit(p_for_file);
}
-Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) {
+Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps,bool p_set_inherited) {
if (!is_inside_tree()) {
defer_load_scene = p_scene;
@@ -3442,6 +3448,13 @@ Error EditorNode::load_scene(const String& p_scene, bool p_ignore_broken_deps) {
}
*/
+ if (p_set_inherited) {
+ Ref<SceneState> state = sdata->get_state();
+ state->set_path(lpath);
+ new_scene->set_scene_inherited_state(state);
+ }
+
+
set_edited_scene(new_scene);
_get_scene_metadata();
/*
@@ -4404,6 +4417,7 @@ void EditorNode::_scene_tab_changed(int p_tab) {
EditorNode::EditorNode() {
EditorHelp::generate_doc(); //before any editor classes are crated
+ SceneState::set_disable_placeholders(true);
InputDefault *id = Input::get_singleton()->cast_to<InputDefault>();
@@ -4794,6 +4808,7 @@ EditorNode::EditorNode() {
file_menu->set_tooltip("Operations with scene files.");
p=file_menu->get_popup();
p->add_item("New Scene",FILE_NEW_SCENE);
+ p->add_item("New Inherited Scene..",FILE_NEW_INHERITED_SCENE);
p->add_item("Open Scene..",FILE_OPEN_SCENE,KEY_MASK_CMD+KEY_O);
p->add_separator();
p->add_item("Save Scene",FILE_SAVE_SCENE,KEY_MASK_CMD+KEY_S);
diff --git a/tools/editor/editor_node.h b/tools/editor/editor_node.h
index 53e16d9c58..d52e08191d 100644
--- a/tools/editor/editor_node.h
+++ b/tools/editor/editor_node.h
@@ -107,6 +107,7 @@ class EditorNode : public Node {
enum MenuOptions {
FILE_NEW_SCENE,
+ FILE_NEW_INHERITED_SCENE,
FILE_OPEN_SCENE,
FILE_SAVE_SCENE,
FILE_SAVE_AS_SCENE,
@@ -565,7 +566,7 @@ public:
void fix_dependencies(const String& p_for_file);
void clear_scene() { _cleanup_scene(); }
- Error load_scene(const String& p_scene,bool p_ignore_broken_deps=false);
+ Error load_scene(const String& p_scene, bool p_ignore_broken_deps=false, bool p_set_inherited=false);
Error load_resource(const String& p_scene);
bool is_scene_open(const String& p_path);
diff --git a/tools/editor/property_editor.cpp b/tools/editor/property_editor.cpp
index b513e32c13..1a07f30e4e 100644
--- a/tools/editor/property_editor.cpp
+++ b/tools/editor/property_editor.cpp
@@ -42,6 +42,8 @@
#include "multi_node_edit.h"
#include "array_property_edit.h"
#include "editor_help.h"
+#include "scene/resources/packed_scene.h"
+
void CustomPropertyEditor::_notification(int p_what) {
@@ -1655,25 +1657,101 @@ CustomPropertyEditor::CustomPropertyEditor() {
menu->connect("item_pressed",this,"_menu_option");
}
+bool PropertyEditor::_might_be_in_instance() {
-
-Node *PropertyEditor::get_instanced_node() {
-
- //this sucks badly
if (!obj)
return NULL;
Node *node = obj->cast_to<Node>();
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool might_be=false;
+
+ while(node) {
+
+ if (node->get_scene_instance_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ if (node==edited_scene) {
+ if (node->get_scene_inherited_state().is_valid()) {
+ might_be=true;
+ break;
+ }
+ might_be=false;
+ break;
+ }
+ node=node->get_owner();
+ }
+
+ return might_be;
+
+}
+
+bool PropertyEditor::_get_instanced_node_original_property(const StringName& p_prop,Variant& value) {
+
+ Node *node = obj->cast_to<Node>();
+
if (!node)
- return NULL;
+ return false;
- if (node->get_filename()=="")
- return NULL;
+ Node *orig=node;
+
+ Node* edited_scene =EditorNode::get_singleton()->get_edited_scene();
+
+ bool found=false;
- if (!node->get_owner())
- return NULL; //scene root i guess
+// print_line("for prop - "+String(p_prop));
- return node;
+ while(node) {
+
+ Ref<SceneState> ss;
+
+ if (node==edited_scene) {
+ ss=node->get_scene_inherited_state();
+ } else {
+ ss=node->get_scene_instance_state();
+ }
+// print_line("at - "+String(edited_scene->get_path_to(node)));
+
+ if (ss.is_valid()) {
+ NodePath np = node->get_path_to(orig);
+ int node_idx = ss->find_node_by_path(np);
+// print_line("\t valid, nodeidx "+itos(node_idx));
+ if (node_idx>=0) {
+ bool lfound=false;
+ Variant lvar;
+ lvar=ss->get_property_value(node_idx,p_prop,lfound);
+ if (lfound) {
+ found=true;
+ value=lvar;
+// print_line("\t found value "+String(value));
+ }
+ }
+ }
+ if (node==edited_scene) {
+ //just in case
+ break;
+ }
+ node=node->get_owner();
+
+ }
+
+ return found;
+}
+
+bool PropertyEditor::_is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage) {
+
+ if (p_orig.get_type()==Variant::NIL) {
+ //special cases
+ if (p_current.is_zero() && p_usage&PROPERTY_USAGE_STORE_IF_NONZERO)
+ return false;
+ if (p_current.is_one() && p_usage&PROPERTY_USAGE_STORE_IF_NONONE)
+ return false;
+ }
+
+ return bool(Variant::evaluate(Variant::OP_NOT_EQUAL,p_current,p_orig));
}
TreeItem *PropertyEditor::find_item(TreeItem *p_item,const String& p_name) {
@@ -1910,6 +1988,8 @@ void PropertyEditor::set_item_text(TreeItem *p_item, int p_type, const String& p
}
+
+
void PropertyEditor::_notification(int p_what) {
if (p_what==NOTIFICATION_ENTER_TREE) {
@@ -1950,12 +2030,16 @@ void PropertyEditor::_notification(int p_what) {
if (!item)
continue;
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
+
+
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(*k)) {
+
+ if (_get_instanced_node_original_property(*k,vorig) || usage) {
Variant v = obj->get(*k);
- Variant vorig = d[*k];
int found=-1;
for(int i=0;i<item->get_button_count(1);i++) {
@@ -1966,7 +2050,7 @@ void PropertyEditor::_notification(int p_what) {
}
}
- bool changed = ! (v==vorig);
+ bool changed = _is_property_different(v,vorig,usage);
if ((found!=-1)!=changed) {
@@ -2049,12 +2133,14 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) {
if (name!=String()) {
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
+
+ Variant vorig;
+ Dictionary d=p_item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(name)) {
+ if (_get_instanced_node_original_property(name,vorig) || usage) {
Variant v = obj->get(name);
- Variant vorig = d[name];
int found=-1;
for(int i=0;i<p_item->get_button_count(1);i++) {
@@ -2065,7 +2151,7 @@ void PropertyEditor::_refresh_item(TreeItem *p_item) {
}
}
- bool changed = ! (v==vorig);
+ bool changed = _is_property_different(v,vorig,usage);
if ((found!=-1)!=changed) {
@@ -2326,7 +2412,8 @@ void PropertyEditor::update_tree() {
d["type"]=(int)p.type;
d["hint"]=(int)p.hint;
d["hint_text"]=p.hint_string;
-
+ d["usage"]=(int)p.usage;
+
item->set_metadata( 0, d );
item->set_metadata( 1, p.name );
@@ -2777,14 +2864,17 @@ void PropertyEditor::update_tree() {
}
}
- if (get_instanced_node()) {
+ if (_might_be_in_instance()) {
- Dictionary d = get_instanced_node()->get_instance_state();
- if (d.has(p.name)) {
+ Variant vorig;
+ Dictionary d=item->get_metadata(0);
+ int usage = d.has("usage")?int(int(d["usage"])&(PROPERTY_USAGE_STORE_IF_NONONE|PROPERTY_USAGE_STORE_IF_NONZERO)):0;
+ if (_get_instanced_node_original_property(p.name,vorig) || usage) {
Variant v = obj->get(p.name);
- Variant vorig = d[p.name];
- if (! (v==vorig)) {
+
+ if (_is_property_different(v,vorig,usage)) {
+ //print_line("FOR "+String(p.name)+" RELOAD WITH: "+String(v)+"("+Variant::get_type_name(v.get_type())+")=="+String(vorig)+"("+Variant::get_type_name(vorig.get_type())+")");
item->add_button(1,get_icon("Reload","EditorIcons"),3);
}
}
@@ -3081,17 +3171,18 @@ void PropertyEditor::_edit_button(Object *p_item, int p_column, int p_button) {
call_deferred("_set_range_def",ti,prop,ti->get_range(p_column)+1.0);
} else if (p_button==3) {
- if (!get_instanced_node())
+ if (!_might_be_in_instance())
return;
if (!d.has("name"))
return;
String prop=d["name"];
- Dictionary d2 = get_instanced_node()->get_instance_state();
- if (d2.has(prop)) {
+ Variant vorig;
+
+ if (_get_instanced_node_original_property(prop,vorig)) {
- _edit_set(prop,d2[prop]);
+ _edit_set(prop,vorig);
}
} else {
diff --git a/tools/editor/property_editor.h b/tools/editor/property_editor.h
index 36ecc794ed..9686bf0bd7 100644
--- a/tools/editor/property_editor.h
+++ b/tools/editor/property_editor.h
@@ -121,6 +121,7 @@ class CustomPropertyEditor : public Popup {
void show_value_editors(int p_amount);
void config_value_editors(int p_amount, int p_columns,int p_label_w,const List<String>& p_strings);
void config_action_buttons(const List<String>& p_strings);
+
protected:
void _notification(int p_what);
@@ -184,13 +185,17 @@ class PropertyEditor : public Control {
virtual void _changed_callback(Object *p_changed,const char * p_what);
virtual void _changed_callbacks(Object *p_changed,const String& p_callback);
+
void _edit_button(Object *p_item, int p_column, int p_button);
void _node_removed(Node *p_node);
void _edit_set(const String& p_name, const Variant& p_value);
void _draw_flags(Object *ti,const Rect2& p_rect);
- Node *get_instanced_node();
+ bool _might_be_in_instance();
+ bool _get_instanced_node_original_property(const StringName& p_prop,Variant& value);
+ bool _is_property_different(const Variant& p_current, const Variant& p_orig,int p_usage=0);
+
void _refresh_item(TreeItem *p_item);
void _set_range_def(Object *p_item, String prop, float p_frame);
diff --git a/tools/editor/reparent_dialog.cpp b/tools/editor/reparent_dialog.cpp
index 6d0c5b867e..78ba47d54b 100644
--- a/tools/editor/reparent_dialog.cpp
+++ b/tools/editor/reparent_dialog.cpp
@@ -103,6 +103,7 @@ ReparentDialog::ReparentDialog() {
add_child(node_only);
node_only->hide();
+ tree->set_show_enabled_subscene(true);
//vbc->add_margin_child("Options:",node_only);;
diff --git a/tools/editor/scene_tree_dock.cpp b/tools/editor/scene_tree_dock.cpp
index 276f2dea33..920ab599e9 100644
--- a/tools/editor/scene_tree_dock.cpp
+++ b/tools/editor/scene_tree_dock.cpp
@@ -36,8 +36,8 @@
#include "script_editor_debugger.h"
#include "tools/editor/plugins/script_editor_plugin.h"
#include "multi_node_edit.h"
-void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
+void SceneTreeDock::_unhandled_key_input(InputEvent p_event) {
uint32_t sc = p_event.key.get_scancode_with_modifiers();
if (!p_event.key.pressed || p_event.key.echo)
@@ -71,7 +71,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
Node*instanced_scene=NULL;
Ref<PackedScene> sdata = ResourceLoader::load(p_file);
if (sdata.is_valid())
- instanced_scene=sdata->instance();
+ instanced_scene=sdata->instance(true);
if (!instanced_scene) {
@@ -96,7 +96,7 @@ Node* SceneTreeDock::instance(const String& p_file) {
}
}
- instanced_scene->generate_instance_state();
+ //instanced_scene->generate_instance_state();
instanced_scene->set_filename( Globals::get_singleton()->localize_path(p_file) );
editor_data->get_undo_redo().create_action("Instance Scene");
@@ -158,8 +158,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
case TOOL_NEW: {
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
create_dialog->popup_centered_ratio();
} break;
case TOOL_INSTANCE: {
@@ -176,8 +176,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
break;
}
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
file->set_mode(EditorFileDialog::MODE_OPEN_FILE);
List<String> extensions;
@@ -202,8 +202,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
connect_dialog->popup_centered_ratio();
connect_dialog->set_node(current);
@@ -213,8 +213,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
Node *current = scene_tree->get_selected();
if (!current)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
groups_editor->set_current(current);
groups_editor->popup_centered_ratio();
} break;
@@ -224,8 +224,8 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) {
if (!selected)
break;
- if (!_validate_no_foreign())
- break;
+ //if (!_validate_no_foreign())
+ // break;
Ref<Script> existing = selected->get_script();
if (existing.is_valid())
@@ -573,9 +573,9 @@ Node *SceneTreeDock::_duplicate(Node *p_node, Map<Node*,Node*> &duplimap) {
Ref<PackedScene> sd = ResourceLoader::load( p_node->get_filename() );
ERR_FAIL_COND_V(!sd.is_valid(),NULL);
- node = sd->instance();
+ node = sd->instance(true);
ERR_FAIL_COND_V(!node,NULL);
- node->generate_instance_state();
+ //node->generate_instance_state();
} else {
Object *obj = ObjectTypeDB::instance(p_node->get_type());
ERR_FAIL_COND_V(!obj,NULL);
@@ -874,6 +874,16 @@ bool SceneTreeDock::_validate_no_foreign() {
return false;
}
+
+ if (edited_scene->get_scene_instance_state().is_valid() && edited_scene->get_scene_instance_state()->find_node_by_path(edited_scene->get_path_to(E->get()))>=0) {
+
+ accept->get_ok()->set_text("Makes Sense!");
+ accept->set_text("Can't operate on nodes the current scene inherits from!");
+ accept->popup_centered_minsize();
+ return false;
+
+ }
+
}
return true;
@@ -1078,14 +1088,15 @@ void SceneTreeDock::_delete_confirm() {
void SceneTreeDock::_update_tool_buttons() {
Node *sel = scene_tree->get_selected();
- bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene);
+ bool disable = !sel || (sel!=edited_scene && sel->get_owner()!=edited_scene) || (edited_scene->get_scene_instance_state().is_valid() && edited_scene->get_scene_instance_state()->find_node_by_path(edited_scene->get_path_to(sel))>=0);
bool disable_root = disable || sel->get_parent()==scene_root;
+ bool disable_edit = !sel;
- tool_buttons[TOOL_INSTANCE]->set_disabled(disable);
+ tool_buttons[TOOL_INSTANCE]->set_disabled(disable_edit);
tool_buttons[TOOL_REPLACE]->set_disabled(disable);
- tool_buttons[TOOL_CONNECT]->set_disabled(disable);
- tool_buttons[TOOL_GROUP]->set_disabled(disable);
- tool_buttons[TOOL_SCRIPT]->set_disabled(disable);
+ tool_buttons[TOOL_CONNECT]->set_disabled(disable_edit);
+ tool_buttons[TOOL_GROUP]->set_disabled(disable_edit);
+ tool_buttons[TOOL_SCRIPT]->set_disabled(disable_edit);
tool_buttons[TOOL_MOVE_UP]->set_disabled(disable_root);
tool_buttons[TOOL_MOVE_DOWN]->set_disabled(disable_root);
tool_buttons[TOOL_DUPLICATE]->set_disabled(disable_root);
diff --git a/tools/editor/scene_tree_editor.cpp b/tools/editor/scene_tree_editor.cpp
index fd841028a2..b746af7f0e 100644
--- a/tools/editor/scene_tree_editor.cpp
+++ b/tools/editor/scene_tree_editor.cpp
@@ -34,6 +34,8 @@
#include "scene/main/viewport.h"
#include "tools/editor/plugins/canvas_item_editor_plugin.h"
+#include "scene/resources/packed_scene.h"
+
Node *SceneTreeEditor::get_scene_node() {
ERR_FAIL_COND_V(!is_inside_tree(),NULL);
@@ -57,22 +59,58 @@ void SceneTreeEditor::_subscene_option(int p_idx) {
switch(p_idx) {
- case SCENE_MENU_SHOW_CHILDREN: {
+ case SCENE_MENU_EDITABLE_CHILDREN: {
- if (node->has_meta("__editor_show_subtree")) {
- instance_menu->set_item_checked(0,true);
- node->set_meta("__editor_show_subtree",Variant());
- _update_tree();
- } else {
- node->set_meta("__editor_show_subtree",true);
- _update_tree();
+ bool editable = EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(node);
+ editable = !editable;
+
+ //node->set_instance_children_editable(editable);
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,editable);
+ instance_menu->set_item_checked(0,editable);
+ if (editable) {
+ node->set_scene_instance_load_placeholder(false);
+ instance_menu->set_item_checked(1,false);
+ }
+
+ _update_tree();
+
+ } break;
+ case SCENE_MENU_USE_PLACEHOLDER: {
+
+ bool placeholder = node->get_scene_instance_load_placeholder();
+ placeholder = !placeholder;
+
+ //node->set_instance_children_editable(editable);
+ if (placeholder) {
+ EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(node,false);
}
+ node->set_scene_instance_load_placeholder(placeholder);
+ instance_menu->set_item_checked(0,false);
+ instance_menu->set_item_checked(1,placeholder);
+
+ _update_tree();
} break;
case SCENE_MENU_OPEN: {
emit_signal("open",node->get_filename());
} break;
+ case SCENE_MENU_CLEAR_INHERITANCE: {
+ clear_inherit_confirm->popup_centered_minsize();
+ } break;
+ case SCENE_MENU_OPEN_INHERITED: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ emit_signal("open",node->get_scene_inherited_state()->get_path());
+ }
+ } break;
+ case SCENE_MENU_CLEAR_INHERITANCE_CONFIRM: {
+ if (node && node->get_scene_inherited_state().is_valid()) {
+ node->set_scene_inherited_state(Ref<SceneState>());
+ update_tree();
+ EditorNode::get_singleton()->get_property_editor()->update_tree();
+ }
+
+ } break;
}
@@ -94,15 +132,33 @@ void SceneTreeEditor::_cell_button_pressed(Object *p_item,int p_column,int p_id)
Rect2 item_rect = tree->get_item_rect(item,0);
item_rect.pos.y-=tree->get_scroll().y;
item_rect.pos+=tree->get_global_pos();
- instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
- instance_menu->set_size(Vector2(item_rect.size.x,0));
- if (n->has_meta("__editor_show_subtree"))
- instance_menu->set_item_checked(0,true);
- else
- instance_menu->set_item_checked(0,false);
- instance_menu->popup();
- instance_node=n->get_instance_ID();
+ if (n==get_scene_node()) {
+ inheritance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ inheritance_menu->set_size(Vector2(item_rect.size.x,0));
+ inheritance_menu->popup();
+ instance_node=n->get_instance_ID();
+
+ } else {
+ instance_menu->set_pos(item_rect.pos+Vector2(0,item_rect.size.y));
+ instance_menu->set_size(Vector2(item_rect.size.x,0));
+ if (EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(n))
+ instance_menu->set_item_checked(0,true);
+ else
+ instance_menu->set_item_checked(0,false);
+
+ if (n->get_owner()==get_scene_node()) {
+ instance_menu->set_item_checked(1,n->get_scene_instance_load_placeholder());
+ instance_menu->set_item_disabled(1,false);
+ } else {
+
+ instance_menu->set_item_checked(1,false);
+ instance_menu->set_item_disabled(1,true);
+ }
+
+ instance_menu->popup();
+ instance_node=n->get_instance_ID();
+ }
//emit_signal("open",n->get_filename());
} else if (p_id==BUTTON_SCRIPT) {
RefPtr script=n->get_script();
@@ -168,15 +224,17 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
bool part_of_subscene=false;
- if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
+ if (!display_foreign && p_node->get_owner()!=get_scene_node() && p_node!=get_scene_node()) {
- if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && p_node->get_owner()->get_owner()==get_scene_node() && p_node->get_owner()->has_meta("__editor_show_subtree")) {
+ if ((show_enabled_subscene || can_open_instance) && p_node->get_owner() && (get_scene_node()->is_editable_instance(p_node->get_owner()))) {
part_of_subscene=true;
//allow
} else {
return;
}
+ } else {
+ part_of_subscene = get_scene_node()->get_scene_inherited_state().is_valid() && get_scene_node()->get_scene_inherited_state()->find_node_by_path(get_scene_node()->get_path_to(p_node))>=0;
}
TreeItem *item = tree->create_item(p_parent);
@@ -199,6 +257,7 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
icon=get_icon( (has_icon(p_node->get_type(),"EditorIcons")?p_node->get_type():String("Object")),"EditorIcons");
item->set_icon(0, icon );
item->set_metadata( 0,p_node->get_path() );
+
if (part_of_subscene) {
//item->set_selectable(0,marked_selectable);
@@ -221,7 +280,10 @@ void SceneTreeEditor::_add_nodes(Node *p_node,TreeItem *p_parent) {
}
}
- if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
+ if (p_node==get_scene_node() && p_node->get_scene_inherited_state().is_valid()) {
+ item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
+ item->set_tooltip(0,"Inherits: "+p_node->get_scene_inherited_state()->get_path()+"\nType: "+p_node->get_type());
+ } else if (p_node!=get_scene_node() && p_node->get_filename()!="" && can_open_instance) {
item->add_button(0,get_icon("InstanceOptions","EditorIcons"),BUTTON_SUBSCENE);
item->set_tooltip(0,"Instance: "+p_node->get_filename()+"\nType: "+p_node->get_type());
@@ -487,8 +549,11 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->connect("tree_changed",this,"_tree_changed");
get_tree()->connect("node_removed",this,"_node_removed");
- instance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ instance_menu->set_item_icon(3,get_icon("Load","EditorIcons"));
tree->connect("item_collapsed",this,"_cell_collapsed");
+ inheritance_menu->set_item_icon(2,get_icon("Load","EditorIcons"));
+ clear_inherit_confirm->connect("confirmed",this,"_subscene_option",varray(SCENE_MENU_CLEAR_INHERITANCE_CONFIRM));
+
// get_scene()->connect("tree_changed",this,"_tree_changed",Vector<Variant>(),CONNECT_DEFERRED);
// get_scene()->connect("node_removed",this,"_node_removed",Vector<Variant>(),CONNECT_DEFERRED);
@@ -499,6 +564,7 @@ void SceneTreeEditor::_notification(int p_what) {
get_tree()->disconnect("tree_changed",this,"_tree_changed");
get_tree()->disconnect("node_removed",this,"_node_removed");
tree->disconnect("item_collapsed",this,"_cell_collapsed");
+ clear_inherit_confirm->disconnect("confirmed",this,"_subscene_option");
_update_tree();
}
@@ -788,12 +854,27 @@ SceneTreeEditor::SceneTreeEditor(bool p_label,bool p_can_rename, bool p_can_open
blocked=0;
instance_menu = memnew( PopupMenu );
- instance_menu->add_check_item("Show Children",SCENE_MENU_SHOW_CHILDREN);
+ instance_menu->add_check_item("Editable Children",SCENE_MENU_EDITABLE_CHILDREN);
+ instance_menu->add_check_item("Load As Placeholder",SCENE_MENU_USE_PLACEHOLDER);
instance_menu->add_separator();
instance_menu->add_item("Open in Editor",SCENE_MENU_OPEN);
instance_menu->connect("item_pressed",this,"_subscene_option");
add_child(instance_menu);
+ inheritance_menu = memnew( PopupMenu );
+ inheritance_menu->add_item("Clear Inheritance",SCENE_MENU_CLEAR_INHERITANCE);
+ inheritance_menu->add_separator();
+ inheritance_menu->add_item("Open in Editor",SCENE_MENU_OPEN_INHERITED);
+ inheritance_menu->connect("item_pressed",this,"_subscene_option");
+
+ add_child(inheritance_menu);
+
+ clear_inherit_confirm = memnew( ConfirmationDialog );
+ clear_inherit_confirm->set_text("Clear Inheritance? (No Undo!)");
+ clear_inherit_confirm->get_ok()->set_text("Clear!");
+ add_child(clear_inherit_confirm);
+
+
}
diff --git a/tools/editor/scene_tree_editor.h b/tools/editor/scene_tree_editor.h
index b05a52a2da..50cca4e24b 100644
--- a/tools/editor/scene_tree_editor.h
+++ b/tools/editor/scene_tree_editor.h
@@ -52,16 +52,22 @@ class SceneTreeEditor : public Control {
};
enum {
- SCENE_MENU_SHOW_CHILDREN,
+ SCENE_MENU_EDITABLE_CHILDREN,
+ SCENE_MENU_USE_PLACEHOLDER,
SCENE_MENU_OPEN,
+ SCENE_MENU_CLEAR_INHERITANCE,
+ SCENE_MENU_OPEN_INHERITED,
+ SCENE_MENU_CLEAR_INHERITANCE_CONFIRM,
};
Tree *tree;
Node *selected;
PopupMenu *instance_menu;
+ PopupMenu *inheritance_menu;
ObjectID instance_node;
AcceptDialog *error;
+ ConfirmationDialog *clear_inherit_confirm;
int blocked;