summaryrefslogtreecommitdiff
path: root/scene/2d
diff options
context:
space:
mode:
Diffstat (limited to 'scene/2d')
-rw-r--r--scene/2d/SCsub7
-rw-r--r--scene/2d/animated_sprite.cpp352
-rw-r--r--scene/2d/animated_sprite.h119
-rw-r--r--scene/2d/area_2d.cpp326
-rw-r--r--scene/2d/area_2d.h122
-rw-r--r--scene/2d/camera_2d.cpp484
-rw-r--r--scene/2d/camera_2d.h118
-rw-r--r--scene/2d/canvas_item.cpp875
-rw-r--r--scene/2d/canvas_item.h209
-rw-r--r--scene/2d/collision_object_2d.cpp282
-rw-r--r--scene/2d/collision_object_2d.h89
-rw-r--r--scene/2d/collision_polygon_2d.cpp187
-rw-r--r--scene/2d/collision_polygon_2d.h75
-rw-r--r--scene/2d/collision_shape_2d.cpp248
-rw-r--r--scene/2d/collision_shape_2d.h61
-rw-r--r--scene/2d/joints_2d.cpp412
-rw-r--r--scene/2d/joints_2d.h141
-rw-r--r--scene/2d/node_2d.cpp291
-rw-r--r--scene/2d/node_2d.h89
-rw-r--r--scene/2d/node_2d_singleton.cpp30
-rw-r--r--scene/2d/node_2d_singleton.h33
-rw-r--r--scene/2d/parallax_background.cpp200
-rw-r--r--scene/2d/parallax_background.h78
-rw-r--r--scene/2d/parallax_layer.cpp138
-rw-r--r--scene/2d/parallax_layer.h62
-rw-r--r--scene/2d/particles_2d.cpp1045
-rw-r--r--scene/2d/particles_2d.h231
-rw-r--r--scene/2d/path_2d.cpp92
-rw-r--r--scene/2d/path_2d.h57
-rw-r--r--scene/2d/physics_body_2d.cpp794
-rw-r--r--scene/2d/physics_body_2d.h232
-rw-r--r--scene/2d/position_2d.cpp65
-rw-r--r--scene/2d/position_2d.h49
-rw-r--r--scene/2d/ray_cast_2d.cpp198
-rw-r--r--scene/2d/ray_cast_2d.h68
-rw-r--r--scene/2d/remote_transform_2d.cpp122
-rw-r--r--scene/2d/remote_transform_2d.h52
-rw-r--r--scene/2d/sample_player_2d.cpp252
-rw-r--r--scene/2d/sample_player_2d.h92
-rw-r--r--scene/2d/screen_button.cpp372
-rw-r--r--scene/2d/screen_button.h95
-rw-r--r--scene/2d/sound_player_2d.cpp123
-rw-r--r--scene/2d/sound_player_2d.h82
-rw-r--r--scene/2d/sprite.cpp337
-rw-r--r--scene/2d/sprite.h108
-rw-r--r--scene/2d/tile_map.cpp592
-rw-r--r--scene/2d/tile_map.h154
-rw-r--r--scene/2d/visibility_notifier_2d.cpp322
-rw-r--r--scene/2d/visibility_notifier_2d.h109
49 files changed, 10671 insertions, 0 deletions
diff --git a/scene/2d/SCsub b/scene/2d/SCsub
new file mode 100644
index 0000000000..055d2f2474
--- /dev/null
+++ b/scene/2d/SCsub
@@ -0,0 +1,7 @@
+Import('env')
+
+env.add_source_files(env.scene_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/scene/2d/animated_sprite.cpp b/scene/2d/animated_sprite.cpp
new file mode 100644
index 0000000000..316dffb3f9
--- /dev/null
+++ b/scene/2d/animated_sprite.cpp
@@ -0,0 +1,352 @@
+/*************************************************************************/
+/* animated_sprite.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 "animated_sprite.h"
+
+void AnimatedSprite::edit_set_pivot(const Point2& p_pivot) {
+
+ set_offset(p_pivot);
+}
+
+Point2 AnimatedSprite::edit_get_pivot() const {
+
+ return get_offset();
+}
+bool AnimatedSprite::edit_has_pivot() const {
+
+ return true;
+}
+
+
+void SpriteFrames::add_frame(const Ref<Texture>& p_frame,int p_at_pos) {
+
+ if (p_at_pos>=0 && p_at_pos<frames.size())
+ frames.insert(p_at_pos,p_frame);
+ else
+ frames.push_back(p_frame);
+
+ emit_changed();
+}
+
+int SpriteFrames::get_frame_count() const {
+
+ return frames.size();
+}
+
+void SpriteFrames::remove_frame(int p_idx) {
+
+ frames.remove(p_idx);
+ emit_changed();
+}
+void SpriteFrames::clear() {
+
+ frames.clear();
+ emit_changed();
+}
+
+
+Array SpriteFrames::_get_frames() const {
+
+ Array arr;
+ arr.resize(frames.size());
+ for(int i=0;i<frames.size();i++)
+ arr[i]=frames[i];
+
+ return arr;
+}
+
+void SpriteFrames::_set_frames(const Array& p_frames) {
+
+ frames.resize(p_frames.size());
+ for(int i=0;i<frames.size();i++)
+ frames[i]=p_frames[i];
+
+}
+
+
+void SpriteFrames::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("add_frame","frame","atpos"),&SpriteFrames::add_frame,DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("get_frame_count"),&SpriteFrames::get_frame_count);
+ ObjectTypeDB::bind_method(_MD("get_frame","idx"),&SpriteFrames::get_frame);
+ ObjectTypeDB::bind_method(_MD("set_frame","idx","txt"),&SpriteFrames::set_frame);
+ ObjectTypeDB::bind_method(_MD("remove_frame","idx"),&SpriteFrames::remove_frame);
+ ObjectTypeDB::bind_method(_MD("clear"),&SpriteFrames::clear);
+
+ ObjectTypeDB::bind_method(_MD("_set_frames"),&SpriteFrames::_set_frames);
+ ObjectTypeDB::bind_method(_MD("_get_frames"),&SpriteFrames::_get_frames);
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::ARRAY,"frames",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_frames"),_SCS("_get_frames"));
+
+}
+
+
+
+
+SpriteFrames::SpriteFrames() {
+
+
+}
+
+
+
+
+
+
+
+//////////////////////////
+
+
+void AnimatedSprite::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_DRAW: {
+
+ if (frames.is_null())
+ return;
+
+ if (frame<0 || frame>=frames->get_frame_count())
+ return;
+
+ Ref<Texture> texture = frames->get_frame(frame);
+ if (texture.is_null())
+ return;
+
+ //print_line("DECIDED TO DRAW");
+
+ RID ci = get_canvas_item();
+
+ /*
+ texture->draw(ci,Point2());
+ break;
+ */
+
+ Size2i s;
+ s = texture->get_size();
+ Point2i ofs=offset;
+ if (centered)
+ ofs-=s/2;
+
+ Rect2i dst_rect(ofs,s);
+
+ if (hflip)
+ dst_rect.size.x=-dst_rect.size.x;
+ if (vflip)
+ dst_rect.size.y=-dst_rect.size.y;
+
+ texture->draw_rect(ci,dst_rect,false,modulate);
+// VisualServer::get_singleton()->canvas_item_add_texture_rect_region(ci,dst_rect,texture->get_rid(),src_rect,modulate);
+
+ } break;
+ }
+
+}
+
+void AnimatedSprite::set_sprite_frames(const Ref<SpriteFrames> &p_frames) {
+
+ if (frames.is_valid())
+ frames->disconnect("changed",this,"_res_changed");
+ frames=p_frames;
+ if (frames.is_valid())
+ frames->connect("changed",this,"_res_changed");
+
+ if (!frames.is_valid()) {
+ frame=0;
+
+ } else {
+ set_frame(frame);
+ }
+ update();
+
+}
+
+Ref<SpriteFrames> AnimatedSprite::get_sprite_frames() const {
+
+ return frames;
+}
+
+void AnimatedSprite::set_frame(int p_frame) {
+
+ if (!frames.is_valid()) {
+ return;
+ }
+ if (p_frame>=frames->get_frame_count())
+ p_frame=frames->get_frame_count()-1;
+ if (p_frame<0)
+ p_frame=0;
+
+ if (frame==p_frame)
+ return;
+
+ frame=p_frame;
+ update();
+ _change_notify("frame");
+
+
+}
+int AnimatedSprite::get_frame() const {
+
+ return frame;
+}
+
+
+void AnimatedSprite::set_centered(bool p_center) {
+
+ centered=p_center;
+ update();
+ item_rect_changed();
+}
+
+bool AnimatedSprite::is_centered() const {
+
+ return centered;
+}
+
+void AnimatedSprite::set_offset(const Point2& p_offset) {
+
+ offset=p_offset;
+ update();
+ item_rect_changed();
+}
+Point2 AnimatedSprite::get_offset() const {
+
+ return offset;
+}
+
+void AnimatedSprite::set_flip_h(bool p_flip) {
+
+ hflip=p_flip;
+ update();
+}
+bool AnimatedSprite::is_flipped_h() const {
+
+ return hflip;
+}
+
+void AnimatedSprite::set_flip_v(bool p_flip) {
+
+ vflip=p_flip;
+ update();
+}
+bool AnimatedSprite::is_flipped_v() const {
+
+ return vflip;
+}
+
+
+void AnimatedSprite::set_modulate(const Color& p_color) {
+
+ modulate=p_color;
+ update();
+}
+
+Color AnimatedSprite::get_modulate() const{
+
+ return modulate;
+}
+
+
+Rect2 AnimatedSprite::get_item_rect() const {
+
+ if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) {
+ return Node2D::get_item_rect();
+ }
+
+ Ref<Texture> t = frames->get_frame(frame);
+ if (t.is_null())
+ return Node2D::get_item_rect();
+ Size2i s = t->get_size();
+
+ Point2i ofs=offset;
+ if (centered)
+ ofs-=s/2;
+
+ if (s==Size2(0,0))
+ s=Size2(1,1);
+
+ return Rect2(ofs,s);
+}
+
+void AnimatedSprite::_res_changed() {
+
+ set_frame(frame);
+ update();
+}
+
+void AnimatedSprite::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_sprite_frames","sprite_frames:SpriteFrames"),&AnimatedSprite::set_sprite_frames);
+ ObjectTypeDB::bind_method(_MD("get_sprite_frames:SpriteFrames"),&AnimatedSprite::get_sprite_frames);
+
+ ObjectTypeDB::bind_method(_MD("set_centered","centered"),&AnimatedSprite::set_centered);
+ ObjectTypeDB::bind_method(_MD("is_centered"),&AnimatedSprite::is_centered);
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&AnimatedSprite::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&AnimatedSprite::get_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_flip_h","flip_h"),&AnimatedSprite::set_flip_h);
+ ObjectTypeDB::bind_method(_MD("is_flipped_h"),&AnimatedSprite::is_flipped_h);
+
+ ObjectTypeDB::bind_method(_MD("set_flip_v","flip_v"),&AnimatedSprite::set_flip_v);
+ ObjectTypeDB::bind_method(_MD("is_flipped_v"),&AnimatedSprite::is_flipped_v);
+
+ ObjectTypeDB::bind_method(_MD("set_frame","frame"),&AnimatedSprite::set_frame);
+ ObjectTypeDB::bind_method(_MD("get_frame"),&AnimatedSprite::get_frame);
+
+ ObjectTypeDB::bind_method(_MD("set_modulate","modulate"),&AnimatedSprite::set_modulate);
+ ObjectTypeDB::bind_method(_MD("get_modulate"),&AnimatedSprite::get_modulate);
+
+
+ ObjectTypeDB::bind_method(_MD("_res_changed"),&AnimatedSprite::_res_changed);
+
+ ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "frames",PROPERTY_HINT_RESOURCE_TYPE,"SpriteFrames"), _SCS("set_sprite_frames"),_SCS("get_sprite_frames"));
+ ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered"));
+ ADD_PROPERTYNZ( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_v"), _SCS("set_flip_v"),_SCS("is_flipped_v"));
+ ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate"));
+
+}
+
+AnimatedSprite::AnimatedSprite() {
+
+ centered=true;
+ hflip=false;
+ vflip=false;
+
+ frame=0;
+
+
+ modulate=Color(1,1,1,1);
+
+
+}
+
+
diff --git a/scene/2d/animated_sprite.h b/scene/2d/animated_sprite.h
new file mode 100644
index 0000000000..78d738240f
--- /dev/null
+++ b/scene/2d/animated_sprite.h
@@ -0,0 +1,119 @@
+/*************************************************************************/
+/* animated_sprite.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 ANIMATED_SPRITE_H
+#define ANIMATED_SPRITE_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/texture.h"
+
+
+class SpriteFrames : public Resource {
+
+ OBJ_TYPE(SpriteFrames,Resource);
+
+ Vector< Ref<Texture> > frames;
+
+ Array _get_frames() const;
+ void _set_frames(const Array& p_frames);
+protected:
+
+ static void _bind_methods();
+
+public:
+
+
+ void add_frame(const Ref<Texture>& p_frame,int p_at_pos=-1);
+ int get_frame_count() const;
+ _FORCE_INLINE_ Ref<Texture> get_frame(int p_idx) const { ERR_FAIL_INDEX_V(p_idx,frames.size(),Ref<Texture>()); return frames[p_idx]; }
+ void set_frame(int p_idx,const Ref<Texture>& p_frame){ ERR_FAIL_INDEX(p_idx,frames.size()); frames[p_idx]=p_frame; }
+ void remove_frame(int p_idx);
+ void clear();
+
+ SpriteFrames();
+
+};
+
+
+
+class AnimatedSprite : public Node2D {
+
+ OBJ_TYPE(AnimatedSprite,Node2D);
+
+ Ref<SpriteFrames> frames;
+ int frame;
+
+ bool centered;
+ Point2 offset;
+
+ bool hflip;
+ bool vflip;
+
+ Color modulate;
+
+ void _res_changed();
+protected:
+
+ static void _bind_methods();
+ void _notification(int p_what);
+
+public:
+
+
+ virtual void edit_set_pivot(const Point2& p_pivot);
+ virtual Point2 edit_get_pivot() const;
+ virtual bool edit_has_pivot() const;
+
+ void set_sprite_frames(const Ref<SpriteFrames> &p_frames);
+ Ref<SpriteFrames> get_sprite_frames() const;
+
+ void set_frame(int p_frame);
+ int get_frame() const;
+
+ void set_centered(bool p_center);
+ bool is_centered() const;
+
+ void set_offset(const Point2& p_offset);
+ Point2 get_offset() const;
+
+ void set_flip_h(bool p_flip);
+ bool is_flipped_h() const;
+
+ void set_flip_v(bool p_flip);
+ bool is_flipped_v() const;
+
+ void set_modulate(const Color& p_color);
+ Color get_modulate() const;
+
+ virtual Rect2 get_item_rect() const;
+
+
+ AnimatedSprite();
+};
+
+#endif // ANIMATED_SPRITE_H
diff --git a/scene/2d/area_2d.cpp b/scene/2d/area_2d.cpp
new file mode 100644
index 0000000000..70b5a652d5
--- /dev/null
+++ b/scene/2d/area_2d.cpp
@@ -0,0 +1,326 @@
+/*************************************************************************/
+/* area_2d.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 "area_2d.h"
+#include "scene/scene_string_names.h"
+#include "servers/physics_2d_server.h"
+void Area2D::set_space_override_mode(SpaceOverride p_mode) {
+
+ space_override=p_mode;
+ Physics2DServer::get_singleton()->area_set_space_override_mode(get_rid(),Physics2DServer::AreaSpaceOverrideMode(p_mode));
+
+
+}
+Area2D::SpaceOverride Area2D::get_space_override_mode() const{
+
+ return space_override;
+}
+
+void Area2D::set_gravity_is_point(bool p_enabled){
+
+ gravity_is_point=p_enabled;
+ Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY_IS_POINT,p_enabled);
+
+}
+bool Area2D::is_gravity_a_point() const{
+
+ return gravity_is_point;
+}
+
+void Area2D::set_gravity_vector(const Vector2& p_vec){
+
+ gravity_vec=p_vec;
+ Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY_VECTOR,p_vec);
+
+}
+Vector2 Area2D::get_gravity_vector() const{
+
+ return gravity_vec;
+}
+
+void Area2D::set_gravity(real_t p_gravity){
+
+ gravity=p_gravity;
+ Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_GRAVITY,p_gravity);
+}
+real_t Area2D::get_gravity() const{
+
+ return gravity;
+}
+
+void Area2D::set_density(real_t p_density){
+
+ density=p_density;
+ Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_DENSITY,p_density);
+}
+real_t Area2D::get_density() const{
+
+ return density;
+}
+
+void Area2D::set_priority(real_t p_priority){
+
+ priority=p_priority;
+ Physics2DServer::get_singleton()->area_set_param(get_rid(),Physics2DServer::AREA_PARAM_PRIORITY,p_priority);
+}
+real_t Area2D::get_priority() const{
+
+ return priority;
+}
+
+
+void Area2D::_body_enter_scene(ObjectID p_id) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+ ERR_FAIL_COND(!node);
+
+ Map<ObjectID,BodyState>::Element *E=body_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ ERR_FAIL_COND(E->get().in_scene);
+
+ E->get().in_scene=true;
+ emit_signal(SceneStringNames::get_singleton()->body_enter,node);
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape);
+ }
+
+}
+
+void Area2D::_body_exit_scene(ObjectID p_id) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+ ERR_FAIL_COND(!node);
+ Map<ObjectID,BodyState>::Element *E=body_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ ERR_FAIL_COND(!E->get().in_scene);
+ E->get().in_scene=false;
+ emit_signal(SceneStringNames::get_singleton()->body_exit,node);
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape);
+ }
+
+}
+
+void Area2D::_body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape) {
+
+ bool body_in = p_status==Physics2DServer::AREA_BODY_ADDED;
+ ObjectID objid=p_instance;
+
+ Object *obj = ObjectDB::get_instance(objid);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+
+ Map<ObjectID,BodyState>::Element *E=body_map.find(objid);
+
+ ERR_FAIL_COND(!body_in && !E);
+
+ if (body_in) {
+ if (!E) {
+
+ E = body_map.insert(objid,BodyState());
+ E->get().rc=0;
+ E->get().in_scene=node && node->is_inside_scene();
+ if (node) {
+ node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid));
+ if (E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_enter,node);
+ }
+ }
+
+ }
+ E->get().rc++;
+ if (node)
+ E->get().shapes.insert(ShapePair(p_body_shape,p_area_shape));
+
+
+ if (E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_area_shape);
+ }
+
+ } else {
+
+ E->get().rc--;
+
+ if (node)
+ E->get().shapes.erase(ShapePair(p_body_shape,p_area_shape));
+
+ bool eraseit=false;
+
+ if (E->get().rc==0) {
+
+ if (node) {
+ node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene);
+ node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene);
+ if (E->get().in_scene)
+ emit_signal(SceneStringNames::get_singleton()->body_exit,obj);
+
+ }
+
+ eraseit=true;
+
+ }
+ if (node && E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_area_shape);
+ }
+
+ if (eraseit)
+ body_map.erase(E);
+
+ }
+
+}
+
+
+void Area2D::_clear_monitoring() {
+
+
+ Map<ObjectID,BodyState> bmcopy = body_map;
+ body_map.clear();
+ //disconnect all monitored stuff
+
+ for (Map<ObjectID,BodyState>::Element *E=bmcopy.front();E;E=E->next()) {
+
+ Object *obj = ObjectDB::get_instance(E->key());
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+ ERR_CONTINUE(!node);
+ if (!E->get().in_scene)
+ continue;
+
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ emit_signal(SceneStringNames::get_singleton()->body_exit_shape,E->key(),node,E->get().shapes[i].body_shape,E->get().shapes[i].area_shape);
+ }
+
+ emit_signal(SceneStringNames::get_singleton()->body_exit,obj);
+
+ node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene);
+ node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene);
+ }
+
+}
+
+void Area2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_EXIT_SCENE: {
+
+ _clear_monitoring();
+ } break;
+ }
+
+}
+
+
+void Area2D::set_enable_monitoring(bool p_enable) {
+
+ if (p_enable==monitoring)
+ return;
+
+ monitoring=p_enable;
+
+ if (monitoring) {
+
+ Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),this,"_body_inout");
+ } else {
+ Physics2DServer::get_singleton()->area_set_monitor_callback(get_rid(),NULL,StringName());
+ _clear_monitoring();
+
+ }
+}
+
+bool Area2D::is_monitoring_enabled() const {
+
+ return monitoring;
+}
+
+
+void Area2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_body_enter_scene","id"),&Area2D::_body_enter_scene);
+ ObjectTypeDB::bind_method(_MD("_body_exit_scene","id"),&Area2D::_body_exit_scene);
+
+ ObjectTypeDB::bind_method(_MD("set_space_override_mode","enable"),&Area2D::set_space_override_mode);
+ ObjectTypeDB::bind_method(_MD("get_space_override_mode"),&Area2D::get_space_override_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity_is_point","enable"),&Area2D::set_gravity_is_point);
+ ObjectTypeDB::bind_method(_MD("is_gravity_a_point"),&Area2D::is_gravity_a_point);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity_vector","vector"),&Area2D::set_gravity_vector);
+ ObjectTypeDB::bind_method(_MD("get_gravity_vector"),&Area2D::get_gravity_vector);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&Area2D::set_gravity);
+ ObjectTypeDB::bind_method(_MD("get_gravity"),&Area2D::get_gravity);
+
+ ObjectTypeDB::bind_method(_MD("set_density","density"),&Area2D::set_density);
+ ObjectTypeDB::bind_method(_MD("get_density"),&Area2D::get_density);
+
+ ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area2D::set_priority);
+ ObjectTypeDB::bind_method(_MD("get_priority"),&Area2D::get_priority);
+
+ ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area2D::set_enable_monitoring);
+ ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area2D::is_monitoring_enabled);
+
+ ObjectTypeDB::bind_method(_MD("_body_inout"),&Area2D::_body_inout);
+
+
+ ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
+ ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"area_shape")));
+ ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body")));
+ ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body")));
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"space_override",PROPERTY_HINT_ENUM,"Disabled,Combine,Replace"),_SCS("set_space_override_mode"),_SCS("get_space_override_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"gravity_point"),_SCS("set_gravity_is_point"),_SCS("is_gravity_a_point"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"gravity_vec"),_SCS("set_gravity_vector"),_SCS("get_gravity_vector"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_gravity"),_SCS("get_gravity"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"density",PROPERTY_HINT_RANGE,"0,1024,0.001"),_SCS("set_density"),_SCS("get_density"));
+ ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"priority",PROPERTY_HINT_RANGE,"0,128,1"),_SCS("set_priority"),_SCS("get_priority"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"monitoring"),_SCS("set_enable_monitoring"),_SCS("is_monitoring_enabled"));
+
+}
+
+Area2D::Area2D() : CollisionObject2D(Physics2DServer::get_singleton()->area_create(),true) {
+
+ space_override=SPACE_OVERRIDE_DISABLED;
+ set_gravity(98);;
+ set_gravity_vector(Vector2(0,1));
+ gravity_is_point=false;
+ density=0.1;
+ priority=0;
+ monitoring=false;
+
+}
+
+Area2D::~Area2D() {
+
+
+}
diff --git a/scene/2d/area_2d.h b/scene/2d/area_2d.h
new file mode 100644
index 0000000000..b4c8edf138
--- /dev/null
+++ b/scene/2d/area_2d.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* area_2d.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 AREA_2D_H
+#define AREA_2D_H
+
+#include "scene/2d/collision_object_2d.h"
+#include "vset.h"
+
+class Area2D : public CollisionObject2D {
+
+ OBJ_TYPE( Area2D, CollisionObject2D );
+public:
+
+ enum SpaceOverride {
+ SPACE_OVERRIDE_DISABLED,
+ SPACE_OVERRIDE_COMBINE,
+ SPACE_OVERRIDE_REPLACE
+ };
+private:
+
+
+ SpaceOverride space_override;
+ Vector2 gravity_vec;
+ real_t gravity;
+ bool gravity_is_point;
+ real_t density;
+ int priority;
+ bool monitoring;
+
+ void _body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape);
+
+ void _body_enter_scene(ObjectID p_id);
+ void _body_exit_scene(ObjectID p_id);
+
+ struct ShapePair {
+
+ int body_shape;
+ int area_shape;
+ bool operator<(const ShapePair& p_sp) const {
+ if (body_shape==p_sp.body_shape)
+ return area_shape < p_sp.area_shape;
+ else
+ return body_shape < p_sp.body_shape;
+ }
+
+ ShapePair() {}
+ ShapePair(int p_bs, int p_as) { body_shape=p_bs; area_shape=p_as; }
+ };
+
+ struct BodyState {
+
+ int rc;
+ bool in_scene;
+ VSet<ShapePair> shapes;
+ };
+
+ Map<ObjectID,BodyState> body_map;
+
+ void _clear_monitoring();
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_space_override_mode(SpaceOverride p_mode);
+ SpaceOverride get_space_override_mode() const;
+
+ void set_gravity_is_point(bool p_enabled);
+ bool is_gravity_a_point() const;
+
+ void set_gravity_vector(const Vector2& p_vec);
+ Vector2 get_gravity_vector() const;
+
+ void set_gravity(real_t p_gravity);
+ real_t get_gravity() const;
+
+ void set_density(real_t p_density);
+ real_t get_density() const;
+
+ void set_priority(real_t p_priority);
+ real_t get_priority() const;
+
+ void set_enable_monitoring(bool p_enable);
+ bool is_monitoring_enabled() const;
+
+
+ Area2D();
+ ~Area2D();
+};
+
+VARIANT_ENUM_CAST(Area2D::SpaceOverride);
+
+#endif // AREA_2D_H
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
new file mode 100644
index 0000000000..1920ce0081
--- /dev/null
+++ b/scene/2d/camera_2d.cpp
@@ -0,0 +1,484 @@
+/*************************************************************************/
+/* camera_2d.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 "camera_2d.h"
+#include "scene/scene_string_names.h"
+#include "servers/visual_server.h"
+
+void Camera2D::_update_scroll() {
+
+
+ if (!is_inside_scene())
+ return;
+
+ if (get_scene()->is_editor_hint()) {
+ update(); //will just be drawn
+ return;
+ }
+
+ if (current) {
+ Matrix32 xform = get_camera_transform();
+
+ RID vp = viewport->get_viewport();
+ if (viewport) {
+ viewport->set_canvas_transform( xform );
+ }
+ get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,group_name,"_camera_moved",xform);
+ };
+
+}
+
+void Camera2D::set_zoom(const Vector2 &p_zoom) {
+
+ zoom = p_zoom;
+ _update_scroll();
+};
+
+Vector2 Camera2D::get_zoom() const {
+
+ return zoom;
+};
+
+
+Matrix32 Camera2D::get_camera_transform() {
+
+ if (!get_scene())
+ return Matrix32();
+
+ Size2 screen_size = get_viewport_rect().size;
+ screen_size=get_viewport_rect().size;
+
+
+ Point2 new_camera_pos = get_global_transform().get_origin();
+ Point2 ret_camera_pos;
+
+ if (!first) {
+
+
+ if (centered) {
+
+ if (h_drag_enabled) {
+ camera_pos.x = MIN( camera_pos.x, (new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT]));
+ camera_pos.x = MAX( camera_pos.x, (new_camera_pos.x - screen_size.x * 0.5 * drag_margin[MARGIN_LEFT]));
+ } else {
+
+ if (h_ofs<0) {
+ camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_RIGHT] * h_ofs;
+ } else {
+ camera_pos.x = new_camera_pos.x + screen_size.x * 0.5 * drag_margin[MARGIN_LEFT] * h_ofs;
+ }
+ }
+
+ if (v_drag_enabled) {
+
+ camera_pos.y = MIN( camera_pos.y, (new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM]));
+ camera_pos.y = MAX( camera_pos.y, (new_camera_pos.y - screen_size.y * 0.5 * drag_margin[MARGIN_TOP]));
+
+ } else {
+
+ if (v_ofs<0) {
+ camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_TOP] * v_ofs;
+ } else {
+ camera_pos.y = new_camera_pos.y + screen_size.y * 0.5 * drag_margin[MARGIN_BOTTOM] * v_ofs;
+ }
+ }
+
+ }
+
+
+ if (smoothing>0.0) {
+
+ float c = smoothing*get_fixed_process_delta_time();
+ smoothed_camera_pos = ((new_camera_pos-smoothed_camera_pos)*c)+smoothed_camera_pos;
+ ret_camera_pos=smoothed_camera_pos;
+// camera_pos=camera_pos*(1.0-smoothing)+new_camera_pos*smoothing;
+ } else {
+
+ ret_camera_pos=smoothed_camera_pos=camera_pos;
+
+ }
+
+
+
+ } else {
+ ret_camera_pos=smoothed_camera_pos=camera_pos=new_camera_pos;
+ first=false;
+ }
+
+
+ Point2 screen_offset = (centered ? (screen_size * 0.5 * zoom) : Point2());;
+ screen_offset+=offset;
+
+ Rect2 screen_rect(-screen_offset+ret_camera_pos,screen_size);
+ if (screen_rect.pos.x + screen_rect.size.x > limit[MARGIN_RIGHT])
+ screen_rect.pos.x = limit[MARGIN_RIGHT] - screen_rect.size.x;
+
+ if (screen_rect.pos.y + screen_rect.size.y > limit[MARGIN_BOTTOM])
+ screen_rect.pos.y = limit[MARGIN_BOTTOM] - screen_rect.size.y;
+
+
+ if (screen_rect.pos.x < limit[MARGIN_LEFT])
+ screen_rect.pos.x=limit[MARGIN_LEFT];
+
+ if (screen_rect.pos.y < limit[MARGIN_TOP])
+ screen_rect.pos.y =limit[MARGIN_TOP];
+
+ camera_screen_center=screen_rect.pos+screen_rect.size*0.5;
+
+ Matrix32 xform;
+ xform.scale_basis(zoom);
+ xform.set_origin(screen_rect.pos/*.floor()*/);
+
+
+/*
+ if (0) {
+
+ xform = get_global_transform() * xform;
+ } else {
+
+ xform.elements[2]+=get_global_transform().get_origin();
+ }
+*/
+
+
+ return (xform).affine_inverse();
+}
+
+
+
+void Camera2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ _update_scroll();
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+
+ if (!is_fixed_processing())
+ _update_scroll();
+
+ } break;
+ case NOTIFICATION_ENTER_SCENE: {
+
+ viewport = NULL;
+ Node *n=this;
+ while(n){
+
+ viewport = n->cast_to<Viewport>();
+ if (viewport)
+ break;
+ n=n->get_parent();
+ }
+
+ canvas = get_canvas();
+
+ RID vp = viewport->get_viewport();
+
+ group_name = "__cameras_"+itos(vp.get_id());
+ canvas_group_name ="__cameras_c"+itos(canvas.get_id());
+ add_to_group(group_name);
+ add_to_group(canvas_group_name);
+
+ _update_scroll();
+ first=true;
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (is_current()) {
+ if (viewport) {
+ viewport->set_canvas_transform( Matrix32() );
+ }
+ }
+ remove_from_group(group_name);
+ remove_from_group(canvas_group_name);
+ viewport=NULL;
+
+ } break;
+ }
+}
+
+void Camera2D::set_offset(const Vector2& p_offset) {
+
+ offset=p_offset;
+ _update_scroll();
+
+}
+
+Vector2 Camera2D::get_offset() const{
+
+ return offset;
+}
+
+void Camera2D::set_centered(bool p_centered){
+
+ centered=p_centered;
+ _update_scroll();
+}
+
+bool Camera2D::is_centered() const {
+
+ return centered;
+}
+
+
+void Camera2D::_make_current(Object *p_which) {
+
+ if (p_which==this) {
+
+ current=true;
+ _update_scroll();
+ } else {
+ current=false;
+ }
+}
+
+
+void Camera2D::_set_current(bool p_current) {
+
+ if (p_current)
+ make_current();
+
+ current=p_current;
+}
+
+bool Camera2D::is_current() const {
+
+ return current;
+}
+
+void Camera2D::make_current() {
+
+ if (!is_inside_scene()) {
+ current=true;
+ } else {
+ get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,group_name,"_make_current",this);
+ }
+}
+
+void Camera2D::set_limit(Margin p_margin,int p_limit) {
+
+ ERR_FAIL_INDEX(p_margin,4);
+ limit[p_margin]=p_limit;
+}
+
+int Camera2D::get_limit(Margin p_margin) const{
+
+ ERR_FAIL_INDEX_V(p_margin,4,0);
+ return limit[p_margin];
+
+}
+
+void Camera2D::set_drag_margin(Margin p_margin,float p_drag_margin) {
+
+ ERR_FAIL_INDEX(p_margin,4);
+ drag_margin[p_margin]=p_drag_margin;
+}
+
+float Camera2D::get_drag_margin(Margin p_margin) const{
+
+ ERR_FAIL_INDEX_V(p_margin,4,0);
+ return drag_margin[p_margin];
+
+}
+
+
+Vector2 Camera2D::get_camera_pos() const {
+
+
+ return camera_pos;
+}
+
+void Camera2D::force_update_scroll() {
+
+
+ _update_scroll();
+}
+
+
+void Camera2D::set_follow_smoothing(float p_speed) {
+
+ smoothing=p_speed;
+ if (smoothing>0)
+ set_fixed_process(true);
+ else
+ set_fixed_process(false);
+}
+
+float Camera2D::get_follow_smoothing() const{
+
+ return smoothing;
+}
+
+Point2 Camera2D::get_camera_screen_center() const {
+
+ return camera_screen_center;
+}
+
+
+void Camera2D::set_h_drag_enabled(bool p_enabled) {
+
+ h_drag_enabled=p_enabled;
+}
+
+bool Camera2D::is_h_drag_enabled() const{
+
+ return h_drag_enabled;
+}
+
+void Camera2D::set_v_drag_enabled(bool p_enabled){
+
+ v_drag_enabled=p_enabled;
+}
+
+bool Camera2D::is_v_drag_enabled() const{
+
+ return v_drag_enabled;
+}
+
+void Camera2D::set_v_offset(float p_offset) {
+
+ v_ofs=p_offset;
+}
+
+float Camera2D::get_v_offset() const{
+
+ return v_ofs;
+}
+
+void Camera2D::set_h_offset(float p_offset){
+
+ h_ofs=p_offset;
+}
+float Camera2D::get_h_offset() const{
+
+ return h_ofs;
+}
+
+
+void Camera2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Camera2D::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&Camera2D::get_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Camera2D::set_centered);
+ ObjectTypeDB::bind_method(_MD("is_centered"),&Camera2D::is_centered);
+
+ ObjectTypeDB::bind_method(_MD("make_current"),&Camera2D::make_current);
+ ObjectTypeDB::bind_method(_MD("_make_current"),&Camera2D::_make_current);
+
+ ObjectTypeDB::bind_method(_MD("_update_scroll"),&Camera2D::_update_scroll);
+
+
+ ObjectTypeDB::bind_method(_MD("_set_current","current"),&Camera2D::_set_current);
+ ObjectTypeDB::bind_method(_MD("is_current"),&Camera2D::is_current);
+
+ ObjectTypeDB::bind_method(_MD("set_limit","margin","limit"),&Camera2D::set_limit);
+ ObjectTypeDB::bind_method(_MD("get_limit","margin"),&Camera2D::get_limit);
+
+ ObjectTypeDB::bind_method(_MD("set_v_drag_enabled","enabled"),&Camera2D::set_v_drag_enabled);
+ ObjectTypeDB::bind_method(_MD("is_v_drag_enabled"),&Camera2D::is_v_drag_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_h_drag_enabled","enabled"),&Camera2D::set_h_drag_enabled);
+ ObjectTypeDB::bind_method(_MD("is_h_drag_enabled"),&Camera2D::is_h_drag_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_v_offset","ofs"),&Camera2D::set_v_offset);
+ ObjectTypeDB::bind_method(_MD("get_v_offset"),&Camera2D::get_v_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_h_offset","ofs"),&Camera2D::set_h_offset);
+ ObjectTypeDB::bind_method(_MD("get_h_offset"),&Camera2D::get_h_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_drag_margin","margin","drag_margin"),&Camera2D::set_drag_margin);
+ ObjectTypeDB::bind_method(_MD("get_drag_margin","margin"),&Camera2D::get_drag_margin);
+
+ ObjectTypeDB::bind_method(_MD("get_camera_pos"),&Camera2D::get_camera_pos);
+ ObjectTypeDB::bind_method(_MD("get_camera_screen_center"),&Camera2D::get_camera_screen_center);
+
+ ObjectTypeDB::bind_method(_MD("set_zoom"),&Camera2D::set_zoom);
+ ObjectTypeDB::bind_method(_MD("get_zoom"),&Camera2D::get_zoom);
+
+
+ ObjectTypeDB::bind_method(_MD("set_follow_smoothing","follow_smoothing"),&Camera2D::set_follow_smoothing);
+ ObjectTypeDB::bind_method(_MD("get_follow_smoothing"),&Camera2D::get_follow_smoothing);
+
+ ObjectTypeDB::bind_method(_MD("force_update_scroll"),&Camera2D::force_update_scroll);
+
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"offset"),_SCS("set_offset"),_SCS("get_offset"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"centered"),_SCS("set_centered"),_SCS("is_centered"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"current"),_SCS("_set_current"),_SCS("is_current"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"smoothing"),_SCS("set_follow_smoothing"),_SCS("get_follow_smoothing") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"zoom"),_SCS("set_zoom"),_SCS("get_zoom") );
+
+ ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/left"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_LEFT);
+ ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/top"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_TOP);
+ ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/right"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_RIGHT);
+ ADD_PROPERTYI( PropertyInfo(Variant::INT,"limit/bottom"),_SCS("set_limit"),_SCS("get_limit"),MARGIN_BOTTOM);
+
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"drag_margin/h_enabled"),_SCS("set_h_drag_enabled"),_SCS("is_h_drag_enabled") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"drag_margin/v_enabled"),_SCS("set_v_drag_enabled"),_SCS("is_v_drag_enabled") );
+
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/left",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_LEFT);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/top",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_TOP);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/right",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_RIGHT);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"drag_margin/bottom",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_drag_margin"),_SCS("get_drag_margin"),MARGIN_BOTTOM);
+
+
+
+}
+
+Camera2D::Camera2D() {
+
+
+
+ centered=true;
+ current=false;
+ limit[MARGIN_LEFT]=-10000000;
+ limit[MARGIN_TOP]=-10000000;
+ limit[MARGIN_RIGHT]=10000000;
+ limit[MARGIN_BOTTOM]=10000000;
+ drag_margin[MARGIN_LEFT]=0.2;
+ drag_margin[MARGIN_TOP]=0.2;
+ drag_margin[MARGIN_RIGHT]=0.2;
+ drag_margin[MARGIN_BOTTOM]=0.2;
+ camera_pos=Vector2();
+
+ smoothing=0.0;
+ zoom = Vector2(1, 1);
+
+ h_drag_enabled=true;
+ v_drag_enabled=true;
+ h_ofs=0;
+ v_ofs=0;
+
+}
diff --git a/scene/2d/camera_2d.h b/scene/2d/camera_2d.h
new file mode 100644
index 0000000000..9d06df2d1b
--- /dev/null
+++ b/scene/2d/camera_2d.h
@@ -0,0 +1,118 @@
+/*************************************************************************/
+/* camera_2d.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 CAMERA_2D_H
+#define CAMERA_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/main/viewport.h"
+
+
+class Camera2D : public Node2D {
+
+ OBJ_TYPE( Camera2D, Node2D );
+
+protected:
+ Point2 camera_pos;
+ Point2 smoothed_camera_pos;
+ bool first;
+
+ Viewport *viewport;
+
+ StringName group_name;
+ StringName canvas_group_name;
+ RID canvas;
+ Vector2 offset;
+ Vector2 zoom;
+ bool centered;
+ bool current;
+ float smoothing;
+ int limit[4];
+ float drag_margin[4];
+
+ bool h_drag_enabled;
+ bool v_drag_enabled;
+ float h_ofs;
+ float v_ofs;
+
+
+ Point2 camera_screen_center;
+ void _update_scroll();
+
+ void _make_current(Object *p_which);
+ void _set_current(bool p_current);
+protected:
+
+ virtual Matrix32 get_camera_transform();
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_offset(const Vector2& p_offset);
+ Vector2 get_offset() const;
+
+ void set_centered(bool p_centered);
+ bool is_centered() const;
+
+ void set_limit(Margin p_margin,int p_limit);
+ int get_limit(Margin p_margin) const;
+
+
+ void set_h_drag_enabled(bool p_enabled);
+ bool is_h_drag_enabled() const;
+
+ void set_v_drag_enabled(bool p_enabled);
+ bool is_v_drag_enabled() const;
+
+ void set_drag_margin(Margin p_margin,float p_drag_margin);
+ float get_drag_margin(Margin p_margin) const;
+
+ void set_v_offset(float p_offset);
+ float get_v_offset() const;
+
+ void set_h_offset(float p_offset);
+ float get_h_offset() const;
+
+ void set_follow_smoothing(float p_speed);
+ float get_follow_smoothing() const;
+
+ void make_current();
+ bool is_current() const;
+
+ void set_zoom(const Vector2& p_zoom);
+ Vector2 get_zoom() const;
+
+ Point2 get_camera_screen_center() const;
+
+ Vector2 get_camera_pos() const;
+ void force_update_scroll();
+
+ Camera2D();
+};
+
+#endif // CAMERA_2D_H
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
new file mode 100644
index 0000000000..8a461c76fc
--- /dev/null
+++ b/scene/2d/canvas_item.cpp
@@ -0,0 +1,875 @@
+/*************************************************************************/
+/* canvas_item.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_item.h"
+#include "servers/visual_server.h"
+#include "scene/main/viewport.h"
+#include "scene/main/canvas_layer.h"
+#include "message_queue.h"
+#include "scene/scene_string_names.h"
+#include "scene/resources/font.h"
+#include "scene/resources/texture.h"
+#include "scene/resources/style_box.h"
+
+bool CanvasItem::is_visible() const {
+
+ if (!is_inside_scene())
+ return false;
+
+ const CanvasItem *p=this;
+
+ while(p) {
+ if (p->hidden)
+ return false;
+ p=p->get_parent_item();
+ }
+
+
+ return true;
+}
+
+bool CanvasItem::is_hidden() const {
+
+ /*if (!is_inside_scene())
+ return false;*/
+
+ return hidden;
+}
+
+void CanvasItem::_propagate_visibility_changed(bool p_visible) {
+
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
+
+ if (p_visible)
+ update(); //todo optimize
+ else
+ emit_signal(SceneStringNames::get_singleton()->hide);
+ _block();
+
+ for(int i=0;i<get_child_count();i++) {
+
+ CanvasItem *c=get_child(i)->cast_to<CanvasItem>();
+
+ if (c && c->hidden!=p_visible) //should the toplevels stop propagation? i think so but..
+ c->_propagate_visibility_changed(p_visible);
+ }
+
+ _unblock();
+
+}
+
+void CanvasItem::show() {
+
+ if (!hidden)
+ return;
+
+
+ hidden=false;
+ VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,true);
+
+ if (!is_inside_scene())
+ return;
+
+ if (is_visible()) {
+ _propagate_visibility_changed(true);
+ }
+}
+
+
+void CanvasItem::hide() {
+
+ if (hidden)
+ return;
+
+ bool propagate=is_inside_scene() && is_visible();
+ hidden=true;
+ VisualServer::get_singleton()->canvas_item_set_visible(canvas_item,false);
+
+ if (!is_inside_scene())
+ return;
+ if (propagate)
+ _propagate_visibility_changed(false);
+
+}
+
+
+Variant CanvasItem::edit_get_state() const {
+
+
+ return Variant();
+}
+void CanvasItem::edit_set_state(const Variant& p_state) {
+
+
+}
+
+void CanvasItem::edit_set_rect(const Rect2& p_edit_rect) {
+
+ //used by editors, implement at will
+}
+
+void CanvasItem::edit_rotate(float p_rot) {
+
+
+}
+
+Size2 CanvasItem::edit_get_minimum_size() const {
+
+ return Size2(-1,-1); //no limit
+}
+
+void CanvasItem::_update_callback() {
+
+
+
+ if (!is_inside_scene()) {
+ pending_update=false;
+ return;
+ }
+
+
+ VisualServer::get_singleton()->canvas_item_clear(get_canvas_item());
+ //todo updating = true - only allow drawing here
+ if (is_visible()) { //todo optimize this!!
+ if (first_draw) {
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
+ first_draw=false;
+ }
+ drawing=true;
+ notification(NOTIFICATION_DRAW);
+ emit_signal(SceneStringNames::get_singleton()->draw);
+ if (get_script_instance()) {
+ Variant::CallError err;
+ get_script_instance()->call_multilevel_reversed(SceneStringNames::get_singleton()->_draw,NULL,0);
+ }
+ drawing=false;
+
+ }
+ //todo updating = false
+ pending_update=false; // don't change to false until finished drawing (avoid recursive update)
+}
+
+Matrix32 CanvasItem::get_global_transform_with_canvas() const {
+
+ const CanvasItem *ci = this;
+ Matrix32 xform;
+ const CanvasItem *last_valid=NULL;
+
+ while(ci) {
+
+ last_valid=ci;
+ xform = ci->get_transform() * xform;
+ ci=ci->get_parent_item();
+ }
+
+ if (last_valid->canvas_layer)
+ return last_valid->canvas_layer->get_transform() * xform;
+ else
+ return xform;
+}
+
+Matrix32 CanvasItem::get_global_transform() const {
+
+
+ if (global_invalid) {
+
+ const CanvasItem *pi = get_parent_item();
+ if (pi)
+ global_transform = pi->get_global_transform() * get_transform();
+ else
+ global_transform = get_transform();
+
+ global_invalid=false;
+ }
+
+ return global_transform;
+
+}
+
+
+void CanvasItem::_queue_sort_children() {
+
+ if (pending_children_sort)
+ return;
+
+ pending_children_sort=true;
+ MessageQueue::get_singleton()->push_call(this,"_sort_children");
+}
+
+void CanvasItem::_sort_children() {
+
+ pending_children_sort=false;
+
+ if (!is_inside_scene())
+ return;
+
+ for(int i=0;i<get_child_count();i++) {
+
+ Node *n = get_child(i);
+ CanvasItem *ci=n->cast_to<CanvasItem>();
+
+ if (ci) {
+ if (ci->toplevel || ci->group!="")
+ continue;
+ VisualServer::get_singleton()->canvas_item_raise(n->cast_to<CanvasItem>()->canvas_item);
+ }
+ }
+}
+
+void CanvasItem::_raise_self() {
+
+ if (!is_inside_scene())
+ return;
+
+ VisualServer::get_singleton()->canvas_item_raise(canvas_item);
+}
+
+
+void CanvasItem::_enter_canvas() {
+
+ if ((!get_parent() || !get_parent()->cast_to<CanvasItem>()) || toplevel) {
+
+ Node *n = this;
+ viewport=NULL;
+ canvas_layer=NULL;
+
+ while(n) {
+
+ if (n->cast_to<Viewport>()) {
+
+ viewport = n->cast_to<Viewport>();
+ break;
+ }
+ if (!canvas_layer && n->cast_to<CanvasLayer>()) {
+
+ canvas_layer = n->cast_to<CanvasLayer>();
+ }
+ n=n->get_parent();
+ }
+
+ RID canvas;
+ if (canvas_layer)
+ canvas=canvas_layer->get_world_2d()->get_canvas();
+ else
+ canvas=viewport->find_world_2d()->get_canvas();
+
+ VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,canvas);
+
+ group = "root_canvas"+itos(canvas.get_id());
+
+ add_to_group(group);
+ get_scene()->call_group(SceneMainLoop::GROUP_CALL_UNIQUE,group,"_raise_self");
+
+ } else {
+
+ CanvasItem *parent = get_parent_item();
+ viewport=parent->viewport;
+ VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,parent->get_canvas_item());
+ parent->_queue_sort_children();
+ }
+
+ if (!viewport) {
+
+ print_line("no viewport wtf!");
+ }
+ pending_update=false;
+ update();
+
+ notification(NOTIFICATION_ENTER_CANVAS);
+
+}
+
+void CanvasItem::_exit_canvas() {
+
+ notification(NOTIFICATION_EXIT_CANVAS,true); //reverse the notification
+ VisualServer::get_singleton()->canvas_item_set_parent(canvas_item,RID());
+ viewport=NULL;
+ canvas_layer=NULL;
+ group="";
+
+}
+
+
+void CanvasItem::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+ first_draw=true;
+ pending_children_sort=false;
+ if (get_parent()) {
+ CanvasItem *ci = get_parent()->cast_to<CanvasItem>();
+ if (ci)
+ C=ci->children_items.push_back(this);
+ }
+ _enter_canvas();
+ if (!block_transform_notify && !xform_change.in_list()) {
+ get_scene()->xform_change_list.add(&xform_change);
+ }
+ } break;
+ case NOTIFICATION_MOVED_IN_PARENT: {
+
+
+ if (group!="") {
+ get_scene()->call_group(SceneMainLoop::GROUP_CALL_UNIQUE,group,"_raise_self");
+ } else {
+ CanvasItem *p = get_parent_item();
+ ERR_FAIL_COND(!p);
+ p->_queue_sort_children();
+ }
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+ if (xform_change.in_list())
+ get_scene()->xform_change_list.remove(&xform_change);
+ _exit_canvas();
+ if (C)
+ get_parent()->cast_to<CanvasItem>()->children_items.erase(C);
+ } break;
+ case NOTIFICATION_DRAW: {
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+
+ } break;
+ case NOTIFICATION_VISIBILITY_CHANGED: {
+
+ emit_signal(SceneStringNames::get_singleton()->visibility_changed);
+ } break;
+
+ }
+}
+
+void CanvasItem::_set_visible_(bool p_visible) {
+
+ if (p_visible)
+ show();
+ else
+ hide();
+}
+bool CanvasItem::_is_visible_() const {
+
+ return !is_hidden();
+}
+
+
+void CanvasItem::update() {
+
+ if (!is_inside_scene())
+ return;
+ if (pending_update)
+ return;
+
+ pending_update=true;
+
+ MessageQueue::get_singleton()->push_call(this,"_update_callback");
+}
+
+void CanvasItem::set_opacity(float p_opacity) {
+
+ opacity=p_opacity;
+ VisualServer::get_singleton()->canvas_item_set_opacity(canvas_item,opacity);
+
+}
+float CanvasItem::get_opacity() const {
+
+ return opacity;
+}
+
+
+void CanvasItem::set_as_toplevel(bool p_toplevel) {
+
+ if (toplevel==p_toplevel)
+ return;
+
+ if (!is_inside_scene()) {
+ toplevel=p_toplevel;
+ return;
+ }
+
+ _exit_canvas();
+ toplevel=p_toplevel;
+ _enter_canvas();
+}
+
+bool CanvasItem::is_set_as_toplevel() const {
+
+ return toplevel;
+}
+
+CanvasItem *CanvasItem::get_parent_item() const {
+
+ if (toplevel)
+ return NULL;
+
+ Node *parent = get_parent();
+ if (!parent)
+ return NULL;
+
+ return parent->cast_to<CanvasItem>();
+}
+
+
+void CanvasItem::set_self_opacity(float p_self_opacity) {
+
+ self_opacity=p_self_opacity;
+ VisualServer::get_singleton()->canvas_item_set_self_opacity(canvas_item,self_opacity);
+
+}
+float CanvasItem::get_self_opacity() const {
+
+ return self_opacity;
+}
+
+void CanvasItem::set_blend_mode(BlendMode p_blend_mode) {
+
+ ERR_FAIL_INDEX(p_blend_mode,4);
+ blend_mode=p_blend_mode;
+ VisualServer::get_singleton()->canvas_item_set_blend_mode(canvas_item,VS::MaterialBlendMode(blend_mode));
+
+}
+
+CanvasItem::BlendMode CanvasItem::get_blend_mode() const {
+
+ return blend_mode;
+}
+
+
+
+void CanvasItem::item_rect_changed() {
+
+ update();
+ emit_signal(SceneStringNames::get_singleton()->item_rect_changed);
+}
+
+
+void CanvasItem::draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_line(canvas_item,p_from,p_to,p_color,p_width);
+}
+
+void CanvasItem::draw_rect(const Rect2& p_rect, const Color& p_color) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_rect(canvas_item,p_rect,p_color);
+
+}
+
+void CanvasItem::draw_circle(const Point2& p_pos, float p_radius, const Color& p_color) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ VisualServer::get_singleton()->canvas_item_add_circle(canvas_item,p_pos,p_radius,p_color);
+
+}
+
+void CanvasItem::draw_texture(const Ref<Texture>& p_texture,const Point2& p_pos) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ ERR_FAIL_COND(p_texture.is_null());
+
+ p_texture->draw(canvas_item,p_pos);
+}
+
+void CanvasItem::draw_texture_rect(const Ref<Texture>& p_texture,const Rect2& p_rect, bool p_tile,const Color& p_modulate) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ ERR_FAIL_COND(p_texture.is_null());
+ p_texture->draw_rect(canvas_item,p_rect,p_tile,p_modulate);
+
+}
+void CanvasItem::draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+ ERR_FAIL_COND(p_texture.is_null());
+ p_texture->draw_rect_region(canvas_item,p_rect,p_src_rect,p_modulate);
+}
+
+void CanvasItem::draw_style_box(const Ref<StyleBox>& p_style_box,const Rect2& p_rect) {
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ ERR_FAIL_COND(p_style_box.is_null());
+
+ p_style_box->draw(canvas_item,p_rect);
+
+}
+void CanvasItem::draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, Ref<Texture> p_texture,float p_width) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+
+ VisualServer::get_singleton()->canvas_item_add_primitive(canvas_item,p_points,p_colors,p_uvs,rid,p_width);
+}
+void CanvasItem::draw_set_transform(const Point2& p_offset, float p_rot, const Size2& p_scale) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ Matrix32 xform(p_rot,p_offset);
+ xform.scale_basis(p_scale);
+ VisualServer::get_singleton()->canvas_item_set_transform(canvas_item,xform);
+}
+
+void CanvasItem::draw_polygon(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, Ref<Texture> p_texture) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+
+ VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item,p_points,p_colors,p_uvs,rid);
+
+
+}
+
+void CanvasItem::draw_colored_polygon(const Vector<Point2>& p_points, const Color& p_color,const Vector<Point2>& p_uvs, Ref<Texture> p_texture) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ Vector<Color> colors;
+ colors.push_back(p_color);
+ RID rid = p_texture.is_valid() ? p_texture->get_rid() : RID();
+
+ VisualServer::get_singleton()->canvas_item_add_polygon(canvas_item,p_points,colors,p_uvs,rid);
+}
+
+void CanvasItem::draw_string(const Ref<Font>& p_font,const Point2& p_pos, const String& p_text,const Color& p_modulate,int p_clip_w) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL();
+ }
+
+ ERR_FAIL_COND(p_font.is_null());
+ p_font->draw(canvas_item,p_pos,p_text,p_modulate,p_clip_w);
+
+}
+
+float CanvasItem::draw_char(const Ref<Font>& p_font,const Point2& p_pos, const String& p_char,const String& p_next,const Color& p_modulate) {
+
+ if (!drawing) {
+ ERR_EXPLAIN("Drawing is only allowed inside NOTIFICATION_DRAW, _draw() function or 'draw' signal.");
+ ERR_FAIL_V(0);
+ }
+
+ ERR_FAIL_COND_V(p_char.length()!=1,0);
+ ERR_FAIL_COND_V(p_font.is_null(),0);
+
+ return p_font->draw_char(canvas_item,p_pos,p_char[0],p_next.c_str()[0],p_modulate);
+
+}
+
+
+void CanvasItem::_notify_transform(CanvasItem *p_node) {
+
+ if (p_node->xform_change.in_list() && p_node->global_invalid)
+ return; //nothing to do
+
+ p_node->global_invalid=true;
+
+ if (!p_node->xform_change.in_list()) {
+ if (!p_node->block_transform_notify) {
+ if (p_node->is_inside_scene())
+ get_scene()->xform_change_list.add(&p_node->xform_change);
+ }
+ }
+
+
+ for(List<CanvasItem*>::Element *E=p_node->children_items.front();E;E=E->next()) {
+
+ CanvasItem* ci=E->get();
+ if (ci->toplevel)
+ continue;
+ _notify_transform(ci);
+ }
+}
+
+
+Rect2 CanvasItem::get_viewport_rect() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),Rect2());
+ return viewport->get_visible_rect();
+}
+
+RID CanvasItem::get_canvas() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),RID());
+
+ if (canvas_layer)
+ return canvas_layer->get_world_2d()->get_canvas();
+ else
+ return viewport->find_world_2d()->get_canvas();
+
+
+}
+
+CanvasItem *CanvasItem::get_toplevel() const {
+
+ CanvasItem *ci=const_cast<CanvasItem*>(this);
+ while(!ci->toplevel && ci->get_parent() && ci->get_parent()->cast_to<CanvasItem>()) {
+ ci=ci->get_parent()->cast_to<CanvasItem>();
+ }
+
+ return ci;
+}
+
+Viewport *CanvasItem::get_viewport() const {
+
+ return viewport;
+}
+
+
+Ref<World2D> CanvasItem::get_world_2d() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),Ref<World2D>());
+
+ CanvasItem *tl=get_toplevel();
+
+ if (tl->canvas_layer) {
+ return tl->canvas_layer->get_world_2d();
+ } else if (tl->viewport) {
+ return tl->viewport->find_world_2d();
+ } else {
+ return Ref<World2D>();
+ }
+
+}
+
+RID CanvasItem::get_viewport_rid() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),RID());
+ return viewport->get_viewport();
+}
+
+void CanvasItem::set_block_transform_notify(bool p_enable) {
+ block_transform_notify=p_enable;
+}
+
+bool CanvasItem::is_block_transform_notify_enabled() const {
+
+ return block_transform_notify;
+}
+
+void CanvasItem::set_on_top(bool p_on_top) {
+
+ if (on_top==p_on_top)
+ return;
+ on_top=p_on_top;
+ VisualServer::get_singleton()->canvas_item_set_on_top(canvas_item,on_top);
+}
+
+bool CanvasItem::is_on_top() const {
+
+ return on_top;
+}
+
+
+void CanvasItem::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_sort_children"),&CanvasItem::_sort_children);
+ ObjectTypeDB::bind_method(_MD("_raise_self"),&CanvasItem::_raise_self);
+ ObjectTypeDB::bind_method(_MD("_update_callback"),&CanvasItem::_update_callback);
+ ObjectTypeDB::bind_method(_MD("_set_visible_"),&CanvasItem::_set_visible_);
+ ObjectTypeDB::bind_method(_MD("_is_visible_"),&CanvasItem::_is_visible_);
+
+ ObjectTypeDB::bind_method(_MD("edit_set_state","state"),&CanvasItem::edit_set_state);
+ ObjectTypeDB::bind_method(_MD("edit_get"),&CanvasItem::edit_get_state);
+ ObjectTypeDB::bind_method(_MD("edit_set_rect","rect"),&CanvasItem::edit_set_rect);
+ ObjectTypeDB::bind_method(_MD("edit_rotate","degrees"),&CanvasItem::edit_rotate);
+
+ ObjectTypeDB::bind_method(_MD("get_item_rect"),&CanvasItem::get_item_rect);
+ //ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform);
+ ObjectTypeDB::bind_method(_MD("get_canvas_item"),&CanvasItem::get_canvas_item);
+
+ ObjectTypeDB::bind_method(_MD("is_visible"),&CanvasItem::is_visible);
+ ObjectTypeDB::bind_method(_MD("is_hidden"),&CanvasItem::is_hidden);
+ ObjectTypeDB::bind_method(_MD("show"),&CanvasItem::show);
+ ObjectTypeDB::bind_method(_MD("hide"),&CanvasItem::hide);
+
+ ObjectTypeDB::bind_method(_MD("update"),&CanvasItem::update);
+
+ ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"),&CanvasItem::set_as_toplevel);
+ ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"),&CanvasItem::is_set_as_toplevel);
+
+ ObjectTypeDB::bind_method(_MD("set_blend_mode","blend_mode"),&CanvasItem::set_blend_mode);
+ ObjectTypeDB::bind_method(_MD("get_blend_mode"),&CanvasItem::get_blend_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_opacity","opacity"),&CanvasItem::set_opacity);
+ ObjectTypeDB::bind_method(_MD("get_opacity"),&CanvasItem::get_opacity);
+ ObjectTypeDB::bind_method(_MD("set_self_opacity","self_opacity"),&CanvasItem::set_self_opacity);
+ ObjectTypeDB::bind_method(_MD("get_self_opacity"),&CanvasItem::get_self_opacity);
+
+ ObjectTypeDB::bind_method(_MD("set_on_top","on_top"),&CanvasItem::set_on_top);
+ ObjectTypeDB::bind_method(_MD("is_on_top"),&CanvasItem::is_on_top);
+
+ //ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform);
+
+ ObjectTypeDB::bind_method(_MD("draw_line","from","to","color","width"),&CanvasItem::draw_line,DEFVAL(1.0));
+ ObjectTypeDB::bind_method(_MD("draw_rect","rect","color"),&CanvasItem::draw_rect);
+ ObjectTypeDB::bind_method(_MD("draw_circle","pos","radius","color"),&CanvasItem::draw_circle);
+ ObjectTypeDB::bind_method(_MD("draw_texture","texture:Texture","pos"),&CanvasItem::draw_texture);
+ ObjectTypeDB::bind_method(_MD("draw_texture_rect","texture:Texture","rect","tile","modulate"),&CanvasItem::draw_texture_rect,DEFVAL(false),DEFVAL(Color(1,1,1)));
+ ObjectTypeDB::bind_method(_MD("draw_texture_rect_region","texture:Texture","rect","src_rect","modulate"),&CanvasItem::draw_texture_rect_region,DEFVAL(Color(1,1,1)));
+ ObjectTypeDB::bind_method(_MD("draw_style_box","style_box:StyleBox","rect"),&CanvasItem::draw_style_box);
+ ObjectTypeDB::bind_method(_MD("draw_primitive","points","colors","uvs","texture:Texture","width"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref<Texture>()),DEFVAL(1.0));
+ ObjectTypeDB::bind_method(_MD("draw_polygon","points","colors","uvs","texture:Texture"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref<Texture>()));
+ ObjectTypeDB::bind_method(_MD("draw_colored_polygon","points","color","uvs","texture:Texture"),&CanvasItem::draw_primitive,DEFVAL(Array()),DEFVAL(Ref<Texture>()));
+ ObjectTypeDB::bind_method(_MD("draw_string","font:Font","pos","text","modulate","clip_w"),&CanvasItem::draw_string,DEFVAL(Color(1,1,1)),DEFVAL(-1));
+ ObjectTypeDB::bind_method(_MD("draw_char","font:Font","pos","char","next","modulate"),&CanvasItem::draw_char,DEFVAL(Color(1,1,1)));
+
+ ObjectTypeDB::bind_method(_MD("draw_set_transform","pos","rot","scale"),&CanvasItem::draw_set_transform);
+ ObjectTypeDB::bind_method(_MD("get_transform"),&CanvasItem::get_transform);
+ ObjectTypeDB::bind_method(_MD("get_global_transform"),&CanvasItem::get_global_transform);
+ ObjectTypeDB::bind_method(_MD("get_viewport_transform"),&CanvasItem::get_viewport_transform);
+ ObjectTypeDB::bind_method(_MD("get_viewport_rect"),&CanvasItem::get_viewport_rect);
+ ObjectTypeDB::bind_method(_MD("get_canvas"),&CanvasItem::get_canvas);
+ ObjectTypeDB::bind_method(_MD("get_world_2d"),&CanvasItem::get_world_2d);
+ ObjectTypeDB::bind_method(_MD("get_viewport"),&CanvasItem::get_viewport);
+
+ BIND_VMETHOD(MethodInfo("_draw"));
+
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"),_SCS("_is_visible_") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"visibility/opacity",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_opacity"),_SCS("get_opacity") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"visibility/self_opacity",PROPERTY_HINT_RANGE, "0,1,0.01"), _SCS("set_self_opacity"),_SCS("get_self_opacity") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"visibility/on_top"), _SCS("set_on_top"),_SCS("is_on_top") );
+
+ ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"visibility/blend_mode",PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), _SCS("set_blend_mode"),_SCS("get_blend_mode") );
+ //exporting these two things doesn't really make much sense i think
+ //ADD_PROPERTY( PropertyInfo(Variant::BOOL,"transform/toplevel"), _SCS("set_as_toplevel"),_SCS("is_set_as_toplevel") );
+ //ADD_PROPERTY(PropertyInfo(Variant::BOOL,"transform/notify"),_SCS("set_transform_notify"),_SCS("is_transform_notify_enabled"));
+
+ ADD_SIGNAL( MethodInfo("draw") );
+ ADD_SIGNAL( MethodInfo("visibility_changed") );
+ ADD_SIGNAL( MethodInfo("hide") );
+ ADD_SIGNAL( MethodInfo("item_rect_changed") );
+
+
+
+ BIND_CONSTANT( BLEND_MODE_MIX );
+ BIND_CONSTANT( BLEND_MODE_ADD );
+ BIND_CONSTANT( BLEND_MODE_SUB );
+ BIND_CONSTANT( BLEND_MODE_MUL );
+
+
+ BIND_CONSTANT( NOTIFICATION_DRAW);
+ BIND_CONSTANT( NOTIFICATION_VISIBILITY_CHANGED );
+ BIND_CONSTANT( NOTIFICATION_ENTER_CANVAS );
+ BIND_CONSTANT( NOTIFICATION_EXIT_CANVAS );
+ BIND_CONSTANT( NOTIFICATION_TRANSFORM_CHANGED );
+
+
+}
+
+Matrix32 CanvasItem::get_viewport_transform() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),Matrix32());
+
+ if (canvas_layer) {
+
+ if (viewport) {
+ return viewport->get_final_transform() * canvas_layer->get_transform();
+ } else {
+ return canvas_layer->get_transform();
+ }
+
+ } else if (viewport) {
+ return viewport->get_final_transform() * viewport->get_canvas_transform();
+ }
+
+ return Matrix32();
+
+}
+
+
+CanvasItem::CanvasItem() : xform_change(this) {
+
+
+ canvas_item=VisualServer::get_singleton()->canvas_item_create();
+ hidden=false;
+ pending_update=false;
+ opacity=1;
+ self_opacity=1;
+ toplevel=false;
+ pending_children_sort=false;
+ first_draw=false;
+ blend_mode=BLEND_MODE_MIX;
+ drawing=false;
+ on_top=true;
+ block_transform_notify=false;
+ viewport=NULL;
+ canvas_layer=NULL;
+ global_invalid=true;
+
+ C=NULL;
+
+}
+
+CanvasItem::~CanvasItem() {
+
+ VisualServer::get_singleton()->free(canvas_item);
+}
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
new file mode 100644
index 0000000000..0da4aa3086
--- /dev/null
+++ b/scene/2d/canvas_item.h
@@ -0,0 +1,209 @@
+/*************************************************************************/
+/* canvas_item.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_ITEM_H
+#define CANVAS_ITEM_H
+
+#include "scene/main/node.h"
+#include "scene/resources/texture.h"
+#include "scene/main/scene_main_loop.h"
+
+class CanvasLayer;
+class Viewport;
+class Font;
+
+class StyleBox;
+
+class CanvasItem : public Node {
+
+ OBJ_TYPE( CanvasItem, Node );
+public:
+
+ enum BlendMode {
+
+ BLEND_MODE_MIX, //default
+ BLEND_MODE_ADD,
+ BLEND_MODE_SUB,
+ BLEND_MODE_MUL
+ };
+
+private:
+
+ mutable SelfList<Node> xform_change;
+
+ RID canvas_item;
+ String group;
+
+ Viewport *viewport;
+ CanvasLayer *canvas_layer;
+
+ float opacity;
+ float self_opacity;
+
+ List<CanvasItem*> children_items;
+ List<CanvasItem*>::Element *C;
+
+ BlendMode blend_mode;
+
+ bool first_draw;
+ bool hidden;
+ bool pending_update;
+ bool toplevel;
+ bool pending_children_sort;
+ bool drawing;
+ bool block_transform_notify;
+ bool on_top;
+
+ mutable Matrix32 global_transform;
+ mutable bool global_invalid;
+
+
+ void _raise_self();
+
+ void _propagate_visibility_changed(bool p_visible);
+
+ void _set_visible_(bool p_visible);
+ bool _is_visible_() const;
+
+ void _update_callback();
+
+ void _enter_canvas();
+ void _exit_canvas();
+
+ void _queue_sort_children();
+ void _sort_children();
+
+
+
+ void _notify_transform(CanvasItem *p_node);
+
+protected:
+
+
+
+ _FORCE_INLINE_ void _notify_transform() { if (!is_inside_scene()) return; _notify_transform(this); if (!block_transform_notify) notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); }
+
+ void item_rect_changed();
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+
+ enum {
+ NOTIFICATION_TRANSFORM_CHANGED=SceneMainLoop::NOTIFICATION_TRANSFORM_CHANGED, //unique
+ NOTIFICATION_DRAW=30,
+ NOTIFICATION_VISIBILITY_CHANGED=31,
+ NOTIFICATION_ENTER_CANVAS=32,
+ NOTIFICATION_EXIT_CANVAS=33,
+ NOTIFICATION_LOCAL_TRANSFORM_CHANGED=35,
+ NOTIFICATION_WORLD_2D_CHANGED=36,
+
+ };
+
+ /* EDITOR */
+
+ virtual Variant edit_get_state() const;
+ virtual void edit_set_state(const Variant& p_state);
+ virtual void edit_set_rect(const Rect2& p_edit_rect);
+ virtual void edit_rotate(float p_rot);
+ virtual Size2 edit_get_minimum_size() const;
+
+ /* VISIBILITY */
+
+ bool is_visible() const;
+ bool is_hidden() const;
+ void show();
+ void hide();
+
+ void update();
+
+ void set_blend_mode(BlendMode p_blend_mode);
+ BlendMode get_blend_mode() const;
+
+ void set_opacity(float p_opacity);
+ float get_opacity() const;
+
+ void set_self_opacity(float p_self_opacity);
+ float get_self_opacity() const;
+
+ /* DRAWING API */
+
+ void draw_line(const Point2& p_from, const Point2& p_to,const Color& p_color,float p_width=1.0);
+ void draw_rect(const Rect2& p_rect, const Color& p_color);
+ void draw_circle(const Point2& p_pos, float p_radius, const Color& p_color);
+ void draw_texture(const Ref<Texture>& p_texture,const Point2& p_pos);
+ void draw_texture_rect(const Ref<Texture>& p_texture, const Rect2& p_rect, bool p_tile=false,const Color& p_modulate=Color(1,1,1));
+ void draw_texture_rect_region(const Ref<Texture>& p_texture,const Rect2& p_rect, const Rect2& p_src_rect,const Color& p_modulate=Color(1,1,1));
+ void draw_style_box(const Ref<StyleBox>& p_style_box,const Rect2& p_rect);
+ void draw_primitive(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs, Ref<Texture> p_texture=Ref<Texture>(),float p_width=1);
+ void draw_polygon(const Vector<Point2>& p_points, const Vector<Color>& p_colors,const Vector<Point2>& p_uvs=Vector<Point2>(), Ref<Texture> p_texture=Ref<Texture>());
+ void draw_colored_polygon(const Vector<Point2>& p_points, const Color& p_color,const Vector<Point2>& p_uvs=Vector<Point2>(), Ref<Texture> p_texture=Ref<Texture>());
+
+ void draw_string(const Ref<Font>& p_font, const Point2& p_pos, const String& p_text,const Color& p_modulate=Color(1,1,1),int p_clip_w=-1);
+ float draw_char(const Ref<Font>& p_font,const Point2& p_pos, const String& p_char,const String& p_next="",const Color& p_modulate=Color(1,1,1));
+
+ void draw_set_transform(const Point2& p_offset, float p_rot, const Size2& p_scale);
+
+ /* RECT / TRANSFORM */
+
+ void set_as_toplevel(bool p_toplevel);
+ bool is_set_as_toplevel() const;
+
+ void set_on_top(bool p_on_top);
+ bool is_on_top() const;
+
+ CanvasItem *get_parent_item() const;
+
+ virtual Rect2 get_item_rect() const=0;
+ virtual Matrix32 get_transform() const=0;
+
+ virtual Matrix32 get_global_transform() const;
+ virtual Matrix32 get_global_transform_with_canvas() const;
+
+ CanvasItem *get_toplevel() const;
+ _FORCE_INLINE_ RID get_canvas_item() const { return canvas_item; }
+
+ void set_block_transform_notify(bool p_enable);
+ bool is_block_transform_notify_enabled() const;
+
+ Matrix32 get_viewport_transform() const;
+ Rect2 get_viewport_rect() const;
+ RID get_viewport_rid() const;
+ RID get_canvas() const;
+ Ref<World2D> get_world_2d() const;
+ Viewport *get_viewport() const;
+
+
+ CanvasItem();
+ ~CanvasItem();
+};
+
+VARIANT_ENUM_CAST( CanvasItem::BlendMode );
+
+#endif // CANVAS_ITEM_H
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
new file mode 100644
index 0000000000..ab8c4551ee
--- /dev/null
+++ b/scene/2d/collision_object_2d.cpp
@@ -0,0 +1,282 @@
+/*************************************************************************/
+/* collision_object_2d.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 "collision_object_2d.h"
+#include "servers/physics_2d_server.h"
+
+void CollisionObject2D::_update_shapes_from_children() {
+
+ shapes.resize(0);
+ for(int i=0;i<get_child_count();i++) {
+
+ Node* n = get_child(i);
+ n->call("_add_to_collision_object",this);
+ }
+
+// _update_shapes();
+}
+
+void CollisionObject2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+
+ RID space = get_world_2d()->get_space();
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_space(rid,space);
+ } else
+ Physics2DServer::get_singleton()->body_set_space(rid,space);
+
+ //get space
+ }
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (area)
+ Physics2DServer::get_singleton()->area_set_transform(rid,get_global_transform());
+ else
+ Physics2DServer::get_singleton()->body_set_state(rid,Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform());
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (area) {
+ Physics2DServer::get_singleton()->area_set_space(rid,RID());
+ } else
+ Physics2DServer::get_singleton()->body_set_space(rid,RID());
+
+ } break;
+ }
+}
+
+void CollisionObject2D::_update_shapes() {
+
+ if (!rid.is_valid())
+ return;
+
+ if (area)
+ Physics2DServer::get_singleton()->area_clear_shapes(rid);
+ else
+ Physics2DServer::get_singleton()->body_clear_shapes(rid);
+
+ for(int i=0;i<shapes.size();i++) {
+
+ if (shapes[i].shape.is_null())
+ continue;
+ if (area)
+ Physics2DServer::get_singleton()->area_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform);
+ else {
+ Physics2DServer::get_singleton()->body_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform);
+ if (shapes[i].trigger)
+ Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid,i,shapes[i].trigger);
+ }
+ }
+}
+
+
+bool CollisionObject2D::_set(const StringName& p_name, const Variant& p_value) {
+ String name=p_name;
+
+ if (name=="shape_count") {
+
+ shapes.resize(p_value);
+ _update_shapes();
+ _change_notify();
+
+ } else if (name.begins_with("shapes/")) {
+
+ int idx=name.get_slice("/",1).to_int();
+ String what=name.get_slice("/",2);
+ if (what=="shape")
+ set_shape(idx,RefPtr(p_value));
+ else if (what=="transform")
+ set_shape_transform(idx,p_value);
+ else if (what=="trigger")
+ set_shape_as_trigger(idx,p_value);
+ } else
+ return false;
+
+ return true;
+
+
+}
+
+bool CollisionObject2D::_get(const StringName& p_name,Variant &r_ret) const {
+
+ String name=p_name;
+
+ if (name=="shape_count") {
+ r_ret= shapes.size();
+ } else if (name.begins_with("shapes/")) {
+
+ int idx=name.get_slice("/",1).to_int();
+ String what=name.get_slice("/",2);
+ if (what=="shape")
+ r_ret= get_shape(idx);
+ else if (what=="transform")
+ r_ret= get_shape_transform(idx);
+ else if (what=="trigger")
+ r_ret= is_shape_set_as_trigger(idx);
+ } else
+ return false;
+
+ return true;
+}
+
+void CollisionObject2D::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo(Variant::INT,"shape_count",PROPERTY_HINT_RANGE,"0,256,1",PROPERTY_USAGE_NOEDITOR) );
+
+ for(int i=0;i<shapes.size();i++) {
+ String path="shapes/"+itos(i)+"/";
+ p_list->push_back( PropertyInfo(Variant::OBJECT,path+"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape2D",PROPERTY_USAGE_NOEDITOR) );
+ p_list->push_back( PropertyInfo(Variant::TRANSFORM,path+"transform",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) );
+ p_list->push_back( PropertyInfo(Variant::BOOL,path+"trigger",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR) );
+ }
+}
+
+void CollisionObject2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("add_shape","shape:Shape2D","transform"),&CollisionObject2D::add_shape,DEFVAL(Matrix32()));
+ ObjectTypeDB::bind_method(_MD("get_shape_count"),&CollisionObject2D::get_shape_count);
+ ObjectTypeDB::bind_method(_MD("set_shape","shape_idx","shape:Shape"),&CollisionObject2D::set_shape);
+ ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject2D::set_shape_transform);
+ ObjectTypeDB::bind_method(_MD("set_shape_as_trigger","shape_idx","enable"),&CollisionObject2D::set_shape_as_trigger);
+ ObjectTypeDB::bind_method(_MD("get_shape:Shape2D","shape_idx"),&CollisionObject2D::get_shape);
+ ObjectTypeDB::bind_method(_MD("get_shape_transform","shape_idx"),&CollisionObject2D::get_shape_transform);
+ ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger","shape_idx"),&CollisionObject2D::is_shape_set_as_trigger);
+ ObjectTypeDB::bind_method(_MD("remove_shape","shape_idx"),&CollisionObject2D::remove_shape);
+ ObjectTypeDB::bind_method(_MD("clear_shapes"),&CollisionObject2D::clear_shapes);
+ ObjectTypeDB::bind_method(_MD("get_rid"),&CollisionObject2D::get_rid);
+
+}
+
+
+void CollisionObject2D::add_shape(const Ref<Shape2D>& p_shape, const Matrix32& p_transform) {
+
+ ShapeData sdata;
+ sdata.shape=p_shape;
+ sdata.xform=p_transform;
+ sdata.trigger=false;
+ shapes.push_back(sdata);
+ _update_shapes();
+
+}
+int CollisionObject2D::get_shape_count() const {
+
+ return shapes.size();
+
+}
+void CollisionObject2D::set_shape(int p_shape_idx, const Ref<Shape2D>& p_shape) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes[p_shape_idx].shape=p_shape;
+ _update_shapes();
+}
+
+void CollisionObject2D::set_shape_transform(int p_shape_idx, const Matrix32& p_transform) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes[p_shape_idx].xform=p_transform;
+
+ _update_shapes();
+}
+
+Ref<Shape2D> CollisionObject2D::get_shape(int p_shape_idx) const {
+
+ ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Ref<Shape2D>());
+ return shapes[p_shape_idx].shape;
+
+}
+Matrix32 CollisionObject2D::get_shape_transform(int p_shape_idx) const {
+
+ ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Matrix32());
+ return shapes[p_shape_idx].xform;
+
+}
+void CollisionObject2D::remove_shape(int p_shape_idx) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes.remove(p_shape_idx);
+
+ _update_shapes();
+}
+
+void CollisionObject2D::set_shape_as_trigger(int p_shape_idx, bool p_trigger) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes[p_shape_idx].trigger=p_trigger;
+ if (!area && rid.is_valid()) {
+
+ Physics2DServer::get_singleton()->body_set_shape_as_trigger(rid,p_shape_idx,p_trigger);
+
+ }
+}
+
+bool CollisionObject2D::is_shape_set_as_trigger(int p_shape_idx) const {
+
+ ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),false);
+ return shapes[p_shape_idx].trigger;
+}
+
+void CollisionObject2D::clear_shapes() {
+
+ shapes.clear();
+
+ _update_shapes();
+}
+
+
+CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
+
+ rid=p_rid;
+ area=p_area;
+ if (p_area) {
+ Physics2DServer::get_singleton()->area_attach_object_instance_ID(rid,get_instance_ID());
+ } else {
+ Physics2DServer::get_singleton()->body_attach_object_instance_ID(rid,get_instance_ID());
+ }
+
+
+}
+
+
+CollisionObject2D::CollisionObject2D() {
+
+
+ //owner=
+
+
+}
+
+CollisionObject2D::~CollisionObject2D() {
+
+ Physics2DServer::get_singleton()->free(rid);
+}
diff --git a/scene/2d/collision_object_2d.h b/scene/2d/collision_object_2d.h
new file mode 100644
index 0000000000..4a529ce062
--- /dev/null
+++ b/scene/2d/collision_object_2d.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* collision_object_2d.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 COLLISION_OBJECT_2D_H
+#define COLLISION_OBJECT_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/shape_2d.h"
+
+class CollisionObject2D : public Node2D {
+
+ OBJ_TYPE( CollisionObject2D, Node2D );
+
+ bool area;
+ RID rid;
+
+ struct ShapeData {
+ Matrix32 xform;
+ Ref<Shape2D> shape;
+ bool trigger;
+
+ ShapeData() {
+ trigger=false;
+ }
+ };
+
+
+ Vector<ShapeData> shapes;
+
+ void _update_shapes();
+
+friend class CollisionShape2D;
+friend class CollisionPolygon2D;
+ void _update_shapes_from_children();
+protected:
+
+ CollisionObject2D(RID p_rid, bool p_area);
+
+ void _notification(int p_what);
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+ static void _bind_methods();
+public:
+
+
+ void add_shape(const Ref<Shape2D>& p_shape, const Matrix32& p_transform=Matrix32());
+ int get_shape_count() const;
+ void set_shape(int p_shape_idx, const Ref<Shape2D>& p_shape);
+ void set_shape_transform(int p_shape_idx, const Matrix32& p_transform);
+ Ref<Shape2D> get_shape(int p_shape_idx) const;
+ Matrix32 get_shape_transform(int p_shape_idx) const;
+ void set_shape_as_trigger(int p_shape_idx, bool p_trigger);
+ bool is_shape_set_as_trigger(int p_shape_idx) const;
+ void remove_shape(int p_shape_idx);
+ void clear_shapes();
+
+ _FORCE_INLINE_ RID get_rid() const { return rid; }
+
+ CollisionObject2D();
+ ~CollisionObject2D();
+};
+
+#endif // COLLISION_OBJECT_2D_H
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
new file mode 100644
index 0000000000..5ab223a1b8
--- /dev/null
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -0,0 +1,187 @@
+/*************************************************************************/
+/* collision_polygon_2d.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 "collision_polygon_2d.h"
+#include "collision_object_2d.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+
+void CollisionPolygon2D::_add_to_collision_object(Object *p_obj) {
+
+ CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
+ ERR_FAIL_COND(!co);
+
+ if (polygon.size()==0)
+ return;
+
+ bool solids=build_mode==BUILD_SOLIDS;
+
+
+ if (solids) {
+
+ //here comes the sun, lalalala
+ //decompose concave into multiple convex polygons and add them
+ Vector< Vector<Vector2> > decomp = Geometry::decompose_polygon(polygon);
+ for(int i=0;i<decomp.size();i++) {
+ Ref<ConvexPolygonShape2D> convex = memnew( ConvexPolygonShape2D );
+ convex->set_points(decomp[i]);
+ co->add_shape(convex,get_transform());
+
+ }
+
+ } else {
+
+ Ref<ConcavePolygonShape2D> concave = memnew( ConcavePolygonShape2D );
+
+ DVector<Vector2> segments;
+ segments.resize(polygon.size()*2);
+ DVector<Vector2>::Write w=segments.write();
+
+ for(int i=0;i<polygon.size();i++) {
+ w[(i<<1)+0]=polygon[i];
+ w[(i<<1)+1]=polygon[(i+1)%polygon.size()];
+ }
+
+ w=DVector<Vector2>::Write();
+ concave->set_segments(segments);
+
+ co->add_shape(concave,get_transform());
+
+ }
+
+
+ //co->add_shape(shape,get_transform());
+}
+
+void CollisionPolygon2D::_update_parent() {
+
+ Node *parent = get_parent();
+ if (!parent)
+ return;
+ CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
+ if (!co)
+ return;
+ co->_update_shapes_from_children();
+}
+
+void CollisionPolygon2D::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+
+ if (!is_inside_scene())
+ break;
+ _update_parent();
+
+ } break;
+
+ case NOTIFICATION_DRAW: {
+ for(int i=0;i<polygon.size();i++) {
+
+ Vector2 p = polygon[i];
+ Vector2 n = polygon[(i+1)%polygon.size()];
+ draw_line(p,n,Color(0,0.6,0.7,0.5),3);
+ }
+
+ Vector< Vector<Vector2> > decomp = Geometry::decompose_polygon(polygon);
+#define DEBUG_DECOMPOSE
+#ifdef DEBUG_DECOMPOSE
+ Color c(0.4,0.9,0.1);
+ for(int i=0;i<decomp.size();i++) {
+
+ c.set_hsv( Math::fmod(c.get_h() + 0.738,1),c.get_s(),c.get_v(),0.5);
+ draw_colored_polygon(decomp[i],c);
+ }
+#endif
+ } break;
+ }
+}
+
+void CollisionPolygon2D::set_polygon(const Vector<Point2>& p_polygon) {
+
+ polygon=p_polygon;
+
+ for(int i=0;i<polygon.size();i++) {
+ if (i==0)
+ aabb=Rect2(polygon[i],Size2());
+ else
+ aabb.expand_to(polygon[i]);
+ }
+ if (aabb==Rect2()) {
+
+ aabb=Rect2(-10,-10,20,20);
+ } else {
+ aabb.pos-=aabb.size*0.3;
+ aabb.size+=aabb.size*0.6;
+ }
+ _update_parent();
+ update();
+}
+
+Vector<Point2> CollisionPolygon2D::get_polygon() const {
+
+ return polygon;
+}
+
+void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
+
+ ERR_FAIL_INDEX(p_mode,2);
+ build_mode=p_mode;
+}
+
+CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const{
+
+ return build_mode;
+}
+
+Rect2 CollisionPolygon2D::get_item_rect() const {
+
+ return aabb;
+}
+
+void CollisionPolygon2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionPolygon2D::_add_to_collision_object);
+ ObjectTypeDB::bind_method(_MD("set_polygon","polygon"),&CollisionPolygon2D::set_polygon);
+ ObjectTypeDB::bind_method(_MD("get_polygon"),&CollisionPolygon2D::get_polygon);
+
+ ObjectTypeDB::bind_method(_MD("set_build_mode"),&CollisionPolygon2D::set_build_mode);
+ ObjectTypeDB::bind_method(_MD("get_build_mode"),&CollisionPolygon2D::get_build_mode);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"build_mode",PROPERTY_HINT_ENUM,"Automatic,Segments,Solids"),_SCS("set_build_mode"),_SCS("get_build_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon"));
+}
+
+CollisionPolygon2D::CollisionPolygon2D() {
+
+ aabb=Rect2(-10,-10,20,20);
+ build_mode=BUILD_SOLIDS;
+
+
+}
diff --git a/scene/2d/collision_polygon_2d.h b/scene/2d/collision_polygon_2d.h
new file mode 100644
index 0000000000..09c2060088
--- /dev/null
+++ b/scene/2d/collision_polygon_2d.h
@@ -0,0 +1,75 @@
+/*************************************************************************/
+/* collision_polygon_2d.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 COLLISION_POLYGON_2D_H
+#define COLLISION_POLYGON_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/shape_2d.h"
+
+
+
+class CollisionPolygon2D : public Node2D {
+
+ OBJ_TYPE(CollisionPolygon2D,Node2D);
+public:
+
+ enum BuildMode {
+ BUILD_SOLIDS,
+ BUILD_SEGMENTS,
+ };
+
+protected:
+
+
+ Rect2 aabb;
+ BuildMode build_mode;
+ Vector<Point2> polygon;
+
+ void _add_to_collision_object(Object *p_obj);
+ void _update_parent();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_build_mode(BuildMode p_mode);
+ BuildMode get_build_mode() const;
+
+ void set_polygon(const Vector<Point2>& p_polygon);
+ Vector<Point2> get_polygon() const;
+
+ virtual Rect2 get_item_rect() const;
+ CollisionPolygon2D();
+};
+
+VARIANT_ENUM_CAST( CollisionPolygon2D::BuildMode );
+
+#endif // COLLISION_POLYGON_2D_H
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
new file mode 100644
index 0000000000..4ae9b37c21
--- /dev/null
+++ b/scene/2d/collision_shape_2d.cpp
@@ -0,0 +1,248 @@
+/*************************************************************************/
+/* collision_shape_2d.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 "collision_shape_2d.h"
+#include "collision_object_2d.h"
+#include "scene/resources/segment_shape_2d.h"
+#include "scene/resources/shape_line_2d.h"
+#include "scene/resources/circle_shape_2d.h"
+#include "scene/resources/rectangle_shape_2d.h"
+#include "scene/resources/capsule_shape_2d.h"
+#include "scene/resources/convex_polygon_shape_2d.h"
+#include "scene/resources/concave_polygon_shape_2d.h"
+
+
+void CollisionShape2D::_add_to_collision_object(Object *p_obj) {
+
+ CollisionObject2D *co = p_obj->cast_to<CollisionObject2D>();
+ ERR_FAIL_COND(!co);
+ co->add_shape(shape,get_transform());
+ if (trigger)
+ co->set_shape_as_trigger(co->get_shape_count()-1,true);
+
+}
+
+void CollisionShape2D::_shape_changed() {
+
+ update();
+ _update_parent();
+}
+
+void CollisionShape2D::_update_parent() {
+
+
+ Node *parent = get_parent();
+ if (!parent)
+ return;
+ CollisionObject2D *co = parent->cast_to<CollisionObject2D>();
+ if (!co)
+ return;
+ co->_update_shapes_from_children();
+}
+
+void CollisionShape2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
+
+ if (!is_inside_scene())
+ break;
+ _update_parent();
+
+ } break;
+ /*
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (!is_inside_scene())
+ break;
+ _update_parent();
+
+ } break;*/
+ case NOTIFICATION_DRAW: {
+
+ rect=Rect2();
+
+ Color draw_col=Color(0,0.6,0.7,0.5);
+
+ if (shape->cast_to<LineShape2D>()) {
+
+ LineShape2D *l = shape->cast_to<LineShape2D>();
+ Vector2 point = l->get_d() * l->get_normal();
+
+ Vector2 l1[2]={point-l->get_normal().tangent()*100,point+l->get_normal().tangent()*100};
+ draw_line(l1[0],l1[1],draw_col,3);
+ Vector2 l2[2]={point,point+l->get_normal()*30};
+ draw_line(l2[0],l2[1],draw_col,3);
+ rect.pos=l1[0];
+ rect.expand_to(l1[1]);
+ rect.expand_to(l2[0]);
+ rect.expand_to(l2[1]);
+
+ } else if (shape->cast_to<SegmentShape2D>()) {
+
+ SegmentShape2D *s = shape->cast_to<SegmentShape2D>();
+ draw_line(s->get_a(),s->get_b(),draw_col,3);
+ rect.pos=s->get_a();
+ rect.expand_to(s->get_b());
+
+ } else if (shape->cast_to<RayShape2D>()) {
+
+ RayShape2D *s = shape->cast_to<RayShape2D>();
+
+ Vector2 tip = Vector2(0,s->get_length());
+ draw_line(Vector2(),tip,draw_col,3);
+ Vector<Vector2> pts;
+ float tsize=4;
+ pts.push_back(tip+Vector2(0,tsize));
+ pts.push_back(tip+Vector2(0.707*tsize,0));
+ pts.push_back(tip+Vector2(-0.707*tsize,0));
+ Vector<Color> cols;
+ for(int i=0;i<3;i++)
+ cols.push_back(draw_col);
+
+ draw_primitive(pts,cols,Vector<Vector2>()); //small arrow
+
+ rect.pos=Vector2();
+ rect.expand_to(tip);
+ rect=rect.grow(0.707*tsize);
+
+ } else if (shape->cast_to<CircleShape2D>()) {
+
+ CircleShape2D *s = shape->cast_to<CircleShape2D>();
+ Vector<Vector2> points;
+ for(int i=0;i<24;i++) {
+
+ points.push_back(Vector2(Math::cos(i*Math_PI*2/24.0),Math::sin(i*Math_PI*2/24.0))*s->get_radius());
+ }
+
+ draw_colored_polygon(points,draw_col);
+ rect.pos=-Point2(s->get_radius(),s->get_radius());
+ rect.size=Point2(s->get_radius(),s->get_radius())*2.0;
+
+ } else if (shape->cast_to<RectangleShape2D>()) {
+
+ RectangleShape2D *s = shape->cast_to<RectangleShape2D>();
+ Vector2 he = s->get_extents();
+ rect=Rect2(-he,he*2.0);
+ draw_rect(rect,draw_col);;
+
+ } else if (shape->cast_to<CapsuleShape2D>()) {
+
+ CapsuleShape2D *s = shape->cast_to<CapsuleShape2D>();
+
+ Vector<Vector2> points;
+ for(int i=0;i<24;i++) {
+ Vector2 ofs = Vector2(0,(i>6 && i<=18) ? -s->get_height()*0.5 : s->get_height()*0.5);
+
+ points.push_back(Vector2(Math::sin(i*Math_PI*2/24.0),Math::cos(i*Math_PI*2/24.0))*s->get_radius() + ofs);
+ if (i==6 || i==18)
+ points.push_back(Vector2(Math::sin(i*Math_PI*2/24.0),Math::cos(i*Math_PI*2/24.0))*s->get_radius() - ofs);
+ }
+
+ draw_colored_polygon(points,draw_col);
+ Vector2 he=Point2(s->get_radius(),s->get_radius()+s->get_height()*0.5);
+ rect.pos=-he;
+ rect.size=he*2.0;
+
+ } else if (shape->cast_to<ConvexPolygonShape2D>()) {
+
+ ConvexPolygonShape2D *s = shape->cast_to<ConvexPolygonShape2D>();
+
+ Vector<Vector2> points = s->get_points();
+ for(int i=0;i<points.size();i++) {
+ if (i==0)
+ rect.pos=points[i];
+ else
+ rect.expand_to(points[i]);
+ }
+
+ draw_colored_polygon(points,draw_col);
+
+ }
+
+ rect=rect.grow(3);
+
+ } break;
+
+ }
+
+}
+
+void CollisionShape2D::set_shape(const Ref<Shape2D>& p_shape) {
+
+ if (shape.is_valid())
+ shape->disconnect("changed",this,"_shape_changed");
+ shape=p_shape;
+ update();
+ _update_parent();
+ if (shape.is_valid())
+ shape->connect("changed",this,"_shape_changed");
+
+}
+
+Ref<Shape2D> CollisionShape2D::get_shape() const {
+
+ return shape;
+}
+
+Rect2 CollisionShape2D::get_item_rect() const {
+
+ return rect;
+}
+
+void CollisionShape2D::set_trigger(bool p_trigger) {
+
+ trigger=p_trigger;
+ _update_parent();
+}
+
+bool CollisionShape2D::is_trigger() const{
+
+ return trigger;
+}
+
+void CollisionShape2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_shape","shape"),&CollisionShape2D::set_shape);
+ ObjectTypeDB::bind_method(_MD("get_shape"),&CollisionShape2D::get_shape);
+ ObjectTypeDB::bind_method(_MD("_shape_changed"),&CollisionShape2D::_shape_changed);
+ ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionShape2D::_add_to_collision_object);
+ ObjectTypeDB::bind_method(_MD("set_trigger","enable"),&CollisionShape2D::set_trigger);
+ ObjectTypeDB::bind_method(_MD("is_trigger"),&CollisionShape2D::is_trigger);
+
+ ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"shape",PROPERTY_HINT_RESOURCE_TYPE,"Shape2D"),_SCS("set_shape"),_SCS("get_shape"));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"trigger"),_SCS("set_trigger"),_SCS("is_trigger"));
+}
+
+CollisionShape2D::CollisionShape2D() {
+
+ rect=Rect2(-Point2(10,10),Point2(20,20));
+
+ trigger=false;
+}
diff --git a/scene/2d/collision_shape_2d.h b/scene/2d/collision_shape_2d.h
new file mode 100644
index 0000000000..2e2023f54d
--- /dev/null
+++ b/scene/2d/collision_shape_2d.h
@@ -0,0 +1,61 @@
+/*************************************************************************/
+/* collision_shape_2d.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 COLLISION_SHAPE_2D_H
+#define COLLISION_SHAPE_2D_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/shape_2d.h"
+
+class CollisionShape2D : public Node2D {
+
+ OBJ_TYPE(CollisionShape2D,Node2D);
+ Ref<Shape2D> shape;
+ Rect2 rect;
+ bool trigger;
+
+ void _shape_changed();
+protected:
+
+ void _update_parent();
+ void _notification(int p_what);
+ static void _bind_methods();
+
+ void _add_to_collision_object(Object *p_obj);
+public:
+
+ void set_shape(const Ref<Shape2D>& p_shape);
+ Ref<Shape2D> get_shape() const;
+ virtual Rect2 get_item_rect() const;
+ void set_trigger(bool p_trigger);
+ bool is_trigger() const;
+
+ CollisionShape2D();
+};
+
+#endif // COLLISION_SHAPE_2D_H
diff --git a/scene/2d/joints_2d.cpp b/scene/2d/joints_2d.cpp
new file mode 100644
index 0000000000..0e673ff791
--- /dev/null
+++ b/scene/2d/joints_2d.cpp
@@ -0,0 +1,412 @@
+/*************************************************************************/
+/* joints_2d.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 "joints_2d.h"
+#include "servers/physics_2d_server.h"
+#include "physics_body_2d.h"
+
+void Joint2D::_update_joint() {
+
+ if (!is_inside_scene())
+ return;
+
+ if (joint.is_valid()) {
+ Physics2DServer::get_singleton()->free(joint);
+ }
+
+ joint=RID();
+
+
+ joint = _configure_joint();
+ Physics2DServer::get_singleton()->get_singleton()->joint_set_param(joint,Physics2DServer::JOINT_PARAM_BIAS,bias);
+
+
+}
+
+
+void Joint2D::set_node_a(const NodePath& p_node_a) {
+
+
+ if (a==p_node_a)
+ return;
+
+ a=p_node_a;
+ _update_joint();
+}
+
+NodePath Joint2D::get_node_a() const{
+
+ return a;
+}
+
+void Joint2D::set_node_b(const NodePath& p_node_b){
+
+ if (b==p_node_b)
+ return;
+ b=p_node_b;
+ _update_joint();
+
+}
+NodePath Joint2D::get_node_b() const{
+
+
+ return b;
+}
+
+
+void Joint2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+ _update_joint();
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+ if (joint.is_valid()) {
+
+ Physics2DServer::get_singleton()->free(joint);
+ joint=RID();
+ }
+ } break;
+
+ }
+
+}
+
+void Joint2D::set_bias(real_t p_bias) {
+
+ bias=p_bias;
+ if (joint.is_valid())
+ Physics2DServer::get_singleton()->get_singleton()->joint_set_param(joint,Physics2DServer::JOINT_PARAM_BIAS,bias);
+}
+
+real_t Joint2D::get_bias() const{
+
+
+ return bias;
+}
+
+
+void Joint2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method( _MD("set_node_a","node"), &Joint2D::set_node_a );
+ ObjectTypeDB::bind_method( _MD("get_node_a"), &Joint2D::get_node_a );
+
+ ObjectTypeDB::bind_method( _MD("set_node_b","node"), &Joint2D::set_node_b );
+ ObjectTypeDB::bind_method( _MD("get_node_b"), &Joint2D::get_node_b );
+
+ ObjectTypeDB::bind_method( _MD("set_bias","bias"), &Joint2D::set_bias );
+ ObjectTypeDB::bind_method( _MD("get_bias"), &Joint2D::get_bias );
+
+ ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "node_a"), _SCS("set_node_a"),_SCS("get_node_a") );
+ ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "node_b"), _SCS("set_node_b"),_SCS("get_node_b") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "bias/bias",PROPERTY_HINT_RANGE,"0,0.9,0.01"), _SCS("set_bias"),_SCS("get_bias") );
+
+}
+
+
+
+Joint2D::Joint2D() {
+
+ bias=0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+void PinJoint2D::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_DRAW: {
+ if (is_inside_scene() && get_scene()->is_editor_hint()) {
+
+ draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(0,-10),Point2(0,+10),Color(0.7,0.6,0.0,0.5),3);
+ }
+ } break;
+ }
+
+}
+
+RID PinJoint2D::_configure_joint() {
+
+ Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
+ Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;
+
+ if (!node_a && !node_b)
+ return RID();
+
+ PhysicsBody2D *body_a=node_a ? node_a->cast_to<PhysicsBody2D>() : (PhysicsBody2D*)NULL;
+ PhysicsBody2D *body_b=node_b ? node_b->cast_to<PhysicsBody2D>() : (PhysicsBody2D*)NULL;
+
+ if (!body_a && !body_b)
+ return RID();
+
+ if (!body_a) {
+ SWAP(body_a,body_b);
+ } else if (body_b) {
+ //add a collision exception between both
+ Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
+ }
+
+ return Physics2DServer::get_singleton()->pin_joint_create(get_global_transform().get_origin(),body_a->get_rid(),body_b?body_b->get_rid():RID());
+
+}
+
+
+PinJoint2D::PinJoint2D() {
+
+
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+void GrooveJoint2D::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_DRAW: {
+ if (is_inside_scene() && get_scene()->is_editor_hint()) {
+
+ draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(-10,length),Point2(+10,length),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(0,0),Point2(0,length),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(-10,initial_offset),Point2(+10,initial_offset),Color(0.8,0.8,0.9,0.5),5);
+ }
+ } break;
+ }
+}
+
+RID GrooveJoint2D::_configure_joint(){
+
+
+ Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
+ Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;
+
+ if (!node_a || !node_b)
+ return RID();
+
+ PhysicsBody2D *body_a=node_a->cast_to<PhysicsBody2D>();
+ PhysicsBody2D *body_b=node_b->cast_to<PhysicsBody2D>();
+
+ if (!body_a || !body_b)
+ return RID();
+
+ Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
+
+ Matrix32 gt = get_global_transform();
+ Vector2 groove_A1 = gt.get_origin();
+ Vector2 groove_A2 = gt.xform( Vector2(0,length) );
+ Vector2 anchor_B = gt.xform( Vector2(0,initial_offset) );
+
+
+ return Physics2DServer::get_singleton()->groove_joint_create(groove_A1,groove_A2,anchor_B,body_a->get_rid(),body_b->get_rid());
+}
+
+
+void GrooveJoint2D::set_length(real_t p_length) {
+
+ length=p_length;
+ update();
+}
+
+real_t GrooveJoint2D::get_length() const {
+
+ return length;
+}
+
+
+void GrooveJoint2D::set_initial_offset(real_t p_initial_offset) {
+
+ initial_offset=p_initial_offset;
+ update();
+}
+
+real_t GrooveJoint2D::get_initial_offset() const {
+
+ return initial_offset;
+}
+
+
+
+void GrooveJoint2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_length","length"),&GrooveJoint2D::set_length);
+ ObjectTypeDB::bind_method(_MD("get_length"),&GrooveJoint2D::get_length);
+ ObjectTypeDB::bind_method(_MD("set_initial_offset","offset"),&GrooveJoint2D::set_initial_offset);
+ ObjectTypeDB::bind_method(_MD("get_initial_offset"),&GrooveJoint2D::get_initial_offset);
+
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "length", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_length"),_SCS("get_length"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "initial_offset", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_initial_offset"),_SCS("get_initial_offset"));
+}
+
+GrooveJoint2D::GrooveJoint2D() {
+
+ length=50;
+ initial_offset=25;
+}
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+
+
+void DampedSpringJoint2D::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_DRAW: {
+ if (is_inside_scene() && get_scene()->is_editor_hint()) {
+
+ draw_line(Point2(-10,0),Point2(+10,0),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(-10,length),Point2(+10,length),Color(0.7,0.6,0.0,0.5),3);
+ draw_line(Point2(0,0),Point2(0,length),Color(0.7,0.6,0.0,0.5),3);
+ }
+ } break;
+ }
+}
+
+RID DampedSpringJoint2D::_configure_joint(){
+
+
+ Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
+ Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;
+
+ if (!node_a || !node_b)
+ return RID();
+
+ PhysicsBody2D *body_a=node_a->cast_to<PhysicsBody2D>();
+ PhysicsBody2D *body_b=node_b->cast_to<PhysicsBody2D>();
+
+ if (!body_a || !body_b)
+ return RID();
+
+ Physics2DServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
+
+ Matrix32 gt = get_global_transform();
+ Vector2 anchor_A = gt.get_origin();
+ Vector2 anchor_B = gt.xform( Vector2(0,length) );
+
+ RID dsj = Physics2DServer::get_singleton()->damped_spring_joint_create(anchor_A,anchor_B,body_a->get_rid(),body_b->get_rid());
+ if (rest_length)
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_REST_LENGTH,rest_length);
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_STIFFNESS,stiffness);
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(dsj,Physics2DServer::DAMPED_STRING_DAMPING,damping);
+
+ return dsj;
+}
+
+
+void DampedSpringJoint2D::set_length(real_t p_length) {
+
+ length=p_length;
+ update();
+}
+
+real_t DampedSpringJoint2D::get_length() const {
+
+ return length;
+}
+
+void DampedSpringJoint2D::set_rest_length(real_t p_rest_length) {
+
+ rest_length=p_rest_length;
+ update();
+ if (get_joint().is_valid())
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_REST_LENGTH,p_rest_length?p_rest_length:length);
+
+}
+
+real_t DampedSpringJoint2D::get_rest_length() const {
+
+ return rest_length;
+}
+
+void DampedSpringJoint2D::set_stiffness(real_t p_stiffness) {
+
+ stiffness=p_stiffness;
+ update();
+ if (get_joint().is_valid())
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_STIFFNESS,p_stiffness);
+}
+
+real_t DampedSpringJoint2D::get_stiffness() const {
+
+ return stiffness;
+}
+
+void DampedSpringJoint2D::set_damping(real_t p_damping) {
+
+ damping=p_damping;
+ update();
+ if (get_joint().is_valid())
+ Physics2DServer::get_singleton()->damped_string_joint_set_param(get_joint(),Physics2DServer::DAMPED_STRING_DAMPING,p_damping);
+}
+
+real_t DampedSpringJoint2D::get_damping() const {
+
+ return damping;
+}
+
+
+void DampedSpringJoint2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_length","length"),&DampedSpringJoint2D::set_length);
+ ObjectTypeDB::bind_method(_MD("get_length"),&DampedSpringJoint2D::get_length);
+ ObjectTypeDB::bind_method(_MD("set_rest_length","rest_length"),&DampedSpringJoint2D::set_rest_length);
+ ObjectTypeDB::bind_method(_MD("get_rest_length"),&DampedSpringJoint2D::get_rest_length);
+ ObjectTypeDB::bind_method(_MD("set_stiffness","stiffness"),&DampedSpringJoint2D::set_stiffness);
+ ObjectTypeDB::bind_method(_MD("get_stiffness"),&DampedSpringJoint2D::get_stiffness);
+ ObjectTypeDB::bind_method(_MD("set_damping","damping"),&DampedSpringJoint2D::set_damping);
+ ObjectTypeDB::bind_method(_MD("get_damping"),&DampedSpringJoint2D::get_damping);
+
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "length", PROPERTY_HINT_EXP_RANGE,"1,65535,1"), _SCS("set_length"),_SCS("get_length"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "rest_length", PROPERTY_HINT_EXP_RANGE,"0,65535,1"), _SCS("set_rest_length"),_SCS("get_rest_length"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "stiffness", PROPERTY_HINT_EXP_RANGE,"0.1,64,0.1"), _SCS("set_stiffness"),_SCS("get_stiffness"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "damping", PROPERTY_HINT_EXP_RANGE,"0.01,16,0.01"), _SCS("set_damping"),_SCS("get_damping"));
+
+}
+
+DampedSpringJoint2D::DampedSpringJoint2D() {
+
+ length=50;
+ rest_length=0;
+ stiffness=20;
+ damping=1;
+}
diff --git a/scene/2d/joints_2d.h b/scene/2d/joints_2d.h
new file mode 100644
index 0000000000..7027e4386a
--- /dev/null
+++ b/scene/2d/joints_2d.h
@@ -0,0 +1,141 @@
+/*************************************************************************/
+/* joints_2d.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 JOINTS_2D_H
+#define JOINTS_2D_H
+
+
+#include "node_2d.h"
+
+class Joint2D : public Node2D {
+
+ OBJ_TYPE(Joint2D,Node2D);
+
+ RID joint;
+
+ NodePath a;
+ NodePath b;
+ real_t bias;
+
+
+protected:
+
+ void _update_joint();
+
+ void _notification(int p_what);
+ virtual RID _configure_joint()=0;
+
+ static void _bind_methods();
+public:
+
+ void set_node_a(const NodePath& p_node_a);
+ NodePath get_node_a() const;
+
+ void set_node_b(const NodePath& p_node_b);
+ NodePath get_node_b() const;
+
+ void set_bias(real_t p_bias);
+ real_t get_bias() const;
+
+ RID get_joint() const { return joint; }
+ Joint2D();
+
+};
+
+
+class PinJoint2D : public Joint2D {
+
+ OBJ_TYPE(PinJoint2D,Joint2D);
+
+protected:
+
+ void _notification(int p_what);
+ virtual RID _configure_joint();
+public:
+
+
+
+ PinJoint2D();
+};
+
+class GrooveJoint2D : public Joint2D {
+
+ OBJ_TYPE(GrooveJoint2D,Joint2D);
+
+ real_t length;
+ real_t initial_offset;
+
+protected:
+
+ void _notification(int p_what);
+ virtual RID _configure_joint();
+ static void _bind_methods();
+public:
+
+ void set_length(real_t p_length);
+ real_t get_length() const;
+
+ void set_initial_offset(real_t p_initial_offset);
+ real_t get_initial_offset() const;
+
+ GrooveJoint2D();
+};
+
+class DampedSpringJoint2D : public Joint2D {
+
+ OBJ_TYPE(DampedSpringJoint2D,Joint2D);
+
+ real_t stiffness;
+ real_t damping;
+ real_t rest_length;
+ real_t length;
+
+protected:
+
+ void _notification(int p_what);
+ virtual RID _configure_joint();
+ static void _bind_methods();
+public:
+
+ void set_length(real_t p_length);
+ real_t get_length() const;
+
+ void set_rest_length(real_t p_rest_length);
+ real_t get_rest_length() const;
+
+ void set_damping(real_t p_damping);
+ real_t get_damping() const;
+
+ void set_stiffness(real_t p_stiffness);
+ real_t get_stiffness() const;
+
+ DampedSpringJoint2D();
+};
+
+
+#endif // JOINTS_2D_H
diff --git a/scene/2d/node_2d.cpp b/scene/2d/node_2d.cpp
new file mode 100644
index 0000000000..9b2994ef84
--- /dev/null
+++ b/scene/2d/node_2d.cpp
@@ -0,0 +1,291 @@
+/*************************************************************************/
+/* node_2d.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_2d.h"
+
+#include "servers/visual_server.h"
+#include "scene/gui/control.h"
+#include "scene/main/viewport.h"
+#include "message_queue.h"
+
+
+void Node2D::edit_set_pivot(const Point2& p_pivot) {
+
+
+}
+
+Point2 Node2D::edit_get_pivot() const {
+
+ return Point2();
+}
+bool Node2D::edit_has_pivot() const {
+
+ return false;
+}
+
+Variant Node2D::edit_get_state() const {
+
+ Array state;
+ state.push_back(pos);
+ state.push_back(angle);
+ state.push_back(scale);
+
+ return state;
+
+}
+void Node2D::edit_set_state(const Variant& p_state) {
+
+ Array state = p_state;
+ ERR_FAIL_COND( state.size() != 3);
+
+ pos = state[0];
+ angle = state[1];
+ scale = state[2];
+ _update_transform();
+ _change_notify("transform/rot");
+ _change_notify("transform/scale");
+ _change_notify("transform/pos");
+
+}
+
+void Node2D::edit_set_rect(const Rect2& p_edit_rect) {
+
+ Rect2 r = get_item_rect();
+
+ Vector2 zero_offset;
+ if (r.size.x!=0)
+ zero_offset.x = -r.pos.x / r.size.x;
+ if (r.size.y!=0)
+ zero_offset.y = -r.pos.y / r.size.y;
+
+ Size2 new_scale(1,1);
+
+ if (r.size.x!=0)
+ new_scale.x = p_edit_rect.size.x / r.size.x;
+ if (r.size.y!=0)
+ new_scale.y = p_edit_rect.size.y / r.size.y;
+
+ Point2 new_pos = p_edit_rect.pos + p_edit_rect.size*zero_offset;//p_edit_rect.pos - r.pos;
+
+ Matrix32 postxf;
+ postxf.set_rotation_and_scale(angle,scale);
+ new_pos = postxf.xform(new_pos);
+
+ pos+=new_pos;
+ scale*=new_scale;
+
+ _update_transform();
+ _change_notify("transform/scale");
+ _change_notify("transform/pos");
+
+}
+
+
+void Node2D::edit_rotate(float p_rot) {
+
+ angle+=p_rot;
+ _update_transform();
+ _change_notify("transform/rot");
+}
+
+
+void Node2D::_update_xform_values() {
+
+ pos=_mat.elements[2];
+ angle=_mat.get_rotation();
+ scale=_mat.get_scale();
+ _xform_dirty=false;
+}
+
+void Node2D::_update_transform() {
+
+ Matrix32 mat(angle,pos);
+ _mat.set_rotation_and_scale(angle,scale);
+ _mat.elements[2]=pos;
+
+ VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(),_mat);
+
+ if (!is_inside_scene())
+ return;
+
+
+ _notify_transform();
+}
+
+void Node2D::set_pos(const Point2& p_pos) {
+
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+ pos=p_pos;
+ _update_transform();
+ _change_notify("transform/pos");
+
+
+}
+
+void Node2D::set_rot(float p_angle) {
+
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+ angle=p_angle;
+ _update_transform();
+ _change_notify("transform/rot");
+}
+
+void Node2D::set_scale(const Size2& p_scale) {
+
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+ scale=p_scale;
+ _update_transform();
+ _change_notify("transform/scale");
+
+}
+
+Point2 Node2D::get_pos() const {
+
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+ return pos;
+}
+float Node2D::get_rot() const {
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+
+ return angle;
+}
+Size2 Node2D::get_scale() const {
+ if (_xform_dirty)
+ ((Node2D*)this)->_update_xform_values();
+
+ return scale;
+}
+
+void Node2D::_set_rotd(float p_angle) {
+
+ set_rot(Math::deg2rad(p_angle));
+}
+
+float Node2D::_get_rotd() const {
+
+ return Math::rad2deg(get_rot());
+}
+
+
+void Node2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ }
+}
+
+Matrix32 Node2D::get_transform() const {
+
+ return _mat;
+}
+
+Rect2 Node2D::get_item_rect() const {
+
+ if (get_script_instance()) {
+ Variant::CallError err;
+ Rect2 r = get_script_instance()->call("_get_item_rect",NULL,0,err);
+ if (err.error==Variant::CallError::CALL_OK)
+ return r;
+ }
+ return Rect2(Point2(-32,-32),Size2(64,64));
+}
+
+Point2 Node2D::get_global_pos() const {
+
+ return get_global_transform().get_origin();
+}
+
+void Node2D::set_transform(const Matrix32& p_transform) {
+
+ _mat=p_transform;
+ _xform_dirty=true;
+
+ VisualServer::get_singleton()->canvas_item_set_transform(get_canvas_item(),_mat);
+
+ if (!is_inside_scene())
+ return;
+
+ _notify_transform();
+}
+
+void Node2D::set_global_transform(const Matrix32& p_transform) {
+
+ CanvasItem *pi = get_parent_item();
+ if (pi)
+ set_transform( pi->get_global_transform().affine_inverse() * p_transform);
+ else
+ set_transform(p_transform);
+
+
+}
+
+void Node2D::_bind_methods() {
+
+
+
+ ObjectTypeDB::bind_method(_MD("_get_rotd"),&Node2D::_get_rotd);
+ ObjectTypeDB::bind_method(_MD("_set_rotd"),&Node2D::_set_rotd);
+
+ ObjectTypeDB::bind_method(_MD("set_pos","pos"),&Node2D::set_pos);
+ ObjectTypeDB::bind_method(_MD("set_rot","rot"),&Node2D::set_rot);
+ ObjectTypeDB::bind_method(_MD("set_scale","scale"),&Node2D::set_scale);
+
+ ObjectTypeDB::bind_method(_MD("get_pos"),&Node2D::get_pos);
+ ObjectTypeDB::bind_method(_MD("get_rot"),&Node2D::get_rot);
+ ObjectTypeDB::bind_method(_MD("get_scale"),&Node2D::get_scale);
+
+ ObjectTypeDB::bind_method(_MD("get_global_pos"),&Node2D::get_global_pos);
+
+ ObjectTypeDB::bind_method(_MD("set_transform","xform"),&Node2D::set_transform);
+ ObjectTypeDB::bind_method(_MD("set_global_transform","xform"),&Node2D::set_global_transform);
+
+ ObjectTypeDB::bind_method(_MD("edit_set_pivot"),&Node2D::edit_set_pivot);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/pos"),_SCS("set_pos"),_SCS("get_pos"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"transform/rot",PROPERTY_HINT_RANGE,"-1440,1440,0.1"),_SCS("_set_rotd"),_SCS("_get_rotd"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"transform/scale"),_SCS("set_scale"),_SCS("get_scale"));
+
+
+
+}
+
+
+Node2D::Node2D() {
+
+
+ angle=0;
+ scale=Vector2(1,1);
+ _xform_dirty=false;
+
+}
+
diff --git a/scene/2d/node_2d.h b/scene/2d/node_2d.h
new file mode 100644
index 0000000000..8da441dc63
--- /dev/null
+++ b/scene/2d/node_2d.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* node_2d.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 NODE2D_H
+#define NODE2D_H
+
+#include "scene/2d/canvas_item.h"
+
+class Node2D : public CanvasItem {
+
+ OBJ_TYPE(Node2D, CanvasItem );
+
+ Point2 pos;
+ float angle;
+ Size2 scale;
+
+ Matrix32 _mat;
+
+ bool _xform_dirty;
+
+ void _update_transform();
+
+ void _set_rotd(float p_angle);
+ float _get_rotd() const;
+
+ void _update_xform_values();
+
+protected:
+
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+public:
+
+ virtual Variant edit_get_state() const;
+ virtual void edit_set_state(const Variant& p_state);
+ virtual void edit_set_rect(const Rect2& p_edit_rect);
+ virtual void edit_rotate(float p_rot);
+ virtual void edit_set_pivot(const Point2& p_pivot);
+ virtual Point2 edit_get_pivot() const;
+ virtual bool edit_has_pivot() const;
+
+ void set_pos(const Point2& p_pos);
+ void set_rot(float p_angle);
+ void set_scale(const Size2& p_scale);
+
+ Point2 get_pos() const;
+ float get_rot() const;
+ Size2 get_scale() const;
+
+ Point2 get_global_pos() const;
+ virtual Rect2 get_item_rect() const;
+
+ void set_transform(const Matrix32& p_transform);
+ void set_global_transform(const Matrix32& p_transform);
+
+
+ Matrix32 get_transform() const;
+
+ Node2D();
+};
+
+#endif // NODE2D_H
diff --git a/scene/2d/node_2d_singleton.cpp b/scene/2d/node_2d_singleton.cpp
new file mode 100644
index 0000000000..58e078101d
--- /dev/null
+++ b/scene/2d/node_2d_singleton.cpp
@@ -0,0 +1,30 @@
+/*************************************************************************/
+/* node_2d_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 "node_2d_singleton.h"
+
diff --git a/scene/2d/node_2d_singleton.h b/scene/2d/node_2d_singleton.h
new file mode 100644
index 0000000000..b1d1e65b8b
--- /dev/null
+++ b/scene/2d/node_2d_singleton.h
@@ -0,0 +1,33 @@
+/*************************************************************************/
+/* node_2d_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 NODE_2D_SINGLETON_H
+#define NODE_2D_SINGLETON_H
+
+
+#endif // NODE_2D_SINGLETON_H
diff --git a/scene/2d/parallax_background.cpp b/scene/2d/parallax_background.cpp
new file mode 100644
index 0000000000..55607bd4eb
--- /dev/null
+++ b/scene/2d/parallax_background.cpp
@@ -0,0 +1,200 @@
+/*************************************************************************/
+/* parallax_background.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 "parallax_background.h"
+#include "parallax_layer.h"
+
+
+
+void ParallaxBackground::_notification(int p_what) {
+
+ switch(p_what) {
+
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ group_name = "__cameras_"+itos(get_viewport().get_id());
+ add_to_group(group_name);
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ remove_from_group(group_name);
+ } break;
+ }
+
+}
+
+void ParallaxBackground::_camera_moved(const Matrix32& p_transform) {
+
+
+ set_scroll_offset(p_transform.get_origin());
+ set_scroll_scale(p_transform.get_scale().dot(Vector2(0.5,0.5)));
+}
+
+
+void ParallaxBackground::set_scroll_scale(float p_scale) {
+
+ scale=p_scale;
+}
+
+float ParallaxBackground::get_scroll_scale() const{
+
+ return scale;
+}
+
+
+void ParallaxBackground::set_scroll_offset(const Point2& p_ofs) {
+
+ offset=p_ofs;
+
+ _update_scroll();
+}
+
+void ParallaxBackground::_update_scroll() {
+
+ if (!is_inside_scene())
+ return;
+
+ Vector2 ofs = base_offset+offset*base_scale;
+
+ Size2 vps = get_viewport_size();
+
+ ofs = -ofs;
+ if (limit_begin.x < limit_end.x) {
+
+ if (ofs.x < limit_begin.x)
+ ofs.x=limit_begin.x;
+ else if (ofs.x+vps.x > limit_end.x)
+ ofs.x=limit_end.x-vps.x;
+ }
+
+
+ if (limit_begin.y < limit_end.y) {
+
+ if (ofs.y < limit_begin.y)
+ ofs.y=limit_begin.y;
+ else if (ofs.y+vps.y > limit_end.y)
+ ofs.y=limit_end.y-vps.y;
+ }
+ ofs = -ofs;
+
+ for(int i=0;i<get_child_count();i++) {
+
+ ParallaxLayer *l=get_child(i)->cast_to<ParallaxLayer>();
+ if (!l)
+ continue;
+
+ l->set_base_offset_and_scale(ofs,scale);
+ }
+}
+
+Point2 ParallaxBackground::get_scroll_offset() const {
+
+ return offset;
+}
+
+void ParallaxBackground::set_scroll_base_offset(const Point2& p_ofs) {
+
+ base_offset=p_ofs;
+ _update_scroll();
+}
+
+Point2 ParallaxBackground::get_scroll_base_offset() const{
+
+ return base_offset;
+}
+
+void ParallaxBackground::set_scroll_base_scale(const Point2& p_ofs) {
+
+ base_scale=p_ofs;
+ _update_scroll();
+}
+
+Point2 ParallaxBackground::get_scroll_base_scale() const{
+
+ return base_scale;
+}
+
+
+void ParallaxBackground::set_limit_begin(const Point2& p_ofs) {
+
+ limit_begin=p_ofs;
+ _update_scroll();
+}
+
+Point2 ParallaxBackground::get_limit_begin() const {
+
+ return limit_begin;
+}
+
+void ParallaxBackground::set_limit_end(const Point2& p_ofs) {
+
+ limit_end=p_ofs;
+ _update_scroll();
+
+}
+
+Point2 ParallaxBackground::get_limit_end() const {
+
+ return limit_end;
+}
+
+void ParallaxBackground::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_camera_moved"),&ParallaxBackground::_camera_moved);
+ ObjectTypeDB::bind_method(_MD("set_scroll_offset","ofs"),&ParallaxBackground::set_scroll_offset);
+ ObjectTypeDB::bind_method(_MD("get_scroll_offset"),&ParallaxBackground::get_scroll_offset);
+ ObjectTypeDB::bind_method(_MD("set_scroll_base_offset","ofs"),&ParallaxBackground::set_scroll_base_offset);
+ ObjectTypeDB::bind_method(_MD("get_scroll_base_offset"),&ParallaxBackground::get_scroll_base_offset);
+ ObjectTypeDB::bind_method(_MD("set_scroll_base_scale","scale"),&ParallaxBackground::set_scroll_base_scale);
+ ObjectTypeDB::bind_method(_MD("get_scroll_base_scale"),&ParallaxBackground::get_scroll_base_scale);
+ ObjectTypeDB::bind_method(_MD("set_limit_begin","ofs"),&ParallaxBackground::set_limit_begin);
+ ObjectTypeDB::bind_method(_MD("get_limit_begin"),&ParallaxBackground::get_limit_begin);
+ ObjectTypeDB::bind_method(_MD("set_limit_end","ofs"),&ParallaxBackground::set_limit_end);
+ ObjectTypeDB::bind_method(_MD("get_limit_end"),&ParallaxBackground::get_limit_end);
+
+
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/offset"),_SCS("set_scroll_offset"),_SCS("get_scroll_offset"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/base_offset"),_SCS("set_scroll_base_offset"),_SCS("get_scroll_base_offset"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/base_scale"),_SCS("set_scroll_base_scale"),_SCS("get_scroll_base_scale"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/limit_begin"),_SCS("set_limit_begin"),_SCS("get_limit_begin"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"scroll/limit_end"),_SCS("set_limit_end"),_SCS("get_limit_end"));
+
+}
+
+
+
+
+
+ParallaxBackground::ParallaxBackground() {
+
+ base_scale=Vector2(1,1);
+ scale=1.0;
+ set_layer(-1); //behind all by default
+}
diff --git a/scene/2d/parallax_background.h b/scene/2d/parallax_background.h
new file mode 100644
index 0000000000..ed6747f01c
--- /dev/null
+++ b/scene/2d/parallax_background.h
@@ -0,0 +1,78 @@
+/*************************************************************************/
+/* parallax_background.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 PARALLAX_BACKGROUND_H
+#define PARALLAX_BACKGROUND_H
+
+#include "scene/main/canvas_layer.h"
+#include "scene/2d/node_2d.h"
+#include "scene/2d/camera_2d.h"
+
+class ParallaxBackground : public CanvasLayer {
+
+ OBJ_TYPE( ParallaxBackground, CanvasLayer );
+
+ Point2 offset;
+ float scale;
+ Point2 base_offset;
+ Point2 base_scale;
+ String group_name;
+ Point2 limit_begin;
+ Point2 limit_end;
+
+ void _update_scroll();
+protected:
+
+ void _camera_moved(const Matrix32& p_transform);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_scroll_offset(const Point2& p_ofs);
+ Point2 get_scroll_offset() const;
+
+ void set_scroll_scale(float p_ofs);
+ float get_scroll_scale() const;
+
+ void set_scroll_base_offset(const Point2& p_ofs);
+ Point2 get_scroll_base_offset() const;
+
+ void set_scroll_base_scale(const Point2& p_ofs);
+ Point2 get_scroll_base_scale() const;
+
+ void set_limit_begin(const Point2& p_ofs);
+ Point2 get_limit_begin() const;
+
+ void set_limit_end(const Point2& p_ofs);
+ Point2 get_limit_end() const;
+
+ ParallaxBackground();
+};
+
+#endif // PARALLAX_BACKGROUND_H
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
new file mode 100644
index 0000000000..2cda51dccb
--- /dev/null
+++ b/scene/2d/parallax_layer.cpp
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* parallax_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 "parallax_layer.h"
+#include "parallax_background.h"
+
+void ParallaxLayer::set_motion_scale(const Size2& p_scale) {
+
+ motion_scale=p_scale;
+}
+
+Size2 ParallaxLayer::get_motion_scale() const {
+
+ return motion_scale;
+
+}
+
+
+void ParallaxLayer::_update_mirroring() {
+
+ if (!get_parent())
+ return;
+
+ ParallaxBackground *pb = get_parent()->cast_to<ParallaxBackground>();
+ if (pb) {
+
+ RID c = pb->get_world_2d()->get_canvas();
+ RID ci = get_canvas_item();
+ VisualServer::get_singleton()->canvas_set_item_mirroring(c,ci,mirroring);
+ }
+
+}
+
+void ParallaxLayer::set_mirroring(const Size2& p_mirroring) {
+
+ mirroring=p_mirroring;
+ _update_mirroring();
+
+}
+
+Size2 ParallaxLayer::get_mirroring() const{
+
+ return mirroring;
+}
+
+
+
+void ParallaxLayer::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ orig_offset=get_pos();
+ orig_scale=get_scale();
+ _update_mirroring();
+ } break;
+ }
+}
+
+void ParallaxLayer::set_base_offset_and_scale(const Point2& p_offset,float p_scale) {
+
+ if (!is_inside_scene())
+ return;
+ if (get_scene()->is_editor_hint())
+ return;
+ Point2 new_ofs = ((orig_offset+p_offset)*motion_scale)*p_scale;
+
+ if (mirroring.x) {
+
+ while( new_ofs.x>=0) {
+ new_ofs.x -= mirroring.x*p_scale;
+ }
+ while(new_ofs.x < -mirroring.x*p_scale) {
+ new_ofs.x += mirroring.x*p_scale;
+ }
+ }
+
+ if (mirroring.y) {
+
+ while( new_ofs.y>=0) {
+ new_ofs.y -= mirroring.y*p_scale;
+ }
+ while(new_ofs.y < -mirroring.y*p_scale) {
+ new_ofs.y += mirroring.y*p_scale;
+ }
+ }
+
+
+ set_pos(new_ofs);
+ set_scale(Vector2(1,1)*p_scale);
+
+
+}
+
+void ParallaxLayer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_motion_scale","scale"),&ParallaxLayer::set_motion_scale);
+ ObjectTypeDB::bind_method(_MD("get_motion_scale"),&ParallaxLayer::get_motion_scale);
+ ObjectTypeDB::bind_method(_MD("set_mirroring","mirror"),&ParallaxLayer::set_mirroring);
+ ObjectTypeDB::bind_method(_MD("get_mirroring"),&ParallaxLayer::get_mirroring);
+
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"motion/scale"),_SCS("set_motion_scale"),_SCS("get_motion_scale"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"motion/mirroring"),_SCS("set_mirroring"),_SCS("get_mirroring"));
+
+}
+
+
+
+ParallaxLayer::ParallaxLayer()
+{
+ motion_scale=Size2(1,1);
+}
diff --git a/scene/2d/parallax_layer.h b/scene/2d/parallax_layer.h
new file mode 100644
index 0000000000..fccd8509b1
--- /dev/null
+++ b/scene/2d/parallax_layer.h
@@ -0,0 +1,62 @@
+/*************************************************************************/
+/* parallax_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 PARALLAX_LAYER_H
+#define PARALLAX_LAYER_H
+
+#include "scene/2d/node_2d.h"
+
+class ParallaxLayer : public Node2D {
+
+ OBJ_TYPE( ParallaxLayer, Node2D );
+
+ Point2 orig_offset;
+ Point2 orig_scale;
+ Size2 motion_scale;
+ Vector2 mirroring;
+ void _update_mirroring();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_motion_scale(const Size2& p_scale);
+ Size2 get_motion_scale() const;
+
+ void set_mirroring(const Size2& p_mirroring);
+ Size2 get_mirroring() const;
+
+ void set_base_offset_and_scale(const Point2& p_offsetf,float p_scale);
+
+ ParallaxLayer();
+};
+
+#endif // PARALLAX_LAYER_H
diff --git a/scene/2d/particles_2d.cpp b/scene/2d/particles_2d.cpp
new file mode 100644
index 0000000000..c10f60f78b
--- /dev/null
+++ b/scene/2d/particles_2d.cpp
@@ -0,0 +1,1045 @@
+/*************************************************************************/
+/* particles_2d.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 "particles_2d.h"
+
+
+
+void ParticleAttractor2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ _update_owner();
+
+ } break;
+ case NOTIFICATION_DRAW: {
+
+ if (!get_scene()->is_editor_hint())
+ return;
+
+ Vector2 pv;
+ float dr = MIN(disable_radius,radius);
+ for(int i=0;i<=32;i++) {
+ Vector2 v(Math::sin(i/32.0*Math_PI*2),Math::cos(i/32.0*Math_PI*2));
+ if (i>0) {
+ draw_line(pv*radius,v*radius,Color(0,0,0.5,0.9));
+ if (dr>0) {
+ draw_line(pv*dr,v*dr,Color(0.5,0,0.0,0.9));
+ }
+ }
+ pv=v;
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+ if (owner) {
+ _set_owner(NULL);
+ }
+
+ } break;
+ }
+}
+
+void ParticleAttractor2D::_owner_exited() {
+
+ ERR_FAIL_COND(!owner);
+ owner->attractors.erase(this);
+ owner=NULL;
+}
+
+void ParticleAttractor2D::_update_owner() {
+
+ if (!is_inside_scene() || !has_node(path)) {
+ _set_owner(NULL);
+ return;
+ }
+
+ Node *n = get_node(path);
+ ERR_FAIL_COND(!n);
+ Particles2D *pn = n->cast_to<Particles2D>();
+ if (!pn) {
+ _set_owner(NULL);
+ return;
+ }
+
+ _set_owner(pn);
+}
+
+void ParticleAttractor2D::_set_owner(Particles2D* p_owner) {
+
+ if (owner==p_owner)
+ return;
+
+ if (owner) {
+ owner->disconnect("exit_scene",this,"_owner_exited");
+ owner->attractors.erase(this);
+ owner=NULL;
+ }
+ owner=p_owner;
+
+ if (owner) {
+
+ owner->connect("exit_scene",this,"_owner_exited",varray(),CONNECT_ONESHOT);
+ owner->attractors.insert(this);
+ }
+}
+
+void ParticleAttractor2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&ParticleAttractor2D::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&ParticleAttractor2D::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_radius","radius"),&ParticleAttractor2D::set_radius);
+ ObjectTypeDB::bind_method(_MD("get_radius"),&ParticleAttractor2D::get_radius);
+
+ ObjectTypeDB::bind_method(_MD("set_disable_radius","radius"),&ParticleAttractor2D::set_disable_radius);
+ ObjectTypeDB::bind_method(_MD("get_disable_radius"),&ParticleAttractor2D::get_disable_radius);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&ParticleAttractor2D::set_gravity);
+ ObjectTypeDB::bind_method(_MD("get_gravity"),&ParticleAttractor2D::get_gravity);
+
+ ObjectTypeDB::bind_method(_MD("set_absorption","absorption"),&ParticleAttractor2D::set_absorption);
+ ObjectTypeDB::bind_method(_MD("get_absorption"),&ParticleAttractor2D::get_absorption);
+
+ ObjectTypeDB::bind_method(_MD("set_particles_path","path"),&ParticleAttractor2D::set_particles_path);
+ ObjectTypeDB::bind_method(_MD("get_particles_path"),&ParticleAttractor2D::get_particles_path);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.1,16000,0.1"),_SCS("set_radius"),_SCS("get_radius"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"disable_radius",PROPERTY_HINT_RANGE,"0.1,16000,0.1"),_SCS("set_disable_radius"),_SCS("get_disable_radius"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"gravity",PROPERTY_HINT_RANGE,"-512,512,0.01"),_SCS("set_gravity"),_SCS("get_gravity"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"absorption",PROPERTY_HINT_RANGE,"0,512,0.01"),_SCS("set_absorption"),_SCS("get_absorption"));
+ ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH,"particles_path",PROPERTY_HINT_RESOURCE_TYPE,"Particles2D"),_SCS("set_particles_path"),_SCS("get_particles_path"));
+
+
+
+}
+
+
+void ParticleAttractor2D::set_enabled(bool p_enabled) {
+
+ enabled=p_enabled;
+}
+
+bool ParticleAttractor2D::is_enabled() const{
+
+ return enabled;
+}
+
+void ParticleAttractor2D::set_radius(float p_radius) {
+
+ radius = p_radius;
+ update();
+}
+
+float ParticleAttractor2D::get_radius() const {
+
+ return radius;
+}
+
+void ParticleAttractor2D::set_disable_radius(float p_disable_radius) {
+
+ disable_radius = p_disable_radius;
+ update();
+}
+float ParticleAttractor2D::get_disable_radius() const {
+
+ return disable_radius;
+}
+
+void ParticleAttractor2D::set_gravity(float p_gravity) {
+
+ gravity=p_gravity;
+
+}
+float ParticleAttractor2D::get_gravity() const {
+
+ return gravity;
+}
+
+void ParticleAttractor2D::set_absorption(float p_absorption) {
+
+ absorption=p_absorption;
+
+}
+float ParticleAttractor2D::get_absorption() const {
+
+ return absorption;
+}
+
+void ParticleAttractor2D::set_particles_path(NodePath p_path) {
+
+ path=p_path;
+ _update_owner();
+}
+NodePath ParticleAttractor2D::get_particles_path() const {
+
+ return path;
+}
+
+
+
+ParticleAttractor2D::ParticleAttractor2D() {
+
+ owner=NULL;
+ radius=50;
+ disable_radius=0;
+ gravity=100;
+ absorption=0;
+ path=String("..");
+ enabled=true;
+}
+
+/****************************************/
+
+_FORCE_INLINE_ static float _rand_from_seed(uint32_t *seed) {
+
+ uint32_t k;
+ uint32_t s = (*seed);
+ if (s == 0)
+ s = 0x12345987;
+ k = s / 127773;
+ s = 16807 * (s - k * 127773) - 2836 * k;
+ if (s < 0)
+ s += 2147483647;
+ (*seed) = s;
+
+ float v=((float)((*seed) & 0xFFFFF))/(float)0xFFFFF;
+ v=v*2.0-1.0;
+ return v;
+}
+
+void Particles2D::_process_particles(float p_delta) {
+
+ if (particles.size()==0 || lifetime==0)
+ return;
+
+ p_delta*=time_scale;
+
+ float frame_time=p_delta;
+
+ if (emit_timeout > 0) {
+ time_to_live -= frame_time;
+ if (time_to_live < 0) {
+
+ emitting = false;
+ };
+ };
+
+ float next_time = time+frame_time;
+
+ if (next_time > lifetime)
+ next_time=Math::fmod(next_time,lifetime);
+
+
+ Particle *pdata=&particles[0];
+ int particle_count=particles.size();
+ Matrix32 xform;
+ if (!local_space)
+ xform=get_global_transform();
+
+ active_count=0;
+
+ DVector<Point2>::Read r;
+ int emission_point_count=0;
+ if (emission_points.size()) {
+
+ emission_point_count=emission_points.size();
+ r=emission_points.read();
+ }
+
+ int attractor_count=0;
+ AttractorCache *attractor_ptr=NULL;
+
+ if (attractors.size()) {
+ if (attractors.size()!=attractor_cache.size()) {
+ attractor_cache.resize(attractors.size());
+ }
+
+ int idx=0;
+ Matrix32 m;
+ if (local_space) {
+ m= get_global_transform().affine_inverse();
+ }
+ for (Set<ParticleAttractor2D*>::Element *E=attractors.front();E;E=E->next()) {
+
+ attractor_cache[idx].pos=m.xform( E->get()->get_global_pos() );
+ attractor_cache[idx].attractor=E->get();
+ idx++;
+ }
+
+ attractor_ptr=attractor_cache.ptr();
+ attractor_count=attractor_cache.size();
+ }
+
+ for(int i=0;i<particle_count;i++) {
+
+ Particle &p=pdata[i];
+
+ float restart_time = (i * lifetime / particle_count) * explosiveness;
+
+ bool restart=false;
+
+ if ( next_time < time ) {
+
+ if (restart_time > time || restart_time < next_time )
+ restart=true;
+
+ } else if (restart_time > time && restart_time < next_time ) {
+ restart=true;
+ }
+
+ if (restart) {
+
+
+ if (emitting) {
+
+ p.pos=emissor_offset;
+ if (emission_point_count) {
+
+
+ Vector2 ep = r[Math::rand()%emission_point_count];
+ if (!local_space) {
+ p.pos=xform.xform(p.pos+ep*extents);
+ } else {
+ p.pos+=ep*extents;
+ }
+ } else {
+ if (!local_space) {
+ p.pos=xform.xform(p.pos+Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y)));
+ } else {
+ p.pos+=Vector2(Math::random(-extents.x,extents.x),Math::random(-extents.y,extents.y));
+ }
+ }
+ p.seed=Math::rand() % 12345678;
+ uint32_t rand_seed=p.seed*(i+1);
+
+ float angle = Math::deg2rad(param[PARAM_DIRECTION]+_rand_from_seed(&rand_seed)*param[PARAM_SPREAD]);
+
+ p.velocity=Vector2( Math::sin(angle), Math::cos(angle) );
+ if (!local_space) {
+
+ p.velocity = xform.basis_xform(p.velocity).normalized();
+ }
+
+ p.velocity*=param[PARAM_LINEAR_VELOCITY]+param[PARAM_LINEAR_VELOCITY]*_rand_from_seed(&rand_seed)*randomness[PARAM_LINEAR_VELOCITY];
+ p.velocity+=initial_velocity;
+ p.active=true;
+ p.rot=0;
+ active_count++;
+
+
+ } else {
+
+ p.active=false;
+ }
+
+ } else {
+
+ if (!p.active)
+ continue;
+
+ uint32_t rand_seed=p.seed*(i+1);
+
+ Vector2 force;
+
+ //apply gravity
+ float gravity_dir = Math::deg2rad( param[PARAM_GRAVITY_DIRECTION]+180*randomness[PARAM_GRAVITY_DIRECTION]*_rand_from_seed(&rand_seed));
+ force+=Vector2( Math::sin(gravity_dir), Math::cos(gravity_dir) ) * (param[PARAM_GRAVITY_STRENGTH]+param[PARAM_GRAVITY_STRENGTH]*randomness[PARAM_GRAVITY_STRENGTH]*_rand_from_seed(&rand_seed));
+ //apply radial
+ Vector2 rvec = (p.pos - emissor_offset).normalized();
+ force+=rvec*(param[PARAM_RADIAL_ACCEL]+param[PARAM_RADIAL_ACCEL]*randomness[PARAM_RADIAL_ACCEL]*_rand_from_seed(&rand_seed));
+ //apply orbit
+ float orbitvel = (param[PARAM_ORBIT_VELOCITY]+param[PARAM_ORBIT_VELOCITY]*randomness[PARAM_ORBIT_VELOCITY]*_rand_from_seed(&rand_seed));
+ if (orbitvel!=0) {
+ Vector2 rel = p.pos - xform.elements[2];
+ Matrix32 rot(orbitvel*frame_time,Vector2());
+ p.pos = rot.xform(rel) + xform.elements[2];
+
+ }
+
+ Vector2 tvec=rvec.tangent();
+ force+=tvec*(param[PARAM_TANGENTIAL_ACCEL]+param[PARAM_TANGENTIAL_ACCEL]*randomness[PARAM_TANGENTIAL_ACCEL]*_rand_from_seed(&rand_seed));
+
+ for(int j=0;j<attractor_count;j++) {
+
+ Vector2 vec = (attractor_ptr[j].pos - p.pos);
+ float vl = vec.length();
+
+ if (!attractor_ptr[j].attractor->enabled || vl==0 || vl > attractor_ptr[j].attractor->radius)
+ continue;
+
+
+
+ force+=vec*attractor_ptr[j].attractor->gravity;
+ float fvl = p.velocity.length();
+ if (fvl && attractor_ptr[j].attractor->absorption) {
+ Vector2 target = vec.normalized();
+ p.velocity = p.velocity.normalized().linear_interpolate(target,MIN(frame_time*attractor_ptr[j].attractor->absorption,1))*fvl;
+ }
+
+ if (attractor_ptr[j].attractor->disable_radius && vl < attractor_ptr[j].attractor->disable_radius) {
+ p.active=false;
+ }
+ }
+
+ p.velocity+=force*frame_time;
+
+ if (param[PARAM_DAMPING]) {
+ float dmp = param[PARAM_DAMPING]+param[PARAM_DAMPING]*randomness[PARAM_DAMPING]*_rand_from_seed(&rand_seed);
+ float v = p.velocity.length();
+ v -= dmp * frame_time;
+ if (v<=0) {
+ p.velocity=Vector2();
+ } else {
+ p.velocity=p.velocity.normalized() * v;
+ }
+
+ }
+
+ p.pos+=p.velocity*frame_time;
+ p.rot+=Math::lerp(param[PARAM_SPIN_VELOCITY],param[PARAM_SPIN_VELOCITY]*randomness[PARAM_SPIN_VELOCITY]*_rand_from_seed(&rand_seed),randomness[PARAM_SPIN_VELOCITY])*frame_time;
+
+ active_count++;
+
+ }
+
+
+ }
+
+
+
+ time=Math::fmod( time+frame_time, lifetime );
+ if (!emitting && active_count==0) {
+ set_process(false);
+
+ }
+
+ update();
+
+
+}
+
+
+void Particles2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_PROCESS: {
+
+ _process_particles( get_process_delta_time() );
+ } break;
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ float ppt=preprocess;
+ while(ppt>0) {
+ _process_particles(0.1);
+ ppt-=0.1;
+ }
+ } break;
+ case NOTIFICATION_DRAW: {
+
+
+ if (particles.size()==0 || lifetime==0)
+ return;
+
+ RID ci=get_canvas_item();
+ Size2 size(1,1);
+ Point2 center;
+
+ if (!texture.is_null()) {
+ size=texture->get_size();
+ }
+
+
+ float time_pos=(time/lifetime);
+
+ Particle *pdata=&particles[0];
+ int particle_count=particles.size();
+ Rect2 r(Point2(),size);
+ RID texrid;
+
+ if (texture.is_valid())
+ texrid = texture->get_rid();
+
+ Matrix32 invxform;
+ if (!local_space)
+ invxform=get_global_transform().affine_inverse();
+
+ int col_count=0;
+ float last=-1;
+ ColorPhase cphase[MAX_COLOR_PHASES];
+
+ for(int i=0;i<color_phase_count;i++) {
+
+ if (color_phases[i].pos<=last)
+ break;
+ cphase[i]=color_phases[i];
+ col_count++;
+ }
+
+
+ for(int i=0;i<particle_count;i++) {
+
+ Particle &p=pdata[i];
+ if (!p.active)
+ continue;
+
+ float ptime = ((float)i / particle_count)*explosiveness;
+
+ if (ptime<time_pos)
+ ptime=time_pos-ptime;
+ else
+ ptime=(1.0-ptime)+time_pos;
+
+ uint32_t rand_seed=p.seed*(i+1);
+
+
+ int cpos=0;
+
+ while(cpos<col_count) {
+
+ if (cphase[cpos].pos > ptime)
+ break;
+ cpos++;
+ }
+
+ cpos--;
+
+ Color color;
+ //could be faster..
+ if (cpos==-1)
+ color=Color(1,1,1,1);
+ else {
+ if (cpos==col_count-1)
+ color=cphase[cpos].color;
+ else {
+ float diff = (cphase[cpos+1].pos-cphase[cpos].pos);
+ if (diff>0)
+ color=cphase[cpos].color.linear_interpolate(cphase[cpos+1].color, (ptime - cphase[cpos].pos) / diff );
+ else
+ color=cphase[cpos+1].color;
+ }
+ }
+
+
+ {
+ float huerand=_rand_from_seed(&rand_seed);
+ float huerot = param[PARAM_HUE_VARIATION] + randomness[PARAM_HUE_VARIATION] * huerand;
+
+ if (Math::abs(huerot) > CMP_EPSILON) {
+
+ float h=color.get_h();
+ float s=color.get_s();
+ float v=color.get_v();
+ float a=color.a;
+ //float preh=h;
+ h+=huerot;
+ h=Math::abs(Math::fposmod(h,1.0));
+ //print_line("rand: "+rtos(randomness[PARAM_HUE_VARIATION])+" rand: "+rtos(huerand));
+ //print_line(itos(i)+":hue: "+rtos(preh)+" + "+rtos(huerot)+" = "+rtos(h));
+ color.set_hsv(h,s,v);
+ color.a=a;
+ }
+ }
+
+ float initial_size = param[PARAM_INITIAL_SIZE]+param[PARAM_INITIAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE];
+ float final_size = param[PARAM_FINAL_SIZE]+param[PARAM_FINAL_SIZE]*_rand_from_seed(&rand_seed)*randomness[PARAM_FINAL_SIZE];
+
+ float size_mult=initial_size*(1.0-ptime) + final_size*ptime;
+
+ //Size2 rectsize=size * size_mult;
+ //rectsize=rectsize.floor();
+
+ //Rect2 r = Rect2(Vecto,rectsize);
+
+ Matrix32 xform;
+
+ if (p.rot) {
+
+ xform.set_rotation(p.rot);
+ xform.translate(-size*size_mult/2.0);
+ xform.elements[2]+=p.pos;
+ } else {
+ xform.elements[2]=-size*size_mult/2.0;
+ xform.elements[2]+=p.pos;
+ }
+
+ if (!local_space) {
+ xform = invxform * xform;
+ }
+
+
+ xform.scale_basis(Size2(size_mult,size_mult));
+
+
+ VisualServer::get_singleton()->canvas_item_add_set_transform(ci,xform);
+
+
+ if (texrid.is_valid()) {
+
+ VisualServer::get_singleton()->canvas_item_add_texture_rect(ci,r,texrid,false,color);
+ } else {
+ VisualServer::get_singleton()->canvas_item_add_rect(ci,r,color);
+
+ }
+
+ }
+
+
+ } break;
+
+ }
+
+}
+
+static const char* _particlesframe_property_names[Particles2D::PARAM_MAX]={
+ "params/direction",
+ "params/spread",
+ "params/linear_velocity",
+ "params/spin_velocity",
+ "params/orbit_velocity",
+ "params/gravity_direction",
+ "params/gravity_strength",
+ "params/radial_accel",
+ "params/tangential_accel",
+ "params/damping",
+ "params/initial_size",
+ "params/final_size",
+ "params/hue_variation"
+};
+
+static const char* _particlesframe_property_rnames[Particles2D::PARAM_MAX]={
+ "randomness/direction",
+ "randomness/spread",
+ "randomness/linear_velocity",
+ "randomness/spin_velocity",
+ "randomness/orbit_velocity",
+ "randomness/gravity_direction",
+ "randomness/gravity_strength",
+ "randomness/radial_accel",
+ "randomness/tangential_accel",
+ "randomness/damping",
+ "randomness/initial_size",
+ "randomness/final_size",
+ "randomness/hue_variation"
+};
+
+static const char* _particlesframe_property_ranges[Particles2D::PARAM_MAX]={
+ "0,360,0.01",
+ "0,180,0.01",
+ "-1024,1024,0.01",
+ "-1024,1024,0.01",
+ "-1024,1024,0.01",
+ "0,360,0.01",
+ "0,1024,0.01",
+ "-128,128,0.01",
+ "-128,128,0.01",
+ "0,1024,0.001",
+ "0,1024,0.01",
+ "0,1024,0.01",
+ "0,1,0.01"
+};
+
+
+void Particles2D::set_emitting(bool p_emitting) {
+
+ if (emitting==p_emitting)
+ return;
+
+ if (p_emitting) {
+
+ if (active_count==0)
+ time=0;
+ set_process(true);
+ time_to_live = emit_timeout;
+ };
+ emitting=p_emitting;
+}
+
+bool Particles2D::is_emitting() const {
+
+ return emitting;
+}
+
+void Particles2D::set_amount(int p_amount) {
+
+ ERR_FAIL_INDEX(p_amount,1024);
+
+ particles.resize(p_amount);
+}
+int Particles2D::get_amount() const {
+
+ return particles.size();
+}
+
+void Particles2D::set_emit_timeout(float p_timeout) {
+
+ emit_timeout = p_timeout;
+ time_to_live = p_timeout;
+};
+
+float Particles2D::get_emit_timeout() const {
+
+ return emit_timeout;
+};
+
+void Particles2D::set_lifetime(float p_lifetime) {
+
+ ERR_FAIL_INDEX(p_lifetime,3600);
+
+ lifetime=p_lifetime;
+}
+float Particles2D::get_lifetime() const {
+
+ return lifetime;
+}
+
+void Particles2D::set_time_scale(float p_time_scale) {
+
+ time_scale=p_time_scale;
+}
+float Particles2D::get_time_scale() const {
+
+ return time_scale;
+}
+
+void Particles2D::set_pre_process_time(float p_pre_process_time) {
+
+ preprocess=p_pre_process_time;
+}
+
+float Particles2D::get_pre_process_time() const{
+
+ return preprocess;
+}
+
+
+void Particles2D::set_param(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ param[p_param]=p_value;
+}
+float Particles2D::get_param(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return param[p_param];
+}
+
+void Particles2D::set_randomness(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ randomness[p_param]=p_value;
+
+}
+float Particles2D::get_randomness(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return randomness[p_param];
+
+}
+
+void Particles2D::set_texture(const Ref<Texture>& p_texture) {
+
+ texture=p_texture;
+}
+
+Ref<Texture> Particles2D::get_texture() const {
+
+ return texture;
+}
+
+void Particles2D::set_emissor_offset(const Point2& p_offset) {
+
+ emissor_offset=p_offset;
+}
+
+Point2 Particles2D::get_emissor_offset() const {
+
+ return emissor_offset;
+}
+
+
+void Particles2D::set_use_local_space(bool p_use) {
+
+ local_space=p_use;
+}
+
+bool Particles2D::is_using_local_space() const {
+
+ return local_space;
+}
+
+
+void Particles2D::set_color_phases(int p_phases) {
+
+ ERR_FAIL_INDEX(p_phases,MAX_COLOR_PHASES+1);
+ color_phase_count=p_phases;
+}
+
+int Particles2D::get_color_phases() const {
+
+ return color_phase_count;
+}
+
+void Particles2D::set_color_phase_color(int p_phase,const Color& p_color) {
+
+ ERR_FAIL_INDEX(p_phase,MAX_COLOR_PHASES);
+ color_phases[p_phase].color=p_color;
+
+}
+Color Particles2D::get_color_phase_color(int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase,MAX_COLOR_PHASES,Color());
+ return color_phases[p_phase].color;
+}
+
+void Particles2D::set_color_phase_pos(int p_phase,float p_pos) {
+ ERR_FAIL_INDEX(p_phase,MAX_COLOR_PHASES);
+ ERR_FAIL_COND(p_pos<0.0 || p_pos>1.0);
+ color_phases[p_phase].pos=p_pos;
+
+}
+float Particles2D::get_color_phase_pos(int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase,MAX_COLOR_PHASES,0);
+ return color_phases[p_phase].pos;
+}
+
+void Particles2D::set_emission_half_extents(const Vector2& p_extents) {
+
+ extents=p_extents;
+}
+
+Vector2 Particles2D::get_emission_half_extents() const {
+
+ return extents;
+}
+
+void Particles2D::testee(int a, int b, int c, int d, int e) {
+
+ print_line(itos(a));
+ print_line(itos(b));
+ print_line(itos(c));
+ print_line(itos(d));
+ print_line(itos(e));
+}
+
+void Particles2D::set_initial_velocity(const Vector2& p_velocity) {
+
+
+ initial_velocity=p_velocity;
+}
+Vector2 Particles2D::get_initial_velocity() const{
+
+ return initial_velocity;
+}
+
+
+void Particles2D::pre_process(float p_delta) {
+
+ _process_particles(p_delta);
+}
+
+
+void Particles2D::set_explosiveness(float p_value) {
+
+ explosiveness=p_value;
+}
+
+float Particles2D::get_explosiveness() const{
+
+ return explosiveness;
+}
+
+void Particles2D::set_emission_points(const DVector<Vector2>& p_points) {
+
+ emission_points=p_points;
+}
+
+DVector<Vector2> Particles2D::get_emission_points() const{
+
+ return emission_points;
+}
+
+void Particles2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_emitting","active"),&Particles2D::set_emitting);
+ ObjectTypeDB::bind_method(_MD("is_emitting"),&Particles2D::is_emitting);
+
+ ObjectTypeDB::bind_method(_MD("set_amount","amount"),&Particles2D::set_amount);
+ ObjectTypeDB::bind_method(_MD("get_amount"),&Particles2D::get_amount);
+
+ ObjectTypeDB::bind_method(_MD("set_lifetime","lifetime"),&Particles2D::set_lifetime);
+ ObjectTypeDB::bind_method(_MD("get_lifetime"),&Particles2D::get_lifetime);
+
+ ObjectTypeDB::bind_method(_MD("set_time_scale","time_scale"),&Particles2D::set_time_scale);
+ ObjectTypeDB::bind_method(_MD("get_time_scale"),&Particles2D::get_time_scale);
+
+ ObjectTypeDB::bind_method(_MD("set_pre_process_time","time"),&Particles2D::set_pre_process_time);
+ ObjectTypeDB::bind_method(_MD("get_pre_process_time"),&Particles2D::get_pre_process_time);
+
+ ObjectTypeDB::bind_method(_MD("set_emit_timeout","value"),&Particles2D::set_emit_timeout);
+ ObjectTypeDB::bind_method(_MD("get_emit_timeout"),&Particles2D::get_emit_timeout);
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&Particles2D::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&Particles2D::get_param);
+
+ ObjectTypeDB::bind_method(_MD("set_randomness","param","value"),&Particles2D::set_randomness);
+ ObjectTypeDB::bind_method(_MD("get_randomness","param"),&Particles2D::get_randomness);
+
+ ObjectTypeDB::bind_method(_MD("set_texture:Texture","texture"),&Particles2D::set_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture:Texture"),&Particles2D::get_texture);
+
+ ObjectTypeDB::bind_method(_MD("set_emissor_offset","offset"),&Particles2D::set_emissor_offset);
+ ObjectTypeDB::bind_method(_MD("get_emissor_offset"),&Particles2D::get_emissor_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_emission_half_extents","extents"),&Particles2D::set_emission_half_extents);
+ ObjectTypeDB::bind_method(_MD("get_emission_half_extents"),&Particles2D::get_emission_half_extents);
+
+ ObjectTypeDB::bind_method(_MD("set_color_phases","phases"),&Particles2D::set_color_phases);
+ ObjectTypeDB::bind_method(_MD("get_color_phases"),&Particles2D::get_color_phases);
+
+ ObjectTypeDB::bind_method(_MD("set_color_phase_color","phase","color"),&Particles2D::set_color_phase_color);
+ ObjectTypeDB::bind_method(_MD("get_color_phase_color","phase"),&Particles2D::get_color_phase_color);
+
+ ObjectTypeDB::bind_method(_MD("set_color_phase_pos","phase","pos"),&Particles2D::set_color_phase_pos);
+ ObjectTypeDB::bind_method(_MD("get_color_phase_pos","phase"),&Particles2D::get_color_phase_pos);
+
+ ObjectTypeDB::bind_method(_MD("pre_process","time"),&Particles2D::pre_process);
+
+ ObjectTypeDB::bind_method(_MD("set_use_local_space","enable"),&Particles2D::set_use_local_space);
+ ObjectTypeDB::bind_method(_MD("is_using_local_space"),&Particles2D::is_using_local_space);
+
+ ObjectTypeDB::bind_method(_MD("set_initial_velocity","velocity"),&Particles2D::set_initial_velocity);
+ ObjectTypeDB::bind_method(_MD("get_initial_velocity"),&Particles2D::get_initial_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_explosiveness","amount"),&Particles2D::set_explosiveness);
+ ObjectTypeDB::bind_method(_MD("get_explosiveness"),&Particles2D::get_explosiveness);
+
+ ObjectTypeDB::bind_method(_MD("set_emission_points","points"),&Particles2D::set_emission_points);
+ ObjectTypeDB::bind_method(_MD("get_emission_points"),&Particles2D::get_emission_points);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"config/amount",PROPERTY_HINT_EXP_RANGE,"1,1024"),_SCS("set_amount"),_SCS("get_amount") );
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/lifetime",PROPERTY_HINT_EXP_RANGE,"0.1,3600,0.1"),_SCS("set_lifetime"),_SCS("get_lifetime") );
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/time_scale",PROPERTY_HINT_EXP_RANGE,"0.01,128,0.01"),_SCS("set_time_scale"),_SCS("get_time_scale") );
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/preprocess",PROPERTY_HINT_EXP_RANGE,"0.1,3600,0.1"),_SCS("set_pre_process_time"),_SCS("get_pre_process_time") );
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/emit_timeout",PROPERTY_HINT_RANGE,"0,3600,0.1"),_SCS("set_emit_timeout"),_SCS("get_emit_timeout") );
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/emitting"),_SCS("set_emitting"),_SCS("is_emitting") );
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"config/offset"),_SCS("set_emissor_offset"),_SCS("get_emissor_offset"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"config/half_extents"),_SCS("set_emission_half_extents"),_SCS("get_emission_half_extents"));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"config/local_space"),_SCS("set_use_local_space"),_SCS("is_using_local_space"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"config/explosiveness",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_explosiveness"),_SCS("get_explosiveness"));
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"config/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
+
+
+ for(int i=0;i<PARAM_MAX;i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,_particlesframe_property_names[i],PROPERTY_HINT_RANGE,_particlesframe_property_ranges[i]),_SCS("set_param"),_SCS("get_param"),i);
+ }
+
+ for(int i=0;i<PARAM_MAX;i++) {
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,_particlesframe_property_rnames[i],PROPERTY_HINT_RANGE,"-1,1,0.01"),_SCS("set_randomness"),_SCS("get_randomness"),i);
+ }
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "color_phases/count",PROPERTY_HINT_RANGE,"0,4,1"), _SCS("set_color_phases"), _SCS("get_color_phases"));
+
+ for(int i=0;i<MAX_COLOR_PHASES;i++) {
+ String phase="phase_"+itos(i)+"/";
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, phase+"pos", PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_color_phase_pos"),_SCS("get_color_phase_pos"),i );
+ ADD_PROPERTYI( PropertyInfo( Variant::COLOR, phase+"color"),_SCS("set_color_phase_color"),_SCS("get_color_phase_color"),i );
+ }
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2_ARRAY,"emission_points",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_emission_points"),_SCS("get_emission_points"));
+
+ BIND_CONSTANT( PARAM_DIRECTION );
+ BIND_CONSTANT( PARAM_SPREAD );
+ BIND_CONSTANT( PARAM_LINEAR_VELOCITY );
+ BIND_CONSTANT( PARAM_SPIN_VELOCITY );
+ BIND_CONSTANT( PARAM_GRAVITY_DIRECTION );
+ BIND_CONSTANT( PARAM_GRAVITY_STRENGTH );
+ BIND_CONSTANT( PARAM_RADIAL_ACCEL );
+ BIND_CONSTANT( PARAM_TANGENTIAL_ACCEL );
+ BIND_CONSTANT( PARAM_INITIAL_SIZE );
+ BIND_CONSTANT( PARAM_FINAL_SIZE );
+ BIND_CONSTANT( PARAM_HUE_VARIATION );
+ BIND_CONSTANT( PARAM_MAX );
+
+ BIND_CONSTANT( MAX_COLOR_PHASES );
+
+}
+
+
+
+Particles2D::Particles2D() {
+
+ for(int i=0;i<PARAM_MAX;i++) {
+
+ param[i]=0;
+ randomness[i]=0;
+ }
+
+
+ set_param(PARAM_SPREAD,10);
+ set_param(PARAM_LINEAR_VELOCITY,20);
+ set_param(PARAM_GRAVITY_STRENGTH,9.8);
+ set_param(PARAM_RADIAL_ACCEL,0);
+ set_param(PARAM_TANGENTIAL_ACCEL,0);
+ set_param(PARAM_INITIAL_SIZE,1.0);
+ set_param(PARAM_FINAL_SIZE,1.0);
+
+
+ time=0;
+ lifetime=2;
+ emitting=false;
+ particles.resize(32);
+ active_count=-1;
+ set_emitting(true);
+ local_space=true;
+ preprocess=0;
+ time_scale=1.0;
+
+ color_phase_count=1;
+
+ set_color_phase_pos(0,0.0);
+ set_color_phase_pos(1,1.0);
+ set_color_phase_pos(2,1.0);
+ set_color_phase_pos(3,1.0);
+
+ set_color_phase_color(0,Color(1,1,1));
+ set_color_phase_color(1,Color(0,0,0));
+ set_color_phase_color(2,Color(0,0,0));
+ set_color_phase_color(3,Color(0,0,0));
+
+ emit_timeout = 0;
+ time_to_live = 0;
+ explosiveness=1.0;
+}
diff --git a/scene/2d/particles_2d.h b/scene/2d/particles_2d.h
new file mode 100644
index 0000000000..8849149127
--- /dev/null
+++ b/scene/2d/particles_2d.h
@@ -0,0 +1,231 @@
+/*************************************************************************/
+/* particles_2d.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 PARTICLES_FRAME_H
+#define PARTICLES_FRAME_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/texture.h"
+
+class Particles2D;
+class ParticleAttractor2D : public Node2D {
+
+ OBJ_TYPE(ParticleAttractor2D,Node2D);
+
+
+friend class Particles2D;
+ bool enabled;
+ float radius;
+ float disable_radius;
+ float gravity;
+ float absorption;
+ NodePath path;
+
+ Particles2D *owner;
+
+ void _update_owner();
+ void _owner_exited();
+ void _set_owner(Particles2D* p_owner);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ void set_disable_radius(float p_disable_radius);
+ float get_disable_radius() const;
+
+ void set_gravity(float p_gravity);
+ float get_gravity() const;
+
+ void set_absorption(float p_absorption);
+ float get_absorption() const;
+
+ void set_particles_path(NodePath p_path);
+ NodePath get_particles_path() const;
+
+ ParticleAttractor2D();
+};
+
+
+
+class Particles2D : public Node2D {
+
+ OBJ_TYPE(Particles2D, Node2D);
+public:
+
+ enum Parameter {
+ PARAM_DIRECTION,
+ PARAM_SPREAD,
+ PARAM_LINEAR_VELOCITY,
+ PARAM_SPIN_VELOCITY,
+ PARAM_ORBIT_VELOCITY,
+ PARAM_GRAVITY_DIRECTION,
+ PARAM_GRAVITY_STRENGTH,
+ PARAM_RADIAL_ACCEL,
+ PARAM_TANGENTIAL_ACCEL,
+ PARAM_DAMPING,
+ PARAM_INITIAL_SIZE,
+ PARAM_FINAL_SIZE,
+ PARAM_HUE_VARIATION,
+ PARAM_MAX
+ };
+
+ enum {
+ MAX_COLOR_PHASES=4
+ };
+
+private:
+
+ float param[PARAM_MAX];
+ float randomness[PARAM_MAX];
+
+ struct Particle {
+
+ bool active;
+ Point2 pos;
+ Vector2 velocity;
+ float rot;
+ uint32_t seed;
+ Particle() { active=false; seed=123465789; rot=0;}
+ };
+
+ Vector<Particle> particles;
+ int color_phase_count;
+ struct ColorPhase {
+ Color color;
+ float pos;
+ } color_phases[MAX_COLOR_PHASES];
+
+ struct AttractorCache {
+
+ Vector2 pos;
+ ParticleAttractor2D *attractor;
+ };
+
+ Vector<AttractorCache> attractor_cache;
+
+ float explosiveness;
+ float preprocess;
+ float lifetime;
+ bool emitting;
+ bool local_space;
+ float emit_timeout;
+ float time_to_live;
+ float time_scale;
+ Point2 emissor_offset;
+ Vector2 initial_velocity;
+ Vector2 extents;
+ DVector<Vector2> emission_points;
+
+ float time;
+ int active_count;
+
+ Ref<Texture> texture;
+
+
+ void testee(int a, int b, int c, int d, int e);
+ void _process_particles(float p_delta);
+friend class ParticleAttractor2D;
+
+ Set<ParticleAttractor2D*> attractors;
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_emitting(bool p_emitting);
+ bool is_emitting() const;
+
+ void set_amount(int p_amount);
+ int get_amount() const;
+
+ void set_lifetime(float p_lifetime);
+ float get_lifetime() const;
+
+ void set_time_scale(float p_time_scale);
+ float get_time_scale() const;
+
+ void set_pre_process_time(float p_pre_process_time);
+ float get_pre_process_time() const;
+
+ void set_emit_timeout(float p_timeout);
+ float get_emit_timeout() const;
+
+ void set_emission_half_extents(const Vector2& p_extents);
+ Vector2 get_emission_half_extents() const;
+
+ void set_param(Parameter p_param, float p_value);
+ float get_param(Parameter p_param) const;
+
+ void set_randomness(Parameter p_randomness, float p_value);
+ float get_randomness(Parameter p_randomness) const;
+
+ void set_explosiveness(float p_value);
+ float get_explosiveness() const;
+
+ void set_color_phases(int p_phases);
+ int get_color_phases() const;
+
+ void set_color_phase_color(int p_phase,const Color& p_color);
+ Color get_color_phase_color(int p_phase) const;
+
+ void set_color_phase_pos(int p_phase,float p_pos);
+ float get_color_phase_pos(int p_phase) const;
+
+ void set_texture(const Ref<Texture>& p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_emissor_offset(const Point2& p_offset);
+ Point2 get_emissor_offset() const;
+
+ void set_use_local_space(bool p_use);
+ bool is_using_local_space() const;
+
+ void set_initial_velocity(const Vector2& p_velocity);
+ Vector2 get_initial_velocity() const;
+
+ void set_emission_points(const DVector<Vector2>& p_points);
+ DVector<Vector2> get_emission_points() const;
+
+ void pre_process(float p_delta);
+
+ Particles2D();
+};
+
+VARIANT_ENUM_CAST( Particles2D::Parameter );
+
+#endif // PARTICLES_FRAME_H
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
new file mode 100644
index 0000000000..22d56609ee
--- /dev/null
+++ b/scene/2d/path_2d.cpp
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* path_2d.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 "path_2d.h"
+
+
+void Path2D::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_DRAW && curve.is_valid() && is_inside_scene() && get_scene()->is_editor_hint()) {
+ //draw the curve!!
+
+ for(int i=0;i<curve->get_point_count();i++) {
+
+ Vector2 prev_p=curve->get_point_pos(i);
+
+ for(int j=1;j<=8;j++) {
+
+ real_t frac = j/8.0;
+ Vector2 p = curve->interpolate(i,frac);
+ draw_line(prev_p,p,Color(0.5,0.6,1.0,0.7),2);
+ prev_p=p;
+ }
+ }
+ }
+}
+
+void Path2D::_curve_changed() {
+
+
+ if (is_inside_scene() && get_scene()->is_editor_hint())
+ update();
+
+}
+
+
+void Path2D::set_curve(const Ref<Curve2D>& p_curve) {
+
+ if (curve.is_valid()) {
+ curve->disconnect("changed",this,"_curve_changed");
+ }
+
+ curve=p_curve;
+
+ if (curve.is_valid()) {
+ curve->connect("changed",this,"_curve_changed");
+ }
+
+}
+
+Ref<Curve2D> Path2D::get_curve() const{
+
+ return curve;
+}
+
+void Path2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_curve","curve:Curve2D"),&Path2D::set_curve);
+ ObjectTypeDB::bind_method(_MD("get_curve:Curve2D","curve"),&Path2D::get_curve);
+ ObjectTypeDB::bind_method(_MD("_curve_changed"),&Path2D::_curve_changed);
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve2D"), _SCS("set_curve"),_SCS("get_curve"));
+}
+
+Path2D::Path2D() {
+
+ set_curve(Ref<Curve2D>( memnew( Curve2D ))); //create one by default
+}
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
new file mode 100644
index 0000000000..f401f9da4c
--- /dev/null
+++ b/scene/2d/path_2d.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* path_2d.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 PATH_2D_H
+#define PATH_2D_H
+
+#include "scene/resources/curve.h"
+#include "scene/2d/node_2d.h"
+
+class Path2D : public Node2D {
+
+ OBJ_TYPE( Path2D, Node2D );
+
+ Ref<Curve2D> curve;
+
+ void _curve_changed();
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_curve(const Ref<Curve2D>& p_curve);
+ Ref<Curve2D> get_curve() const;
+
+
+ Path2D();
+};
+
+#endif // PATH_2D_H
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
new file mode 100644
index 0000000000..945e50ff51
--- /dev/null
+++ b/scene/2d/physics_body_2d.cpp
@@ -0,0 +1,794 @@
+/*************************************************************************/
+/* physics_body_2d.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 "physics_body_2d.h"
+#include "scene/scene_string_names.h"
+
+void PhysicsBody2D::_notification(int p_what) {
+
+/*
+ switch(p_what) {
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform());
+
+ } break;
+ }
+ */
+}
+
+PhysicsBody2D::PhysicsBody2D(Physics2DServer::BodyMode p_mode) : CollisionObject2D( Physics2DServer::get_singleton()->body_create(p_mode), false) {
+
+
+
+}
+
+void StaticBody2D::set_constant_linear_velocity(const Vector2& p_vel) {
+
+ constant_linear_velocity=p_vel;
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_LINEAR_VELOCITY,constant_linear_velocity);
+
+}
+
+void StaticBody2D::set_constant_angular_velocity(real_t p_vel) {
+
+ constant_angular_velocity=p_vel;
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity);
+}
+
+Vector2 StaticBody2D::get_constant_linear_velocity() const {
+
+ return constant_linear_velocity;
+}
+real_t StaticBody2D::get_constant_angular_velocity() const {
+
+ return constant_angular_velocity;
+}
+
+
+void StaticBody2D::_state_notify(Object *p_object) {
+
+ if (!pre_xform)
+ return;
+
+ Physics2DDirectBodyState *p2d = (Physics2DDirectBodyState*)p_object;
+ setting=true;
+
+ Matrix32 new_xform = p2d->get_transform();
+ *pre_xform=new_xform;
+ set_block_transform_notify(true);
+ set_global_transform(new_xform);
+ set_block_transform_notify(false);
+
+ setting=false;
+
+
+}
+
+void StaticBody2D::_update_xform() {
+
+ if (!pre_xform || !pending)
+ return;
+
+ setting=true;
+
+
+ Matrix32 new_xform = get_global_transform(); //obtain the new one
+
+ set_block_transform_notify(true);
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,*pre_xform); //then simulate motion!
+ set_global_transform(*pre_xform); //but restore state to previous one in both visual and physics
+ set_block_transform_notify(false);
+
+ Physics2DServer::get_singleton()->body_static_simulate_motion(get_rid(),new_xform); //then simulate motion!
+
+ setting=false;
+ pending=false;
+
+}
+
+void StaticBody2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (pre_xform)
+ *pre_xform = get_global_transform();
+ pending=false;
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (simulating_motion && !pending && is_inside_scene() && !setting && !get_scene()->is_editor_hint()) {
+
+
+ call_deferred(SceneStringNames::get_singleton()->_update_xform);
+ pending=true;
+ }
+
+ } break;
+ }
+
+
+}
+
+void StaticBody2D::set_simulate_motion(bool p_enable) {
+
+ if (p_enable==simulating_motion)
+ return;
+ simulating_motion=p_enable;
+
+ if (p_enable) {
+ pre_xform = memnew( Matrix32 );
+ if (is_inside_scene())
+ *pre_xform=get_transform();
+// query = Physics2DServer::get_singleton()->query_create(this,"_state_notify",Variant());
+ // Physics2DServer::get_singleton()->query_body_direct_state(query,get_rid());
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_state_notify");
+
+ } else {
+ memdelete( pre_xform );
+ pre_xform=NULL;
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),NULL,StringName());
+ pending=false;
+ }
+}
+
+bool StaticBody2D::is_simulating_motion() const {
+
+ return simulating_motion;
+}
+
+
+void StaticBody2D::set_friction(real_t p_friction){
+
+ ERR_FAIL_COND(p_friction<0 || p_friction>1);
+
+ friction=p_friction;
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_FRICTION,friction);
+
+}
+real_t StaticBody2D::get_friction() const{
+
+ return friction;
+}
+
+void StaticBody2D::set_bounce(real_t p_bounce){
+
+ ERR_FAIL_COND(p_bounce<0 || p_bounce>1);
+
+ bounce=p_bounce;
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_BOUNCE,bounce);
+
+}
+real_t StaticBody2D::get_bounce() const{
+
+ return bounce;
+}
+
+void StaticBody2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_simulate_motion","enabled"),&StaticBody2D::set_simulate_motion);
+ ObjectTypeDB::bind_method(_MD("is_simulating_motion"),&StaticBody2D::is_simulating_motion);
+ ObjectTypeDB::bind_method(_MD("_update_xform"),&StaticBody2D::_update_xform);
+ ObjectTypeDB::bind_method(_MD("_state_notify"),&StaticBody2D::_state_notify);
+ ObjectTypeDB::bind_method(_MD("set_constant_linear_velocity","vel"),&StaticBody2D::set_constant_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("set_constant_angular_velocity","vel"),&StaticBody2D::set_constant_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_constant_linear_velocity"),&StaticBody2D::get_constant_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_constant_angular_velocity"),&StaticBody2D::get_constant_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&StaticBody2D::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&StaticBody2D::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&StaticBody2D::set_bounce);
+ ObjectTypeDB::bind_method(_MD("get_bounce"),&StaticBody2D::get_bounce);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"simulate_motion"),_SCS("set_simulate_motion"),_SCS("is_simulating_motion"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"constant_linear_velocity"),_SCS("set_constant_linear_velocity"),_SCS("get_constant_linear_velocity"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"constant_angular_velocity"),_SCS("set_constant_angular_velocity"),_SCS("get_constant_angular_velocity"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_friction"),_SCS("get_friction"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_bounce"),_SCS("get_bounce"));
+}
+
+StaticBody2D::StaticBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_STATIC) {
+
+ simulating_motion=false;
+ pre_xform=NULL;
+ setting=false;
+ pending=false;
+ constant_angular_velocity=0;
+ bounce=0;
+ friction=1;
+
+
+}
+
+StaticBody2D::~StaticBody2D() {
+
+ if (pre_xform)
+ memdelete(pre_xform);
+ //if (query.is_valid())
+ // Physics2DServer::get_singleton()->free(query);
+}
+
+
+
+
+void RigidBody2D::_body_enter_scene(ObjectID p_id) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+ ERR_FAIL_COND(!node);
+
+ Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ ERR_FAIL_COND(E->get().in_scene);
+
+ E->get().in_scene=true;
+ emit_signal(SceneStringNames::get_singleton()->body_enter,node);
+
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ emit_signal(SceneStringNames::get_singleton()->body_enter_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape);
+ }
+
+}
+
+void RigidBody2D::_body_exit_scene(ObjectID p_id) {
+
+ Object *obj = ObjectDB::get_instance(p_id);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+ ERR_FAIL_COND(!node);
+ Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.find(p_id);
+ ERR_FAIL_COND(!E);
+ ERR_FAIL_COND(!E->get().in_scene);
+ E->get().in_scene=false;
+ emit_signal(SceneStringNames::get_singleton()->body_exit,node);
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ emit_signal(SceneStringNames::get_singleton()->body_exit_shape,p_id,node,E->get().shapes[i].body_shape,E->get().shapes[i].local_shape);
+ }
+}
+
+void RigidBody2D::_body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape) {
+
+ bool body_in = p_status==1;
+ ObjectID objid=p_instance;
+
+ Object *obj = ObjectDB::get_instance(objid);
+ Node *node = obj ? obj->cast_to<Node>() : NULL;
+
+ Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.find(objid);
+
+ ERR_FAIL_COND(!body_in && !E);
+
+ if (body_in) {
+ if (!E) {
+
+ E = contact_monitor->body_map.insert(objid,BodyState());
+ E->get().rc=0;
+ E->get().in_scene=node && node->is_inside_scene();
+ if (node) {
+ node->connect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene,make_binds(objid));
+ node->connect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene,make_binds(objid));
+ if (E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_enter,node);
+ }
+ }
+
+ }
+ E->get().rc++;
+ if (node)
+ E->get().shapes.insert(ShapePair(p_body_shape,p_local_shape));
+
+
+ if (E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_enter_shape,objid,node,p_body_shape,p_local_shape);
+ }
+
+ } else {
+
+ E->get().rc--;
+
+ if (node)
+ E->get().shapes.erase(ShapePair(p_body_shape,p_local_shape));
+
+ if (E->get().rc==0) {
+
+ if (node) {
+ node->disconnect(SceneStringNames::get_singleton()->enter_scene,this,SceneStringNames::get_singleton()->_body_enter_scene);
+ node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,SceneStringNames::get_singleton()->_body_exit_scene);
+ if (E->get().in_scene)
+ emit_signal(SceneStringNames::get_singleton()->body_exit,obj);
+
+ }
+
+ contact_monitor->body_map.erase(E);
+ }
+ if (node && E->get().in_scene) {
+ emit_signal(SceneStringNames::get_singleton()->body_exit_shape,objid,obj,p_body_shape,p_local_shape);
+ }
+
+ }
+
+}
+
+
+struct _RigidBody2DInOut {
+
+ ObjectID id;
+ int shape;
+ int local_shape;
+};
+
+void RigidBody2D::_direct_state_changed(Object *p_state) {
+
+ //eh.. fuck
+#ifdef DEBUG_ENABLED
+
+ state=p_state->cast_to<Physics2DDirectBodyState>();
+#else
+ state=(Physics2DDirectBodyState*)p_state; //trust it
+#endif
+
+ if (contact_monitor) {
+
+ //untag all
+ int rc=0;
+ for( Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.front();E;E=E->next()) {
+
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ E->get().shapes[i].tagged=false;
+ rc++;
+ }
+ }
+
+ _RigidBody2DInOut *toadd=(_RigidBody2DInOut*)alloca(state->get_contact_count()*sizeof(_RigidBody2DInOut));
+ int toadd_count=0;//state->get_contact_count();
+ RigidBody2D_RemoveAction *toremove=(RigidBody2D_RemoveAction*)alloca(rc*sizeof(RigidBody2D_RemoveAction));
+ int toremove_count=0;
+
+ //put the ones to add
+
+ for(int i=0;i<state->get_contact_count();i++) {
+
+ ObjectID obj = state->get_contact_collider_id(i);
+ int local_shape = state->get_contact_local_shape(i);
+ int shape = state->get_contact_collider_shape(i);
+ toadd[i].local_shape=local_shape;
+ toadd[i].id=obj;
+ toadd[i].shape=shape;
+
+ bool found=false;
+
+ Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.find(obj);
+ if (!E) {
+ toadd_count++;
+ continue;
+ }
+
+ ShapePair sp( shape,local_shape );
+ int idx = E->get().shapes.find(sp);
+ if (idx==-1) {
+
+ toadd_count++;
+ continue;
+ }
+
+ E->get().shapes[idx].tagged=true;
+ }
+
+ //put the ones to remove
+
+ for( Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.front();E;E=E->next()) {
+
+ for(int i=0;i<E->get().shapes.size();i++) {
+
+ if (!E->get().shapes[i].tagged) {
+
+ toremove[toremove_count].body_id=E->key();
+ toremove[toremove_count].pair=E->get().shapes[i];
+ toremove_count++;
+ }
+ }
+ }
+
+
+ //process remotions
+
+ for(int i=0;i<toremove_count;i++) {
+
+ _body_inout(0,toremove[i].body_id,toremove[i].pair.body_shape,toremove[i].pair.local_shape);
+ }
+
+ //process aditions
+
+ for(int i=0;i<toadd_count;i++) {
+
+ _body_inout(1,toadd[i].id,toadd[i].shape,toadd[i].local_shape);
+ }
+
+ }
+
+ set_block_transform_notify(true); // don't want notify (would feedback loop)
+ set_global_transform(state->get_transform());
+ linear_velocity=state->get_linear_velocity();
+ angular_velocity=state->get_angular_velocity();
+ active=!state->is_sleeping();
+ if (get_script_instance())
+ get_script_instance()->call("_integrate_forces",state);
+ set_block_transform_notify(false); // want it back
+
+ state=NULL;
+}
+
+void RigidBody2D::_notification(int p_what) {
+
+
+}
+
+void RigidBody2D::set_mode(Mode p_mode) {
+
+ mode=p_mode;
+ switch(p_mode) {
+
+ case MODE_RIGID: {
+
+ Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_RIGID);
+ } break;
+ case MODE_STATIC: {
+
+ Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_STATIC);
+
+ } break;
+ case MODE_STATIC_ACTIVE: {
+
+ Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_STATIC_ACTIVE);
+
+ } break;
+ case MODE_CHARACTER: {
+ Physics2DServer::get_singleton()->body_set_mode(get_rid(),Physics2DServer::BODY_MODE_CHARACTER);
+
+ } break;
+
+ }
+}
+
+RigidBody2D::Mode RigidBody2D::get_mode() const{
+
+ return mode;
+}
+
+void RigidBody2D::set_mass(real_t p_mass){
+
+ ERR_FAIL_COND(p_mass<=0);
+ mass=p_mass;
+ _change_notify("mass");
+ _change_notify("weight");
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_MASS,mass);
+
+}
+real_t RigidBody2D::get_mass() const{
+
+ return mass;
+}
+
+void RigidBody2D::set_weight(real_t p_weight){
+
+ set_mass(p_weight/9.8);
+}
+real_t RigidBody2D::get_weight() const{
+
+ return mass*9.8;
+}
+
+
+void RigidBody2D::set_friction(real_t p_friction){
+
+ ERR_FAIL_COND(p_friction<0 || p_friction>1);
+
+ friction=p_friction;
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_FRICTION,friction);
+
+}
+real_t RigidBody2D::get_friction() const{
+
+ return friction;
+}
+
+void RigidBody2D::set_bounce(real_t p_bounce){
+
+ ERR_FAIL_COND(p_bounce<0 || p_bounce>1);
+
+ bounce=p_bounce;
+ Physics2DServer::get_singleton()->body_set_param(get_rid(),Physics2DServer::BODY_PARAM_BOUNCE,bounce);
+
+}
+real_t RigidBody2D::get_bounce() const{
+
+ return bounce;
+}
+
+void RigidBody2D::set_axis_velocity(const Vector2& p_axis) {
+
+ Vector2 v = state? state->get_linear_velocity() : linear_velocity;
+ Vector2 axis = p_axis.normalized();
+ v-=axis*axis.dot(v);
+ v+=p_axis;
+ if (state) {
+ set_linear_velocity(v);
+ } else {
+ Physics2DServer::get_singleton()->body_set_axis_velocity(get_rid(),p_axis);
+ linear_velocity=v;
+ }
+}
+
+void RigidBody2D::set_linear_velocity(const Vector2& p_velocity){
+
+ linear_velocity=p_velocity;
+ if (state)
+ state->set_linear_velocity(linear_velocity);
+ else {
+
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_LINEAR_VELOCITY,linear_velocity);
+ }
+
+}
+
+Vector2 RigidBody2D::get_linear_velocity() const{
+
+ return linear_velocity;
+}
+
+void RigidBody2D::set_angular_velocity(real_t p_velocity){
+
+ angular_velocity=p_velocity;
+ if (state)
+ state->set_angular_velocity(angular_velocity);
+ else
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_ANGULAR_VELOCITY,angular_velocity);
+}
+real_t RigidBody2D::get_angular_velocity() const{
+
+ return angular_velocity;
+}
+
+void RigidBody2D::set_use_custom_integrator(bool p_enable){
+
+ if (custom_integrator==p_enable)
+ return;
+
+ custom_integrator=p_enable;
+ Physics2DServer::get_singleton()->body_set_omit_force_integration(get_rid(),p_enable);
+
+
+}
+bool RigidBody2D::is_using_custom_integrator(){
+
+ return custom_integrator;
+}
+
+void RigidBody2D::set_active(bool p_active) {
+
+ active=p_active;
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_SLEEPING,!active);
+
+}
+
+void RigidBody2D::set_can_sleep(bool p_active) {
+
+ can_sleep=p_active;
+ Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_CAN_SLEEP,p_active);
+}
+
+bool RigidBody2D::is_able_to_sleep() const {
+
+ return can_sleep;
+}
+
+bool RigidBody2D::is_active() const {
+
+ return active;
+}
+
+void RigidBody2D::set_max_contacts_reported(int p_amount) {
+
+ max_contacts_reported=p_amount;
+ Physics2DServer::get_singleton()->body_set_max_contacts_reported(get_rid(),p_amount);
+}
+
+int RigidBody2D::get_max_contacts_reported() const{
+
+ return max_contacts_reported;
+}
+
+void RigidBody2D::apply_impulse(const Vector2& p_pos, const Vector2& p_impulse) {
+
+ Physics2DServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse);
+}
+
+void RigidBody2D::set_applied_force(const Vector2& p_force) {
+
+ Physics2DServer::get_singleton()->body_set_applied_force(get_rid(), p_force);
+};
+
+Vector2 RigidBody2D::get_applied_force() const {
+
+ return Physics2DServer::get_singleton()->body_get_applied_force(get_rid());
+};
+
+void RigidBody2D::set_use_continuous_collision_detection(bool p_enable) {
+
+ ccd=p_enable;
+ Physics2DServer::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(),p_enable);
+}
+
+bool RigidBody2D::is_using_continuous_collision_detection() const {
+
+
+ return ccd;
+}
+
+
+void RigidBody2D::set_contact_monitor(bool p_enabled) {
+
+ if (p_enabled==is_contact_monitor_enabled())
+ return;
+
+ if (!p_enabled) {
+
+ for(Map<ObjectID,BodyState>::Element *E=contact_monitor->body_map.front();E;E=E->next()) {
+
+ //clean up mess
+ }
+
+ memdelete( contact_monitor );
+ contact_monitor=NULL;
+ } else {
+
+ contact_monitor = memnew( ContactMonitor );
+ }
+
+}
+
+bool RigidBody2D::is_contact_monitor_enabled() const {
+
+ return contact_monitor!=NULL;
+}
+
+
+
+void RigidBody2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_mode","mode"),&RigidBody2D::set_mode);
+ ObjectTypeDB::bind_method(_MD("get_mode"),&RigidBody2D::get_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody2D::set_mass);
+ ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody2D::get_mass);
+
+ ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody2D::set_weight);
+ ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody2D::get_weight);
+
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&RigidBody2D::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&RigidBody2D::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&RigidBody2D::set_bounce);
+ ObjectTypeDB::bind_method(_MD("get_bounce"),&RigidBody2D::get_bounce);
+
+ ObjectTypeDB::bind_method(_MD("set_linear_velocity","linear_velocity"),&RigidBody2D::set_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&RigidBody2D::get_linear_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_angular_velocity","angular_velocity"),&RigidBody2D::set_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&RigidBody2D::get_angular_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_max_contacts_reported","amount"),&RigidBody2D::set_max_contacts_reported);
+ ObjectTypeDB::bind_method(_MD("get_max_contacts_reported"),&RigidBody2D::get_max_contacts_reported);
+
+ ObjectTypeDB::bind_method(_MD("set_use_custom_integrator","enable"),&RigidBody2D::set_use_custom_integrator);
+ ObjectTypeDB::bind_method(_MD("is_using_custom_integrator"),&RigidBody2D::is_using_custom_integrator);
+
+ ObjectTypeDB::bind_method(_MD("set_contact_monitor","enabled"),&RigidBody2D::set_contact_monitor);
+ ObjectTypeDB::bind_method(_MD("is_contact_monitor_enabled"),&RigidBody2D::is_contact_monitor_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_use_continuous_collision_detection","enable"),&RigidBody2D::set_use_continuous_collision_detection);
+ ObjectTypeDB::bind_method(_MD("is_using_continuous_collision_detection"),&RigidBody2D::is_using_continuous_collision_detection);
+
+ ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody2D::set_axis_velocity);
+ ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody2D::apply_impulse);
+
+ ObjectTypeDB::bind_method(_MD("set_applied_force","force"),&RigidBody2D::set_applied_force);
+ ObjectTypeDB::bind_method(_MD("get_applied_force"),&RigidBody2D::get_applied_force);
+
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&RigidBody2D::set_active);
+ ObjectTypeDB::bind_method(_MD("is_active"),&RigidBody2D::is_active);
+
+ ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody2D::set_can_sleep);
+ ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody2D::is_able_to_sleep);
+
+ ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody2D::_direct_state_changed);
+ ObjectTypeDB::bind_method(_MD("_body_enter_scene"),&RigidBody2D::_body_enter_scene);
+ ObjectTypeDB::bind_method(_MD("_body_exit_scene"),&RigidBody2D::_body_exit_scene);
+
+ BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:Physics2DDirectBodyState")));
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Rigid,Static,Character,Static Active"),_SCS("set_mode"),_SCS("get_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"mass",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01"),_SCS("set_mass"),_SCS("get_mass"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"weight",PROPERTY_HINT_EXP_RANGE,"0.01,65535,0.01",PROPERTY_USAGE_EDITOR),_SCS("set_weight"),_SCS("get_weight"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"friction",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_friction"),_SCS("get_friction"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"bounce",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_bounce"),_SCS("get_bounce"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"custom_integrator"),_SCS("set_use_custom_integrator"),_SCS("is_using_custom_integrator"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"continuous_cd"),_SCS("set_use_continuous_collision_detection"),_SCS("is_using_continuous_collision_detection"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"contacts_reported"),_SCS("set_max_contacts_reported"),_SCS("get_max_contacts_reported"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"contact_monitor"),_SCS("set_contact_monitor"),_SCS("is_contact_monitor_enabled"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"active"),_SCS("set_active"),_SCS("is_active"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"can_sleep"),_SCS("set_can_sleep"),_SCS("is_able_to_sleep"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"velocity/linear"),_SCS("set_linear_velocity"),_SCS("get_linear_velocity"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"velocity/angular"),_SCS("set_angular_velocity"),_SCS("get_angular_velocity"));
+
+ ADD_SIGNAL( MethodInfo("body_enter_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape")));
+ ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape")));
+ ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body")));
+ ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body")));
+
+ BIND_CONSTANT( MODE_STATIC );
+ BIND_CONSTANT( MODE_STATIC_ACTIVE );
+ BIND_CONSTANT( MODE_RIGID );
+ BIND_CONSTANT( MODE_CHARACTER );
+}
+
+RigidBody2D::RigidBody2D() : PhysicsBody2D(Physics2DServer::BODY_MODE_RIGID) {
+
+ mode=MODE_RIGID;
+
+ bounce=0;
+ mass=1;
+ friction=1;
+ max_contacts_reported=0;
+ state=NULL;
+
+ angular_velocity=0;
+ active=true;
+ ccd=false;
+
+ custom_integrator=false;
+ contact_monitor=NULL;
+ can_sleep=true;
+
+ Physics2DServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed");
+}
+
+RigidBody2D::~RigidBody2D() {
+
+ if (contact_monitor)
+ memdelete( contact_monitor );
+
+
+
+}
+
diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h
new file mode 100644
index 0000000000..9eff59d8a7
--- /dev/null
+++ b/scene/2d/physics_body_2d.h
@@ -0,0 +1,232 @@
+/*************************************************************************/
+/* physics_body_2d.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 PHYSICS_BODY_2D_H
+#define PHYSICS_BODY_2D_H
+
+#include "scene/2d/collision_object_2d.h"
+#include "servers/physics_2d_server.h"
+#include "vset.h"
+
+
+class PhysicsBody2D : public CollisionObject2D {
+
+ OBJ_TYPE(PhysicsBody2D,CollisionObject2D);
+
+protected:
+
+ void _notification(int p_what);
+ PhysicsBody2D(Physics2DServer::BodyMode p_mode);
+public:
+
+ PhysicsBody2D();
+
+};
+
+class StaticBody2D : public PhysicsBody2D {
+
+ OBJ_TYPE(StaticBody2D,PhysicsBody2D);
+
+ Matrix32 *pre_xform;
+ //RID query;
+ bool setting;
+ bool pending;
+ bool simulating_motion;
+ Vector2 constant_linear_velocity;
+ real_t constant_angular_velocity;
+ void _update_xform();
+ void _state_notify(Object *p_object);
+
+ real_t bounce;
+ real_t friction;
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_bounce(real_t p_bounce);
+ real_t get_bounce() const;
+
+ void set_simulate_motion(bool p_enable);
+ bool is_simulating_motion() const;
+
+ void set_constant_linear_velocity(const Vector2& p_vel);
+ void set_constant_angular_velocity(real_t p_vel);
+
+ Vector2 get_constant_linear_velocity() const;
+ real_t get_constant_angular_velocity() const;
+
+ StaticBody2D();
+ ~StaticBody2D();
+
+};
+
+class RigidBody2D : public PhysicsBody2D {
+
+ OBJ_TYPE(RigidBody2D,PhysicsBody2D);
+public:
+
+ enum Mode {
+ MODE_RIGID,
+ MODE_STATIC,
+ MODE_CHARACTER,
+ MODE_STATIC_ACTIVE,
+ };
+private:
+
+ bool can_sleep;
+ Physics2DDirectBodyState *state;
+ Mode mode;
+
+ real_t bounce;
+ real_t mass;
+ real_t friction;
+
+ Vector2 linear_velocity;
+ real_t angular_velocity;
+ bool active;
+ bool ccd;
+
+
+ int max_contacts_reported;
+
+ bool custom_integrator;
+
+
+ struct ShapePair {
+
+ int body_shape;
+ int local_shape;
+ bool tagged;
+ bool operator<(const ShapePair& p_sp) const {
+ if (body_shape==p_sp.body_shape)
+ return local_shape < p_sp.local_shape;
+ else
+ return body_shape < p_sp.body_shape;
+ }
+
+ ShapePair() {}
+ ShapePair(int p_bs, int p_ls) { body_shape=p_bs; local_shape=p_ls; }
+ };
+ struct RigidBody2D_RemoveAction {
+
+
+ ObjectID body_id;
+ ShapePair pair;
+
+ };
+ struct BodyState {
+
+ int rc;
+ bool in_scene;
+ VSet<ShapePair> shapes;
+ };
+
+ struct ContactMonitor {
+
+
+ Map<ObjectID,BodyState> body_map;
+
+ };
+
+
+ ContactMonitor *contact_monitor;
+ void _body_enter_scene(ObjectID p_id);
+ void _body_exit_scene(ObjectID p_id);
+
+
+ void _body_inout(int p_status, ObjectID p_instance, int p_body_shape,int p_local_shape);
+ void _direct_state_changed(Object *p_state);
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_mode(Mode p_mode);
+ Mode get_mode() const;
+
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+
+ void set_weight(real_t p_weight);
+ real_t get_weight() const;
+
+ void set_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_bounce(real_t p_bounce);
+ real_t get_bounce() const;
+
+ void set_linear_velocity(const Vector2& p_velocity);
+ Vector2 get_linear_velocity() const;
+
+ void set_axis_velocity(const Vector2& p_axis);
+
+ void set_angular_velocity(real_t p_velocity);
+ real_t get_angular_velocity() const;
+
+ void set_use_custom_integrator(bool p_enable);
+ bool is_using_custom_integrator();
+
+ void set_active(bool p_active);
+ bool is_active() const;
+
+ void set_can_sleep(bool p_active);
+ bool is_able_to_sleep() const;
+
+ void set_contact_monitor(bool p_enabled);
+ bool is_contact_monitor_enabled() const;
+
+ void set_max_contacts_reported(int p_amount);
+ int get_max_contacts_reported() const;
+
+ void set_use_continuous_collision_detection(bool p_enable);
+ bool is_using_continuous_collision_detection() const;
+
+ void apply_impulse(const Vector2& p_pos, const Vector2& p_impulse);
+
+ void set_applied_force(const Vector2& p_force);
+ Vector2 get_applied_force() const;
+
+ RigidBody2D();
+ ~RigidBody2D();
+
+};
+
+VARIANT_ENUM_CAST(RigidBody2D::Mode);
+#endif // PHYSICS_BODY_2D_H
diff --git a/scene/2d/position_2d.cpp b/scene/2d/position_2d.cpp
new file mode 100644
index 0000000000..ca3be9aa8f
--- /dev/null
+++ b/scene/2d/position_2d.cpp
@@ -0,0 +1,65 @@
+/*************************************************************************/
+/* position_2d.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 "position_2d.h"
+#include "scene/resources/texture.h"
+
+void Position2D::_draw_cross() {
+
+ draw_line(Point2(-10,0),Point2(+10,0),Color(1,0.5,0.5));
+ draw_line(Point2(0,-10),Point2(0,+10),Color(0.5,1,0.5));
+
+}
+
+Rect2 Position2D::get_item_rect() const {
+
+ return Rect2(Point2(-10,-10),Size2(20,20));
+}
+
+void Position2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ update();
+ } break;
+ case NOTIFICATION_DRAW: {
+ if (!is_inside_scene())
+ break;
+ if (get_scene()->is_editor_hint())
+ _draw_cross();
+
+ } break;
+ }
+
+}
+
+Position2D::Position2D()
+{
+}
diff --git a/scene/2d/position_2d.h b/scene/2d/position_2d.h
new file mode 100644
index 0000000000..caabf98eae
--- /dev/null
+++ b/scene/2d/position_2d.h
@@ -0,0 +1,49 @@
+/*************************************************************************/
+/* position_2d.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 POSITION_2D_H
+#define POSITION_2D_H
+
+
+#include "scene/2d/node_2d.h"
+
+class Position2D : public Node2D {
+
+ OBJ_TYPE(Position2D,Node2D)
+
+ void _draw_cross();
+protected:
+
+ void _notification(int p_what);
+public:
+
+ virtual Rect2 get_item_rect() const;
+ Position2D();
+};
+
+#endif // POSITION_2D_H
diff --git a/scene/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
new file mode 100644
index 0000000000..a2c57b8ffb
--- /dev/null
+++ b/scene/2d/ray_cast_2d.cpp
@@ -0,0 +1,198 @@
+/*************************************************************************/
+/* ray_cast_2d.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 "ray_cast_2d.h"
+#include "servers/physics_2d_server.h"
+
+void RayCast2D::set_cast_to(const Vector2& p_point) {
+
+ cast_to=p_point;
+ if (is_inside_scene() && get_scene()->is_editor_hint())
+ update();
+
+}
+
+Vector2 RayCast2D::get_cast_to() const{
+
+ return cast_to;
+}
+
+bool RayCast2D::is_colliding() const{
+
+ return collided;
+}
+Object *RayCast2D::get_collider() const{
+
+ if (against==0)
+ return NULL;
+
+ return ObjectDB::get_instance(against);
+}
+
+int RayCast2D::get_collider_shape() const {
+
+ return against_shape;
+}
+Vector2 RayCast2D::get_collision_point() const{
+
+ return collision_point;
+}
+Vector2 RayCast2D::get_collision_normal() const{
+
+ return collision_normal;
+}
+
+
+void RayCast2D::set_enabled(bool p_enabled) {
+
+ enabled=p_enabled;
+ if (is_inside_scene() && !get_scene()->is_editor_hint())
+ set_fixed_process(p_enabled);
+ if (!p_enabled)
+ collided=false;
+
+}
+
+
+bool RayCast2D::is_enabled() const {
+
+
+ return enabled;
+}
+
+
+void RayCast2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (enabled && !get_scene()->is_editor_hint())
+ set_fixed_process(true);
+ else
+ set_fixed_process(false);
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (enabled)
+ set_fixed_process(false);
+
+ } break;
+#ifdef TOOLS_ENABLED
+ case NOTIFICATION_DRAW: {
+
+ if (!get_scene()->is_editor_hint())
+ break;
+ Matrix32 xf;
+ xf.rotate(cast_to.atan2());
+ xf.translate(Vector2(0,cast_to.length()));
+
+ //Vector2 tip = Vector2(0,s->get_length());
+ Color dcol(0.9,0.2,0.2,0.4);
+ draw_line(Vector2(),cast_to,dcol,3);
+ Vector<Vector2> pts;
+ float tsize=4;
+ pts.push_back(xf.xform(Vector2(0,tsize)));
+ pts.push_back(xf.xform(Vector2(0.707*tsize,0)));
+ pts.push_back(xf.xform(Vector2(-0.707*tsize,0)));
+ Vector<Color> cols;
+ for(int i=0;i<3;i++)
+ cols.push_back(dcol);
+
+ draw_primitive(pts,cols,Vector<Vector2>()); //small arrow
+
+ } break;
+#endif
+
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ if (!enabled)
+ break;
+
+
+
+ Ref<World2D> w2d = get_world_2d();
+ ERR_BREAK( w2d.is_null() );
+
+ Physics2DDirectSpaceState *dss = Physics2DServer::get_singleton()->space_get_direct_state(w2d->get_space());
+ ERR_BREAK( !dss );
+
+ Matrix32 gt = get_global_transform();
+
+ Vector2 to = cast_to;
+ if (to==Vector2())
+ to=Vector2(0,0.01);
+
+ Physics2DDirectSpaceState::RayResult rr;
+
+ if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr)) {
+
+ collided=true;
+ against=rr.collider_id;
+ collision_point=rr.position;
+ collision_normal=rr.normal;
+ against_shape=rr.shape;
+ } else {
+ collided=false;
+ }
+
+
+
+ } break;
+ }
+}
+
+void RayCast2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&RayCast2D::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&RayCast2D::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_cast_to","local_point"),&RayCast2D::set_cast_to);
+ ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast2D::get_cast_to);
+
+ ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast2D::is_colliding);
+
+ ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast2D::get_collider);
+ ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast2D::get_collider_shape);
+ ObjectTypeDB::bind_method(_MD("get_collision_point"),&RayCast2D::get_collision_point);
+ ObjectTypeDB::bind_method(_MD("get_collision_normal"),&RayCast2D::get_collision_normal);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to"));
+}
+
+RayCast2D::RayCast2D() {
+
+ enabled=false;
+ against=0;
+ collided=false;
+ against_shape=0;
+ cast_to=Vector2(0,50);
+}
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
new file mode 100644
index 0000000000..62bcb946a6
--- /dev/null
+++ b/scene/2d/ray_cast_2d.h
@@ -0,0 +1,68 @@
+/*************************************************************************/
+/* ray_cast_2d.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 RAY_CAST_2D_H
+#define RAY_CAST_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class RayCast2D : public Node2D {
+
+ OBJ_TYPE(RayCast2D,Node2D);
+
+
+ bool enabled;
+ bool collided;
+ ObjectID against;
+ int against_shape;
+ Vector2 collision_point;
+ Vector2 collision_normal;
+
+ Vector2 cast_to;
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_cast_to(const Vector2& p_point);
+ Vector2 get_cast_to() const;
+
+ bool is_colliding() const;
+ Object *get_collider() const;
+ int get_collider_shape() const;
+ Vector2 get_collision_point() const;
+ Vector2 get_collision_normal() const;
+
+ RayCast2D();
+};
+
+#endif // RAY_CAST_2D_H
diff --git a/scene/2d/remote_transform_2d.cpp b/scene/2d/remote_transform_2d.cpp
new file mode 100644
index 0000000000..80d038f6f8
--- /dev/null
+++ b/scene/2d/remote_transform_2d.cpp
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* remote_transform_2d.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 "remote_transform_2d.h"
+#include "scene/scene_string_names.h"
+
+void RemoteTransform2D::_update_cache() {
+
+ cache=0;
+ if (has_node(remote_node)) {
+ Node *node = get_node(remote_node);
+ if (!node || this==node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) {
+ return;
+ }
+
+ cache=node->get_instance_ID();
+ }
+}
+
+void RemoteTransform2D::_update_remote() {
+
+
+ if (!is_inside_scene())
+ return;
+
+ if (!cache)
+ return;
+
+ Object *obj = ObjectDB::get_instance(cache);
+ if (!obj)
+ return;
+
+ Node2D *n = obj->cast_to<Node2D>();
+ if (!n)
+ return;
+
+ if (!n->is_inside_scene())
+ return;
+
+ //todo make faster
+ n->set_global_transform(get_global_transform());
+
+}
+
+void RemoteTransform2D::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+
+ _update_cache();
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ if (!is_inside_scene())
+ break;
+
+ if (cache) {
+
+ _update_remote();
+
+ }
+
+ } break;
+
+ }
+}
+
+
+void RemoteTransform2D::set_remote_node(const NodePath& p_remote_node) {
+
+ remote_node=p_remote_node;
+ if (is_inside_scene())
+ _update_cache();
+}
+
+NodePath RemoteTransform2D::get_remote_node() const{
+
+ return remote_node;
+}
+
+
+void RemoteTransform2D::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_remote_node","path"),&RemoteTransform2D::set_remote_node);
+ ObjectTypeDB::bind_method(_MD("get_remote_node"),&RemoteTransform2D::get_remote_node);
+
+ ADD_PROPERTY( PropertyInfo(Variant::NODE_PATH,"remote_path"),_SCS("set_remote_node"),_SCS("get_remote_node"));
+}
+
+RemoteTransform2D::RemoteTransform2D() {
+
+ cache=0;
+
+}
+
+
diff --git a/scene/2d/remote_transform_2d.h b/scene/2d/remote_transform_2d.h
new file mode 100644
index 0000000000..9561e72255
--- /dev/null
+++ b/scene/2d/remote_transform_2d.h
@@ -0,0 +1,52 @@
+/*************************************************************************/
+/* remote_transform_2d.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. */
+/*************************************************************************/
+#include "scene/2d/node_2d.h"
+
+class RemoteTransform2D : public Node2D {
+
+ OBJ_TYPE(RemoteTransform2D,Node2D);
+
+ NodePath remote_node;
+
+ ObjectID cache;
+
+ void _update_remote();
+ void _update_cache();
+ void _node_exited_scene();
+protected:
+
+ static void _bind_methods();
+ void _notification(int p_what);
+public:
+
+ void set_remote_node(const NodePath& p_remote_node);
+ NodePath get_remote_node() const;
+
+ RemoteTransform2D();
+};
diff --git a/scene/2d/sample_player_2d.cpp b/scene/2d/sample_player_2d.cpp
new file mode 100644
index 0000000000..bc1734ec06
--- /dev/null
+++ b/scene/2d/sample_player_2d.cpp
@@ -0,0 +1,252 @@
+/*************************************************************************/
+/* sample_player_2d.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 "sample_player_2d.h"
+
+#include "servers/audio_server.h"
+#include "servers/audio_server.h"
+#include "servers/spatial_sound_server.h"
+
+
+bool SamplePlayer2D::_set(const StringName& p_name, const Variant& p_value) {
+
+ String name=p_name;
+
+ if (name=="play/play") {
+ if (library.is_valid()) {
+
+ String what=p_value;
+ if (what=="")
+ stop_all();
+ else
+ play(what);
+
+ played_back=what;
+ }
+ } else
+ return false;
+
+ return true;
+}
+
+bool SamplePlayer2D::_get(const StringName& p_name,Variant &r_ret) const {
+
+
+ String name=p_name;
+
+ if (name=="play/play") {
+ r_ret=played_back;
+ } else
+ return false;
+
+ return true;
+}
+
+void SamplePlayer2D::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ String en="";
+ if (library.is_valid()) {
+ List<StringName> samples;
+ Ref<SampleLibrary> ncl=library;
+ ncl->get_sample_list(&samples);
+ for (List<StringName>::Element *E=samples.front();E;E=E->next()) {
+
+ en+=",";
+ en+=E->get();
+ }
+ }
+
+ p_list->push_back( PropertyInfo( Variant::STRING, "play/play", PROPERTY_HINT_ENUM, en,PROPERTY_USAGE_EDITOR));
+}
+
+void SamplePlayer2D::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ SpatialSound2DServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony);
+
+
+ } break;
+ }
+
+}
+
+void SamplePlayer2D::set_sample_library(const Ref<SampleLibrary>& p_library) {
+
+ library=p_library;
+}
+
+Ref<SampleLibrary> SamplePlayer2D::get_sample_library() const {
+
+ return library;
+}
+
+void SamplePlayer2D::set_polyphony(int p_voice_count) {
+
+ ERR_FAIL_COND(p_voice_count<0 || p_voice_count>64);
+ polyphony=p_voice_count;
+ if (get_source_rid().is_valid())
+ SpatialSound2DServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony);
+
+}
+
+int SamplePlayer2D::get_polyphony() const {
+
+ return polyphony;
+}
+
+SamplePlayer2D::VoiceID SamplePlayer2D::play(const String& p_sample,int p_voice) {
+
+ if (!get_source_rid().is_valid())
+ return INVALID_VOICE;
+ if (library.is_null())
+ return INVALID_VOICE;
+ if (!library->has_sample(p_sample))
+ return INVALID_VOICE;
+ Ref<Sample> sample = library->get_sample(p_sample);
+ float vol_change = library->sample_get_volume_db(p_sample);
+ float pitch_change = library->sample_get_pitch_scale(p_sample);
+
+ VoiceID vid = SpatialSound2DServer::get_singleton()->source_play_sample(get_source_rid(),sample->get_rid(),sample->get_mix_rate()*pitch_change,p_voice);
+ if (vol_change)
+ SpatialSound2DServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),vid,vol_change);
+
+
+ if (random_pitch_scale) {
+ float ps = Math::random(-random_pitch_scale,random_pitch_scale);
+ if (ps>0)
+ ps=1.0+ps;
+ else
+ ps=1.0/(1.0-ps);
+ SpatialSound2DServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),vid,ps*pitch_change);
+
+ }
+
+ return vid;
+}
+//voices
+void SamplePlayer2D::voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale) {
+
+ if (!get_source_rid().is_valid())
+ return;
+
+ SpatialSound2DServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),p_voice,p_pitch_scale);
+
+}
+
+void SamplePlayer2D::voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db) {
+
+ if (!get_source_rid().is_valid())
+ return;
+ SpatialSound2DServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),p_voice,p_volume_db);
+
+}
+
+bool SamplePlayer2D::is_voice_active(VoiceID p_voice) const {
+
+ if (!get_source_rid().is_valid())
+ return false;
+ return SpatialSound2DServer::get_singleton()->source_is_voice_active(get_source_rid(),p_voice);
+
+}
+
+void SamplePlayer2D::stop_voice(VoiceID p_voice) {
+
+ if (!get_source_rid().is_valid())
+ return;
+ SpatialSound2DServer::get_singleton()->source_stop_voice(get_source_rid(),p_voice);
+
+}
+
+void SamplePlayer2D::stop_all() {
+
+ if (!get_source_rid().is_valid())
+ return;
+
+ for(int i=0;i<polyphony;i++) {
+
+ SpatialSound2DServer::get_singleton()->source_stop_voice(get_source_rid(),i);
+ }
+}
+
+void SamplePlayer2D::set_random_pitch_scale(float p_scale) {
+ random_pitch_scale=p_scale;
+}
+
+float SamplePlayer2D::get_random_pitch_scale() const {
+
+ return random_pitch_scale;
+}
+
+
+void SamplePlayer2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_sample_library","library:SampleLibrary"),&SamplePlayer2D::set_sample_library);
+ ObjectTypeDB::bind_method(_MD("get_sample_library:SampleLibrary"),&SamplePlayer2D::get_sample_library);
+
+ ObjectTypeDB::bind_method(_MD("set_polyphony","voices"),&SamplePlayer2D::set_polyphony);
+ ObjectTypeDB::bind_method(_MD("get_polyphony"),&SamplePlayer2D::get_polyphony);
+
+ ObjectTypeDB::bind_method(_MD("play","sample","voice"),&SamplePlayer2D::play,DEFVAL(NEXT_VOICE));
+ //voices,DEV
+ ObjectTypeDB::bind_method(_MD("voice_set_pitch_scale","voice","ratio"),&SamplePlayer2D::voice_set_pitch_scale);
+ ObjectTypeDB::bind_method(_MD("voice_set_volume_scale_db","voice","db"),&SamplePlayer2D::voice_set_volume_scale_db);
+
+ ObjectTypeDB::bind_method(_MD("is_voice_active","voice"),&SamplePlayer2D::is_voice_active);
+ ObjectTypeDB::bind_method(_MD("stop_voice","voice"),&SamplePlayer2D::stop_voice);
+ ObjectTypeDB::bind_method(_MD("stop_all"),&SamplePlayer2D::stop_all);
+
+ ObjectTypeDB::bind_method(_MD("set_random_pitch_scale","val"),&SamplePlayer2D::set_random_pitch_scale);
+ ObjectTypeDB::bind_method(_MD("get_random_pitch_scale"),&SamplePlayer2D::get_random_pitch_scale);
+
+ BIND_CONSTANT( INVALID_VOICE );
+ BIND_CONSTANT( NEXT_VOICE );
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "config/polyphony", PROPERTY_HINT_RANGE, "1,64,1"),_SCS("set_polyphony"),_SCS("get_polyphony"));
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "config/samples", PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),_SCS("set_sample_library"),_SCS("get_sample_library"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "config/pitch_random", PROPERTY_HINT_RESOURCE_TYPE,"SampleLibrary"),_SCS("set_random_pitch_scale"),_SCS("get_random_pitch_scale"));
+
+
+}
+
+
+SamplePlayer2D::SamplePlayer2D() {
+
+ polyphony=1;
+ random_pitch_scale=0;
+
+}
+
+SamplePlayer2D::~SamplePlayer2D() {
+
+
+}
diff --git a/scene/2d/sample_player_2d.h b/scene/2d/sample_player_2d.h
new file mode 100644
index 0000000000..d04abae244
--- /dev/null
+++ b/scene/2d/sample_player_2d.h
@@ -0,0 +1,92 @@
+/*************************************************************************/
+/* sample_player_2d.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 SAMPLE_PLAYER_2D_H
+#define SAMPLE_PLAYER_2D_H
+
+#include "scene/2d/sound_player_2d.h"
+#include "scene/resources/sample_library.h"
+
+class SamplePlayer2D : public SoundPlayer2D {
+
+ OBJ_TYPE(SamplePlayer2D,SoundPlayer2D);
+public:
+
+ enum {
+
+ INVALID_VOICE=SpatialSoundServer::SOURCE_INVALID_VOICE,
+ NEXT_VOICE=SpatialSoundServer::SOURCE_NEXT_VOICE
+ };
+
+ typedef int VoiceID;
+
+
+private:
+
+ Ref<SampleLibrary> library;
+ int polyphony;
+ String played_back;
+ float random_pitch_scale;
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list(List<PropertyInfo> *p_list) const;
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+
+ void set_sample_library(const Ref<SampleLibrary>& p_library);
+ Ref<SampleLibrary> get_sample_library() const;
+
+ void set_polyphony(int p_voice_count);
+ int get_polyphony() const;
+
+ VoiceID play(const String& p_sample,int p_voice=NEXT_VOICE);
+ //voices
+ void voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale);
+ void voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db);
+
+ bool is_voice_active(VoiceID p_voice) const;
+ void stop_voice(VoiceID p_voice);
+ void stop_all();
+
+ void set_random_pitch_scale(float p_scale);
+ float get_random_pitch_scale() const;
+
+ SamplePlayer2D();
+ ~SamplePlayer2D();
+
+
+};
+
+#endif // SAMPLE_PLAYER_2D_H
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
new file mode 100644
index 0000000000..f386dc63e8
--- /dev/null
+++ b/scene/2d/screen_button.cpp
@@ -0,0 +1,372 @@
+/*************************************************************************/
+/* screen_button.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 "screen_button.h"
+#include "os/os.h"
+#include "input_map.h"
+#include "os/input.h"
+
+void TouchScreenButton::set_texture(const Ref<Texture>& p_texture) {
+
+ texture=p_texture;
+ update();
+}
+
+Ref<Texture> TouchScreenButton::get_texture() const{
+
+ return texture;
+}
+
+void TouchScreenButton::set_texture_pressed(const Ref<Texture>& p_texture_pressed) {
+
+ texture_pressed=p_texture_pressed;
+ update();
+}
+
+Ref<Texture> TouchScreenButton::get_texture_pressed() const{
+
+ return texture_pressed;
+}
+
+void TouchScreenButton::set_bitmask(const Ref<BitMap>& p_bitmask){
+
+ bitmask=p_bitmask;
+}
+
+Ref<BitMap> TouchScreenButton::get_bitmask() const{
+
+ return bitmask;
+}
+
+void TouchScreenButton::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_DRAW: {
+
+ if (!is_inside_scene())
+ return;
+ if (!get_scene()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility==VISIBILITY_TOUCHSCREEN_ONLY)
+ return;
+
+ if (finger_pressed!=-1) {
+
+ if (texture_pressed.is_valid())
+ draw_texture(texture_pressed,Point2());
+ else if (texture.is_valid())
+ draw_texture(texture,Point2());
+
+ } else {
+ if (texture.is_valid())
+ draw_texture(texture,Point2());
+ }
+
+ } break;
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (!get_scene()->is_editor_hint() && !OS::get_singleton()->has_touchscreen_ui_hint() && visibility==VISIBILITY_TOUCHSCREEN_ONLY)
+ return;
+ update();
+ set_process_input(true);
+ if (action.operator String()!="" && InputMap::get_singleton()->has_action(action)) {
+ action_id=InputMap::get_singleton()->get_action_id(action);
+ } else {
+ action_id=-1;
+ }
+ } break;
+ }
+}
+
+
+bool TouchScreenButton::is_pressed() const{
+
+ return finger_pressed!=-1;
+}
+
+void TouchScreenButton::set_action(const String& p_action) {
+
+ action=p_action;
+ if (action.operator String()!="" && InputMap::get_singleton()->has_action(action)) {
+ action_id=InputMap::get_singleton()->get_action_id(action);
+ } else {
+ action_id=-1;
+ }
+
+}
+
+String TouchScreenButton::get_action() const {
+
+ return action;
+}
+
+void TouchScreenButton::_input(const InputEvent& p_event) {
+
+ if (!get_scene())
+ return;
+
+ if (passby_press) {
+
+ if (p_event.type==InputEvent::SCREEN_TOUCH && !p_event.screen_touch.pressed && finger_pressed==p_event.screen_touch.index) {
+
+ emit_signal("released");
+
+ if (action_id!=-1) {
+
+ Input::get_singleton()->action_release(action);
+ InputEvent ie;
+ ie.type=InputEvent::ACTION;
+ ie.ID=0;
+ ie.action.action=action_id;
+ ie.action.pressed=false;
+ get_scene()->input_event(ie);
+ }
+ finger_pressed=-1;
+
+ update();
+
+ }
+
+ if ((p_event.type==InputEvent::SCREEN_TOUCH && p_event.screen_touch.pressed)|| p_event.type==InputEvent::SCREEN_DRAG) {
+
+ if (finger_pressed==-1 || p_event.screen_touch.index==finger_pressed) {
+
+ Point2 coord = (get_viewport_transform() * get_global_transform()).affine_inverse().xform(Point2(p_event.screen_touch.x,p_event.screen_touch.y));
+
+ bool touched=false;
+ if (bitmask.is_valid()) {
+
+ if (Rect2(Point2(),bitmask->get_size()).has_point(coord)) {
+
+ if (bitmask->get_bit(coord))
+ touched=true;
+ }
+ } else {
+
+ touched=Rect2(Point2(),texture->get_size()).has_point(coord);
+ }
+
+
+
+ if (touched) {
+
+ if (finger_pressed==-1) {
+ finger_pressed=p_event.screen_touch.index;
+ //emit change stuff
+ emit_signal("pressed");
+ if (action_id!=-1) {
+
+ Input::get_singleton()->action_press(action);
+ InputEvent ie;
+ ie.type=InputEvent::ACTION;
+ ie.ID=0;
+ ie.action.action=action_id;
+ ie.action.pressed=true;
+ get_scene()->input_event(ie);
+ }
+
+ update();
+ }
+
+ } else {
+
+ if (finger_pressed!=-1) {
+
+ emit_signal("released");
+
+ if (action_id!=-1) {
+
+ Input::get_singleton()->action_release(action);
+ InputEvent ie;
+ ie.type=InputEvent::ACTION;
+ ie.ID=0;
+ ie.action.action=action_id;
+ ie.action.pressed=false;
+ get_scene()->input_event(ie);
+ }
+ finger_pressed=-1;
+
+ update();
+ }
+ }
+
+ }
+
+
+ }
+
+ } else {
+
+ if (p_event.type==InputEvent::SCREEN_TOUCH) {
+
+ if (p_event.screen_touch.pressed) {
+
+ if (!is_visible())
+ return;
+
+ if (finger_pressed!=-1)
+ return; //already fingering
+
+ Point2 coord = (get_viewport_transform() * get_global_transform()).affine_inverse().xform(Point2(p_event.screen_touch.x,p_event.screen_touch.y));
+
+ bool touched=false;
+ if (bitmask.is_valid()) {
+
+ if (Rect2(Point2(),bitmask->get_size()).has_point(coord)) {
+
+ if (bitmask->get_bit(coord))
+ touched=true;
+ }
+ } else {
+
+ touched=Rect2(Point2(),texture->get_size()).has_point(coord);
+ }
+
+
+
+ if (touched) {
+
+ finger_pressed=p_event.screen_touch.index;
+ //emit change stuff
+ emit_signal("pressed");
+ if (action_id!=-1) {
+
+ Input::get_singleton()->action_press(action);
+ InputEvent ie;
+ ie.type=InputEvent::ACTION;
+ ie.ID=0;
+ ie.action.action=action_id;
+ ie.action.pressed=true;
+ get_scene()->input_event(ie);
+ }
+ update();
+
+ }
+ } else {
+
+
+ if (p_event.screen_touch.index==finger_pressed) {
+ //untouch
+
+ emit_signal("released");
+
+ if (action_id!=-1) {
+
+ Input::get_singleton()->action_release(action);
+ InputEvent ie;
+ ie.type=InputEvent::ACTION;
+ ie.ID=0;
+ ie.action.action=action_id;
+ ie.action.pressed=false;
+ get_scene()->input_event(ie);
+ }
+ finger_pressed=-1;
+ update();
+ }
+ }
+ }
+ }
+}
+
+Rect2 TouchScreenButton::get_item_rect() const {
+
+ if (texture.is_null())
+ return Rect2(0,0,1,1);
+ //if (texture.is_null())
+ // return CanvasItem::get_item_rect();
+
+ return Rect2(Size2(),texture->get_size());
+}
+
+
+void TouchScreenButton::set_visibility_mode(VisibilityMode p_mode) {
+ visibility=p_mode;
+ update();
+}
+
+TouchScreenButton::VisibilityMode TouchScreenButton::get_visibility_mode() const {
+
+ return visibility;
+}
+
+void TouchScreenButton::set_passby_press(bool p_enable) {
+
+ passby_press=p_enable;
+}
+
+bool TouchScreenButton::is_passby_press_enabled() const{
+
+ return passby_press;
+}
+
+
+
+void TouchScreenButton::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_texture","texture"),&TouchScreenButton::set_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture"),&TouchScreenButton::get_texture);
+
+ ObjectTypeDB::bind_method(_MD("set_texture_pressed","texture_pressed"),&TouchScreenButton::set_texture_pressed);
+ ObjectTypeDB::bind_method(_MD("get_texture_pressed"),&TouchScreenButton::get_texture_pressed);
+
+ ObjectTypeDB::bind_method(_MD("set_bitmask","bitmask"),&TouchScreenButton::set_bitmask);
+ ObjectTypeDB::bind_method(_MD("get_bitmask"),&TouchScreenButton::get_bitmask);
+
+ ObjectTypeDB::bind_method(_MD("set_action","action"),&TouchScreenButton::set_action);
+ ObjectTypeDB::bind_method(_MD("get_action"),&TouchScreenButton::get_action);
+
+ ObjectTypeDB::bind_method(_MD("set_visibility_mode","mode"),&TouchScreenButton::set_visibility_mode);
+ ObjectTypeDB::bind_method(_MD("get_visibility_mode"),&TouchScreenButton::get_visibility_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_passby_press","enabled"),&TouchScreenButton::set_passby_press);
+ ObjectTypeDB::bind_method(_MD("is_passby_press_enabled"),&TouchScreenButton::is_passby_press_enabled);
+
+ ObjectTypeDB::bind_method(_MD("is_pressed"),&TouchScreenButton::is_pressed);
+
+ ObjectTypeDB::bind_method(_MD("_input"),&TouchScreenButton::_input);
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"normal",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture"),_SCS("get_texture"));
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"pressed",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_texture_pressed"),_SCS("get_texture_pressed"));
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"bitmask",PROPERTY_HINT_RESOURCE_TYPE,"BitMap"),_SCS("set_bitmask"),_SCS("get_bitmask"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"passby_press"),_SCS("set_passby_press"),_SCS("is_passby_press_enabled"));
+ ADD_PROPERTY( PropertyInfo(Variant::STRING,"action"),_SCS("set_action"),_SCS("get_action"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"visibility_mode",PROPERTY_HINT_ENUM,"Always,TouchScreen Only"),_SCS("set_visibility_mode"),_SCS("get_visibility_mode"));
+
+ ADD_SIGNAL( MethodInfo("pressed" ) );
+ ADD_SIGNAL( MethodInfo("release" ) );
+
+
+
+}
+
+TouchScreenButton::TouchScreenButton() {
+
+ finger_pressed=-1;
+ action_id=-1;
+ passby_press=false;
+ visibility=VISIBILITY_ALWAYS;
+}
diff --git a/scene/2d/screen_button.h b/scene/2d/screen_button.h
new file mode 100644
index 0000000000..7305fd29fe
--- /dev/null
+++ b/scene/2d/screen_button.h
@@ -0,0 +1,95 @@
+/*************************************************************************/
+/* screen_button.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 SCREEN_BUTTON_H
+#define SCREEN_BUTTON_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/texture.h"
+#include "scene/resources/bit_mask.h"
+
+class TouchScreenButton : public Node2D {
+
+ OBJ_TYPE(TouchScreenButton,Node2D);
+public:
+
+ enum VisibilityMode {
+ VISIBILITY_ALWAYS,
+ VISIBILITY_TOUCHSCREEN_ONLY
+ };
+
+private:
+ Ref<Texture> texture;
+ Ref<Texture> texture_pressed;
+ Ref<BitMap> bitmask;
+
+ StringName action;
+ bool passby_press;
+ int finger_pressed;
+ int action_id;
+
+ VisibilityMode visibility;
+
+ void _input(const InputEvent& p_Event);
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+
+ void set_texture(const Ref<Texture>& p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_texture_pressed(const Ref<Texture>& p_texture_pressed);
+ Ref<Texture> get_texture_pressed() const;
+
+ void set_bitmask(const Ref<BitMap>& p_bitmask);
+ Ref<BitMap> get_bitmask() const;
+
+ void set_action(const String& p_action);
+ String get_action() const;
+
+ void set_passby_press(bool p_enable);
+ bool is_passby_press_enabled() const;
+
+ void set_visibility_mode(VisibilityMode p_mode);
+ VisibilityMode get_visibility_mode() const;
+
+ bool is_pressed() const;
+
+ Rect2 get_item_rect() const;
+
+
+ TouchScreenButton();
+};
+
+VARIANT_ENUM_CAST(TouchScreenButton::VisibilityMode);
+
+#endif // SCREEN_BUTTON_H
diff --git a/scene/2d/sound_player_2d.cpp b/scene/2d/sound_player_2d.cpp
new file mode 100644
index 0000000000..2fffef4e51
--- /dev/null
+++ b/scene/2d/sound_player_2d.cpp
@@ -0,0 +1,123 @@
+/*************************************************************************/
+/* sound_player_2d.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 "sound_player_2d.h"
+
+#include "servers/audio_server.h"
+
+#include "servers/spatial_sound_2d_server.h"
+#include "scene/resources/surface_tool.h"
+
+
+void SoundPlayer2D::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+ //find the sound space
+
+ source_rid = SpatialSound2DServer::get_singleton()->source_create(get_world_2d()->get_sound_space());
+
+ for(int i=0;i<PARAM_MAX;i++)
+ set_param(Param(i),params[i]);
+
+ SpatialSound2DServer::get_singleton()->source_set_transform(source_rid,get_global_transform());
+
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ SpatialSound2DServer::get_singleton()->source_set_transform(source_rid,get_global_transform());
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (source_rid.is_valid())
+ SpatialSound2DServer::get_singleton()->free(source_rid);
+
+ } break;
+ }
+
+}
+
+
+void SoundPlayer2D::set_param( Param p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params[p_param]=p_value;
+ if (source_rid.is_valid())
+ SpatialSound2DServer::get_singleton()->source_set_param(source_rid,(SpatialSound2DServer::SourceParam)p_param,p_value);
+
+}
+
+float SoundPlayer2D::get_param( Param p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params[p_param];
+
+}
+
+
+void SoundPlayer2D::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SoundPlayer2D::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&SoundPlayer2D::get_param);
+
+ BIND_CONSTANT( PARAM_VOLUME_DB );
+ BIND_CONSTANT( PARAM_PITCH_SCALE );
+ BIND_CONSTANT( PARAM_ATTENUATION_MIN_DISTANCE );
+ BIND_CONSTANT( PARAM_ATTENUATION_MAX_DISTANCE );
+ BIND_CONSTANT( PARAM_ATTENUATION_DISTANCE_EXP );
+ BIND_CONSTANT( PARAM_MAX );
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/volume_db",PROPERTY_HINT_RANGE, "-80,24,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_VOLUME_DB);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/pitch_scale",PROPERTY_HINT_RANGE, "0.001,32,0.001"),_SCS("set_param"),_SCS("get_param"),PARAM_PITCH_SCALE);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/min_distance",PROPERTY_HINT_EXP_RANGE, "16,16384,1"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MIN_DISTANCE);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/max_distance",PROPERTY_HINT_EXP_RANGE, "16,16384,1"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MAX_DISTANCE);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/distance_exp",PROPERTY_HINT_EXP_EASING, "attenuation"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_DISTANCE_EXP);
+
+}
+
+
+SoundPlayer2D::SoundPlayer2D() {
+
+ params[PARAM_VOLUME_DB]=0.0;
+ params[PARAM_PITCH_SCALE]=1.0;
+ params[PARAM_ATTENUATION_MIN_DISTANCE]=1;
+ params[PARAM_ATTENUATION_MAX_DISTANCE]=2048;
+ params[PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good)
+
+
+}
+
+SoundPlayer2D::~SoundPlayer2D() {
+
+
+}
diff --git a/scene/2d/sound_player_2d.h b/scene/2d/sound_player_2d.h
new file mode 100644
index 0000000000..9578c6b323
--- /dev/null
+++ b/scene/2d/sound_player_2d.h
@@ -0,0 +1,82 @@
+/*************************************************************************/
+/* sound_player_2d.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 SOUND_PLAYER_2D_H
+#define SOUND_PLAYER_2D_H
+
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/sample_library.h"
+#include "servers/spatial_sound_2d_server.h"
+#include "scene/main/viewport.h"
+
+class SoundPlayer2D : public Node2D {
+
+ OBJ_TYPE(SoundPlayer2D,Node2D);
+public:
+
+
+ enum Param {
+
+ PARAM_VOLUME_DB=SpatialSound2DServer::SOURCE_PARAM_VOLUME_DB,
+ PARAM_PITCH_SCALE=SpatialSound2DServer::SOURCE_PARAM_PITCH_SCALE,
+ PARAM_ATTENUATION_MIN_DISTANCE=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_MIN_DISTANCE,
+ PARAM_ATTENUATION_MAX_DISTANCE=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_MAX_DISTANCE,
+ PARAM_ATTENUATION_DISTANCE_EXP=SpatialSound2DServer::SOURCE_PARAM_ATTENUATION_DISTANCE_EXP,
+ PARAM_MAX=SpatialSound2DServer::SOURCE_PARAM_MAX
+ };
+
+private:
+
+ float params[PARAM_MAX];
+ RID source_rid;
+
+
+protected:
+
+ _FORCE_INLINE_ RID get_source_rid() const { return source_rid; }
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+
+ void set_param( Param p_param, float p_value);
+ float get_param( Param p_param) const;
+
+
+ SoundPlayer2D();
+ ~SoundPlayer2D();
+
+
+};
+
+VARIANT_ENUM_CAST(SoundPlayer2D::Param );
+
+#endif // SOUND_PLAYER_2D_H
diff --git a/scene/2d/sprite.cpp b/scene/2d/sprite.cpp
new file mode 100644
index 0000000000..0265ec4df2
--- /dev/null
+++ b/scene/2d/sprite.cpp
@@ -0,0 +1,337 @@
+/*************************************************************************/
+/* sprite.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 "sprite.h"
+#include "core/core_string_names.h"
+#include "scene/scene_string_names.h"
+void Sprite::edit_set_pivot(const Point2& p_pivot) {
+
+ set_offset(p_pivot);
+}
+
+Point2 Sprite::edit_get_pivot() const {
+
+ return get_offset();
+}
+bool Sprite::edit_has_pivot() const {
+
+ return true;
+}
+
+void Sprite::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_DRAW: {
+
+ if (texture.is_null())
+ return;
+
+
+
+
+ RID ci = get_canvas_item();
+
+ /*
+ texture->draw(ci,Point2());
+ break;
+ */
+
+ Size2i s;
+ Rect2i src_rect;
+
+ if (region) {
+
+ s=region_rect.size;
+ src_rect=region_rect;
+ } else {
+ s = texture->get_size();
+ s=s/Size2i(hframes,vframes);
+
+ src_rect.size=s;
+ src_rect.pos.x+=(frame%hframes)*s.x;
+ src_rect.pos.y+=(frame/hframes)*s.y;
+
+ }
+
+ Point2i ofs=offset;
+ if (centered)
+ ofs-=s/2;
+
+ Rect2i dst_rect(ofs,s);
+
+ if (hflip)
+ dst_rect.size.x=-dst_rect.size.x;
+ if (vflip)
+ dst_rect.size.y=-dst_rect.size.y;
+
+ texture->draw_rect_region(ci,dst_rect,src_rect,modulate);
+
+ } break;
+ }
+}
+
+void Sprite::set_texture(const Ref<Texture>& p_texture) {
+
+ if (p_texture==texture)
+ return;
+ if (texture.is_valid()) {
+ texture->disconnect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->update);
+ }
+ texture=p_texture;
+ if (texture.is_valid()) {
+ texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites
+ texture->connect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->update);
+ }
+ update();
+ item_rect_changed();
+}
+
+Ref<Texture> Sprite::get_texture() const {
+
+ return texture;
+}
+
+void Sprite::set_centered(bool p_center) {
+
+ centered=p_center;
+ update();
+ item_rect_changed();
+}
+
+bool Sprite::is_centered() const {
+
+ return centered;
+}
+
+void Sprite::set_offset(const Point2& p_offset) {
+
+ offset=p_offset;
+ update();
+ item_rect_changed();
+}
+Point2 Sprite::get_offset() const {
+
+ return offset;
+}
+
+void Sprite::set_flip_h(bool p_flip) {
+
+ hflip=p_flip;
+ update();
+}
+bool Sprite::is_flipped_h() const {
+
+ return hflip;
+}
+
+void Sprite::set_flip_v(bool p_flip) {
+
+ vflip=p_flip;
+ update();
+}
+bool Sprite::is_flipped_v() const {
+
+ return vflip;
+}
+
+void Sprite::set_region(bool p_region) {
+
+ if (p_region==region)
+ return;
+
+ region=p_region;
+ update();
+}
+
+bool Sprite::is_region() const{
+
+ return region;
+}
+
+void Sprite::set_region_rect(const Rect2& p_region_rect) {
+
+ bool changed=region_rect!=p_region_rect;
+ region_rect=p_region_rect;
+ if (region && changed) {
+ update();
+ item_rect_changed();
+ }
+}
+
+Rect2 Sprite::get_region_rect() const {
+
+ return region_rect;
+}
+
+void Sprite::set_frame(int p_frame) {
+
+ ERR_FAIL_INDEX(p_frame,vframes*hframes);
+
+ if (frame != p_frame)
+ item_rect_changed();
+
+ frame=p_frame;
+}
+
+int Sprite::get_frame() const {
+
+ return frame;
+}
+
+void Sprite::set_vframes(int p_amount) {
+
+ ERR_FAIL_COND(p_amount<1);
+ vframes=p_amount;
+ update();
+ item_rect_changed();
+ _change_notify("frame");
+}
+int Sprite::get_vframes() const {
+
+ return vframes;
+}
+
+void Sprite::set_hframes(int p_amount) {
+
+ ERR_FAIL_COND(p_amount<1);
+ hframes=p_amount;
+ update();
+ item_rect_changed();
+ _change_notify("frame");
+}
+int Sprite::get_hframes() const {
+
+ return hframes;
+}
+
+void Sprite::set_modulate(const Color& p_color) {
+
+ modulate=p_color;
+ update();
+}
+
+Color Sprite::get_modulate() const{
+
+ return modulate;
+}
+
+
+Rect2 Sprite::get_item_rect() const {
+
+ if (texture.is_null())
+ return Rect2(0,0,1,1);
+ //if (texture.is_null())
+ // return CanvasItem::get_item_rect();
+
+ Size2i s;
+
+ if (region) {
+
+ s=region_rect.size;
+ } else {
+ s = texture->get_size();
+ s=s/Point2(hframes,vframes);
+ }
+
+ Point2i ofs=offset;
+ if (centered)
+ ofs-=s/2;
+
+ if (s==Size2(0,0))
+ s=Size2(1,1);
+
+ return Rect2(ofs,s);
+}
+
+
+void Sprite::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_texture","texture:Texture"),&Sprite::set_texture);
+ ObjectTypeDB::bind_method(_MD("get_texture:Texture"),&Sprite::get_texture);
+
+ ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Sprite::set_centered);
+ ObjectTypeDB::bind_method(_MD("is_centered"),&Sprite::is_centered);
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Sprite::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&Sprite::get_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_flip_h","flip_h"),&Sprite::set_flip_h);
+ ObjectTypeDB::bind_method(_MD("is_flipped_h"),&Sprite::is_flipped_h);
+
+ ObjectTypeDB::bind_method(_MD("set_flip_v","flip_v"),&Sprite::set_flip_v);
+ ObjectTypeDB::bind_method(_MD("is_flipped_v"),&Sprite::is_flipped_v);
+
+ ObjectTypeDB::bind_method(_MD("set_region","enabled"),&Sprite::set_region);
+ ObjectTypeDB::bind_method(_MD("is_region"),&Sprite::is_region);
+
+ ObjectTypeDB::bind_method(_MD("set_region_rect","rect"),&Sprite::set_region_rect);
+ ObjectTypeDB::bind_method(_MD("get_region_rect"),&Sprite::get_region_rect);
+
+ ObjectTypeDB::bind_method(_MD("set_frame","frame"),&Sprite::set_frame);
+ ObjectTypeDB::bind_method(_MD("get_frame"),&Sprite::get_frame);
+
+ ObjectTypeDB::bind_method(_MD("set_vframes","vframes"),&Sprite::set_vframes);
+ ObjectTypeDB::bind_method(_MD("get_vframes"),&Sprite::get_vframes);
+
+ ObjectTypeDB::bind_method(_MD("set_hframes","hframes"),&Sprite::set_hframes);
+ ObjectTypeDB::bind_method(_MD("get_hframes"),&Sprite::get_hframes);
+
+ ObjectTypeDB::bind_method(_MD("set_modulate","modulate"),&Sprite::set_modulate);
+ ObjectTypeDB::bind_method(_MD("get_modulate"),&Sprite::get_modulate);
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_texture"),_SCS("get_texture"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "centered"), _SCS("set_centered"),_SCS("is_centered"));
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "offset"), _SCS("set_offset"),_SCS("get_offset"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_h"), _SCS("set_flip_h"),_SCS("is_flipped_h"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flip_v"), _SCS("set_flip_v"),_SCS("is_flipped_v"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "vframes"), _SCS("set_vframes"),_SCS("get_vframes"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "hframes"), _SCS("set_hframes"),_SCS("get_hframes"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "frame"), _SCS("set_frame"),_SCS("get_frame"));
+ ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "region"), _SCS("set_region"),_SCS("is_region"));
+ ADD_PROPERTY( PropertyInfo( Variant::RECT2, "region_rect"), _SCS("set_region_rect"),_SCS("get_region_rect"));
+
+}
+
+Sprite::Sprite() {
+
+ centered=true;
+ hflip=false;
+ vflip=false;
+ region=false;
+
+ frame=0;
+
+ vframes=1;
+ hframes=1;
+
+ modulate=Color(1,1,1,1);
+
+
+}
diff --git a/scene/2d/sprite.h b/scene/2d/sprite.h
new file mode 100644
index 0000000000..42f737fa91
--- /dev/null
+++ b/scene/2d/sprite.h
@@ -0,0 +1,108 @@
+/*************************************************************************/
+/* sprite.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 SPRITE_H
+#define SPRITE_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/texture.h"
+
+
+class Sprite : public Node2D {
+
+ OBJ_TYPE( Sprite, Node2D );
+
+ Ref<Texture> texture;
+
+ bool centered;
+ Point2 offset;
+
+ bool hflip;
+ bool vflip;
+ bool region;
+ Rect2 region_rect;
+
+ int frame;
+
+ int vframes;
+ int hframes;
+
+ Color modulate;
+
+
+protected:
+
+ void _notification(int p_what);
+
+ static void _bind_methods();;
+
+public:
+
+ virtual void edit_set_pivot(const Point2& p_pivot);
+ virtual Point2 edit_get_pivot() const;
+ virtual bool edit_has_pivot() const;
+
+ void set_texture(const Ref<Texture>& p_texture);
+ Ref<Texture> get_texture() const;
+
+ void set_centered(bool p_center);
+ bool is_centered() const;
+
+ void set_offset(const Point2& p_offset);
+ Point2 get_offset() const;
+
+ void set_flip_h(bool p_flip);
+ bool is_flipped_h() const;
+
+ void set_flip_v(bool p_flip);
+ bool is_flipped_v() const;
+
+ void set_region(bool p_region);
+ bool is_region() const;
+
+ void set_region_rect(const Rect2& p_region_rect);
+ Rect2 get_region_rect() const;
+
+ void set_frame(int p_frame);
+ int get_frame() const;
+
+ void set_vframes(int p_amount);
+ int get_vframes() const;
+
+ void set_hframes(int p_amount);
+ int get_hframes() const;
+
+ void set_modulate(const Color& p_color);
+ Color get_modulate() const;
+
+ virtual Rect2 get_item_rect() const;
+
+ Sprite();
+};
+
+#endif // SPRITE_H
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
new file mode 100644
index 0000000000..26efa99a88
--- /dev/null
+++ b/scene/2d/tile_map.cpp
@@ -0,0 +1,592 @@
+/*************************************************************************/
+/* tile_map.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 "tile_map.h"
+#include "io/marshalls.h"
+#include "servers/physics_2d_server.h"
+void TileMap::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ pending_update=true;
+ _update_dirty_quadrants();
+ RID space = get_world_2d()->get_space();
+ _update_quadrant_transform();
+ _update_quadrant_space(space);
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ _update_quadrant_space(RID());
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ //move stuff
+ _update_quadrant_transform();
+
+ } break;
+ }
+}
+
+void TileMap::_update_quadrant_space(const RID& p_space) {
+
+ for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
+
+ Quadrant &q=E->get();
+ Physics2DServer::get_singleton()->body_set_space(q.static_body,p_space);
+ }
+}
+
+void TileMap::_update_quadrant_transform() {
+
+ if (!is_inside_scene())
+ return;
+
+ Matrix32 global_transform = get_global_transform();
+
+ for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
+
+ Quadrant &q=E->get();
+ Matrix32 xform;
+ xform.set_origin( q.pos );
+ xform = global_transform * xform;
+ Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
+ }
+}
+
+void TileMap::set_tileset(const Ref<TileSet>& p_tileset) {
+
+ if (tile_set.is_valid())
+ tile_set->disconnect("changed",this,"_recreate_quadrants");
+
+ _clear_quadrants();
+ tile_set=p_tileset;
+
+ if (tile_set.is_valid())
+ tile_set->connect("changed",this,"_recreate_quadrants");
+ else
+ clear();
+
+ _recreate_quadrants();
+
+}
+
+Ref<TileSet> TileMap::get_tileset() const {
+
+ return tile_set;
+}
+
+void TileMap::set_cell_size(int p_size) {
+
+ ERR_FAIL_COND(p_size<1);
+
+ _clear_quadrants();
+ cell_size=p_size;
+ _recreate_quadrants();
+
+
+}
+int TileMap::get_cell_size() const {
+
+ return cell_size;
+}
+void TileMap::set_quadrant_size(int p_size) {
+
+ ERR_FAIL_COND(p_size<1);
+
+ _clear_quadrants();
+ quadrant_size=p_size;
+ _recreate_quadrants();
+
+}
+int TileMap::get_quadrant_size() const {
+
+ return quadrant_size;
+}
+
+void TileMap::set_center_x(bool p_enable) {
+
+ center_x=p_enable;
+ _recreate_quadrants();
+
+}
+bool TileMap::get_center_x() const {
+
+ return center_x;
+}
+void TileMap::set_center_y(bool p_enable) {
+
+ center_y=p_enable;
+ _recreate_quadrants();
+
+}
+bool TileMap::get_center_y() const {
+
+ return center_y;
+}
+
+void TileMap::_update_dirty_quadrants() {
+
+ if (!pending_update)
+ return;
+ if (!is_inside_scene())
+ return;
+ if (!tile_set.is_valid())
+ return;
+
+ VisualServer *vs = VisualServer::get_singleton();
+ Physics2DServer *ps = Physics2DServer::get_singleton();
+
+ while (dirty_quadrant_list.first()) {
+
+ Quadrant &q = *dirty_quadrant_list.first()->self();
+
+ vs->canvas_item_clear(q.canvas_item);
+ ps->body_clear_shapes(q.static_body);
+
+ for(int i=0;i<q.cells.size();i++) {
+
+ Map<PosKey,Cell>::Element *E=tile_map.find( q.cells[i] );
+ Cell &c=E->get();
+ //moment of truth
+ if (!tile_set->has_tile(c.id))
+ continue;
+ Ref<Texture> tex = tile_set->tile_get_texture(c.id);
+ Vector2 tile_ofs = tile_set->tile_get_offset(c.id);
+
+ Vector2 offset = Point2( E->key().x, E->key().y )*cell_size - q.pos;
+
+ if (!tex.is_valid())
+ continue;
+
+
+ Rect2 r = tile_set->tile_get_region(c.id);
+ Size2 s = tex->get_size();
+
+ if (r==Rect2())
+ s = tex->get_size();
+ else {
+ s = r.size;
+ r.pos.x+=fp_adjust;
+ r.pos.y+=fp_adjust;
+ r.size.x-=fp_adjust*2.0;
+ r.size.y-=fp_adjust*2.0;
+ }
+
+ Rect2 rect;
+ rect.pos=offset.floor();
+ rect.size=s;
+
+ rect.size.x+=fp_adjust;
+ rect.size.y+=fp_adjust;
+
+ if (c.flip_h)
+ rect.size.x=-rect.size.x;
+ if (c.flip_v)
+ rect.size.y=-rect.size.y;
+
+
+ if (r==Rect2()) {
+
+ tex->draw_rect(q.canvas_item,rect);
+ } else {
+
+ tex->draw_rect_region(q.canvas_item,rect,r);
+ }
+
+ Vector< Ref<Shape2D> > shapes = tile_set->tile_get_shapes(c.id);
+
+
+ for(int i=0;i<shapes.size();i++) {
+
+ Ref<Shape2D> shape = shapes[i];
+ if (shape.is_valid()) {
+
+ Matrix32 xform;
+ xform.set_origin(offset.floor());
+ if (c.flip_h) {
+ xform.elements[0]=-xform.elements[0];
+ xform.elements[2].x+=s.x;
+ }
+ if (c.flip_v) {
+ xform.elements[1]=-xform.elements[1];
+ xform.elements[2].y+=s.y;
+ }
+
+ ps->body_add_shape(q.static_body,shape->get_rid(),xform);
+ }
+ }
+ }
+
+ dirty_quadrant_list.remove( dirty_quadrant_list.first() );
+ }
+
+
+
+ pending_update=false;
+
+ _recompute_rect_cache();
+
+}
+
+void TileMap::_recompute_rect_cache() {
+
+
+#ifdef DEBUG_ENABLED
+
+ if (!rect_cache_dirty)
+ return;
+
+ Rect2 r_total;
+ for (Map<PosKey,Quadrant>::Element *E=quadrant_map.front();E;E=E->next()) {
+
+
+ Rect2 r( Point2(E->key().x, E->key().y)*cell_size*quadrant_size, Size2(1,1)*cell_size*quadrant_size );
+ if (E==quadrant_map.front())
+ r_total=r;
+ else
+ r_total=r_total.merge(r);
+
+ }
+ if (r_total==Rect2()) {
+ rect_cache=Rect2(-10,-10,20,20);
+ } else {
+ rect_cache=r_total;
+ }
+
+ item_rect_changed();
+
+ rect_cache_dirty=false;
+#endif
+
+
+}
+
+Map<TileMap::PosKey,TileMap::Quadrant>::Element *TileMap::_create_quadrant(const PosKey& p_qk) {
+
+ Matrix32 xform;
+ xform.set_origin(Point2(p_qk.x,p_qk.y)*quadrant_size*cell_size);
+ Quadrant q;
+ q.canvas_item = VisualServer::get_singleton()->canvas_item_create();
+ VisualServer::get_singleton()->canvas_item_set_parent( q.canvas_item, get_canvas_item() );
+ VisualServer::get_singleton()->canvas_item_set_transform( q.canvas_item, xform );
+ q.static_body=Physics2DServer::get_singleton()->body_create(Physics2DServer::BODY_MODE_STATIC);
+ if (is_inside_scene()) {
+ xform = get_global_transform() * xform;
+ RID space = get_world_2d()->get_space();
+ Physics2DServer::get_singleton()->body_set_space(q.static_body,space);
+ }
+
+ Physics2DServer::get_singleton()->body_set_state(q.static_body,Physics2DServer::BODY_STATE_TRANSFORM,xform);
+ q.pos=Vector2(p_qk.x,p_qk.y)*quadrant_size*cell_size;
+
+ rect_cache_dirty=true;
+ return quadrant_map.insert(p_qk,q);
+}
+
+void TileMap::_erase_quadrant(Map<PosKey,Quadrant>::Element *Q) {
+
+ Quadrant &q=Q->get();
+ Physics2DServer::get_singleton()->free(q.static_body);
+ VisualServer::get_singleton()->free(q.canvas_item);
+ if (q.dirty_list.in_list())
+ dirty_quadrant_list.remove(&q.dirty_list);
+
+ quadrant_map.erase(Q);
+ rect_cache_dirty=true;
+}
+
+void TileMap::_make_quadrant_dirty(Map<PosKey,Quadrant>::Element *Q) {
+
+ Quadrant &q=Q->get();
+ if (!q.dirty_list.in_list())
+ dirty_quadrant_list.add(&q.dirty_list);
+
+ if (pending_update)
+ return;
+ pending_update=true;
+ if (!is_inside_scene())
+ return;
+ call_deferred("_update_dirty_quadrants");
+}
+
+
+void TileMap::set_cell(int p_x,int p_y,int p_tile,bool p_flip_x,bool p_flip_y) {
+
+ PosKey pk(p_x,p_y);
+
+ Map<PosKey,Cell>::Element *E=tile_map.find(pk);
+ if (!E && p_tile==INVALID_CELL)
+ return; //nothing to do
+
+ PosKey qk(p_x/quadrant_size,p_y/quadrant_size);
+ if (p_tile==INVALID_CELL) {
+ //erase existing
+ tile_map.erase(pk);
+ Map<PosKey,Quadrant>::Element *Q = quadrant_map.find(qk);
+ ERR_FAIL_COND(!Q);
+ Quadrant &q=Q->get();
+ q.cells.erase(pk);
+ if (q.cells.size()==0)
+ _erase_quadrant(Q);
+ else
+ _make_quadrant_dirty(Q);
+
+ return;
+ }
+
+ Map<PosKey,Quadrant>::Element *Q = quadrant_map.find(qk);
+
+ if (!E) {
+ E=tile_map.insert(pk,Cell());
+ if (!Q)
+ Q=_create_quadrant(qk);
+ Quadrant &q=Q->get();
+ q.cells.insert(pk);
+ } else {
+ ERR_FAIL_COND(!Q); // quadrant should exist...
+
+ if (E->get().id==p_tile && E->get().flip_h==p_flip_x && E->get().flip_v==p_flip_y)
+ return; //nothing changed
+
+ }
+
+
+ Cell &c = E->get();
+
+ c.id=p_tile;
+ c.flip_h=p_flip_x;
+ c.flip_v=p_flip_y;
+
+ _make_quadrant_dirty(Q);
+
+}
+
+int TileMap::get_cell(int p_x,int p_y) const {
+
+ PosKey pk(p_x,p_y);
+
+ const Map<PosKey,Cell>::Element *E=tile_map.find(pk);
+
+ if (!E)
+ return INVALID_CELL;
+
+ return E->get().id;
+
+}
+bool TileMap::is_cell_x_flipped(int p_x,int p_y) const {
+
+ PosKey pk(p_x,p_y);
+
+ const Map<PosKey,Cell>::Element *E=tile_map.find(pk);
+
+ if (!E)
+ return false;
+
+ return E->get().flip_h;
+}
+bool TileMap::is_cell_y_flipped(int p_x,int p_y) const {
+
+ PosKey pk(p_x,p_y);
+
+ const Map<PosKey,Cell>::Element *E=tile_map.find(pk);
+
+ if (!E)
+ return false;
+
+ return E->get().flip_v;
+}
+
+
+void TileMap::_recreate_quadrants() {
+
+ _clear_quadrants();
+
+ for (Map<PosKey,Cell>::Element *E=tile_map.front();E;E=E->next()) {
+
+ PosKey qk(E->key().x/quadrant_size,E->key().y/quadrant_size);
+
+ Map<PosKey,Quadrant>::Element *Q=quadrant_map.find(qk);
+ if (!Q) {
+ Q=_create_quadrant(qk);
+ dirty_quadrant_list.add(&Q->get().dirty_list);
+ }
+
+ Q->get().cells.insert(E->key());
+
+ }
+}
+
+void TileMap::_clear_quadrants() {
+
+ while (quadrant_map.size()) {
+ _erase_quadrant( quadrant_map.front() );
+ }
+}
+
+void TileMap::clear() {
+
+ _clear_quadrants();
+ tile_map.clear();
+}
+
+void TileMap::_set_tile_data(const DVector<int>& p_data) {
+
+ int c=p_data.size();
+ DVector<int>::Read r = p_data.read();
+
+
+ for(int i=0;i<c;i+=2) {
+
+ const uint8_t *ptr=(const uint8_t*)&r[i];
+ uint8_t local[8];
+ for(int j=0;j<8;j++)
+ local[j]=ptr[j];
+
+#ifdef BIG_ENDIAN_ENABLED
+
+
+ SWAP(local[0],local[3]);
+ SWAP(local[1],local[2]);
+ SWAP(local[4],local[7]);
+ SWAP(local[5],local[6]);
+#endif
+ int x = decode_uint16(&local[0]);
+ int y = decode_uint16(&local[2]);
+ uint32_t v = decode_uint32(&local[4]);
+ bool flip_h = v&(1<<29);
+ bool flip_v = v&(1<<30);
+ v&=(1<<29)-1;
+
+// if (x<-20 || y <-20 || x>4000 || y>4000)
+// continue;
+ set_cell(x,y,v,flip_h,flip_v);
+ }
+
+}
+
+DVector<int> TileMap::_get_tile_data() const {
+
+ DVector<int> data;
+ data.resize(tile_map.size()*2);
+ DVector<int>::Write w = data.write();
+
+ int idx=0;
+ for(const Map<PosKey,Cell>::Element *E=tile_map.front();E;E=E->next()) {
+
+ uint8_t *ptr = (uint8_t*)&w[idx];
+ encode_uint16(E->key().x,&ptr[0]);
+ encode_uint16(E->key().y,&ptr[2]);
+ uint32_t val = E->get().id;
+ if (E->get().flip_h)
+ val|=(1<<29);
+ if (E->get().flip_v)
+ val|=(1<<30);
+
+ encode_uint32(val,&ptr[4]);
+ idx+=2;
+ }
+
+
+ w = DVector<int>::Write();
+
+ return data;
+
+}
+
+Rect2 TileMap::get_item_rect() const {
+
+ const_cast<TileMap*>(this)->_update_dirty_quadrants();
+ return rect_cache;
+}
+
+void TileMap::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_tileset","tileset:TileSet"),&TileMap::set_tileset);
+ ObjectTypeDB::bind_method(_MD("get_tileset:TileSet"),&TileMap::get_tileset);
+
+
+ ObjectTypeDB::bind_method(_MD("set_cell_size","size"),&TileMap::set_cell_size);
+ ObjectTypeDB::bind_method(_MD("get_cell_size"),&TileMap::get_cell_size);
+
+ ObjectTypeDB::bind_method(_MD("set_quadrant_size","size"),&TileMap::set_quadrant_size);
+ ObjectTypeDB::bind_method(_MD("get_quadrant_size"),&TileMap::get_quadrant_size);
+
+ ObjectTypeDB::bind_method(_MD("set_center_x","enable"),&TileMap::set_center_x);
+ ObjectTypeDB::bind_method(_MD("get_center_x"),&TileMap::get_center_x);
+
+ ObjectTypeDB::bind_method(_MD("set_center_y","enable"),&TileMap::set_center_y);
+ ObjectTypeDB::bind_method(_MD("get_center_y"),&TileMap::get_center_y);
+
+
+ ObjectTypeDB::bind_method(_MD("set_cell","x","y","tile","flip_x","flip_y"),&TileMap::set_cell,DEFVAL(false),DEFVAL(false));
+ ObjectTypeDB::bind_method(_MD("get_cell","x","y"),&TileMap::get_cell);
+ ObjectTypeDB::bind_method(_MD("is_cell_x_flipped","x","y"),&TileMap::is_cell_x_flipped);
+ ObjectTypeDB::bind_method(_MD("is_cell_y_flipped","x","y"),&TileMap::is_cell_y_flipped);
+
+ ObjectTypeDB::bind_method(_MD("clear"),&TileMap::clear);
+
+ ObjectTypeDB::bind_method(_MD("_clear_quadrants"),&TileMap::_clear_quadrants);
+ ObjectTypeDB::bind_method(_MD("_recreate_quadrants"),&TileMap::_recreate_quadrants);
+ ObjectTypeDB::bind_method(_MD("_update_dirty_quadrants"),&TileMap::_update_dirty_quadrants);
+
+ ObjectTypeDB::bind_method(_MD("_set_tile_data"),&TileMap::_set_tile_data);
+ ObjectTypeDB::bind_method(_MD("_get_tile_data"),&TileMap::_get_tile_data);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"cell_size",PROPERTY_HINT_RANGE,"1,8192,1"),_SCS("set_cell_size"),_SCS("get_cell_size"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"quadrant_size",PROPERTY_HINT_RANGE,"1,128,1"),_SCS("set_quadrant_size"),_SCS("get_quadrant_size"));
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"tile_set",PROPERTY_HINT_RESOURCE_TYPE,"TileSet"),_SCS("set_tileset"),_SCS("get_tileset"));
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"tile_data",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_tile_data"),_SCS("_get_tile_data"));
+
+ BIND_CONSTANT( INVALID_CELL );
+}
+
+TileMap::TileMap() {
+
+
+
+ rect_cache_dirty=true;
+ pending_update=false;
+ quadrant_size=16;
+ cell_size=64;
+ center_x=false;
+ center_y=false;
+
+ fp_adjust=0.4;
+ fp_adjust=0.4;
+}
+
+TileMap::~TileMap() {
+
+ clear();
+}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
new file mode 100644
index 0000000000..a2414382c6
--- /dev/null
+++ b/scene/2d/tile_map.h
@@ -0,0 +1,154 @@
+/*************************************************************************/
+/* tile_map.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 TILE_MAP_H
+#define TILE_MAP_H
+
+#include "scene/2d/node_2d.h"
+#include "scene/resources/tile_set.h"
+#include "self_list.h"
+#include "vset.h"
+
+class TileMap : public Node2D {
+
+ OBJ_TYPE( TileMap, Node2D );
+
+
+ Ref<TileSet> tile_set;
+ int cell_size;
+ int quadrant_size;
+ bool center_x,center_y;
+
+ union PosKey {
+
+ struct {
+ int16_t x;
+ int16_t y;
+ };
+
+ uint32_t key;
+ bool operator<(const PosKey& pk) const { return key<pk.key; }
+
+ PosKey(int16_t p_x, int16_t p_y) { x=p_x; y=p_y; }
+ PosKey() { key=0; }
+
+ };
+
+
+ union Cell {
+
+ struct {
+ int32_t id:24;
+ bool flip_h:1;
+ bool flip_v:1;
+ };
+
+ uint32_t _u32t;
+ Cell() { _u32t=0; }
+ };
+
+
+ Map<PosKey,Cell> tile_map;
+ struct Quadrant {
+
+ Vector2 pos;
+ RID canvas_item;
+ RID static_body;
+
+ SelfList<Quadrant> dirty_list;
+
+ VSet<PosKey> cells;
+
+ void operator=(const Quadrant& q) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells; }
+ Quadrant(const Quadrant& q) : dirty_list(this) { pos=q.pos; canvas_item=q.canvas_item; static_body=q.static_body; cells=q.cells;}
+ Quadrant() : dirty_list(this) {}
+ };
+
+ Map<PosKey,Quadrant> quadrant_map;
+
+ SelfList<Quadrant>::List dirty_quadrant_list;
+
+ bool pending_update;
+
+ Rect2 rect_cache;
+ bool rect_cache_dirty;
+ float fp_adjust;
+
+
+ Map<PosKey,Quadrant>::Element *_create_quadrant(const PosKey& p_qk);
+ void _erase_quadrant(Map<PosKey,Quadrant>::Element *Q);
+ void _make_quadrant_dirty(Map<PosKey,Quadrant>::Element *Q);
+ void _recreate_quadrants();
+ void _clear_quadrants();
+ void _update_dirty_quadrants();
+ void _update_quadrant_space(const RID& p_space);
+ void _update_quadrant_transform();
+ void _recompute_rect_cache();
+
+ void _set_tile_data(const DVector<int>& p_data);
+ DVector<int> _get_tile_data() const;
+protected:
+
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ enum {
+ INVALID_CELL=-1
+ };
+
+ void set_tileset(const Ref<TileSet>& p_tileset);
+ Ref<TileSet> get_tileset() const;
+
+ void set_cell_size(int p_size);
+ int get_cell_size() const;
+
+ void set_quadrant_size(int p_size);
+ int get_quadrant_size() const;
+
+ void set_center_x(bool p_enable);
+ bool get_center_x() const;
+ void set_center_y(bool p_enable);
+ bool get_center_y() const;
+
+ void set_cell(int p_x,int p_y,int p_tile,bool p_flip_x=false,bool p_flip_y=false);
+ int get_cell(int p_x,int p_y) const;
+ bool is_cell_x_flipped(int p_x,int p_y) const;
+ bool is_cell_y_flipped(int p_x,int p_y) const;
+
+ Rect2 get_item_rect() const;
+
+ void clear();
+
+ TileMap();
+ ~TileMap();
+};
+
+#endif // TILE_MAP_H
diff --git a/scene/2d/visibility_notifier_2d.cpp b/scene/2d/visibility_notifier_2d.cpp
new file mode 100644
index 0000000000..323d02df4b
--- /dev/null
+++ b/scene/2d/visibility_notifier_2d.cpp
@@ -0,0 +1,322 @@
+/*************************************************************************/
+/* visibility_notifier_2d.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 "visibility_notifier_2d.h"
+
+#include "scene/scene_string_names.h"
+#include "scene/2d/physics_body_2d.h"
+#include "scene/animation/animation_player.h"
+#include "scene/scene_string_names.h"
+
+void VisibilityNotifier2D::_enter_viewport(Viewport* p_viewport) {
+
+ ERR_FAIL_COND(viewports.has(p_viewport));
+ viewports.insert(p_viewport);
+ if (viewports.size()==1) {
+ emit_signal(SceneStringNames::get_singleton()->enter_screen);
+ _screen_enter();
+
+
+ }
+ emit_signal(SceneStringNames::get_singleton()->enter_viewport,p_viewport);
+
+}
+
+void VisibilityNotifier2D::_exit_viewport(Viewport* p_viewport){
+
+ ERR_FAIL_COND(!viewports.has(p_viewport));
+ viewports.erase(p_viewport);
+
+ emit_signal(SceneStringNames::get_singleton()->exit_viewport,p_viewport);
+ if (viewports.size()==0) {
+ emit_signal(SceneStringNames::get_singleton()->exit_screen);
+
+ _screen_exit();
+ }
+}
+
+
+void VisibilityNotifier2D::set_rect(const Rect2& p_rect){
+
+ rect=p_rect;
+ if (is_inside_scene())
+ get_world_2d()->_update_notifier(this,get_global_transform().xform(rect));
+
+ _change_notify("rect");
+}
+
+Rect2 VisibilityNotifier2D::get_item_rect() const {
+
+ return rect;
+}
+
+Rect2 VisibilityNotifier2D::get_rect() const{
+
+ return rect;
+}
+
+
+void VisibilityNotifier2D::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+ //get_world_2d()->
+ get_world_2d()->_register_notifier(this,get_global_transform().xform(rect));
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ //get_world_2d()->
+ get_world_2d()->_update_notifier(this,get_global_transform().xform(rect));
+ } break;
+ case NOTIFICATION_DRAW: {
+
+ if (get_scene()->is_editor_hint()) {
+
+ draw_rect(rect,Color(1,0.5,1,0.2));
+ }
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ get_world_2d()->_remove_notifier(this);
+ } break;
+ }
+}
+
+bool VisibilityNotifier2D::is_on_screen() const {
+
+ return viewports.size()>0;
+}
+
+void VisibilityNotifier2D::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_rect","rect"),&VisibilityNotifier2D::set_rect);
+ ObjectTypeDB::bind_method(_MD("get_rect"),&VisibilityNotifier2D::get_rect);
+ ObjectTypeDB::bind_method(_MD("is_on_screen"),&VisibilityNotifier2D::is_on_screen);
+
+ ADD_PROPERTY( PropertyInfo(Variant::RECT2,"rect"),_SCS("set_rect"),_SCS("get_rect"));
+
+ ADD_SIGNAL( MethodInfo("enter_viewport",PropertyInfo(Variant::OBJECT,"viewport",PROPERTY_HINT_RESOURCE_TYPE,"Viewport")) );
+ ADD_SIGNAL( MethodInfo("exit_viewport",PropertyInfo(Variant::OBJECT,"viewport",PROPERTY_HINT_RESOURCE_TYPE,"Viewport")) );
+ ADD_SIGNAL( MethodInfo("enter_screen"));
+ ADD_SIGNAL( MethodInfo("exit_screen"));
+}
+
+
+VisibilityNotifier2D::VisibilityNotifier2D() {
+
+ rect=Rect2(-10,-10,20,20);
+}
+
+
+
+
+
+//////////////////////////////////////
+
+
+void VisibilityEnabler2D::_screen_enter() {
+
+
+ for(Map<Node*,Variant>::Element *E=nodes.front();E;E=E->next()) {
+
+ _change_node_state(E->key(),true);
+ }
+
+ visible=true;
+}
+
+void VisibilityEnabler2D::_screen_exit(){
+
+ for(Map<Node*,Variant>::Element *E=nodes.front();E;E=E->next()) {
+
+ _change_node_state(E->key(),false);
+ }
+
+ visible=false;
+}
+
+void VisibilityEnabler2D::_find_nodes(Node* p_node) {
+
+
+ bool add=false;
+ Variant meta;
+
+ if (enabler[ENABLER_FREEZE_BODIES]) {
+
+ RigidBody2D *rb2d = p_node->cast_to<RigidBody2D>();
+ if (rb2d && ((rb2d->get_mode()==RigidBody2D::MODE_CHARACTER || (rb2d->get_mode()==RigidBody2D::MODE_RIGID && !rb2d->is_able_to_sleep())))) {
+
+
+ add=true;
+ meta=rb2d->get_mode();
+ }
+ }
+
+ if (enabler[ENABLER_PAUSE_ANIMATIONS]) {
+
+ AnimationPlayer *ap = p_node->cast_to<AnimationPlayer>();
+ if (ap) {
+ add=true;
+ }
+
+ }
+
+ if (add) {
+
+ p_node->connect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed",varray(p_node),CONNECT_ONESHOT);
+ nodes[p_node]=meta;
+ _change_node_state(p_node,false);
+ }
+
+ for(int i=0;i<p_node->get_child_count();i++) {
+ Node *c = p_node->get_child(i);
+ if (c->get_filename()!=String())
+ continue; //skip, instance
+
+ _find_nodes(c);
+ }
+
+}
+
+void VisibilityEnabler2D::_notification(int p_what){
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ if (get_scene()->is_editor_hint())
+ return;
+
+
+ Node *from = this;
+ //find where current scene starts
+ while(from->get_parent() && from->get_filename()==String())
+ from=from->get_parent();
+
+ _find_nodes(from);
+
+ }
+
+ if (p_what==NOTIFICATION_EXIT_SCENE) {
+
+ if (get_scene()->is_editor_hint())
+ return;
+
+
+ Node *from = this;
+ //find where current scene starts
+
+ for (Map<Node*,Variant>::Element *E=nodes.front();E;E=E->next()) {
+
+ if (!visible)
+ _change_node_state(E->key(),true);
+ E->key()->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed");
+ }
+
+ nodes.clear();
+
+ }
+}
+
+void VisibilityEnabler2D::_change_node_state(Node* p_node,bool p_enabled) {
+
+ ERR_FAIL_COND(!nodes.has(p_node));
+
+ {
+ RigidBody2D *rb = p_node->cast_to<RigidBody2D>();
+ if (rb) {
+
+ if (p_enabled) {
+ RigidBody2D::Mode mode = RigidBody2D::Mode(nodes[p_node].operator int());
+ //rb->set_mode(mode);
+ rb->set_active(true);
+ } else {
+ //rb->set_mode(RigidBody2D::MODE_STATIC);
+ rb->set_active(false);
+ }
+ }
+ }
+
+ {
+ AnimationPlayer *ap=p_node->cast_to<AnimationPlayer>();
+
+ if (ap) {
+
+ ap->set_active(p_enabled);
+ }
+ }
+
+}
+
+
+void VisibilityEnabler2D::_node_removed(Node* p_node) {
+
+ if (!visible)
+ _change_node_state(p_node,true);
+ //changed to one shot, not needed
+ //p_node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed");
+ nodes.erase(p_node);
+
+}
+
+void VisibilityEnabler2D::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_enabler","enabler","enabled"),&VisibilityEnabler2D::set_enabler);
+ ObjectTypeDB::bind_method(_MD("is_enabler_enabled","enabler"),&VisibilityEnabler2D::is_enabler_enabled);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&VisibilityEnabler2D::_node_removed);
+
+ ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/pause_animations"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_PAUSE_ANIMATIONS );
+ ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"enabler/freeze_bodies"),_SCS("set_enabler"),_SCS("is_enabler_enabled"), ENABLER_FREEZE_BODIES);
+
+ BIND_CONSTANT( ENABLER_FREEZE_BODIES );
+ BIND_CONSTANT( ENABLER_PAUSE_ANIMATIONS );
+ BIND_CONSTANT( ENABLER_MAX);
+}
+
+void VisibilityEnabler2D::set_enabler(Enabler p_enabler,bool p_enable){
+
+ ERR_FAIL_INDEX(p_enabler,ENABLER_MAX);
+ enabler[p_enabler]=p_enable;
+
+}
+bool VisibilityEnabler2D::is_enabler_enabled(Enabler p_enabler) const{
+
+ ERR_FAIL_INDEX_V(p_enabler,ENABLER_MAX,false);
+ return enabler[p_enabler];
+
+}
+
+VisibilityEnabler2D::VisibilityEnabler2D() {
+
+ for(int i=0;i<ENABLER_MAX;i++)
+ enabler[i]=true;
+
+ visible=false;
+
+}
+
diff --git a/scene/2d/visibility_notifier_2d.h b/scene/2d/visibility_notifier_2d.h
new file mode 100644
index 0000000000..cdf52ecb27
--- /dev/null
+++ b/scene/2d/visibility_notifier_2d.h
@@ -0,0 +1,109 @@
+/*************************************************************************/
+/* visibility_notifier_2d.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 VISIBILITY_NOTIFIER_2D_H
+#define VISIBILITY_NOTIFIER_2D_H
+
+#include "scene/2d/node_2d.h"
+
+class Viewport;
+class VisibilityNotifier2D : public Node2D {
+
+ OBJ_TYPE(VisibilityNotifier2D,Node2D);
+
+ Set<Viewport*> viewports;
+
+ Rect2 rect;
+
+protected:
+friend class SpatialIndexer2D;
+
+ void _enter_viewport(Viewport* p_viewport);
+ void _exit_viewport(Viewport* p_viewport);
+
+
+ virtual void _screen_enter() {}
+ virtual void _screen_exit() {}
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_rect(const Rect2& p_rect);
+ Rect2 get_rect() const;
+
+ bool is_on_screen() const;
+
+ virtual Rect2 get_item_rect() const;
+
+ VisibilityNotifier2D();
+};
+
+
+class VisibilityEnabler2D : public VisibilityNotifier2D {
+
+ OBJ_TYPE(VisibilityEnabler2D,VisibilityNotifier2D);
+public:
+
+ enum Enabler {
+ ENABLER_PAUSE_ANIMATIONS,
+ ENABLER_FREEZE_BODIES,
+ ENABLER_MAX
+ };
+
+protected:
+
+ virtual void _screen_enter();
+ virtual void _screen_exit();
+
+ bool visible;
+
+ void _find_nodes(Node* p_node);
+
+ Map<Node*,Variant> nodes;
+ void _node_removed(Node* p_node);
+ bool enabler[ENABLER_MAX];
+
+ void _change_node_state(Node* p_node,bool p_enabled);
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_enabler(Enabler p_enabler,bool p_enable);
+ bool is_enabler_enabled(Enabler p_enabler) const;
+
+ VisibilityEnabler2D();
+
+};
+
+VARIANT_ENUM_CAST(VisibilityEnabler2D::Enabler);
+
+
+#endif // VISIBILITY_NOTIFIER_2D_H