diff options
Diffstat (limited to '')
| -rw-r--r-- | scene/main/SCsub | 7 | ||||
| -rw-r--r-- | scene/main/canvas_layer.cpp | 247 | ||||
| -rw-r--r-- | scene/main/canvas_layer.h | 88 | ||||
| -rw-r--r-- | scene/main/misc.cpp | 40 | ||||
| -rw-r--r-- | scene/main/misc.h | 45 | ||||
| -rw-r--r-- | scene/main/node.cpp | 1718 | ||||
| -rw-r--r-- | scene/main/node.h | 276 | ||||
| -rw-r--r-- | scene/main/resource_preloader.cpp | 183 | ||||
| -rw-r--r-- | scene/main/resource_preloader.h | 64 | ||||
| -rw-r--r-- | scene/main/scene_main_loop.cpp | 937 | ||||
| -rw-r--r-- | scene/main/scene_main_loop.h | 203 | ||||
| -rw-r--r-- | scene/main/scene_singleton.cpp | 30 | ||||
| -rw-r--r-- | scene/main/scene_singleton.h | 36 | ||||
| -rw-r--r-- | scene/main/timer.cpp | 140 | ||||
| -rw-r--r-- | scene/main/timer.h | 66 | ||||
| -rw-r--r-- | scene/main/viewport.cpp | 834 | ||||
| -rw-r--r-- | scene/main/viewport.h | 212 |
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 |
