aboutsummaryrefslogtreecommitdiff
path: root/scene/main
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scene/main/SCsub7
-rw-r--r--scene/main/canvas_layer.cpp247
-rw-r--r--scene/main/canvas_layer.h88
-rw-r--r--scene/main/misc.cpp40
-rw-r--r--scene/main/misc.h45
-rw-r--r--scene/main/node.cpp1718
-rw-r--r--scene/main/node.h276
-rw-r--r--scene/main/resource_preloader.cpp183
-rw-r--r--scene/main/resource_preloader.h64
-rw-r--r--scene/main/scene_main_loop.cpp937
-rw-r--r--scene/main/scene_main_loop.h203
-rw-r--r--scene/main/scene_singleton.cpp30
-rw-r--r--scene/main/scene_singleton.h36
-rw-r--r--scene/main/timer.cpp140
-rw-r--r--scene/main/timer.h66
-rw-r--r--scene/main/viewport.cpp834
-rw-r--r--scene/main/viewport.h212
17 files changed, 5126 insertions, 0 deletions
diff --git a/scene/main/SCsub b/scene/main/SCsub
new file mode 100644
index 000000000..055d2f247
--- /dev/null
+++ b/scene/main/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.scene_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/scene/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
new file mode 100644
index 000000000..5006f55da
--- /dev/null
+++ b/scene/main/canvas_layer.cpp
@@ -0,0 +1,247 @@
+/*************************************************************************/
+/* canvas_layer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "canvas_layer.h"
+#include "viewport.h"
+
+
+void CanvasLayer::set_layer(int p_xform) {
+
+ layer=p_xform;
+ if (viewport.is_valid())
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport,canvas->get_canvas(),layer);
+
+}
+
+int CanvasLayer::get_layer() const{
+
+ return layer;
+}
+
+void CanvasLayer::set_transform(const Matrix32& p_xform) {
+
+ transform=p_xform;
+ locrotscale_dirty=true;
+ if (viewport.is_valid())
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport,canvas->get_canvas(),transform);
+
+}
+
+Matrix32 CanvasLayer::get_transform() const {
+
+ return transform;
+}
+
+void CanvasLayer::_update_xform() {
+
+ transform.set_rotation_and_scale(rot,scale);
+ transform.set_origin(ofs);
+ if (viewport.is_valid())
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport,canvas->get_canvas(),transform);
+
+}
+
+void CanvasLayer::_update_locrotscale() {
+
+ ofs=transform.elements[2];
+ rot=transform.get_rotation();
+ scale=transform.get_scale();
+ locrotscale_dirty=false;
+}
+
+
+void CanvasLayer::set_offset(const Vector2& p_offset) {
+
+ if (locrotscale_dirty)
+ _update_locrotscale();
+
+ ofs=p_offset;
+ _update_xform();
+
+}
+
+Vector2 CanvasLayer::get_offset() const {
+
+ if (locrotscale_dirty)
+ const_cast<CanvasLayer*>(this)->_update_locrotscale();
+
+ return ofs;
+}
+
+
+void CanvasLayer::set_rotation(real_t p_rotation) {
+
+ if (locrotscale_dirty)
+ _update_locrotscale();
+
+
+ rot=p_rotation;
+ _update_xform();
+
+}
+
+real_t CanvasLayer::get_rotation() const {
+
+ if (locrotscale_dirty)
+ const_cast<CanvasLayer*>(this)->_update_locrotscale();
+
+ return rot;
+}
+
+
+void CanvasLayer::set_scale(const Vector2& p_scale) {
+
+ if (locrotscale_dirty)
+ _update_locrotscale();
+
+ scale=p_scale;
+ _update_xform();
+
+
+}
+
+Vector2 CanvasLayer::get_scale() const {
+
+ if (locrotscale_dirty)
+ const_cast<CanvasLayer*>(this)->_update_locrotscale();
+
+ return scale;
+}
+
+
+
+Ref<World2D> CanvasLayer::get_world_2d() const {
+
+ return canvas;
+}
+
+void CanvasLayer::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ Node *n = this;
+ vp=NULL;
+
+ while(n) {
+
+ if (n->cast_to<Viewport>()) {
+
+ vp = n->cast_to<Viewport>();
+ break;
+ }
+ n=n->get_parent();
+ }
+
+
+ ERR_FAIL_COND(!vp);
+ viewport=vp->get_viewport();
+
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport,canvas->get_canvas());
+ VisualServer::get_singleton()->viewport_set_canvas_layer(viewport,canvas->get_canvas(),layer);
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport,canvas->get_canvas(),transform);
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport,canvas->get_canvas());
+ viewport=RID();
+
+ } break;
+ }
+}
+
+Size2 CanvasLayer::get_viewport_size() const {
+
+ if (!is_inside_scene())
+ return Size2(1,1);
+
+ Rect2 r = vp->get_visible_rect();
+ return r.size;
+}
+
+
+RID CanvasLayer::get_viewport() const {
+
+ return viewport;
+}
+
+void CanvasLayer::_set_rotationd(real_t p_rotation) {
+
+ set_rotation(Math::deg2rad(p_rotation));
+}
+
+real_t CanvasLayer::_get_rotationd() const {
+
+ return Math::rad2deg(get_rotation());
+}
+
+
+void CanvasLayer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_layer","layer"),&CanvasLayer::set_layer);
+ ObjectTypeDB::bind_method(_MD("get_layer"),&CanvasLayer::get_layer);
+
+ ObjectTypeDB::bind_method(_MD("set_transform","transform"),&CanvasLayer::set_transform);
+ ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasLayer::get_transform);
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&CanvasLayer::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&CanvasLayer::get_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_rotation","rotation"),&CanvasLayer::set_rotation);
+ ObjectTypeDB::bind_method(_MD("get_rotation"),&CanvasLayer::get_rotation);
+
+ ObjectTypeDB::bind_method(_MD("_set_rotationd","rotationd"),&CanvasLayer::_set_rotationd);
+ ObjectTypeDB::bind_method(_MD("_get_rotationd"),&CanvasLayer::_get_rotationd);
+
+ ObjectTypeDB::bind_method(_MD("set_scale","scale"),&CanvasLayer::set_scale);
+ ObjectTypeDB::bind_method(_MD("get_scale"),&CanvasLayer::get_scale);
+
+ ObjectTypeDB::bind_method(_MD("get_world_2d:Canvas"),&CanvasLayer::get_world_2d);
+ ObjectTypeDB::bind_method(_MD("get_viewport"),&CanvasLayer::get_viewport);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"layer",PROPERTY_HINT_RANGE,"-128,128,1"),_SCS("set_layer"),_SCS("get_layer") );
+ //ADD_PROPERTY( PropertyInfo(Variant::MATRIX32,"transform",PROPERTY_HINT_RANGE),_SCS("set_transform"),_SCS("get_transform") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_offset"),_SCS("get_offset") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"rotation"),_SCS("_set_rotationd"),_SCS("_get_rotationd") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scale"),_SCS("set_scale"),_SCS("get_scale") );
+
+}
+
+CanvasLayer::CanvasLayer() {
+
+ vp=NULL;
+ scale=Vector2(1,1);
+ rot=0;
+ locrotscale_dirty=false;
+ layer=1;
+ canvas = Ref<World2D>( memnew(World2D) );
+}
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
new file mode 100644
index 000000000..638f3ec57
--- /dev/null
+++ b/scene/main/canvas_layer.h
@@ -0,0 +1,88 @@
+/*************************************************************************/
+/* canvas_layer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef CANVAS_LAYER_H
+#define CANVAS_LAYER_H
+
+#include "scene/main/node.h"
+#include "scene/resources/world_2d.h"
+
+
+class Viewport;
+class CanvasLayer : public Node {
+
+ OBJ_TYPE( CanvasLayer, Node );
+
+ bool locrotscale_dirty;
+ Vector2 ofs;
+ Vector2 scale;
+ real_t rot;
+ int layer;
+ Matrix32 transform;
+ Ref<World2D> canvas;
+ RID viewport;
+ Viewport *vp;
+
+
+ void _set_rotationd(real_t p_rotation);
+ real_t _get_rotationd() const;
+
+ void _update_xform();
+ void _update_locrotscale();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_layer(int p_xform);
+ int get_layer() const;
+
+ void set_transform(const Matrix32& p_xform);
+ Matrix32 get_transform() const;
+
+ void set_offset(const Vector2& p_offset);
+ Vector2 get_offset() const;
+
+ void set_rotation(real_t p_rotation);
+ real_t get_rotation() const;
+
+ void set_scale(const Vector2& p_scale);
+ Vector2 get_scale() const;
+
+ Ref<World2D> get_world_2d() const;
+
+ Size2 get_viewport_size() const;
+
+ RID get_viewport() const;
+
+ CanvasLayer();
+};
+
+#endif // CANVAS_LAYER_H
diff --git a/scene/main/misc.cpp b/scene/main/misc.cpp
new file mode 100644
index 000000000..761f470c1
--- /dev/null
+++ b/scene/main/misc.cpp
@@ -0,0 +1,40 @@
+/*************************************************************************/
+/* misc.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "misc.h"
+
+Misc::Misc()
+{
+}
+
+
+Misc::~Misc()
+{
+}
+
+
diff --git a/scene/main/misc.h b/scene/main/misc.h
new file mode 100644
index 000000000..34b80a08b
--- /dev/null
+++ b/scene/main/misc.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* misc.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef MISC_H
+#define MISC_H
+
+#include "scene/main/node.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class Misc : public Node {
+
+ OBJ_TYPE( Misc, Node );
+public:
+ Misc();
+ ~Misc();
+
+};
+
+#endif
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
new file mode 100644
index 000000000..1f54040de
--- /dev/null
+++ b/scene/main/node.cpp
@@ -0,0 +1,1718 @@
+/*************************************************************************/
+/* node.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "node.h"
+#include "print_string.h"
+#include "scene/io/scene_loader.h"
+#include "message_queue.h"
+#include "scene/scene_string_names.h"
+#include "scene/resources/packed_scene.h"
+#include "io/resource_loader.h"
+
+VARIANT_ENUM_CAST(Node::PauseMode);
+
+
+
+
+void Node::_notification(int p_notification) {
+
+ switch(p_notification) {
+
+ case NOTIFICATION_PROCESS: {
+
+ if (get_script_instance()) {
+
+ Variant time=get_process_delta_time();
+ const Variant*ptr[1]={&time};
+ Variant::CallError err;
+ get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_process,ptr,1);
+ }
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ if (get_script_instance()) {
+
+ Variant time=get_fixed_process_delta_time();
+ const Variant*ptr[1]={&time};
+ Variant::CallError err;
+ get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_fixed_process,ptr,1);
+ }
+
+ } break;
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (data.pause_mode==PAUSE_MODE_INHERIT) {
+
+ if (data.parent)
+ data.pause_owner=data.parent->data.pause_owner;
+ else
+ data.pause_owner=NULL;
+ } else {
+ data.pause_owner=this;
+ }
+
+ get_scene()->node_count++;
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ get_scene()->node_count--;
+
+ } break;
+ case NOTIFICATION_READY: {
+
+ if (get_script_instance()) {
+
+ Variant::CallError err;
+ get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_ready,NULL,0);
+ }
+ //emit_signal(SceneStringNames::get_singleton()->enter_scene);
+
+ } break;
+ case NOTIFICATION_POSTINITIALIZE: {
+ data.in_constructor=false;
+ } break;
+ case NOTIFICATION_PREDELETE: {
+
+ set_owner(NULL);
+
+ while ( data.owned.size() ) {
+
+ data.owned.front()->get()->set_owner(NULL);
+ }
+
+ if (data.parent) {
+
+ data.parent->remove_child(this);
+ }
+
+ // kill children as cleanly as possible
+ while( data.children.size() ) {
+
+ Node *child = data.children[0];
+ remove_child(child);
+ memdelete( child );
+ }
+
+ } break;
+ }
+}
+
+
+void Node::_propagate_ready() {
+
+ data.blocked++;
+ for (int i=0;i<data.children.size();i++) {
+
+ data.children[i]->_propagate_ready();
+ }
+ data.blocked--;
+ notification(NOTIFICATION_READY);
+
+}
+
+
+void Node::_propagate_enter_scene() {
+ // this needs to happen to all childs before any ENTER_SCENE
+
+ if (data.parent) {
+ data.scene=data.parent->data.scene;
+ data.depth=data.parent->data.depth+1;
+ } else {
+
+ data.depth=1;
+ }
+
+
+ data.inside_scene=true;
+
+ const StringName *K=NULL;
+
+ while ((K=data.grouped.next(K))) {
+
+ data.scene->add_to_group(*K,this);
+ }
+
+ notification(NOTIFICATION_ENTER_SCENE);
+
+ if (get_script_instance()) {
+
+ Variant::CallError err;
+ get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_enter_scene,NULL,0);
+ }
+
+ emit_signal(SceneStringNames::get_singleton()->enter_scene);
+
+
+ data.blocked++;
+ //block while adding children
+
+ for (int i=0;i<data.children.size();i++) {
+
+ if (!data.children[i]->is_inside_scene()) // could have been added in ENTER_SCENE
+ data.children[i]->_propagate_enter_scene();
+ }
+
+ data.blocked--;
+ // enter groups
+}
+
+
+
+void Node::_propagate_exit_scene() {
+
+ //block while removing children
+
+ data.blocked++;
+
+ for (int i=data.children.size()-1;i>=0;i--) {
+
+ data.children[i]->_propagate_exit_scene();
+ }
+
+ data.blocked--;
+
+ if (get_script_instance()) {
+
+ Variant::CallError err;
+ get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_scene,NULL,0);
+ }
+ emit_signal(SceneStringNames::get_singleton()->exit_scene);
+
+ notification(NOTIFICATION_EXIT_SCENE,true);
+ if (data.scene)
+ data.scene->node_removed(this);
+
+ // exit groups
+ const StringName *K=NULL;
+
+ while ((K=data.grouped.next(K))) {
+
+ data.scene->remove_from_group(*K,this);
+ }
+
+ if (data.scene)
+ data.scene->tree_changed();
+
+ data.inside_scene=false;
+ data.scene=NULL;
+ data.depth=-1;
+
+}
+
+
+
+
+
+void Node::move_child(Node *p_child,int p_pos) {
+
+ ERR_FAIL_NULL(p_child);
+ ERR_EXPLAIN("Invalid new child position: "+itos(p_pos));
+ ERR_FAIL_INDEX( p_pos, data.children.size()+1 );
+ ERR_EXPLAIN("child is not a child of this node.");
+ ERR_FAIL_COND(p_child->data.parent!=this);
+ ERR_FAIL_COND(data.blocked>0);
+
+ data.children.remove( p_child->data.pos );
+ data.children.insert( p_pos, p_child );
+
+ if (data.scene) {
+ data.scene->tree_changed();
+ }
+
+ data.blocked++;
+ //new pos first
+ for (int i=0;i<data.children.size();i++) {
+
+ data.children[i]->data.pos=i;
+ }
+ // notification second
+ for (int i=0;i<data.children.size();i++) {
+ data.children[i]->notification( NOTIFICATION_MOVED_IN_PARENT );
+
+ }
+
+ data.blocked--;
+
+}
+
+void Node::raise() {
+
+ if (!data.parent)
+ return;
+
+ data.parent->move_child(this,data.parent->data.children.size()-1);
+
+}
+
+void Node::add_child_notify(Node *p_child) {
+
+ // to be used when not wanted
+}
+
+
+void Node::remove_and_delete_child(Node *p_child) {
+
+ ERR_FAIL_NULL( p_child );
+ ERR_FAIL_COND( p_child->get_parent()!=this );
+
+ remove_child(p_child);
+ memdelete(p_child);
+
+}
+
+void Node::remove_child_notify(Node *p_child) {
+
+ // to be used when not wanted
+}
+
+void Node::set_fixed_process(bool p_process) {
+
+ if (data.fixed_process==p_process)
+ return;
+
+ data.fixed_process=p_process;
+
+ if (data.fixed_process)
+ add_to_group("fixed_process",false);
+ else
+ remove_from_group("fixed_process");
+
+ data.fixed_process=p_process;
+ _change_notify("fixed_process");
+}
+
+void Node::set_pause_mode(PauseMode p_mode) {
+
+ if (data.pause_mode==p_mode)
+ return;
+
+ bool prev_inherits=data.pause_mode==PAUSE_MODE_INHERIT;
+ data.pause_mode=p_mode;
+ if (!is_inside_scene())
+ return; //pointless
+ if ((data.pause_mode==PAUSE_MODE_INHERIT) == prev_inherits)
+ return; ///nothing changed
+
+ Node *owner=NULL;
+
+ if (data.pause_mode==PAUSE_MODE_INHERIT) {
+
+ if (data.parent)
+ data.parent->data.pause_owner;
+ } else {
+ owner=this;
+ }
+
+ _propagate_pause_owner(owner);
+
+
+
+}
+
+Node::PauseMode Node::get_pause_mode() const {
+
+ return data.pause_mode;
+}
+
+void Node::_propagate_pause_owner(Node*p_owner) {
+
+ if (data.pause_mode!=PAUSE_MODE_INHERIT)
+ return;
+ data.pause_owner=p_owner;
+ for(int i=0;i<data.children.size();i++) {
+
+ data.children[i]->_propagate_pause_owner(p_owner);
+ }
+}
+
+bool Node::can_process() const {
+
+ ERR_FAIL_COND_V( !is_inside_scene(), false );
+
+ if (get_scene()->is_paused()) {
+
+ if (data.pause_mode==PAUSE_MODE_PROCESS)
+ return true;
+ if (data.pause_mode==PAUSE_MODE_INHERIT) {
+
+ if (!data.pause_owner)
+ return false; //clearly no pause owner by default
+
+ if (data.pause_owner->data.pause_mode==PAUSE_MODE_PROCESS)
+ return true;
+ }
+
+ }
+
+ return true;
+}
+
+
+float Node::get_fixed_process_delta_time() const {
+
+ if (data.scene)
+ return data.scene->get_fixed_process_time();
+ else
+ return 0;
+}
+
+void Node::set_process(bool p_idle_process) {
+
+ if (data.idle_process==p_idle_process)
+ return;
+
+ data.idle_process=p_idle_process;
+
+ if (data.idle_process)
+ add_to_group("idle_process",false);
+ else
+ remove_from_group("idle_process");
+
+ data.idle_process=p_idle_process;
+ _change_notify("idle_process");
+}
+
+float Node::get_process_delta_time() const {
+
+ if (data.scene)
+ return data.scene->get_idle_process_time();
+ else
+ return 0;
+}
+
+bool Node::is_fixed_processing() const {
+
+ return data.fixed_process;
+}
+
+bool Node::is_processing() const {
+
+ return data.idle_process;
+}
+
+
+void Node::set_process_input(bool p_enable) {
+
+ if (p_enable==data.input)
+ return;
+ data.input=p_enable;
+ if (p_enable)
+ add_to_group("input");
+ else
+ remove_from_group("input");
+}
+
+bool Node::is_processing_input() const {
+ return data.input;
+}
+
+void Node::set_process_unhandled_input(bool p_enable) {
+
+ if (p_enable==data.unhandled_input)
+ return;
+ data.unhandled_input=p_enable;
+
+ if (p_enable)
+ add_to_group("unhandled_input");
+ else
+ remove_from_group("unhandled_input");
+}
+
+bool Node::is_processing_unhandled_input() const {
+ return data.unhandled_input;
+}
+
+
+StringName Node::get_name() const {
+
+ return data.name;
+}
+
+void Node::_set_name_nocheck(const StringName& p_name) {
+
+ data.name=p_name;
+
+}
+
+void Node::set_name(const String& p_name) {
+
+ String name=p_name.replace(":","").replace("/","");
+
+ ERR_FAIL_COND(name=="");
+ data.name=name;
+
+ if (data.parent) {
+
+ data.parent->_validate_child_name(this);
+ }
+
+ if (is_inside_scene()) {
+
+ emit_signal("renamed");
+ get_scene()->tree_changed();
+ }
+}
+
+void Node::_validate_child_name(Node *p_child) {
+
+ /* Make sure the name is unique */
+ String basename = p_child->data.name;
+
+ if (basename=="") {
+
+ basename = p_child->get_type();
+ }
+
+ int val=1;
+
+ for(;;) {
+
+ String attempted = val > 1 ? (basename + " " +itos(val) ) : basename;
+
+ bool found=false;
+
+ for (int i=0;i<data.children.size();i++) {
+
+ if (data.children[i]==p_child)
+ continue;
+ if (data.children[i]->get_name() == attempted) {
+ found=true;
+ break;
+ }
+
+ }
+
+ if (found) {
+
+ val++;
+ continue;
+ }
+
+ p_child->data.name=attempted;
+ break;
+ }
+
+}
+
+void Node::_add_child_nocheck(Node* p_child,const StringName& p_name) {
+ //add a child node quickly, without name validation
+
+ p_child->data.name=p_name;
+ p_child->data.pos=data.children.size();
+ data.children.push_back( p_child );
+ p_child->data.parent=this;
+
+ if (data.scene) {
+ p_child->_set_scene(data.scene);
+ }
+
+ /* Notify */
+ //recognize childs created in this node constructor
+ p_child->data.parent_owned=data.in_constructor;
+ p_child->notification(NOTIFICATION_PARENTED);
+ add_child_notify(p_child);
+
+
+}
+
+void Node::add_child(Node *p_child) {
+
+ ERR_FAIL_NULL(p_child);
+ /* Fail if node has a parent */
+ ERR_EXPLAIN("Can't add child "+p_child->get_name()+" to itself.")
+ ERR_FAIL_COND( p_child==this ); // adding to itself!
+ ERR_EXPLAIN("Can't add child, already has a parent");
+ ERR_FAIL_COND( p_child->data.parent );
+ ERR_EXPLAIN("Can't add child while a notification is happening");
+ ERR_FAIL_COND( data.blocked > 0 );
+
+ /* Validate name */
+ _validate_child_name(p_child);
+
+ _add_child_nocheck(p_child,p_child->data.name);
+
+}
+
+void Node::_propagate_validate_owner() {
+
+ if (data.owner) {
+
+ bool found=false;
+ Node *parent = data.parent;
+
+ while(parent) {
+
+
+ if (parent==data.owner) {
+
+ found=true;
+ break;
+ }
+
+ parent=parent->data.parent;
+ }
+
+
+ if (!found) {
+
+
+ data.owner->data.owned.erase(data.OW);
+ data.owner=NULL;
+ }
+
+ }
+
+
+ for(int i=0;i<data.children.size();i++) {
+
+
+ data.children[i]->_propagate_validate_owner();
+ }
+}
+
+void Node::remove_child(Node *p_child) {
+
+ ERR_FAIL_NULL(p_child);
+ ERR_FAIL_COND( data.blocked > 0 );
+
+ int idx=-1;
+ for (int i=0;i<data.children.size();i++) {
+
+ if (data.children[i]==p_child) {
+
+ idx=i;
+ break;
+ }
+ }
+
+ ERR_FAIL_COND( idx==-1 );
+
+
+ //if (data.scene) { does not matter
+
+ p_child->_set_scene(NULL);
+ //}
+
+ remove_child_notify(p_child);
+ p_child->notification(NOTIFICATION_UNPARENTED);
+
+ data.children.remove(idx);
+
+ for (int i=idx;i<data.children.size();i++) {
+
+ data.children[i]->data.pos=i;
+ }
+
+ p_child->data.parent=NULL;
+ p_child->data.pos=-1;
+
+
+
+ // validate owner
+ p_child->_propagate_validate_owner();
+
+}
+
+int Node::get_child_count() const {
+
+ return data.children.size();
+}
+Node *Node::get_child(int p_index) const {
+
+ ERR_FAIL_INDEX_V( p_index, data.children.size(), NULL );
+
+ return data.children[p_index];
+}
+
+Node *Node::_get_node(const NodePath& p_path) const {
+
+ ERR_FAIL_COND_V( !data.inside_scene && p_path.is_absolute(), NULL );
+
+ Node *current=NULL;
+ Node *root=NULL;
+
+ if (!p_path.is_absolute()) {
+ current=const_cast<Node*>(this); //start from this
+ } else {
+
+ root=const_cast<Node*>(this);;
+ while (root->data.parent)
+ root=root->data.parent; //start from root
+ }
+
+
+ for(int i=0;i<p_path.get_name_count();i++) {
+
+
+ StringName name = p_path.get_name(i);
+ Node *next = NULL;
+
+ if (name==SceneStringNames::get_singleton()->dot) { // .
+
+ next=current;
+
+ } else if (name==SceneStringNames::get_singleton()->doubledot) { // ..
+
+ if (current==NULL || !current->data.parent)
+ return NULL;
+
+ next=current->data.parent;
+ } else if (current==NULL) {
+
+ if (name==root->get_name())
+ next=root;
+
+ } else {
+
+ next=NULL;
+
+ for(int j=0;j<current->data.children.size();j++) {
+
+ Node *child = current->data.children[j];
+
+ if ( child->data.name == name ) {
+
+ next = child;
+ break;
+ }
+ }
+ if (next == NULL) {
+ return NULL;
+ };
+ }
+ current=next;
+ }
+
+ return current;
+}
+
+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);
+ return node;
+}
+
+bool Node::has_node(const NodePath& p_path) const {
+
+ return _get_node(p_path)!=NULL;
+}
+
+Node *Node::get_parent() const {
+
+ return data.parent;
+}
+
+
+bool Node::is_a_parent_of(const Node *p_node) const {
+
+ ERR_FAIL_NULL_V(p_node,false);
+ Node *p=p_node->data.parent;
+ while(p) {
+
+ if (p==this)
+ return true;
+ p=p->data.parent;
+ }
+
+ return false;
+}
+
+bool Node::is_greater_than(const Node *p_node) const {
+
+ ERR_FAIL_NULL_V(p_node,false);
+ ERR_FAIL_COND_V( !data.inside_scene, false );
+ ERR_FAIL_COND_V( !p_node->data.inside_scene, false );
+
+ ERR_FAIL_COND_V( data.depth<0, false);
+ ERR_FAIL_COND_V( p_node->data.depth<0, false);
+#ifdef NO_ALLOCA
+
+ Vector<int> this_stack;
+ Vector<int> that_stack;
+ this_stack.resize(data.depth);
+ that_stack.resize(p_node->data.depth);
+
+#else
+
+ int *this_stack=(int*)alloca(sizeof(int)*data.depth);
+ int *that_stack=(int*)alloca(sizeof(int)*p_node->data.depth);
+
+#endif
+
+ const Node *n = this;
+
+ int idx=data.depth-1;
+ while(n) {
+ ERR_FAIL_INDEX_V(idx, data.depth,false);
+ this_stack[idx--]=n->data.pos;
+ n=n->data.parent;
+ }
+ ERR_FAIL_COND_V(idx!=-1,false);
+ n = p_node;
+ idx=p_node->data.depth-1;
+ while(n) {
+ ERR_FAIL_INDEX_V(idx, p_node->data.depth,false);
+ that_stack[idx--]=n->data.pos;
+
+ n=n->data.parent;
+ }
+ ERR_FAIL_COND_V(idx!=-1,false);
+ idx=0;
+
+ bool res;
+ while(true) {
+
+ // using -2 since out-of-tree or nonroot nodes have -1
+ int this_idx = (idx >= data.depth)? -2 : this_stack[idx];
+ int that_idx = (idx >= p_node->data.depth)? -2 : that_stack[idx];
+
+ if (this_idx > that_idx) {
+ res=true;
+ break;
+ } else if (this_idx < that_idx) {
+ res=false;
+ break;
+ } else if (this_idx == -2 ) {
+ res=false; // equal
+ break;
+ }
+ idx++;
+ }
+
+ return res;
+}
+
+void Node::get_owned_by(Node *p_by,List<Node*> *p_owned) {
+
+ if (data.owner==p_by)
+ p_owned->push_back(this);
+
+ for (int i=0;i<get_child_count();i++)
+ get_child(i)->get_owned_by(p_by,p_owned);
+
+}
+
+
+void Node::_set_owner_nocheck(Node* p_owner) {
+
+ data.owner=p_owner;
+ data.owner->data.owned.push_back( this );
+ data.OW = data.owner->data.owned.back();
+}
+
+void Node::set_owner(Node *p_owner) {
+
+ if (data.owner) {
+
+ data.owner->data.owned.erase( data.OW );
+ data.OW=NULL;
+ data.owner=NULL;
+ }
+
+ ERR_FAIL_COND(p_owner==this);
+
+ if (!p_owner)
+ return;
+
+ Node *check=this->get_parent();
+ bool owner_valid=false;
+
+ while(check) {
+
+ if (check==p_owner) {
+ owner_valid=true;
+ break;
+ }
+
+ check=check->data.parent;
+ }
+
+ ERR_FAIL_COND(!owner_valid);
+
+ _set_owner_nocheck(p_owner);
+}
+Node *Node::get_owner() const {
+
+ return data.owner;
+}
+
+NodePath Node::get_path_to(const Node *p_node) const {
+
+ ERR_FAIL_NULL_V(p_node,NodePath());
+
+ if (this==p_node)
+ return NodePath(".");
+
+ Set<const Node*> visited;
+
+ const Node *n=this;
+
+ while(n) {
+
+ visited.insert(n);
+ n=n->data.parent;
+ }
+
+ const Node *common_parent=p_node;
+
+ while(common_parent) {
+
+ if (visited.has(common_parent))
+ break;
+ common_parent=common_parent->data.parent;
+ }
+
+ ERR_FAIL_COND_V(!common_parent,NodePath()); //nodes not in the same tree
+
+ visited.clear();
+
+ Vector<StringName> path;
+
+ n=p_node;
+
+ while(n!=common_parent) {
+
+ path.push_back( n->get_name() );
+ n=n->data.parent;
+ }
+
+ n=this;
+ StringName up=String("..");
+
+ while(n!=common_parent) {
+
+ path.push_back( up );
+ n=n->data.parent;
+ }
+
+ path.invert();
+
+ return NodePath(path,false);
+}
+
+NodePath Node::get_path() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),NodePath());
+ const Node *n = this;
+
+ Vector<StringName> path;
+
+ while(n) {
+ path.push_back(n->get_name());
+ n=n->data.parent;
+ }
+
+ path.invert();
+
+ return NodePath( path, true );
+}
+
+bool Node::is_in_group(const StringName& p_identifier) const {
+
+ return data.grouped.has(p_identifier);
+}
+
+void Node::add_to_group(const StringName& p_identifier,bool p_persistent) {
+
+ ERR_FAIL_COND(!p_identifier.operator String().length());
+
+ if (data.grouped.has(p_identifier))
+ return;
+
+ GroupData gd;
+
+ if (data.scene)
+ data.scene->add_to_group(p_identifier,this);
+
+ gd.persistent=p_persistent;
+
+ data.grouped[p_identifier]=gd;
+
+}
+
+void Node::remove_from_group(const StringName& p_identifier) {
+
+
+ ERR_FAIL_COND(!data.grouped.has(p_identifier) );
+
+ GroupData *g=data.grouped.getptr(p_identifier);
+
+ ERR_FAIL_COND(!g);
+
+ if (data.scene)
+ data.scene->remove_from_group(p_identifier,this);
+
+ data.grouped.erase(p_identifier);
+
+}
+
+void Node::get_groups(List<GroupInfo> *p_groups) const {
+
+ const StringName *K=NULL;
+
+ while ((K=data.grouped.next(K))) {
+
+ GroupInfo gi;
+ gi.name=*K;
+ gi.persistent=data.grouped[*K].persistent;
+ p_groups->push_back(gi);
+ }
+
+}
+
+
+void Node::_print_tree(const Node *p_node) {
+
+ printf("%ls\n", String(p_node->get_path_to(this)).c_str());
+ for (int i=0;i<data.children.size();i++)
+ data.children[i]->_print_tree(p_node);
+}
+
+void Node::print_tree() {
+
+ _print_tree(this);
+}
+
+
+void Node::_propagate_reverse_notification(int p_notification) {
+
+ data.blocked++;
+ for (int i=data.children.size()-1;i>=0;i--) {
+
+ data.children[i]->_propagate_reverse_notification(p_notification);
+ }
+
+ notification(p_notification,true);
+ data.blocked--;
+}
+
+void Node::_propagate_deferred_notification(int p_notification, bool p_reverse) {
+
+ ERR_FAIL_COND(!is_inside_scene());
+
+ data.blocked++;
+
+ if (!p_reverse)
+ MessageQueue::get_singleton()->push_notification(this,p_notification);
+
+ for (int i=0;i<data.children.size();i++) {
+
+ data.children[i]->_propagate_deferred_notification(p_notification,p_reverse);
+ }
+
+ if (p_reverse)
+ MessageQueue::get_singleton()->push_notification(this,p_notification);
+
+ data.blocked--;
+}
+
+void Node::propagate_notification(int p_notification) {
+
+ data.blocked++;
+ notification(p_notification);
+
+ for (int i=0;i<data.children.size();i++) {
+
+ data.children[i]->propagate_notification(p_notification);
+ }
+ data.blocked--;
+}
+
+
+void Node::_propagate_replace_owner(Node *p_owner,Node* p_by_owner) {
+ if (get_owner()==p_owner)
+ set_owner(p_by_owner);
+
+ data.blocked++;
+ for (int i=0;i<data.children.size();i++)
+ data.children[i]->_propagate_replace_owner(p_owner,p_by_owner);
+ data.blocked--;
+}
+
+int Node::get_index() const {
+
+ return data.pos;
+}
+void Node::remove_and_skip() {
+
+ ERR_FAIL_COND(!data.parent);
+
+ Node *new_owner=get_owner();
+
+ List<Node*> children;
+
+ while(true) {
+
+ bool clear=true;
+ for (int i=0;i<data.children.size();i++) {
+ if (!data.children[i]->get_owner())
+ continue;
+
+ remove_child(data.children[i]);
+ data.children[i]->_propagate_replace_owner(this,NULL);
+ children.push_back(data.children[i]);
+ clear=false;
+ break;
+ }
+
+ if (clear)
+ break;
+ }
+
+ while(!children.empty()) {
+
+ Node *c=children.front()->get();
+ data.parent->add_child(c);
+ c->_propagate_replace_owner(NULL,new_owner);
+ children.pop_front();
+ }
+
+ data.parent->remove_child(this);
+}
+
+void Node::set_filename(const String& p_filename) {
+
+ data.filename=p_filename;
+}
+String Node::get_filename() const {
+
+ return data.filename;
+}
+
+
+
+void Node::generate_instance_state() {
+
+ List<PropertyInfo> properties;
+ get_property_list(&properties);
+
+ data.instance_state.clear();
+
+ for( List<PropertyInfo>::Element *E=properties.front();E;E=E->next() ) {
+
+ PropertyInfo &pi=E->get();
+ if (!(pi.usage&PROPERTY_USAGE_EDITOR) || !(pi.usage&PROPERTY_USAGE_STORAGE))
+ continue;
+
+ data.instance_state[pi.name]=get(pi.name);
+ }
+
+ List<GroupInfo> groups;
+ get_groups(&groups);
+ for(List<GroupInfo>::Element *E=groups.front();E;E=E->next()) {
+
+ if (!E->get().persistent)
+ continue;
+ data.instance_groups.push_back(E->get().name);
+ }
+
+ List<MethodInfo> signal_list;
+
+ get_signal_list(&signal_list);
+
+ for(List<MethodInfo>::Element *E=signal_list.front();E;E=E->next()) {
+
+ StringName name = E->get().name;
+ List<Connection> connections;
+ get_signal_connection_list(name,&connections);
+
+ for(List<Connection>::Element *F=connections.front();F;F=F->next()) {
+
+ if (F->get().flags&CONNECT_PERSIST)
+ data.instance_connections.push_back(F->get());
+ }
+
+ }
+}
+
+Dictionary Node::get_instance_state() const {
+
+ return data.instance_state;
+}
+
+Vector<StringName> Node::get_instance_groups() const {
+
+ return data.instance_groups;
+}
+Vector<Node::Connection> Node::get_instance_connections() const{
+
+ return data.instance_connections;
+}
+
+int Node::get_position_in_parent() const {
+
+ return data.pos;
+}
+
+
+
+Node *Node::duplicate() const {
+
+
+ Node *node=NULL;
+
+ Object *obj = ObjectTypeDB::instance(get_type());
+ ERR_FAIL_COND_V(!obj,NULL);
+ node = obj->cast_to<Node>();
+ if (!node)
+ memdelete(obj);
+ ERR_FAIL_COND_V(!node,NULL);
+
+
+
+ if (get_filename()!="") { //an instance
+ node->set_filename(get_filename());
+ }
+
+ List<PropertyInfo> plist;
+
+ get_property_list(&plist);
+
+ for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
+
+ if (!(E->get().usage&PROPERTY_USAGE_STORAGE))
+ continue;
+ String name = E->get().name;
+ node->set( name, get(name) );
+
+ }
+
+ node->set_name(get_name());
+
+ for(int i=0;i<get_child_count();i++) {
+
+ if (get_child(i)->data.parent_owned)
+ continue;
+ Node *dup = get_child(i)->duplicate();
+ if (!dup) {
+
+ memdelete(node);
+ return NULL;
+ }
+
+ node->add_child(dup);
+ }
+
+ return node;
+}
+
+
+void Node::_duplicate_and_reown(Node* p_new_parent, const Map<Node*,Node*>& p_reown_map) const {
+
+ if (get_owner()!=get_parent()->get_owner())
+ return;
+
+ Node *node=NULL;
+
+ if (get_filename()!="") {
+
+ Ref<PackedScene> res = ResourceLoader::load(get_filename());
+ ERR_FAIL_COND(res.is_null());
+ node=res->instance();
+ ERR_FAIL_COND(!node);
+ } else {
+
+ Object *obj = ObjectTypeDB::instance(get_type());
+ if (!obj) {
+ print_line("could not duplicate: "+String(get_type()));
+ }
+ ERR_FAIL_COND(!obj);
+ node = obj->cast_to<Node>();
+ if (!node)
+ memdelete(obj);
+ }
+
+
+ List<PropertyInfo> plist;
+
+ get_property_list(&plist);
+
+ for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) {
+
+ if (!(E->get().usage&PROPERTY_USAGE_STORAGE))
+ continue;
+ String name = E->get().name;
+ node->set( name, get(name) );
+
+ }
+
+ node->set_name(get_name());
+ p_new_parent->add_child(node);
+
+ Node *owner=get_owner();
+
+ if (p_reown_map.has(owner))
+ owner=p_reown_map[owner];
+
+
+ if (owner) {
+ NodePath p = get_path_to(owner);
+ if (owner!=this) {
+ Node *new_owner = node->get_node(p);
+ if (new_owner) {
+ node->set_owner(new_owner);
+ }
+ }
+ }
+
+ for(int i=0;i<get_child_count();i++) {
+
+ get_child(i)->_duplicate_and_reown(node,p_reown_map);
+ }
+
+}
+
+Node *Node::duplicate_and_reown(const Map<Node*,Node*>& p_reown_map) const {
+
+
+ ERR_FAIL_COND_V(get_filename()!="",NULL);
+
+ Node *node=NULL;
+
+ Object *obj = ObjectTypeDB::instance(get_type());
+ if (!obj) {
+ print_line("could not duplicate: "+String(get_type()));
+ }
+ ERR_FAIL_COND_V(!obj,NULL);
+ node = obj->cast_to<Node>();
+ if (!node)
+ memdelete(obj);
+ ERR_FAIL_COND_V(!node,NULL);
+
+ node->set_name(get_name());
+
+ for(int i=0;i<get_child_count();i++) {
+
+ get_child(i)->_duplicate_and_reown(node,p_reown_map);
+ }
+
+ return node;
+
+}
+
+static void find_owned_by(Node* p_by, Node* p_node, List<Node*> *p_owned) {
+
+
+ if (p_node->get_owner()==p_by)
+ p_owned->push_back(p_node);
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ find_owned_by(p_by,p_node->get_child(i),p_owned);
+ }
+}
+
+struct _NodeReplaceByPair {
+
+ String name;
+ Variant value;
+};
+
+void Node::replace_by(Node* p_node,bool p_keep_data) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_COND(p_node->data.parent);
+
+ List<Node*> owned = data.owned;
+ List<Node*> owned_by_owner;
+ Node *owner = (data.owner==this)?p_node:data.owner;
+
+ List<_NodeReplaceByPair> replace_data;
+
+ if (p_keep_data) {
+
+ List<PropertyInfo> plist;
+ get_property_list(&plist);
+
+ for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next() ) {
+
+ _NodeReplaceByPair rd;
+ if (!(E->get().usage&PROPERTY_USAGE_STORAGE))
+ continue;
+ rd.name=E->get().name;
+ rd.value=get(rd.name);
+ }
+ }
+
+ if (data.owner) {
+ for(int i=0;i<get_child_count();i++)
+ find_owned_by(data.owner,get_child(i),&owned_by_owner);
+ }
+
+ Node *parent = data.parent;
+ int pos_in_parent = data.pos;
+
+ if (data.parent) {
+
+ parent->remove_child(this);
+ parent->add_child(p_node);
+ parent->move_child(p_node,pos_in_parent);
+ }
+
+ while(get_child_count()) {
+
+ Node * child = get_child(0);
+ remove_child(child);
+ p_node->add_child(child);
+ }
+
+ p_node->set_owner(owner);
+ for(int i=0;i<owned.size();i++)
+ owned[i]->set_owner(p_node);
+
+ for(int i=0;i<owned_by_owner.size();i++)
+ owned_by_owner[i]->set_owner(owner);
+
+ p_node->set_filename(get_filename());
+
+ for (List<_NodeReplaceByPair>::Element *E=replace_data.front();E;E=E->next()) {
+
+ p_node->set(E->get().name,E->get().value);
+ }
+
+}
+
+Vector<Variant> Node::make_binds(VARIANT_ARG_DECLARE) {
+
+
+ Vector<Variant> ret;
+
+ if (p_arg1.get_type()==Variant::NIL)
+ return ret;
+ else
+ ret.push_back(p_arg1);
+
+ if (p_arg2.get_type()==Variant::NIL)
+ return ret;
+ else
+ ret.push_back(p_arg2);
+
+
+ if (p_arg3.get_type()==Variant::NIL)
+ return ret;
+ else
+ ret.push_back(p_arg3);
+
+ if (p_arg4.get_type()==Variant::NIL)
+ return ret;
+ else
+ ret.push_back(p_arg4);
+
+ if (p_arg5.get_type()==Variant::NIL)
+ return ret;
+ else
+ ret.push_back(p_arg5);
+
+ return ret;
+}
+
+
+
+bool Node::has_node_and_resource(const NodePath& p_path) const {
+
+ if (!has_node(p_path))
+ return false;
+ Node *node = get_node(p_path);
+
+ if (p_path.get_subname_count()) {
+
+ RES r;
+ for(int j=0;j<p_path.get_subname_count();j++) {
+ r = j==0 ? node->get(p_path.get_subname(j)) : r->get(p_path.get_subname(j));
+ if (r.is_null())
+ return false;
+ }
+ }
+
+
+ return true;
+}
+
+
+Array Node::_get_node_and_resource(const NodePath& p_path) {
+
+ Node *node;
+ RES res;
+ node = get_node_and_resource(p_path,res);
+ Array result;
+
+ if (node)
+ result.push_back(node);
+ else
+ result.push_back(Variant());
+
+ if (res.is_valid())
+ result.push_back(res);
+ else
+ result.push_back(Variant());
+
+ return result;
+}
+
+Node *Node::get_node_and_resource(const NodePath& p_path,RES& r_res) const {
+
+ Node *node = get_node(p_path);
+ r_res = RES();
+ if (!node)
+ return NULL;
+
+ if (p_path.get_subname_count()) {
+
+ for(int j=0;j<p_path.get_subname_count();j++) {
+ r_res = j==0 ? node->get(p_path.get_subname(j)) : r_res->get(p_path.get_subname(j));
+ ERR_FAIL_COND_V( r_res.is_null(), node );
+ }
+ }
+
+ return node;
+}
+
+void Node::_set_scene(SceneMainLoop *p_scene) {
+
+ SceneMainLoop *tree_changed_a=NULL;
+ SceneMainLoop *tree_changed_b=NULL;
+
+// ERR_FAIL_COND(p_scene && data.parent && !data.parent->data.scene); //nobug if both are null
+
+ if (data.scene) {
+ _propagate_exit_scene();
+
+ tree_changed_a=data.scene;
+ }
+
+
+ data.scene=p_scene;
+
+ if (data.scene) {
+
+
+ _propagate_enter_scene();
+ _propagate_ready(); //reverse_notification(NOTIFICATION_READY);
+
+ tree_changed_b=data.scene;
+
+ }
+
+ if (tree_changed_a)
+ tree_changed_a->tree_changed();
+ if (tree_changed_b)
+ tree_changed_b->tree_changed();
+
+}
+
+
+static void _Node_debug_sn(Object *p_obj) {
+
+ Node *n = p_obj->cast_to<Node>();
+ if (!n)
+ return;
+
+ if (n->is_inside_scene())
+ return;
+
+ Node *p=n;
+ while(p->get_parent()) {
+ p=p->get_parent();
+ }
+
+ String path;
+ if (p==n)
+ path=n->get_name();
+ else
+ path=String(p->get_name())+"/"+p->get_path_to(n);
+ print_line(itos(p_obj->get_instance_ID())+"- Stray Node: "+path+" (Type: "+n->get_type()+")");
+
+}
+
+void Node::_print_stray_nodes() {
+
+ print_stray_nodes();
+}
+
+void Node::print_stray_nodes() {
+
+#ifdef DEBUG_ENABLED
+
+ ObjectDB::debug_objects(_Node_debug_sn);
+#endif
+}
+
+void Node::queue_delete() {
+
+ ERR_FAIL_COND( !is_inside_scene() );
+ get_scene()->queue_delete(this);
+}
+
+Array Node::_get_children() const {
+
+ Array arr;
+ int cc = get_child_count();
+ arr.resize(cc);
+ for(int i=0;i<cc;i++)
+ arr[i]=get_child(i);
+
+ return arr;
+}
+
+
+void Node::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_name","name"),&Node::set_name);
+ ObjectTypeDB::bind_method(_MD("get_name"),&Node::get_name);
+ ObjectTypeDB::bind_method(_MD("add_child","node:Node"),&Node::add_child);
+ ObjectTypeDB::bind_method(_MD("remove_child","node:Node"),&Node::remove_child);
+ ObjectTypeDB::bind_method(_MD("remove_and_delete_child","node:Node"),&Node::remove_and_delete_child);
+ ObjectTypeDB::bind_method(_MD("get_child_count"),&Node::get_child_count);
+ ObjectTypeDB::bind_method(_MD("get_children"),&Node::_get_children);
+ ObjectTypeDB::bind_method(_MD("get_child:Node","idx"),&Node::get_child);
+ ObjectTypeDB::bind_method(_MD("has_node","path"),&Node::has_node);
+ ObjectTypeDB::bind_method(_MD("get_node:Node","path"),&Node::get_node);
+ ObjectTypeDB::bind_method(_MD("get_parent:Parent"),&Node::get_parent);
+ ObjectTypeDB::bind_method(_MD("has_node_and_resource","path"),&Node::has_node_and_resource);
+ ObjectTypeDB::bind_method(_MD("get_node_and_resource","path"),&Node::_get_node_and_resource);
+
+ ObjectTypeDB::bind_method(_MD("is_inside_scene"),&Node::is_inside_scene);
+ ObjectTypeDB::bind_method(_MD("is_a_parent_of","node:Node"),&Node::is_a_parent_of);
+ ObjectTypeDB::bind_method(_MD("is_greater_than","node:Node"),&Node::is_greater_than);
+ ObjectTypeDB::bind_method(_MD("get_path"),&Node::get_path);
+ ObjectTypeDB::bind_method(_MD("get_path_to","node:Node"),&Node::get_path_to);
+ ObjectTypeDB::bind_method(_MD("add_to_group","group"),&Node::add_to_group,DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("remove_from_group","group"),&Node::remove_from_group);
+ ObjectTypeDB::bind_method(_MD("is_in_group","group"),&Node::is_in_group);
+ ObjectTypeDB::bind_method(_MD("move_child","child_node:Node","to_pos"),&Node::move_child);
+ ObjectTypeDB::bind_method(_MD("raise"),&Node::raise);
+ ObjectTypeDB::bind_method(_MD("set_owner","owner:Node"),&Node::set_owner);
+ ObjectTypeDB::bind_method(_MD("get_owner:Node"),&Node::get_owner);
+ ObjectTypeDB::bind_method(_MD("remove_and_skip"),&Node::remove_and_skip);
+ ObjectTypeDB::bind_method(_MD("get_index"),&Node::get_index);
+ ObjectTypeDB::bind_method(_MD("print_tree"),&Node::print_tree);
+ ObjectTypeDB::bind_method(_MD("set_filename","filename"),&Node::set_filename);
+ ObjectTypeDB::bind_method(_MD("get_filename"),&Node::get_filename);
+ ObjectTypeDB::bind_method(_MD("propagate_notification","what"),&Node::propagate_notification);
+ ObjectTypeDB::bind_method(_MD("set_fixed_process","enable"),&Node::set_fixed_process);
+ ObjectTypeDB::bind_method(_MD("get_fixed_process_delta_time"),&Node::get_fixed_process_delta_time);
+ ObjectTypeDB::bind_method(_MD("is_fixed_processing"),&Node::is_fixed_processing);
+ ObjectTypeDB::bind_method(_MD("set_process","enable"),&Node::set_process);
+ ObjectTypeDB::bind_method(_MD("get_process_delta_time"),&Node::get_process_delta_time);
+ ObjectTypeDB::bind_method(_MD("is_processing"),&Node::is_processing);
+ ObjectTypeDB::bind_method(_MD("set_process_input","enable"),&Node::set_process_input);
+ ObjectTypeDB::bind_method(_MD("is_processing_input"),&Node::is_processing_input);
+ ObjectTypeDB::bind_method(_MD("set_process_unhandled_input","enable"),&Node::set_process_unhandled_input);
+ ObjectTypeDB::bind_method(_MD("is_processing_unhandled_input"),&Node::is_processing_unhandled_input);
+ ObjectTypeDB::bind_method(_MD("set_pause_mode","mode"),&Node::set_pause_mode);
+ ObjectTypeDB::bind_method(_MD("get_pause_mode"),&Node::get_pause_mode);
+ ObjectTypeDB::bind_method(_MD("can_process"),&Node::can_process);
+ ObjectTypeDB::bind_method(_MD("print_stray_nodes"),&Node::_print_stray_nodes);
+ ObjectTypeDB::bind_method(_MD("get_position_in_parent"),&Node::get_position_in_parent);
+
+ ObjectTypeDB::bind_method(_MD("get_scene:SceneMainLoop"),&Node::get_scene);
+
+ ObjectTypeDB::bind_method(_MD("duplicate:Node"),&Node::duplicate);
+ ObjectTypeDB::bind_method(_MD("replace_by","node:Node","keep_data"),&Node::replace_by,DEFVAL(false));
+
+ ObjectTypeDB::bind_method(_MD("queue_free"),&Node::queue_delete);
+
+ BIND_CONSTANT( NOTIFICATION_ENTER_SCENE );
+ BIND_CONSTANT( NOTIFICATION_EXIT_SCENE );
+ BIND_CONSTANT( NOTIFICATION_MOVED_IN_PARENT );
+ //BIND_CONSTANT( NOTIFICATION_PARENT_DECONFIGURED );
+ BIND_CONSTANT( NOTIFICATION_READY );
+ BIND_CONSTANT( NOTIFICATION_FIXED_PROCESS );
+ BIND_CONSTANT( NOTIFICATION_PROCESS );
+ BIND_CONSTANT( NOTIFICATION_PARENTED );
+ BIND_CONSTANT( NOTIFICATION_UNPARENTED );
+ BIND_CONSTANT( NOTIFICATION_PAUSED );
+ BIND_CONSTANT( NOTIFICATION_UNPAUSED );
+
+
+ BIND_CONSTANT( PAUSE_MODE_INHERIT );
+ BIND_CONSTANT( PAUSE_MODE_STOP );
+ BIND_CONSTANT( PAUSE_MODE_PROCESS );
+
+ ADD_SIGNAL( MethodInfo("renamed") );
+ ADD_SIGNAL( MethodInfo("enter_scene") );
+ ADD_SIGNAL( MethodInfo("exit_scene") );
+
+// ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/process" ),_SCS("set_process"),_SCS("is_processing") );
+// ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/fixed_process" ), _SCS("set_fixed_process"),_SCS("is_fixed_processing") );
+ //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/input" ), _SCS("set_process_input"),_SCS("is_processing_input" ) );
+ //ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "process/unhandled_input" ), _SCS("set_process_unhandled_input"),_SCS("is_processing_unhandled_input" ) );
+ ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "process/pause_mode",PROPERTY_HINT_ENUM,"Inherit,Stop,Process" ), _SCS("set_pause_mode"),_SCS("get_pause_mode" ) );
+
+ BIND_VMETHOD( MethodInfo("_process",PropertyInfo(Variant::REAL,"delta")) );
+ BIND_VMETHOD( MethodInfo("_fixed_process",PropertyInfo(Variant::REAL,"delta")) );
+ BIND_VMETHOD( MethodInfo("_enter_scene") );
+ BIND_VMETHOD( MethodInfo("_exit_scene") );
+ BIND_VMETHOD( MethodInfo("_ready") );
+ BIND_VMETHOD( MethodInfo("_input",PropertyInfo(Variant::INPUT_EVENT,"event")) );
+ BIND_VMETHOD( MethodInfo("_unhandled_input",PropertyInfo(Variant::INPUT_EVENT,"event")) );
+ BIND_VMETHOD( MethodInfo("_unhandled_key_input",PropertyInfo(Variant::INPUT_EVENT,"key_event")) );
+
+ //ObjectTypeDB::bind_method(_MD("get_child",&Node::get_child,PH("index")));
+ //ObjectTypeDB::bind_method(_MD("get_node",&Node::get_node,PH("path")));
+}
+
+
+Node::Node() {
+
+ data.pos=-1;
+ data.depth=-1;
+ data.blocked=0;
+ data.parent=NULL;
+ data.scene=NULL;
+ data.fixed_process=false;
+ data.idle_process=false;
+ data.inside_scene=false;
+
+ data.owner=NULL;
+ data.OW=false;
+ data.input=false;
+ data.unhandled_input=false;
+ data.pause_mode=PAUSE_MODE_INHERIT;
+ data.pause_owner=NULL;
+ data.parent_owned=false;
+ data.in_constructor=true;
+}
+
+Node::~Node() {
+
+
+ data.grouped.clear();
+ data.owned.clear();
+ data.children.clear();
+
+ ERR_FAIL_COND(data.parent);
+ ERR_FAIL_COND(data.children.size());
+
+
+}
+
+
diff --git a/scene/main/node.h b/scene/main/node.h
new file mode 100644
index 000000000..f38a78218
--- /dev/null
+++ b/scene/main/node.h
@@ -0,0 +1,276 @@
+/*************************************************************************/
+/* node.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef NODE_H
+#define NODE_H
+
+#include "object.h"
+#include "path_db.h"
+#include "map.h"
+#include "object_type_db.h"
+#include "script_language.h"
+#include "scene/main/scene_main_loop.h"
+
+
+class Node : public Object {
+
+ OBJ_TYPE( Node, Object );
+ OBJ_CATEGORY("Nodes");
+
+public:
+
+ enum PauseMode {
+
+ PAUSE_MODE_INHERIT,
+ PAUSE_MODE_STOP,
+ PAUSE_MODE_PROCESS
+ };
+
+
+ struct Comparator {
+
+ bool operator()(const Node* p_a, const Node* p_b) const { return p_b->is_greater_than(p_a); }
+ };
+
+private:
+
+ struct GroupData {
+
+ bool persistent;
+ GroupData() { persistent=false; }
+ };
+
+ struct Data {
+
+ String filename;
+ Dictionary instance_state;
+ Vector<StringName> instance_groups;
+ Vector<Connection> instance_connections;
+
+ Node *parent;
+ Node *owner;
+ Vector<Node*> children; // list of children
+ int pos;
+ int depth;
+ int blocked; // safeguard that throws an error when attempting to modify the tree in a harmful way while being traversed.
+ StringName name;
+ SceneMainLoop *scene;
+ bool inside_scene;
+
+
+ HashMap< StringName, GroupData,StringNameHasher> grouped;
+ List<Node*>::Element *OW; // owned element
+ List<Node*> owned;
+
+ PauseMode pause_mode;
+ Node *pause_owner;
+ // variables used to properly sort the node when processing, ignored otherwise
+ bool fixed_process;
+ bool idle_process;
+
+ bool input;
+ bool unhandled_input;
+
+ bool parent_owned;
+ bool in_constructor;
+ } data;
+
+
+ void _print_tree(const Node *p_node);
+
+ virtual bool _use_builtin_script() const { return true; }
+ Node *_get_node(const NodePath& p_path) const;
+
+
+ void _validate_child_name(Node *p_name);
+
+ void _propagate_reverse_notification(int p_notification);
+ void _propagate_deferred_notification(int p_notification, bool p_reverse);
+ void _propagate_enter_scene();
+ void _propagate_ready();
+ void _propagate_exit_scene();
+ void _propagate_validate_owner();
+ void _print_stray_nodes();
+ void _propagate_pause_owner(Node*p_owner);
+ Array _get_node_and_resource(const NodePath& p_path);
+
+ void _duplicate_and_reown(Node* p_new_parent, const Map<Node*,Node*>& p_reown_map) const;
+ Array _get_children() const;
+
+friend class SceneMainLoop;
+
+ void _set_scene(SceneMainLoop *p_scene);
+protected:
+
+ void _block() { data.blocked++; }
+ void _unblock() { data.blocked--; }
+
+ void _notification(int p_notification);
+
+ virtual void add_child_notify(Node *p_child);
+ virtual void remove_child_notify(Node *p_child);
+ void remove_and_delete_child(Node *p_child);
+
+ void _propagate_replace_owner(Node *p_owner,Node* p_by_owner);
+
+ static void _bind_methods();
+
+friend class PackedScene;
+
+ void _add_child_nocheck(Node* p_child,const StringName& p_name);
+ void _set_owner_nocheck(Node* p_owner);
+ void _set_name_nocheck(const StringName& p_name);
+
+public:
+
+ enum {
+ // you can make your own, but don't use the same numbers as other notifications in other nodes
+ NOTIFICATION_ENTER_SCENE=10,
+ NOTIFICATION_EXIT_SCENE =11,
+ NOTIFICATION_MOVED_IN_PARENT =12,
+ NOTIFICATION_READY=13,
+ //NOTIFICATION_PARENT_DECONFIGURED =15, - it's confusing, it's going away
+ NOTIFICATION_PAUSED=14,
+ NOTIFICATION_UNPAUSED=15,
+ NOTIFICATION_FIXED_PROCESS = 16,
+ NOTIFICATION_PROCESS = 17,
+ NOTIFICATION_PARENTED=18,
+ NOTIFICATION_UNPARENTED=19,
+ };
+
+ /* NODE/TREE */
+
+ StringName get_name() const;
+ void set_name(const String& p_name);
+
+ void add_child(Node *p_child);
+ void remove_child(Node *p_child);
+
+ int get_child_count() const;
+ Node *get_child(int p_index) const;
+ bool has_node(const NodePath& p_path) const;
+ Node *get_node(const NodePath& p_path) const;
+ bool has_node_and_resource(const NodePath& p_path) const;
+ Node *get_node_and_resource(const NodePath& p_path,RES& r_res) const;
+
+ Node *get_parent() const;
+ _FORCE_INLINE_ SceneMainLoop *get_scene() const { ERR_FAIL_COND_V( !data.scene, NULL ); return data.scene; }
+
+ _FORCE_INLINE_ bool is_inside_scene() const { return data.inside_scene; }
+
+ bool is_a_parent_of(const Node *p_node) const;
+ bool is_greater_than(const Node *p_node) const;
+
+ NodePath get_path() const;
+ NodePath get_path_to(const Node *p_node) const;
+
+ void add_to_group(const StringName& p_identifier,bool p_persistent=false);
+ void remove_from_group(const StringName& p_identifier);
+ bool is_in_group(const StringName& p_identifier) const;
+
+ struct GroupInfo {
+
+ String name;
+ bool persistent;
+ };
+
+ void get_groups(List<GroupInfo> *p_groups) const;
+
+ void move_child(Node *p_child,int p_pos);
+ void raise();
+
+ void set_owner(Node *p_owner);
+ Node *get_owner() const;
+ void get_owned_by(Node *p_by,List<Node*> *p_owned);
+
+
+ void remove_and_skip();
+ int get_index() const;
+
+ void print_tree();
+
+ void set_filename(const String& p_filename);
+ String get_filename() const;
+
+ /* NOTIFICATIONS */
+
+ void propagate_notification(int p_notification);
+
+ /* PROCESSING */
+ void set_fixed_process(bool p_process);
+ float get_fixed_process_delta_time() const;
+ bool is_fixed_processing() const;
+
+ void set_process(bool p_process);
+ float get_process_delta_time() const;
+ bool is_processing() const;
+
+
+ void set_process_input(bool p_enable);
+ bool is_processing_input() const;
+
+ void set_process_unhandled_input(bool p_enable);
+ bool is_processing_unhandled_input() const;
+
+ int get_position_in_parent() const;
+
+ Node *duplicate() const;
+ Node *duplicate_and_reown(const Map<Node*,Node*>& p_reown_map) const;
+ //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;
+
+ static Vector<Variant> make_binds(VARIANT_ARG_LIST);
+
+ void replace_by(Node* p_node,bool p_keep_data=false);
+
+ void set_pause_mode(PauseMode p_mode);
+ PauseMode get_pause_mode() const;
+ bool can_process() const;
+
+ static void print_stray_nodes();
+
+ void queue_delete();
+
+ /* CANVAS */
+
+ Node();
+ ~Node();
+};
+
+
+typedef Set<Node*,Node::Comparator> NodeSet;
+
+
+
+
+#endif
diff --git a/scene/main/resource_preloader.cpp b/scene/main/resource_preloader.cpp
new file mode 100644
index 000000000..8227046ec
--- /dev/null
+++ b/scene/main/resource_preloader.cpp
@@ -0,0 +1,183 @@
+/*************************************************************************/
+/* resource_preloader.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "resource_preloader.h"
+
+void ResourcePreloader::_set_resources(const Array& p_data) {
+
+ resources.clear();
+
+ ERR_FAIL_COND(p_data.size()!=2);
+ DVector<String> names=p_data[0];
+ Array resdata=p_data[1];
+
+ ERR_FAIL_COND(names.size()!=resdata.size());
+
+ for(int i=0;i<resdata.size();i++) {
+
+ String name=names[i];
+ RES resource = resdata[i];
+ ERR_CONTINUE( !resource.is_valid() );
+ resources[name]=resource;
+
+ //add_resource(name,resource);
+ }
+
+}
+
+Array ResourcePreloader::_get_resources() const {
+
+ DVector<String> names;
+ Array arr;
+ arr.resize(resources.size());
+ names.resize(resources.size());
+
+ Set<String> sorted_names;
+
+ for(Map<StringName,RES >::Element *E=resources.front();E;E=E->next()) {
+ sorted_names.insert(E->key());
+ }
+
+ int i=0;
+ for(Set<String>::Element *E=sorted_names.front();E;E=E->next()) {
+
+ names.set(i,E->get());
+ arr[i]=resources[E->get()];
+ i++;
+ }
+
+ Array res;
+ res.push_back(names);
+ res.push_back(arr);
+ return res;
+}
+
+
+void ResourcePreloader::add_resource(const StringName& p_name,const RES& p_resource) {
+
+
+ ERR_FAIL_COND(p_resource.is_null());
+ if (resources.has(p_name)) {
+
+
+ StringName new_name;
+ int idx=2;
+
+ while(true) {
+
+ new_name=p_name.operator String()+" "+itos(idx);
+ if (resources.has(new_name)) {
+ idx++;
+ continue;
+ }
+
+ break;
+
+ }
+
+ add_resource(new_name,p_resource);
+ } else {
+
+ resources[p_name]=p_resource;
+ }
+
+
+
+}
+
+void ResourcePreloader::remove_resource(const StringName& p_name) {
+
+ ERR_FAIL_COND( !resources.has(p_name) );
+ resources.erase(p_name);
+
+}
+void ResourcePreloader::rename_resource(const StringName& p_from_name,const StringName& p_to_name) {
+
+ ERR_FAIL_COND( !resources.has(p_from_name) );
+
+ RES res = resources[p_from_name];
+
+ resources.erase(p_from_name);
+ add_resource(p_to_name,res);
+
+
+
+}
+
+bool ResourcePreloader::has_resource(const StringName& p_name) const {
+
+ return resources.has(p_name);
+}
+RES ResourcePreloader::get_resource(const StringName& p_name) const {
+
+ ERR_FAIL_COND_V(!resources.has(p_name),RES());
+ return resources[p_name];
+}
+
+DVector<String> ResourcePreloader::_get_resource_list() const {
+
+ DVector<String> res;
+ res.resize(resources.size());
+ int i=0;
+ for(Map<StringName,RES >::Element *E=resources.front();E;E=E->next(),i++) {
+ res.set(i,E->key());
+ }
+
+ return res;
+}
+
+void ResourcePreloader::get_resource_list(List<StringName> *p_list) {
+
+ for(Map<StringName,RES >::Element *E=resources.front();E;E=E->next()) {
+
+ p_list->push_back(E->key());
+ }
+
+}
+
+
+void ResourcePreloader::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_set_resources"),&ResourcePreloader::_set_resources);
+ ObjectTypeDB::bind_method(_MD("_get_resources"),&ResourcePreloader::_get_resources);
+
+ ObjectTypeDB::bind_method(_MD("add_resource","name","resource"),&ResourcePreloader::add_resource);
+ ObjectTypeDB::bind_method(_MD("remove_resource","name"),&ResourcePreloader::remove_resource);
+ ObjectTypeDB::bind_method(_MD("rename_resource","name","newname"),&ResourcePreloader::rename_resource);
+ ObjectTypeDB::bind_method(_MD("has_resource","name"),&ResourcePreloader::has_resource);
+ ObjectTypeDB::bind_method(_MD("get_resource","name"),&ResourcePreloader::get_resource);
+ ObjectTypeDB::bind_method(_MD("get_resource_list"),&ResourcePreloader::_get_resource_list);
+
+
+ ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"resources",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR), _SCS("_set_resources"), _SCS("_get_resources"));
+
+}
+
+ResourcePreloader::ResourcePreloader()
+{
+}
diff --git a/scene/main/resource_preloader.h b/scene/main/resource_preloader.h
new file mode 100644
index 000000000..e1e3beb0b
--- /dev/null
+++ b/scene/main/resource_preloader.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* resource_preloader.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef RESOURCE_PRELOADER_H
+#define RESOURCE_PRELOADER_H
+
+
+#include "scene/main/node.h"
+
+class ResourcePreloader : public Node {
+
+ OBJ_TYPE(ResourcePreloader,Node);
+
+ Map<StringName,RES > resources;
+
+
+ void _set_resources(const Array& p_data);
+ Array _get_resources() const;
+ DVector<String> _get_resource_list() const;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void add_resource(const StringName& p_name,const RES& p_resource);
+ void remove_resource(const StringName& p_name);
+ void rename_resource(const StringName& p_from_name,const StringName& p_to_name);
+ bool has_resource(const StringName& p_name) const;
+ RES get_resource(const StringName& p_name) const;
+
+ void get_resource_list(List<StringName> *p_list);
+
+
+ ResourcePreloader();
+};
+
+#endif // RESOURCE_PRELOADER_H
diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp
new file mode 100644
index 000000000..cb3188b3b
--- /dev/null
+++ b/scene/main/scene_main_loop.cpp
@@ -0,0 +1,937 @@
+/*************************************************************************/
+/* scene_main_loop.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "scene_main_loop.h"
+
+#include "print_string.h"
+#include "os/os.h"
+#include "message_queue.h"
+#include "node.h"
+#include "globals.h"
+#include <stdio.h>
+#include "os/keyboard.h"
+#include "servers/spatial_sound_2d_server.h"
+#include "servers/physics_2d_server.h"
+#include "servers/physics_server.h"
+#include "scene/scene_string_names.h"
+#include "io/resource_loader.h"
+#include "viewport.h"
+
+
+void SceneMainLoop::tree_changed() {
+
+ tree_version++;
+ emit_signal(tree_changed_name);
+}
+
+void SceneMainLoop::node_removed(Node *p_node) {
+
+ emit_signal(node_removed_name,p_node);
+ if (call_lock>0)
+ call_skip.insert(p_node);
+
+
+}
+
+
+void SceneMainLoop::add_to_group(const StringName& p_group, Node *p_node) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E) {
+ E=group_map.insert(p_group,Group());
+ }
+
+ if (E->get().nodes.find(p_node)!=-1) {
+ ERR_EXPLAIN("Already in group: "+p_group);
+ ERR_FAIL();
+ }
+ E->get().nodes.push_back(p_node);
+ E->get().last_tree_version=0;
+}
+
+void SceneMainLoop::remove_from_group(const StringName& p_group, Node *p_node) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ ERR_FAIL_COND(!E);
+
+
+ E->get().nodes.erase(p_node);
+ if (E->get().nodes.empty())
+ group_map.erase(E);
+}
+
+void SceneMainLoop::_flush_transform_notifications() {
+
+ SelfList<Node>* n = xform_change_list.first();
+ while(n) {
+
+ Node *node=n->self();
+ SelfList<Node>* nx = n->next();
+ xform_change_list.remove(n);
+ n=nx;
+ node->notification(NOTIFICATION_TRANSFORM_CHANGED);
+ }
+}
+
+void SceneMainLoop::_flush_ugc() {
+
+ ugc_locked=true;
+
+ while (unique_group_calls.size()) {
+
+ Map<UGCall,Vector<Variant> >::Element *E=unique_group_calls.front();
+
+ Variant v[VARIANT_ARG_MAX];
+ for(int i=0;i<E->get().size();i++)
+ v[i]=E->get()[i];
+
+ call_group(GROUP_CALL_REALTIME,E->key().group,E->key().call,v[0],v[1],v[2],v[3],v[4]);
+
+ unique_group_calls.erase(E);
+ }
+
+ ugc_locked=false;
+}
+
+void SceneMainLoop::_update_group_order(Group& g) {
+
+ if (g.last_tree_version==tree_version)
+ return;
+ if (g.nodes.empty())
+ return;
+
+ Node **nodes = &g.nodes[0];
+ int node_count=g.nodes.size();
+
+ SortArray<Node*,Node::Comparator> node_sort;
+ node_sort.sort(nodes,node_count);
+ g.last_tree_version=tree_version;
+}
+
+
+void SceneMainLoop::call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,VARIANT_ARG_DECLARE) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+ Group &g=E->get();
+ if (g.nodes.empty())
+ return;
+
+ _update_group_order(g);
+
+
+ if (p_call_flags&GROUP_CALL_UNIQUE && !(p_call_flags&GROUP_CALL_REALTIME)) {
+
+ ERR_FAIL_COND(ugc_locked);
+
+ UGCall ug;
+ ug.call=p_function;
+ ug.group=p_group;
+
+ if (unique_group_calls.has(ug))
+ return;
+
+ VARIANT_ARGPTRS;
+
+ Vector<Variant> args;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ if (argptr[i]->get_type()==Variant::NIL)
+ break;
+ args.push_back(*argptr[i]);
+ }
+
+ unique_group_calls[ug]=args;
+ return;
+ }
+
+ Vector<Node*> nodes_copy = g.nodes;
+ Node **nodes = &nodes_copy[0];
+ int node_count=nodes_copy.size();
+
+ call_lock++;
+
+ if (p_call_flags&GROUP_CALL_REVERSE) {
+
+ for(int i=node_count-1;i>=0;i--) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME) {
+ if (p_call_flags&GROUP_CALL_MULIILEVEL)
+ nodes[i]->call_multilevel(p_function,VARIANT_ARG_PASS);
+ else
+ nodes[i]->call(p_function,VARIANT_ARG_PASS);
+ } else
+ MessageQueue::get_singleton()->push_call(nodes[i],p_function,VARIANT_ARG_PASS);
+
+ }
+
+ } else {
+
+ for(int i=0;i<node_count;i++) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME) {
+ if (p_call_flags&GROUP_CALL_MULIILEVEL)
+ nodes[i]->call_multilevel(p_function,VARIANT_ARG_PASS);
+ else
+ nodes[i]->call(p_function,VARIANT_ARG_PASS);
+ } else
+ MessageQueue::get_singleton()->push_call(nodes[i],p_function,VARIANT_ARG_PASS);
+ }
+
+ }
+
+ call_lock--;
+ if (call_lock==0)
+ call_skip.clear();
+}
+
+void SceneMainLoop::notify_group(uint32_t p_call_flags,const StringName& p_group,int p_notification) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+ Group &g=E->get();
+ if (g.nodes.empty())
+ return;
+
+ _update_group_order(g);
+
+ Vector<Node*> nodes_copy = g.nodes;
+ Node **nodes = &nodes_copy[0];
+ int node_count=nodes_copy.size();
+
+ call_lock++;
+
+ if (p_call_flags&GROUP_CALL_REVERSE) {
+
+ for(int i=node_count-1;i>=0;i--) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME)
+ nodes[i]->notification(p_notification);
+ else
+ MessageQueue::get_singleton()->push_notification(nodes[i],p_notification);
+ }
+
+ } else {
+
+ for(int i=0;i<node_count;i++) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME)
+ nodes[i]->notification(p_notification);
+ else
+ MessageQueue::get_singleton()->push_notification(nodes[i],p_notification);
+ }
+
+ }
+
+ call_lock--;
+ if (call_lock==0)
+ call_skip.clear();
+}
+
+void SceneMainLoop::set_group(uint32_t p_call_flags,const StringName& p_group,const String& p_name,const Variant& p_value) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+ Group &g=E->get();
+ if (g.nodes.empty())
+ return;
+
+ _update_group_order(g);
+
+ Vector<Node*> nodes_copy = g.nodes;
+ Node **nodes = &nodes_copy[0];
+ int node_count=nodes_copy.size();
+
+ call_lock++;
+
+ if (p_call_flags&GROUP_CALL_REVERSE) {
+
+ for(int i=node_count-1;i>=0;i--) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME)
+ nodes[i]->set(p_name,p_value);
+ else
+ MessageQueue::get_singleton()->push_set(nodes[i],p_name,p_value);
+ }
+
+ } else {
+
+ for(int i=0;i<node_count;i++) {
+
+ if (call_lock && call_skip.has(nodes[i]))
+ continue;
+
+ if (p_call_flags&GROUP_CALL_REALTIME)
+ nodes[i]->set(p_name,p_value);
+ else
+ MessageQueue::get_singleton()->push_set(nodes[i],p_name,p_value);
+ }
+
+ }
+
+ call_lock--;
+ if (call_lock==0)
+ call_skip.clear();
+}
+
+void SceneMainLoop::set_input_as_handled() {
+
+ input_handled=true;
+}
+
+void SceneMainLoop::input_text( const String& p_text ) {
+
+ root_lock++;
+
+ call_group(GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"input",p_text);
+ root_lock--;
+
+}
+
+void SceneMainLoop::input_event( const InputEvent& p_event ) {
+
+
+ root_lock++;
+ last_id=p_event.ID;
+
+ input_handled=false;
+
+
+ InputEvent ev = p_event;
+
+ switch(ev.type) {
+
+ case InputEvent::MOUSE_BUTTON: {
+
+ Matrix32 ai = root->get_final_transform().affine_inverse();
+ Vector2 g = ai.xform(Vector2(ev.mouse_button.global_x,ev.mouse_button.global_y));
+ Vector2 l = ai.xform(Vector2(ev.mouse_button.x,ev.mouse_button.y));
+ ev.mouse_button.x=l.x;
+ ev.mouse_button.y=l.y;
+ ev.mouse_button.global_x=g.x;
+ ev.mouse_button.global_y=g.y;
+
+ } break;
+ case InputEvent::MOUSE_MOTION: {
+
+ Matrix32 ai = root->get_final_transform().affine_inverse();
+ Vector2 g = ai.xform(Vector2(ev.mouse_motion.global_x,ev.mouse_motion.global_y));
+ Vector2 l = ai.xform(Vector2(ev.mouse_motion.x,ev.mouse_motion.y));
+ Vector2 r = ai.xform(Vector2(ev.mouse_motion.relative_x,ev.mouse_motion.relative_y));
+ ev.mouse_motion.x=l.x;
+ ev.mouse_motion.y=l.y;
+ ev.mouse_motion.global_x=g.x;
+ ev.mouse_motion.global_y=g.y;
+ ev.mouse_motion.relative_x=r.x;
+ ev.mouse_motion.relative_y=r.y;
+
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+
+ Matrix32 ai = root->get_final_transform().affine_inverse();
+ Vector2 t = ai.xform(Vector2(ev.screen_touch.x,ev.screen_touch.y));
+ ev.screen_touch.x=t.x;
+ ev.screen_touch.y=t.y;
+
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+
+ Matrix32 ai = root->get_final_transform().affine_inverse();
+ Vector2 t = ai.xform(Vector2(ev.screen_drag.x,ev.screen_drag.y));
+ Vector2 r = ai.xform(Vector2(ev.screen_drag.relative_x,ev.screen_drag.relative_y));
+ Vector2 s = ai.xform(Vector2(ev.screen_drag.speed_x,ev.screen_drag.speed_y));
+ ev.screen_drag.x=t.x;
+ ev.screen_drag.y=t.y;
+ ev.screen_drag.relative_x=r.x;
+ ev.screen_drag.relative_y=r.y;
+ ev.screen_drag.speed_x=s.x;
+ ev.screen_drag.speed_y=s.y;
+ } break;
+ }
+
+
+
+ MainLoop::input_event(p_event);
+
+ _call_input_pause("input","_input",ev);
+
+
+
+
+ call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"_gui_input","_gui_input",p_event); //special one for GUI, as controls use their own process check
+
+ //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"input","_input",ev);
+
+ /*if (ev.type==InputEvent::KEY && ev.key.pressed && !ev.key.echo && ev.key.scancode==KEY_F12) {
+
+ print_line("RAM: "+itos(Memory::get_static_mem_usage()));
+ print_line("DRAM: "+itos(Memory::get_dynamic_mem_usage()));
+ }
+*/
+ //if (ev.type==InputEvent::KEY && ev.key.pressed && !ev.key.echo && ev.key.scancode==KEY_F11) {
+
+ // Memory::dump_static_mem_to_file("memdump.txt");
+ //}
+
+ //transform for the rest
+
+ if (ScriptDebugger::get_singleton() && ScriptDebugger::get_singleton()->is_remote() && ev.type==InputEvent::KEY && ev.key.pressed && !ev.key.echo && ev.key.scancode==KEY_F8) {
+
+ ScriptDebugger::get_singleton()->request_quit();
+ }
+
+ _flush_ugc();
+ root_lock--;
+ MessageQueue::get_singleton()->flush(); //small little hack
+
+ root_lock++;
+
+ if (!input_handled) {
+ _call_input_pause("unhandled_input","_unhandled_input",ev);
+ //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_input","_unhandled_input",ev);
+ if (!input_handled && ev.type==InputEvent::KEY) {
+ _call_input_pause("unhandled_key_input","_unhandled_key_input",ev);
+ //call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_key_input","_unhandled_key_input",ev);
+ }
+
+ input_handled=true;
+ _flush_ugc();
+ root_lock--;
+ MessageQueue::get_singleton()->flush(); //small little hack
+ } else {
+ input_handled=true;
+ root_lock--;
+
+ }
+
+}
+
+void SceneMainLoop::init() {
+
+ //_quit=false;
+ accept_quit=true;
+ initialized=true;
+ input_handled=false;
+
+
+ editor_hint=false;
+ pause=false;
+
+ root->_set_scene(this);
+ MainLoop::init();
+
+}
+
+bool SceneMainLoop::iteration(float p_time) {
+
+
+ root_lock++;
+
+ current_frame++;
+
+ _flush_transform_notifications();
+
+ MainLoop::iteration(p_time);
+
+ fixed_process_time=p_time;
+ _notify_group_pause("fixed_process",Node::NOTIFICATION_FIXED_PROCESS);
+ _flush_ugc();
+ _flush_transform_notifications();
+ call_group(GROUP_CALL_REALTIME,"_viewports","update_worlds");
+ root_lock--;
+
+ _flush_delete_queue();
+
+ return _quit;
+}
+
+bool SceneMainLoop::idle(float p_time){
+
+
+// print_line("ram: "+itos(OS::get_singleton()->get_static_memory_usage())+" sram: "+itos(OS::get_singleton()->get_dynamic_memory_usage()));
+// print_line("node count: "+itos(get_node_count()));
+// print_line("TEXTURE RAM: "+itos(VS::get_singleton()->get_render_info(VS::INFO_TEXTURE_MEM_USED)));
+
+ root_lock++;
+
+ MainLoop::idle(p_time);
+
+ idle_process_time=p_time;
+
+ _flush_transform_notifications();
+
+ _notify_group_pause("idle_process",Node::NOTIFICATION_PROCESS);
+
+ Size2 win_size=Size2( OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height );
+ if(win_size!=last_screen_size) {
+
+
+ last_screen_size=win_size;
+ root->set_rect(Rect2(Point2(),last_screen_size));
+
+ emit_signal("screen_resized");
+
+ }
+
+ _flush_ugc();
+ _flush_transform_notifications(); //transforms after world update, to avoid unnecesary enter/exit notifications
+ call_group(GROUP_CALL_REALTIME,"_viewports","update_worlds");
+
+ root_lock--;
+
+ _flush_delete_queue();
+
+ return _quit;
+}
+
+void SceneMainLoop::finish() {
+
+ _flush_delete_queue();
+
+ _flush_ugc();
+
+ initialized=false;
+
+ MainLoop::finish();
+
+ if (root) {
+ root->_set_scene(NULL);
+ memdelete(root); //delete root
+ }
+
+
+
+
+
+
+
+
+
+}
+
+
+void SceneMainLoop::quit() {
+
+ _quit=true;
+}
+
+void SceneMainLoop::_notification(int p_notification) {
+
+
+
+ switch (p_notification) {
+
+ case NOTIFICATION_WM_QUIT_REQUEST: {
+
+ get_root()->propagate_notification(p_notification);
+
+ if (accept_quit) {
+ _quit=true;
+ break;
+ }
+ } break;
+ case NOTIFICATION_WM_FOCUS_IN:
+ case NOTIFICATION_WM_FOCUS_OUT: {
+
+ get_root()->propagate_notification(p_notification);
+ } break;
+ case NOTIFICATION_WM_UNFOCUS_REQUEST: {
+
+ notify_group(GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"input",NOTIFICATION_WM_UNFOCUS_REQUEST);
+
+ } break;
+
+ default:
+ break;
+ };
+};
+
+
+void SceneMainLoop::set_auto_accept_quit(bool p_enable) {
+
+ accept_quit=p_enable;
+}
+
+void SceneMainLoop::set_editor_hint(bool p_enabled) {
+
+ editor_hint=p_enabled;
+}
+
+bool SceneMainLoop::is_editor_hint() const {
+
+ return editor_hint;
+}
+
+void SceneMainLoop::set_pause(bool p_enabled) {
+
+ if (p_enabled==pause)
+ return;
+ pause=p_enabled;
+ PhysicsServer::get_singleton()->set_active(!p_enabled);
+ Physics2DServer::get_singleton()->set_active(!p_enabled);
+ if (get_root())
+ get_root()->propagate_notification(p_enabled ? Node::NOTIFICATION_PAUSED : Node::NOTIFICATION_UNPAUSED);
+}
+
+bool SceneMainLoop::is_paused() const {
+
+ return pause;
+}
+
+void SceneMainLoop::_call_input_pause(const StringName& p_group,const StringName& p_method,const InputEvent& p_input) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+ Group &g=E->get();
+ if (g.nodes.empty())
+ return;
+
+ _update_group_order(g);
+
+ //copy, so copy on write happens in case something is removed from process while being called
+ //performance is not lost because only if something is added/removed the vector is copied.
+ Vector<Node*> nodes_copy = g.nodes;
+
+ int node_count=nodes_copy.size();
+ Node **nodes = &nodes_copy[0];
+
+ Variant arg=p_input;
+ const Variant *v[1]={&arg};
+
+ call_lock++;
+
+ for(int i=node_count-1;i>=0;i--) {
+
+ if (input_handled)
+ break;
+
+ Node *n = nodes[i];
+ if (call_lock && call_skip.has(n))
+ continue;
+
+ if (!n->can_process())
+ continue;
+
+ Variant::CallError ce;
+ n->call_multilevel(p_method,(const Variant**)v,1);
+ //ERR_FAIL_COND(node_count != g.nodes.size());
+ }
+
+ call_lock--;
+ if (call_lock==0)
+ call_skip.clear();
+}
+
+void SceneMainLoop::_notify_group_pause(const StringName& p_group,int p_notification) {
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+ Group &g=E->get();
+ if (g.nodes.empty())
+ return;
+
+
+ _update_group_order(g);
+
+ //copy, so copy on write happens in case something is removed from process while being called
+ //performance is not lost because only if something is added/removed the vector is copied.
+ Vector<Node*> nodes_copy = g.nodes;
+
+ int node_count=nodes_copy.size();
+ Node **nodes = &nodes_copy[0];
+
+ call_lock++;
+
+ for(int i=0;i<node_count;i++) {
+
+ Node *n = nodes[i];
+ if (call_lock && call_skip.has(n))
+ continue;
+
+ if (!n->can_process())
+ continue;
+
+ n->notification(p_notification);
+ //ERR_FAIL_COND(node_count != g.nodes.size());
+ }
+
+ call_lock--;
+ if (call_lock==0)
+ call_skip.clear();
+}
+
+/*
+void SceneMainLoop::_update_listener_2d() {
+
+ if (listener_2d.is_valid()) {
+
+ SpatialSound2DServer::get_singleton()->listener_set_space( listener_2d, world_2d->get_sound_space() );
+ }
+
+}
+*/
+
+uint32_t SceneMainLoop::get_last_event_id() const {
+
+ return last_id;
+}
+
+
+Variant SceneMainLoop::_call_group(const Variant** p_args, int p_argcount, Variant::CallError& r_error) {
+
+
+ r_error.error=Variant::CallError::CALL_OK;
+
+ ERR_FAIL_COND_V(p_argcount<3,Variant());
+ ERR_FAIL_COND_V(!p_args[0]->is_num(),Variant());
+ ERR_FAIL_COND_V(p_args[1]->get_type()!=Variant::STRING,Variant());
+ ERR_FAIL_COND_V(p_args[2]->get_type()!=Variant::STRING,Variant());
+
+ int flags = *p_args[0];
+ StringName group = *p_args[1];
+ StringName method = *p_args[2];
+ Variant v[VARIANT_ARG_MAX];
+
+ for(int i=0;i<MIN(p_argcount-3,5);i++) {
+
+ v[i]=*p_args[i+3];
+ }
+
+ call_group(flags,group,method,v[0],v[1],v[2],v[3],v[4]);
+ return Variant();
+}
+
+
+int64_t SceneMainLoop::get_frame() const {
+
+ return current_frame;
+}
+
+
+Array SceneMainLoop::_get_nodes_in_group(const StringName& p_group) {
+
+ Array ret;
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return ret;
+
+ _update_group_order(E->get()); //update order just in case
+ int nc = E->get().nodes.size();
+ if (nc==0)
+ return ret;
+
+ ret.resize(nc);
+
+ Node **ptr = E->get().nodes.ptr();
+ for(int i=0;i<nc;i++) {
+
+ ret[i]=ptr[i];
+ }
+
+ return ret;
+}
+
+void SceneMainLoop::get_nodes_in_group(const StringName& p_group,List<Node*> *p_list) {
+
+
+ Map<StringName,Group>::Element *E=group_map.find(p_group);
+ if (!E)
+ return;
+
+ _update_group_order(E->get()); //update order just in case
+ int nc = E->get().nodes.size();
+ if (nc==0)
+ return;
+ Node **ptr = E->get().nodes.ptr();
+ for(int i=0;i<nc;i++) {
+
+ p_list->push_back(ptr[i]);
+ }
+}
+
+
+static void _fill_array(Node *p_node, Array& array, int p_level) {
+
+ array.push_back(p_level);
+ array.push_back(p_node->get_name());
+ array.push_back(p_node->get_type());
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _fill_array(p_node->get_child(i),array,p_level+1);
+ }
+}
+
+void SceneMainLoop::_debugger_request_tree(void *self) {
+
+ SceneMainLoop *sml = (SceneMainLoop *)self;
+
+ Array arr;
+ _fill_array(sml->root,arr,0);
+ ScriptDebugger::get_singleton()->send_message("scene_tree",arr);
+}
+
+
+void SceneMainLoop::_flush_delete_queue() {
+
+ _THREAD_SAFE_METHOD_
+
+ while( delete_queue.size() ) {
+
+ Object *obj = ObjectDB::get_instance( delete_queue.front()->get() );
+ if (obj) {
+ memdelete( obj );
+ }
+ delete_queue.pop_front();
+ }
+}
+
+void SceneMainLoop::queue_delete(Object *p_object) {
+
+ _THREAD_SAFE_METHOD_
+ ERR_FAIL_NULL(p_object);
+ delete_queue.push_back(p_object->get_instance_ID());
+}
+
+
+int SceneMainLoop::get_node_count() const {
+
+ return node_count;
+}
+
+void SceneMainLoop::_bind_methods() {
+
+
+ //ObjectTypeDB::bind_method(_MD("call_group","call_flags","group","method","arg1","arg2"),&SceneMainLoop::_call_group,DEFVAL(Variant()),DEFVAL(Variant()));
+ ObjectTypeDB::bind_method(_MD("notify_group","call_flags","group","notification"),&SceneMainLoop::notify_group);
+ ObjectTypeDB::bind_method(_MD("set_group","call_flags","group","property","value"),&SceneMainLoop::set_group);
+
+ ObjectTypeDB::bind_method(_MD("get_nodes_in_group"),&SceneMainLoop::_get_nodes_in_group);
+
+ ObjectTypeDB::bind_method(_MD("get_root:Viewport"),&SceneMainLoop::get_root);
+
+ ObjectTypeDB::bind_method(_MD("set_auto_accept_quit","enabled"),&SceneMainLoop::set_auto_accept_quit);
+
+ ObjectTypeDB::bind_method(_MD("set_editor_hint","enable"),&SceneMainLoop::set_editor_hint);
+ ObjectTypeDB::bind_method(_MD("is_editor_hint"),&SceneMainLoop::is_editor_hint);
+
+ ObjectTypeDB::bind_method(_MD("set_pause","enable"),&SceneMainLoop::set_pause);
+ ObjectTypeDB::bind_method(_MD("is_paused"),&SceneMainLoop::is_paused);
+ ObjectTypeDB::bind_method(_MD("set_input_as_handled"),&SceneMainLoop::set_input_as_handled);
+
+
+ ObjectTypeDB::bind_method(_MD("get_node_count"),&SceneMainLoop::get_node_count);
+ ObjectTypeDB::bind_method(_MD("get_frame"),&SceneMainLoop::get_frame);
+ ObjectTypeDB::bind_method(_MD("quit"),&SceneMainLoop::quit);
+
+ ObjectTypeDB::bind_method(_MD("queue_delete","obj"),&SceneMainLoop::queue_delete);
+
+
+ MethodInfo mi;
+ mi.name="call_group";
+ mi.arguments.push_back( PropertyInfo( Variant::INT, "flags"));
+ mi.arguments.push_back( PropertyInfo( Variant::STRING, "group"));
+ mi.arguments.push_back( PropertyInfo( Variant::STRING, "method"));
+ Vector<Variant> defargs;
+ for(int i=0;i<VARIANT_ARG_MAX;i++) {
+ mi.arguments.push_back( PropertyInfo( Variant::NIL, "arg"+itos(i)));
+ defargs.push_back(Variant());
+ }
+
+ ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"call_group",&SceneMainLoop::_call_group,mi,defargs);
+
+ ADD_SIGNAL( MethodInfo("tree_changed") );
+ ADD_SIGNAL( MethodInfo("node_removed",PropertyInfo( Variant::OBJECT, "node") ) );
+ ADD_SIGNAL( MethodInfo("screen_resized") );
+
+ BIND_CONSTANT( GROUP_CALL_DEFAULT );
+ BIND_CONSTANT( GROUP_CALL_REVERSE );
+ BIND_CONSTANT( GROUP_CALL_REALTIME );
+ BIND_CONSTANT( GROUP_CALL_UNIQUE );
+
+}
+
+SceneMainLoop::SceneMainLoop() {
+
+ _quit=false;
+ initialized=false;
+ tree_version=1;
+ fixed_process_time=1;
+ idle_process_time=1;
+ last_id=0;
+ root=NULL;
+ current_frame=0;
+ tree_changed_name="tree_changed";
+ node_removed_name="node_removed";
+ ugc_locked=false;
+ call_lock=0;
+ root_lock=0;
+ node_count=0;
+
+ //create with mainloop
+
+ root = memnew( Viewport );
+ root->set_name("root");
+ root->set_world( Ref<World>( memnew( World )));
+ //root->set_world_2d( Ref<World2D>( memnew( World2D )));
+ root->set_as_audio_listener(true);
+ root->set_as_audio_listener_2d(true);
+
+ last_screen_size=Size2( OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height );
+ root->set_rect(Rect2(Point2(),last_screen_size));
+
+ if (ScriptDebugger::get_singleton()) {
+ ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree,this);
+ }
+
+
+}
+
+
+SceneMainLoop::~SceneMainLoop() {
+
+
+}
diff --git a/scene/main/scene_main_loop.h b/scene/main/scene_main_loop.h
new file mode 100644
index 000000000..a3776152c
--- /dev/null
+++ b/scene/main/scene_main_loop.h
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* scene_main_loop.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef SCENE_MAIN_LOOP_H
+#define SCENE_MAIN_LOOP_H
+
+
+#include "os/main_loop.h"
+#include "scene/resources/world.h"
+#include "scene/resources/world_2d.h"
+#include "scene/main/scene_singleton.h"
+#include "os/thread_safe.h"
+#include "self_list.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+class SceneMainLoop;
+
+class Node;
+class Viewport;
+class SceneMainLoop : public MainLoop {
+
+ _THREAD_SAFE_CLASS_
+
+ OBJ_TYPE( SceneMainLoop, MainLoop );
+
+ struct Group {
+
+ Vector<Node*> nodes;
+ uint64_t last_tree_version;
+ Group() { last_tree_version=0; };
+ };
+
+ Viewport *root;
+
+ uint64_t tree_version;
+ float fixed_process_time;
+ float idle_process_time;
+ bool accept_quit;
+ uint32_t last_id;
+
+ bool editor_hint;
+ bool pause;
+ int root_lock;
+
+ Map<StringName,Group> group_map;
+ bool _quit;
+ bool initialized;
+ bool input_handled;
+ Size2 last_screen_size;
+ StringName tree_changed_name;
+ StringName node_removed_name;
+
+
+ int64_t current_frame;
+ int node_count;
+
+ struct UGCall {
+
+ StringName group;
+ StringName call;
+
+ bool operator<(const UGCall& p_with) const { return group==p_with.group?call<p_with.call:group<p_with.group; }
+ };
+
+ //safety for when a node is deleted while a group is being called
+ int call_lock;
+ Set<Node*> call_skip; //skip erased nodes
+
+
+ List<ObjectID> delete_queue;
+
+ Map<UGCall,Vector<Variant> > unique_group_calls;
+ bool ugc_locked;
+ void _flush_ugc();
+ void _flush_transform_notifications();
+
+ void _update_group_order(Group& g);
+ void _update_listener();
+
+ Array _get_nodes_in_group(const StringName& p_group);
+
+
+ //void _call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,const Variant& p_arg1,const Variant& p_arg2);
+
+friend class Node;
+
+ void tree_changed();
+ void node_removed(Node *p_node);
+
+
+ void add_to_group(const StringName& p_group, Node *p_node);
+ void remove_from_group(const StringName& p_group, Node *p_node);
+
+ void _notify_group_pause(const StringName& p_group,int p_notification);
+ void _call_input_pause(const StringName& p_group,const StringName& p_method,const InputEvent& p_input);
+ Variant _call_group(const Variant** p_args, int p_argcount, Variant::CallError& r_error);
+
+
+ static void _debugger_request_tree(void *self);
+ void _flush_delete_queue();
+//optimization
+friend class CanvasItem;
+friend class Spatial;
+ SelfList<Node>::List xform_change_list;
+
+protected:
+
+ void _notification(int p_notification);
+ static void _bind_methods();
+
+public:
+
+ enum {
+ NOTIFICATION_TRANSFORM_CHANGED=29
+ };
+
+ enum CallGroupFlags {
+ GROUP_CALL_DEFAULT=0,
+ GROUP_CALL_REVERSE=1,
+ GROUP_CALL_REALTIME=2,
+ GROUP_CALL_UNIQUE=4,
+ GROUP_CALL_MULIILEVEL=8,
+ };
+
+
+ _FORCE_INLINE_ Viewport *get_root() const { return root; }
+
+ uint32_t get_last_event_id() const;
+
+ void call_group(uint32_t p_call_flags,const StringName& p_group,const StringName& p_function,VARIANT_ARG_LIST);
+ void notify_group(uint32_t p_call_flags,const StringName& p_group,int p_notification);
+ void set_group(uint32_t p_call_flags,const StringName& p_group,const String& p_name,const Variant& p_value);
+
+
+ virtual void input_text( const String& p_text );
+ virtual void input_event( const InputEvent& p_event );
+ virtual void init();
+
+ virtual bool iteration(float p_time);
+ virtual bool idle(float p_time);
+
+ virtual void finish();
+
+ void set_auto_accept_quit(bool p_enable);
+
+ void quit();
+
+ void set_input_as_handled();
+ _FORCE_INLINE_ float get_fixed_process_time() const { return fixed_process_time; }
+ _FORCE_INLINE_ float get_idle_process_time() const { return idle_process_time; }
+
+ void set_editor_hint(bool p_enabled);
+ bool is_editor_hint() const;
+
+ void set_pause(bool p_enabled);
+ bool is_paused() const;
+
+ void set_camera(const RID& p_camera);
+ RID get_camera() const;
+
+ int64_t get_frame() const;
+
+ int get_node_count() const;
+
+ void queue_delete(Object *p_object);
+
+ void get_nodes_in_group(const StringName& p_group,List<Node*> *p_list);
+
+ SceneMainLoop();
+ ~SceneMainLoop();
+
+};
+
+
+#endif
diff --git a/scene/main/scene_singleton.cpp b/scene/main/scene_singleton.cpp
new file mode 100644
index 000000000..042164a1f
--- /dev/null
+++ b/scene/main/scene_singleton.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* scene_singleton.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "scene_singleton.h"
+
diff --git a/scene/main/scene_singleton.h b/scene/main/scene_singleton.h
new file mode 100644
index 000000000..1e0359fe9
--- /dev/null
+++ b/scene/main/scene_singleton.h
@@ -0,0 +1,36 @@
+/*************************************************************************/
+/* scene_singleton.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef SCENE_SINGLETON_H
+#define SCENE_SINGLETON_H
+
+
+#include "reference.h"
+
+
+#endif // SCENE_SINGLETON_H
diff --git a/scene/main/timer.cpp b/scene/main/timer.cpp
new file mode 100644
index 000000000..25d1c8530
--- /dev/null
+++ b/scene/main/timer.cpp
@@ -0,0 +1,140 @@
+/*************************************************************************/
+/* timer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "timer.h"
+
+
+void Timer::_notification(int p_what) {
+
+ switch(p_what) {
+
+
+ case NOTIFICATION_READY: {
+
+ if (autostart)
+ start();
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+ time_left -= get_process_delta_time();
+
+ if (time_left<0) {
+ if (!one_shot)
+ time_left=wait_time+time_left;
+ else
+ stop();
+
+ emit_signal("timeout");
+ }
+
+ } break;
+ }
+}
+
+
+
+void Timer::set_wait_time(float p_time) {
+
+ ERR_EXPLAIN("time should be greater than zero.");
+ ERR_FAIL_COND(p_time<=0);
+ wait_time=p_time;
+
+}
+float Timer::get_wait_time() const {
+
+ return wait_time;
+}
+
+void Timer::set_one_shot(bool p_one_shot) {
+
+ one_shot=p_one_shot;
+}
+bool Timer::is_one_shot() const {
+
+ return one_shot;
+}
+
+void Timer::set_autostart(bool p_start) {
+
+ autostart=p_start;
+}
+bool Timer::has_autostart() const {
+
+ return autostart;
+}
+
+void Timer::start() {
+
+ time_left=wait_time;
+ set_process(true);
+}
+
+void Timer::stop() {
+ time_left=-1;
+ set_process(false);
+ autostart=false;
+}
+
+float Timer::get_time_left() const {
+
+ return time_left >0 ? time_left : 0;
+}
+
+
+void Timer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_wait_time","time_sec"),&Timer::set_wait_time);
+ ObjectTypeDB::bind_method(_MD("get_wait_time"),&Timer::get_wait_time);
+
+ ObjectTypeDB::bind_method(_MD("set_one_shot","enable"),&Timer::set_one_shot);
+ ObjectTypeDB::bind_method(_MD("is_one_shot"),&Timer::is_one_shot);
+
+ ObjectTypeDB::bind_method(_MD("set_autostart","enable"),&Timer::set_autostart);
+ ObjectTypeDB::bind_method(_MD("has_autostart"),&Timer::has_autostart);
+
+ ObjectTypeDB::bind_method(_MD("start"),&Timer::start);
+ ObjectTypeDB::bind_method(_MD("stop"),&Timer::stop);
+
+ ObjectTypeDB::bind_method(_MD("get_time_left"),&Timer::get_time_left);
+
+ ADD_SIGNAL( MethodInfo("timeout") );
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL, "wait_time", PROPERTY_HINT_EXP_RANGE, "0.01,4096,0.01" ), _SCS("set_wait_time"), _SCS("get_wait_time") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL, "one_shot" ), _SCS("set_one_shot"), _SCS("is_one_shot") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL, "autostart" ), _SCS("set_autostart"), _SCS("has_autostart") );
+
+}
+
+Timer::Timer() {
+
+
+ autostart=false;
+ wait_time=1;
+ one_shot=false;
+ time_left=-1;
+}
diff --git a/scene/main/timer.h b/scene/main/timer.h
new file mode 100644
index 000000000..b89cd1066
--- /dev/null
+++ b/scene/main/timer.h
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* timer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef TIMER_H
+#define TIMER_H
+
+#include "scene/main/node.h"
+
+class Timer : public Node {
+
+ OBJ_TYPE( Timer, Node );
+
+ float wait_time;
+ bool one_shot;
+ bool autostart;
+
+ double time_left;
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_wait_time(float p_time);
+ float get_wait_time() const;
+
+ void set_one_shot(bool p_one_shot);
+ bool is_one_shot() const;
+
+ void set_autostart(bool p_start);
+ bool has_autostart() const;
+
+ void start();
+ void stop();
+
+ float get_time_left() const;
+
+ Timer();
+};
+
+#endif // TIMER_H
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
new file mode 100644
index 000000000..ae2b92500
--- /dev/null
+++ b/scene/main/viewport.cpp
@@ -0,0 +1,834 @@
+/*************************************************************************/
+/* viewport.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#include "viewport.h"
+#include "os/os.h"
+#include "scene/3d/spatial.h"
+//#include "scene/3d/camera.h"
+
+#include "servers/spatial_sound_server.h"
+#include "servers/spatial_sound_2d_server.h"
+#include "scene/gui/control.h"
+#include "scene/3d/camera.h"
+#include "scene/3d/spatial_indexer.h"
+
+
+
+int RenderTargetTexture::get_width() const {
+
+ ERR_FAIL_COND_V(!vp,0);
+ return vp->rect.size.width;
+}
+int RenderTargetTexture::get_height() const{
+
+ ERR_FAIL_COND_V(!vp,0);
+ return vp->rect.size.height;
+}
+Size2 RenderTargetTexture::get_size() const{
+
+ ERR_FAIL_COND_V(!vp,Size2());
+ return vp->rect.size;
+}
+RID RenderTargetTexture::get_rid() const{
+
+ ERR_FAIL_COND_V(!vp,RID());
+ return vp->render_target_texture_rid;
+}
+
+bool RenderTargetTexture::has_alpha() const{
+
+ return false;
+}
+
+void RenderTargetTexture::set_flags(uint32_t p_flags){
+
+
+}
+uint32_t RenderTargetTexture::get_flags() const{
+
+ return 0;
+}
+
+RenderTargetTexture::RenderTargetTexture(Viewport *p_vp){
+
+ vp=p_vp;
+}
+
+
+
+void Viewport::_update_stretch_transform() {
+
+ if (size_override_stretch && size_override) {
+
+ stretch_transform=Matrix32();
+ stretch_transform.scale(rect.size/(size_override_size+size_override_margin*2));
+ stretch_transform.elements[2]=size_override_margin;
+
+ } else {
+
+ stretch_transform=Matrix32();
+ }
+
+ _update_global_transform();
+
+}
+
+void Viewport::_update_rect() {
+
+ if (!is_inside_scene())
+ return;
+
+ Node *parent = get_parent();
+
+ if (!render_target && parent && parent->cast_to<Control>()) {
+
+ Control *c = parent->cast_to<Control>();
+
+ rect.pos=Point2();
+ rect.size=c->get_size();
+ }
+
+ VisualServer::ViewportRect vr;
+ vr.x=rect.pos.x;
+ vr.y=rect.pos.y;
+ if (render_target) {
+ vr.x=0;
+ vr.y=0;
+ }
+ vr.width=rect.size.width;
+ vr.height=rect.size.height;
+ VisualServer::get_singleton()->viewport_set_rect(viewport,vr);
+
+ if (canvas_item.is_valid()) {
+ VisualServer::get_singleton()->canvas_item_set_custom_rect(canvas_item,true,rect);
+ }
+
+ emit_signal("size_changed");
+}
+
+void Viewport::_parent_resized() {
+
+ _update_rect();
+}
+
+void Viewport::_parent_draw() {
+
+}
+
+void Viewport::_parent_visibility_changed() {
+
+ Node *parent = get_parent();
+
+ if (parent && parent->cast_to<Control>()) {
+
+ Control *c = parent->cast_to<Control>();
+ VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,c->is_visible());
+ }
+
+
+}
+
+
+void Viewport::_vp_enter_scene() {
+
+ Node *parent = get_parent();
+ //none?
+ if (parent && parent->cast_to<Control>()) {
+
+ Control *cparent=parent->cast_to<Control>();
+ RID parent_ci = cparent->get_canvas_item();
+ ERR_FAIL_COND(!parent_ci.is_valid());
+ canvas_item = VisualServer::get_singleton()->canvas_item_create();
+
+ VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,parent_ci);
+ VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,false);
+ VisualServer::get_singleton()->canvas_item_attach_viewport(canvas_item,viewport);
+ parent->connect("resized",this,"_parent_resized");
+ parent->connect("visibility_changed",this,"_parent_visibility_changed");
+ } else if (!parent){
+
+ VisualServer::get_singleton()->viewport_attach_to_screen(viewport,0);
+
+ }
+
+
+}
+
+void Viewport::_vp_exit_scene() {
+
+ Node *parent = get_parent();
+ if (parent && parent->cast_to<Control>()) {
+
+ parent->disconnect("resized",this,"_parent_resized");
+ }
+
+ if (parent && parent->cast_to<Control>()) {
+
+ parent->disconnect("visibility_changed",this,"_parent_visibility_changed");
+ }
+
+ if (canvas_item.is_valid()) {
+
+ VisualServer::get_singleton()->free(canvas_item);
+ canvas_item=RID();
+
+ }
+
+ if (!parent) {
+
+ VisualServer::get_singleton()->viewport_detach(viewport);
+
+ }
+
+}
+
+
+void Viewport::update_worlds() {
+
+ if (!is_inside_scene())
+ return;
+
+ Rect2 xformed_rect = (global_canvas_transform * canvas_transform).affine_inverse().xform(get_visible_rect());
+ find_world_2d()->_update_viewport(this,xformed_rect);
+ find_world_2d()->_update();
+
+ find_world()->_update(get_scene()->get_frame());
+}
+
+void Viewport::_notification(int p_what) {
+
+
+ switch( p_what ) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+
+ if (!render_target)
+ _vp_enter_scene();
+
+ this->parent=NULL;
+ Node *parent=get_parent();
+
+ if (parent) {
+
+
+ while(parent && !(this->parent=parent->cast_to<Viewport>())) {
+
+ parent=parent->get_parent();
+ }
+ }
+
+ current_canvas=find_world_2d()->get_canvas();
+ VisualServer::get_singleton()->viewport_set_scenario(viewport,find_world()->get_scenario());
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport,current_canvas);
+
+ _update_listener();
+ _update_listener_2d();
+ _update_rect();
+
+ if (world_2d.is_valid()) {
+ find_world_2d()->_register_viewport(this,Rect2());
+//best to defer this and not do it here, as it can annoy a lot of setup logic if user
+//adds a node and then moves it, will get enter/exit screen/viewport notifications
+//unnecesarily
+// update_worlds();
+ }
+
+ add_to_group("_viewports");
+
+ } break;
+ case NOTIFICATION_READY: {
+
+ if (cameras.size() && !camera) {
+ //there are cameras but no current camera, pick first in tree and make it current
+ Camera *first=NULL;
+ for(Set<Camera*>::Element *E=cameras.front();E;E=E->next()) {
+
+ if (first==NULL || first->is_greater_than(E->get())) {
+ first=E->get();
+ }
+ }
+
+ if (first)
+ first->make_current();
+ }
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+
+
+ if (world_2d.is_valid())
+ world_2d->_remove_viewport(this);
+
+ if (!render_target)
+ _vp_exit_scene();
+
+ VisualServer::get_singleton()->viewport_set_scenario(viewport,RID());
+ SpatialSoundServer::get_singleton()->listener_set_space(listener,RID());
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport,current_canvas);
+ remove_from_group("_viewports");
+
+ } break;
+ }
+}
+
+RID Viewport::get_viewport() const {
+
+ return viewport;
+}
+
+void Viewport::set_rect(const Rect2& p_rect) {
+
+ if (rect==p_rect)
+ return;
+ rect=p_rect;
+ _update_rect();
+ _update_stretch_transform();
+
+}
+
+Rect2 Viewport::get_visible_rect() const {
+
+
+ Rect2 r;
+
+ if (rect.pos==Vector2() && rect.size==Size2()) {
+
+ r=Rect2( Point2(), Size2( OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height ) );
+ } else {
+
+ r=Rect2( rect.pos , rect.size );
+ }
+
+ if (size_override) {
+ r.size=size_override_size;
+ }
+
+
+ return r;
+}
+
+Rect2 Viewport::get_rect() const {
+
+ return rect;
+}
+
+
+void Viewport::_update_listener() {
+
+ if (is_inside_scene() && audio_listener && camera) {
+ SpatialSoundServer::get_singleton()->listener_set_space(listener,find_world()->get_sound_space());
+ } else {
+ SpatialSoundServer::get_singleton()->listener_set_space(listener,RID());
+ }
+
+
+}
+
+void Viewport::_update_listener_2d() {
+
+ if (is_inside_scene() && audio_listener_2d)
+ SpatialSound2DServer::get_singleton()->listener_set_space(listener_2d,find_world_2d()->get_sound_space());
+ else
+ SpatialSound2DServer::get_singleton()->listener_set_space(listener_2d,RID());
+
+}
+
+
+void Viewport::set_as_audio_listener(bool p_enable) {
+
+ if (p_enable==audio_listener)
+ return;
+
+ audio_listener=p_enable;
+ _update_listener();
+
+}
+
+bool Viewport::is_audio_listener() const {
+
+ return audio_listener;
+}
+
+void Viewport::set_as_audio_listener_2d(bool p_enable) {
+
+ if (p_enable==audio_listener_2d)
+ return;
+
+ audio_listener_2d=p_enable;
+
+ _update_listener_2d();
+
+
+}
+
+bool Viewport::is_audio_listener_2d() const {
+
+ return audio_listener_2d;
+}
+
+void Viewport::set_canvas_transform(const Matrix32& p_transform) {
+
+ canvas_transform=p_transform;
+ VisualServer::get_singleton()->viewport_set_canvas_transform(viewport,find_world_2d()->get_canvas(),canvas_transform);
+
+ Matrix32 xform = (global_canvas_transform * canvas_transform).affine_inverse();
+ Size2 ss = get_visible_rect().size;
+ SpatialSound2DServer::get_singleton()->listener_set_transform(listener_2d,Matrix32(0,xform.xform(ss*0.5)));
+ Vector2 ss2 = ss*xform.get_scale();
+ float panrange = MAX(ss2.x,ss2.y);
+
+ SpatialSound2DServer::get_singleton()->listener_set_param(listener_2d,SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE,panrange);
+
+
+}
+
+Matrix32 Viewport::get_canvas_transform() const{
+
+ return canvas_transform;
+}
+
+
+
+void Viewport::_update_global_transform() {
+
+
+ Matrix32 sxform = stretch_transform * global_canvas_transform;
+
+ VisualServer::get_singleton()->viewport_set_global_canvas_transform(viewport,sxform);
+
+ Matrix32 xform = (sxform * canvas_transform).affine_inverse();
+ Size2 ss = get_visible_rect().size;
+ SpatialSound2DServer::get_singleton()->listener_set_transform(listener_2d,Matrix32(0,xform.xform(ss*0.5)));
+ Vector2 ss2 = ss*xform.get_scale();
+ float panrange = MAX(ss2.x,ss2.y);
+
+ SpatialSound2DServer::get_singleton()->listener_set_param(listener_2d,SpatialSound2DServer::LISTENER_PARAM_PAN_RANGE,panrange);
+
+}
+
+
+void Viewport::set_global_canvas_transform(const Matrix32& p_transform) {
+
+ global_canvas_transform=p_transform;
+
+ _update_global_transform();
+
+
+}
+
+Matrix32 Viewport::get_global_canvas_transform() const{
+
+ return global_canvas_transform;
+}
+
+
+void Viewport::_camera_transform_changed_notify() {
+
+#ifndef _3D_DISABLED
+ if (camera)
+ SpatialSoundServer::get_singleton()->listener_set_transform(listener,camera->get_camera_transform());
+#endif
+}
+
+void Viewport::_set_camera(Camera* p_camera) {
+
+#ifndef _3D_DISABLED
+
+ if (camera==p_camera)
+ return;
+
+ if (camera && find_world().is_valid()) {
+ camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
+ }
+ camera=p_camera;
+ if (camera)
+ VisualServer::get_singleton()->viewport_attach_camera(viewport,camera->get_camera());
+ else
+ VisualServer::get_singleton()->viewport_attach_camera(viewport,RID());
+
+ if (camera && find_world().is_valid()) {
+ camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
+ }
+
+ _update_listener();
+ _camera_transform_changed_notify();
+#endif
+}
+
+
+void Viewport::set_transparent_background(bool p_enable) {
+
+ transparent_bg=p_enable;
+ VS::get_singleton()->viewport_set_transparent_background(viewport,p_enable);
+
+}
+
+bool Viewport::has_transparent_background() const {
+
+ return transparent_bg;
+}
+
+#if 0
+void Viewport::set_world_2d(const Ref<World2D>& p_world_2d) {
+
+ world_2d=p_world_2d;
+ _update_listener_2d();
+
+ if (is_inside_scene()) {
+ if (current_canvas.is_valid())
+ VisualServer::get_singleton()->viewport_remove_canvas(viewport,current_canvas);
+ current_canvas=find_world_2d()->get_canvas();
+ VisualServer::get_singleton()->viewport_attach_canvas(viewport,current_canvas);
+ }
+
+}
+
+Ref<World2D> Viewport::find_world_2d() const{
+
+ if (world_2d.is_valid())
+ return world_2d;
+ else if (parent)
+ return parent->find_world_2d();
+ else
+ return Ref<World2D>();
+}
+#endif
+
+Ref<World2D> Viewport::find_world_2d() const{
+
+ return world_2d;
+}
+
+
+void Viewport::_propagate_enter_world(Node *p_node) {
+
+
+ if (p_node!=this) {
+
+ if (!p_node->is_inside_scene()) //may not have entered scene yet
+ return;
+
+ Spatial *s = p_node->cast_to<Spatial>();
+ if (s) {
+
+ s->notification(Spatial::NOTIFICATION_ENTER_WORLD);
+ } else {
+ Viewport *v = p_node->cast_to<Viewport>();
+ if (v) {
+
+ if (v->world.is_valid())
+ return;
+ }
+ }
+ }
+
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _propagate_enter_world(p_node->get_child(i));
+ }
+}
+
+
+void Viewport::_propagate_exit_world(Node *p_node) {
+
+ if (p_node!=this) {
+
+ if (!p_node->is_inside_scene()) //may have exited scene already
+ return;
+
+ Spatial *s = p_node->cast_to<Spatial>();
+ if (s) {
+
+ s->notification(Spatial::NOTIFICATION_EXIT_WORLD,false);
+ } else {
+ Viewport *v = p_node->cast_to<Viewport>();
+ if (v) {
+
+ if (v->world.is_valid())
+ return;
+ }
+ }
+ }
+
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+
+ _propagate_exit_world(p_node->get_child(i));
+ }
+
+}
+
+
+void Viewport::set_world(const Ref<World>& p_world) {
+
+ if (world==p_world)
+ return;
+
+ if (is_inside_scene())
+ _propagate_exit_world(this);
+
+#ifndef _3D_DISABLED
+ if (find_world().is_valid() && camera)
+ camera->notification(Camera::NOTIFICATION_LOST_CURRENT);
+#endif
+
+ world=p_world;
+
+ if (is_inside_scene())
+ _propagate_enter_world(this);
+
+#ifndef _3D_DISABLED
+ if (find_world().is_valid() && camera)
+ camera->notification(Camera::NOTIFICATION_BECAME_CURRENT);
+#endif
+
+ //propagate exit
+
+ if (is_inside_scene()) {
+ VisualServer::get_singleton()->viewport_set_scenario(viewport,find_world()->get_scenario());
+ }
+
+ _update_listener();
+
+}
+
+Ref<World> Viewport::get_world() const{
+
+ return world;
+}
+
+Ref<World> Viewport::find_world() const{
+
+ if (world.is_valid())
+ return world;
+ else if (parent)
+ return parent->find_world();
+ else
+ return Ref<World>();
+}
+
+Camera* Viewport::get_camera() const {
+
+ return camera;
+}
+
+
+Matrix32 Viewport::get_final_transform() const {
+
+ return stretch_transform * global_canvas_transform;
+}
+
+void Viewport::set_size_override(bool p_enable, const Size2& p_size, const Vector2 &p_margin) {
+
+ if (size_override==p_enable && p_size==size_override_size)
+ return;
+
+ size_override=p_enable;
+ if (p_size.x>=0 || p_size.y>=0) {
+ size_override_size=p_size;
+ }
+ size_override_margin=p_margin;
+ _update_rect();
+ _update_stretch_transform();
+
+}
+
+Size2 Viewport::get_size_override() const {
+
+ return size_override_size;
+}
+bool Viewport::is_size_override_enabled() const {
+
+ return size_override;
+}
+void Viewport::set_size_override_stretch(bool p_enable) {
+
+ if (p_enable==size_override_stretch)
+ return;
+
+ size_override_stretch=p_enable;
+ if (size_override) {
+ _update_rect();
+ }
+
+
+ _update_stretch_transform();
+}
+
+
+bool Viewport::is_size_override_stretch_enabled() const {
+
+ return size_override;
+}
+
+void Viewport::set_as_render_target(bool p_enable){
+
+ if (render_target==p_enable)
+ return;
+
+ render_target=p_enable;
+
+ VS::get_singleton()->viewport_set_as_render_target(viewport,p_enable);
+ if (is_inside_scene()) {
+
+ if (p_enable)
+ _vp_exit_scene();
+ else
+ _vp_enter_scene();
+ }
+
+ if (p_enable) {
+
+ render_target_texture_rid = VS::get_singleton()->viewport_get_render_target_texture(viewport);
+ } else {
+
+ render_target_texture_rid=RID();
+ }
+}
+
+bool Viewport::is_set_as_render_target() const{
+
+ return render_target;
+}
+void Viewport::set_render_target_update_mode(RenderTargetUpdateMode p_mode){
+
+ render_target_update_mode=p_mode;
+ VS::get_singleton()->viewport_set_render_target_update_mode(viewport,VS::RenderTargetUpdateMode(p_mode));
+
+}
+Viewport::RenderTargetUpdateMode Viewport::get_render_target_update_mode() const{
+
+ return render_target_update_mode;
+}
+//RID get_render_target_texture() const;
+
+void Viewport::queue_screen_capture(){
+
+ VS::get_singleton()->viewport_queue_screen_capture(viewport);
+}
+Image Viewport::get_screen_capture() const {
+
+ return VS::get_singleton()->viewport_get_screen_capture(viewport);
+}
+
+Ref<RenderTargetTexture> Viewport::get_render_target_texture() const {
+
+ return render_target_texture;
+}
+
+
+void Viewport::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_rect","rect"), &Viewport::set_rect);
+ ObjectTypeDB::bind_method(_MD("get_rect"), &Viewport::get_rect);
+ //ObjectTypeDB::bind_method(_MD("set_world_2d","world_2d:World2D"), &Viewport::set_world_2d);
+ //ObjectTypeDB::bind_method(_MD("get_world_2d:World2D"), &Viewport::get_world_2d);
+ ObjectTypeDB::bind_method(_MD("find_world_2d:World2D"), &Viewport::find_world_2d);
+ ObjectTypeDB::bind_method(_MD("set_world","world:World"), &Viewport::set_world);
+ ObjectTypeDB::bind_method(_MD("get_world:World"), &Viewport::get_world);
+ ObjectTypeDB::bind_method(_MD("find_world:World"), &Viewport::find_world);
+
+ ObjectTypeDB::bind_method(_MD("set_canvas_transform","xform"), &Viewport::set_canvas_transform);
+ ObjectTypeDB::bind_method(_MD("get_canvas_transform"), &Viewport::get_canvas_transform);
+
+ ObjectTypeDB::bind_method(_MD("set_global_canvas_transform","xform"), &Viewport::set_global_canvas_transform);
+ ObjectTypeDB::bind_method(_MD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform);
+ ObjectTypeDB::bind_method(_MD("get_final_transform"), &Viewport::get_final_transform);
+
+ ObjectTypeDB::bind_method(_MD("get_visible_rect"), &Viewport::get_visible_rect);
+ ObjectTypeDB::bind_method(_MD("set_transparent_background","enable"), &Viewport::set_transparent_background);
+ ObjectTypeDB::bind_method(_MD("has_transparent_background"), &Viewport::has_transparent_background);
+
+ ObjectTypeDB::bind_method(_MD("_parent_visibility_changed"), &Viewport::_parent_visibility_changed);
+
+ ObjectTypeDB::bind_method(_MD("_parent_resized"), &Viewport::_parent_resized);
+
+ ObjectTypeDB::bind_method(_MD("set_size_override","enable","size","margin"), &Viewport::set_size_override,DEFVAL(Size2(-1,-1)),DEFVAL(Size2(0,0)));
+ ObjectTypeDB::bind_method(_MD("get_size_override"), &Viewport::get_size_override);
+ ObjectTypeDB::bind_method(_MD("is_size_override_enabled"), &Viewport::is_size_override_enabled);
+ ObjectTypeDB::bind_method(_MD("set_size_override_stretch","enabled"), &Viewport::set_size_override_stretch);
+ ObjectTypeDB::bind_method(_MD("is_size_override_stretch_enabled"), &Viewport::is_size_override_stretch_enabled);
+ ObjectTypeDB::bind_method(_MD("queue_screen_capture"), &Viewport::queue_screen_capture);
+ ObjectTypeDB::bind_method(_MD("get_screen_capture"), &Viewport::get_screen_capture);
+
+ ObjectTypeDB::bind_method(_MD("set_as_render_target","enable"), &Viewport::set_as_render_target);
+ ObjectTypeDB::bind_method(_MD("is_set_as_render_target"), &Viewport::is_set_as_render_target);
+
+ ObjectTypeDB::bind_method(_MD("get_viewport"), &Viewport::get_viewport);
+
+ ObjectTypeDB::bind_method(_MD("update_worlds"), &Viewport::update_worlds);
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"world",PROPERTY_HINT_RESOURCE_TYPE,"World"), _SCS("set_world"), _SCS("get_world") );
+// ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"world_2d",PROPERTY_HINT_RESOURCE_TYPE,"World2D"), _SCS("set_world_2d"), _SCS("get_world_2d") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transparent_bg"), _SCS("set_transparent_background"), _SCS("has_transparent_background") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"render_target"), _SCS("set_as_render_target"), _SCS("is_set_as_render_target") );
+
+ ADD_SIGNAL(MethodInfo("size_changed"));
+}
+
+
+
+
+
+Viewport::Viewport() {
+
+ world_2d = Ref<World2D>( memnew( World2D ));
+
+ viewport = VisualServer::get_singleton()->viewport_create();
+ listener=SpatialSoundServer::get_singleton()->listener_create();
+ audio_listener=false;
+ listener_2d=SpatialSound2DServer::get_singleton()->listener_create();
+ audio_listener_2d=false;
+ transparent_bg=false;
+ parent=NULL;
+ camera=NULL;
+ size_override=false;
+ size_override_stretch=false;
+ size_override_size=Size2(1,1);
+ render_target=false;
+ render_target_update_mode=RENDER_TARGET_UPDATE_WHEN_VISIBLE;
+ render_target_texture = Ref<RenderTargetTexture>( memnew( RenderTargetTexture(this) ) );
+
+
+
+
+}
+
+
+Viewport::~Viewport() {
+
+ VisualServer::get_singleton()->free( viewport );
+ SpatialSoundServer::get_singleton()->free(listener);
+ if (render_target_texture.is_valid())
+ render_target_texture->vp=NULL; //so if used, will crash
+}
+
+
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
new file mode 100644
index 000000000..a35bc51e1
--- /dev/null
+++ b/scene/main/viewport.h
@@ -0,0 +1,212 @@
+/*************************************************************************/
+/* viewport.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* http://www.godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+#ifndef VIEWPORT_H
+#define VIEWPORT_H
+
+#include "scene/main/node.h"
+#include "servers/visual_server.h"
+#include "scene/resources/world_2d.h"
+#include "math_2d.h"
+#include "scene/resources/texture.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class Camera;
+class Viewport;
+
+class RenderTargetTexture : public Texture {
+
+ OBJ_TYPE( RenderTargetTexture, Texture );
+
+friend class Viewport;
+ Viewport *vp;
+
+
+public:
+
+
+ virtual int get_width() const;
+ virtual int get_height() const;
+ virtual Size2 get_size() const;
+ virtual RID get_rid() const;
+
+ virtual bool has_alpha() const;
+
+ virtual void set_flags(uint32_t p_flags);
+ virtual uint32_t get_flags() const;
+
+ RenderTargetTexture(Viewport *p_vp=NULL);
+
+};
+
+class Viewport : public Node {
+
+ OBJ_TYPE( Viewport, Node );
+public:
+
+ enum RenderTargetUpdateMode {
+ RENDER_TARGET_UPDATE_DISABLED,
+ RENDER_TARGET_UPDATE_ONCE, //then goes to disabled
+ RENDER_TARGET_UPDATE_WHEN_VISIBLE, // default
+ RENDER_TARGET_UPDATE_ALWAYS
+ };
+
+private:
+
+friend class RenderTargetTexture;
+ Viewport *parent;
+
+ Camera *camera;
+ Set<Camera*> cameras;
+
+ RID viewport;
+ RID canvas_item;
+ RID current_canvas;
+
+ bool audio_listener;
+ RID listener;
+
+ bool audio_listener_2d;
+ RID listener_2d;
+
+ Matrix32 canvas_transform;
+ Matrix32 global_canvas_transform;
+ Matrix32 stretch_transform;
+
+ Rect2 rect;
+
+
+ bool size_override;
+ bool size_override_stretch;
+ Size2 size_override_size;
+ Size2 size_override_margin;
+
+
+ bool transparent_bg;
+
+ void _update_rect();
+
+ void _parent_resized();
+ void _parent_draw();
+ void _parent_visibility_changed();
+
+ Ref<World2D> world_2d;
+ Ref<World> world;
+
+ void _update_listener();
+ void _update_listener_2d();
+
+ void _propagate_enter_world(Node *p_node);
+ void _propagate_exit_world(Node *p_node);
+
+
+ void _update_stretch_transform();
+ void _update_global_transform();
+
+ bool render_target;
+ RenderTargetUpdateMode render_target_update_mode;
+ RID render_target_texture_rid;
+ Ref<RenderTargetTexture> render_target_texture;
+
+
+ void update_worlds();
+
+
+ void _vp_enter_scene();
+ void _vp_exit_scene();
+
+friend class Camera;
+ void _camera_transform_changed_notify();
+ void _set_camera(Camera* p_camera);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+
+ Camera* get_camera() const;
+
+ void set_listener_transform(const Transform& p_xform);
+ void set_as_audio_listener(bool p_enable);
+ bool is_audio_listener() const;
+
+ void set_listener_2d_transform(const Matrix32& p_xform);
+ void set_as_audio_listener_2d(bool p_enable);
+ bool is_audio_listener_2d() const;
+
+ void set_rect(const Rect2& p_rect);
+ Rect2 get_rect() const;
+ Rect2 get_visible_rect() const;
+ RID get_viewport() const;
+
+ void set_world(const Ref<World>& p_world);
+ Ref<World> get_world() const;
+ Ref<World> find_world() const;
+
+ Ref<World2D> find_world_2d() const;
+
+
+ void set_canvas_transform(const Matrix32& p_transform);
+ Matrix32 get_canvas_transform() const;
+
+ void set_global_canvas_transform(const Matrix32& p_transform);
+ Matrix32 get_global_canvas_transform() const;
+
+ Matrix32 get_final_transform() const;
+
+ void set_transparent_background(bool p_enable);
+ bool has_transparent_background() const;
+
+
+ void set_size_override(bool p_enable,const Size2& p_size=Size2(-1,-1),const Vector2& p_margin=Vector2());
+ Size2 get_size_override() const;
+ bool is_size_override_enabled() const;
+ void set_size_override_stretch(bool p_enable);
+ bool is_size_override_stretch_enabled() const;
+
+
+
+ void set_as_render_target(bool p_enable);
+ bool is_set_as_render_target() const;
+ void set_render_target_update_mode(RenderTargetUpdateMode p_mode);
+ RenderTargetUpdateMode get_render_target_update_mode() const;
+ Ref<RenderTargetTexture> get_render_target_texture() const;
+
+ void queue_screen_capture();
+ Image get_screen_capture() const;
+
+ Viewport();
+ ~Viewport();
+
+};
+
+VARIANT_ENUM_CAST(Viewport::RenderTargetUpdateMode);
+#endif