summaryrefslogtreecommitdiff
path: root/scene/3d
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
committerJuan Linietsky <reduzio@gmail.com>2014-02-09 22:10:30 -0300
commit0b806ee0fc9097fa7bda7ac0109191c9c5e0a1ac (patch)
tree276c4d099e178eb67fbd14f61d77b05e3808e9e3 /scene/3d
parent0e49da1687bc8192ed210947da52c9e5c5f301bb (diff)
GODOT IS OPEN SOURCE
Diffstat (limited to 'scene/3d')
-rw-r--r--scene/3d/SCsub14
-rw-r--r--scene/3d/area.cpp317
-rw-r--r--scene/3d/area.h121
-rw-r--r--scene/3d/body_shape.cpp829
-rw-r--r--scene/3d/body_shape.h80
-rw-r--r--scene/3d/bone_attachment.cpp138
-rw-r--r--scene/3d/bone_attachment.h58
-rw-r--r--scene/3d/camera.cpp727
-rw-r--r--scene/3d/camera.h144
-rw-r--r--scene/3d/car_body.cpp741
-rw-r--r--scene/3d/car_body.h170
-rw-r--r--scene/3d/character_camera.cpp718
-rw-r--r--scene/3d/character_camera.h167
-rw-r--r--scene/3d/collision_object.cpp284
-rw-r--r--scene/3d/collision_object.h90
-rw-r--r--scene/3d/editable_shape.cpp85
-rw-r--r--scene/3d/editable_shape.h73
-rw-r--r--scene/3d/follow_camera.cpp778
-rw-r--r--scene/3d/follow_camera.h180
-rw-r--r--scene/3d/interpolated_camera.cpp157
-rw-r--r--scene/3d/interpolated_camera.h63
-rw-r--r--scene/3d/light.cpp585
-rw-r--r--scene/3d/light.h195
-rw-r--r--scene/3d/mesh_instance.cpp218
-rw-r--r--scene/3d/mesh_instance.h80
-rw-r--r--scene/3d/multimesh_instance.cpp81
-rw-r--r--scene/3d/multimesh_instance.h64
-rw-r--r--scene/3d/optimized_spatial_scene.cpp33
-rw-r--r--scene/3d/optimized_spatial_scene.h41
-rw-r--r--scene/3d/particles.cpp559
-rw-r--r--scene/3d/particles.h165
-rw-r--r--scene/3d/path.cpp388
-rw-r--r--scene/3d/path.h122
-rw-r--r--scene/3d/physics_body.cpp741
-rw-r--r--scene/3d/physics_body.h219
-rw-r--r--scene/3d/physics_joint.cpp327
-rw-r--r--scene/3d/physics_joint.h104
-rw-r--r--scene/3d/portal.cpp281
-rw-r--r--scene/3d/portal.h93
-rw-r--r--scene/3d/position_3d.cpp66
-rw-r--r--scene/3d/position_3d.h45
-rw-r--r--scene/3d/proximity_group.cpp198
-rw-r--r--scene/3d/proximity_group.h81
-rw-r--r--scene/3d/quad.cpp232
-rw-r--r--scene/3d/quad.h76
-rw-r--r--scene/3d/ray_cast.cpp190
-rw-r--r--scene/3d/ray_cast.h72
-rw-r--r--scene/3d/room_instance.cpp299
-rw-r--r--scene/3d/room_instance.h102
-rw-r--r--scene/3d/scenario_fx.cpp74
-rw-r--r--scene/3d/scenario_fx.h58
-rw-r--r--scene/3d/skeleton.cpp536
-rw-r--r--scene/3d/skeleton.h144
-rw-r--r--scene/3d/spatial.cpp567
-rw-r--r--scene/3d/spatial.h166
-rw-r--r--scene/3d/spatial_indexer.cpp165
-rw-r--r--scene/3d/spatial_indexer.h83
-rw-r--r--scene/3d/spatial_player.cpp272
-rw-r--r--scene/3d/spatial_player.h87
-rw-r--r--scene/3d/spatial_sample_player.cpp229
-rw-r--r--scene/3d/spatial_sample_player.h89
-rw-r--r--scene/3d/spatial_stream_player.cpp190
-rw-r--r--scene/3d/spatial_stream_player.h72
-rw-r--r--scene/3d/test_cube.cpp53
-rw-r--r--scene/3d/test_cube.h57
-rw-r--r--scene/3d/visibility_notifier.cpp314
-rw-r--r--scene/3d/visibility_notifier.h106
-rw-r--r--scene/3d/visual_instance.cpp260
-rw-r--r--scene/3d/visual_instance.h126
69 files changed, 15239 insertions, 0 deletions
diff --git a/scene/3d/SCsub b/scene/3d/SCsub
new file mode 100644
index 0000000000..6789851aab
--- /dev/null
+++ b/scene/3d/SCsub
@@ -0,0 +1,14 @@
+Import('env')
+
+
+print("V: "+env["disable_3d"])
+if (env["disable_3d"]=="yes"):
+
+ env.scene_sources.append("3d/spatial.cpp")
+ env.scene_sources.append("3d/skeleton.cpp")
+else:
+ env.add_source_files(env.scene_sources,"*.cpp")
+
+Export('env')
+
+
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
new file mode 100644
index 0000000000..964a086cf6
--- /dev/null
+++ b/scene/3d/area.cpp
@@ -0,0 +1,317 @@
+/*************************************************************************/
+/* area.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.h"
+#include "scene/scene_string_names.h"
+#include "servers/physics_server.h"
+void Area::set_space_override_mode(SpaceOverride p_mode) {
+
+ space_override=p_mode;
+ PhysicsServer::get_singleton()->area_set_space_override_mode(get_rid(),PhysicsServer::AreaSpaceOverrideMode(p_mode));
+
+
+}
+Area::SpaceOverride Area::get_space_override_mode() const{
+
+ return space_override;
+}
+
+void Area::set_gravity_is_point(bool p_enabled){
+
+ gravity_is_point=p_enabled;
+ PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY_IS_POINT,p_enabled);
+
+}
+bool Area::is_gravity_a_point() const{
+
+ return gravity_is_point;
+}
+
+void Area::set_gravity_vector(const Vector3& p_vec){
+
+ gravity_vec=p_vec;
+ PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY_VECTOR,p_vec);
+
+}
+Vector3 Area::get_gravity_vector() const{
+
+ return gravity_vec;
+}
+
+void Area::set_gravity(real_t p_gravity){
+
+ gravity=p_gravity;
+ PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_GRAVITY,p_gravity);
+}
+real_t Area::get_gravity() const{
+
+ return gravity;
+}
+
+void Area::set_density(real_t p_density){
+
+ density=p_density;
+ PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_DENSITY,p_density);
+}
+real_t Area::get_density() const{
+
+ return density;
+}
+
+void Area::set_priority(real_t p_priority){
+
+ priority=p_priority;
+ PhysicsServer::get_singleton()->area_set_param(get_rid(),PhysicsServer::AREA_PARAM_PRIORITY,p_priority);
+}
+real_t Area::get_priority() const{
+
+ return priority;
+}
+
+
+void Area::_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 Area::_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 Area::_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==PhysicsServer::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 Area::_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 Area::_notification(int p_what) {
+
+ if (p_what==NOTIFICATION_EXIT_SCENE) {
+ _clear_monitoring();
+ }
+}
+
+void Area::set_enable_monitoring(bool p_enable) {
+
+ if (p_enable==monitoring)
+ return;
+
+ monitoring=p_enable;
+
+ if (monitoring) {
+
+ PhysicsServer::get_singleton()->area_set_monitor_callback(get_rid(),this,"_body_inout");
+ } else {
+ PhysicsServer::get_singleton()->area_set_monitor_callback(get_rid(),NULL,StringName());
+ _clear_monitoring();
+ }
+}
+
+bool Area::is_monitoring_enabled() const {
+
+ return monitoring;
+}
+
+
+void Area::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_body_enter_scene","id"),&Area::_body_enter_scene);
+ ObjectTypeDB::bind_method(_MD("_body_exit_scene","id"),&Area::_body_exit_scene);
+
+ ObjectTypeDB::bind_method(_MD("set_space_override_mode","enable"),&Area::set_space_override_mode);
+ ObjectTypeDB::bind_method(_MD("get_space_override_mode"),&Area::get_space_override_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity_is_point","enable"),&Area::set_gravity_is_point);
+ ObjectTypeDB::bind_method(_MD("is_gravity_a_point"),&Area::is_gravity_a_point);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity_vector","vector"),&Area::set_gravity_vector);
+ ObjectTypeDB::bind_method(_MD("get_gravity_vector"),&Area::get_gravity_vector);
+
+ ObjectTypeDB::bind_method(_MD("set_gravity","gravity"),&Area::set_gravity);
+ ObjectTypeDB::bind_method(_MD("get_gravity"),&Area::get_gravity);
+
+ ObjectTypeDB::bind_method(_MD("set_density","density"),&Area::set_density);
+ ObjectTypeDB::bind_method(_MD("get_density"),&Area::get_density);
+
+ ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area::set_priority);
+ ObjectTypeDB::bind_method(_MD("get_priority"),&Area::get_priority);
+
+ ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area::set_enable_monitoring);
+ ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area::is_monitoring_enabled);
+
+ ObjectTypeDB::bind_method(_MD("_body_inout"),&Area::_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_PROPERTY( 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::VECTOR3,"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_PROPERTY( 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"));
+
+}
+
+Area::Area() : CollisionObject(PhysicsServer::get_singleton()->area_create(),true) {
+
+ space_override=SPACE_OVERRIDE_DISABLED;
+ set_gravity(9.8);;
+ set_gravity_vector(Vector3(0,-1,0));
+ gravity_is_point=false;
+ density=0.1;
+ priority=0;
+ monitoring=false;
+
+}
+
+Area::~Area() {
+
+
+}
diff --git a/scene/3d/area.h b/scene/3d/area.h
new file mode 100644
index 0000000000..79e98f9dab
--- /dev/null
+++ b/scene/3d/area.h
@@ -0,0 +1,121 @@
+/*************************************************************************/
+/* area.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_H
+#define AREA_H
+
+#include "scene/3d/collision_object.h"
+#include "vset.h"
+
+class Area : public CollisionObject {
+
+ OBJ_TYPE( Area, CollisionObject );
+public:
+
+ enum SpaceOverride {
+ SPACE_OVERRIDE_DISABLED,
+ SPACE_OVERRIDE_COMBINE,
+ SPACE_OVERRIDE_REPLACE
+ };
+private:
+
+
+ SpaceOverride space_override;
+ Vector3 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 Vector3& p_vec);
+ Vector3 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;
+
+
+ Area();
+ ~Area();
+};
+
+VARIANT_ENUM_CAST(Area::SpaceOverride);
+
+#endif // AREA__H
diff --git a/scene/3d/body_shape.cpp b/scene/3d/body_shape.cpp
new file mode 100644
index 0000000000..b291ce7c72
--- /dev/null
+++ b/scene/3d/body_shape.cpp
@@ -0,0 +1,829 @@
+/*************************************************************************/
+/* body_shape.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 "body_shape.h"
+#include "servers/visual_server.h"
+#include "scene/resources/sphere_shape.h"
+#include "scene/resources/ray_shape.h"
+#include "scene/resources/box_shape.h"
+#include "scene/resources/capsule_shape.h"
+//#include "scene/resources/cylinder_shape.h"
+#include "scene/resources/convex_polygon_shape.h"
+#include "scene/resources/concave_polygon_shape.h"
+#include "scene/resources/height_map_shape.h"
+#include "scene/resources/plane_shape.h"
+#include "mesh_instance.h"
+#include "physics_body.h"
+#include "quick_hull.h"
+
+void CollisionShape::_update_body() {
+
+
+
+ if (get_parent() && get_parent()->cast_to<CollisionObject>())
+ get_parent()->cast_to<CollisionObject>()->_update_shapes_from_children();
+
+}
+
+void CollisionShape::make_convex_from_brothers() {
+
+ Node *p = get_parent();
+ if (!p)
+ return;
+
+ for(int i=0;i<p->get_child_count();i++) {
+
+ Node *n = p->get_child(i);
+ if (n->cast_to<MeshInstance>()) {
+
+ MeshInstance *mi=n->cast_to<MeshInstance>();
+ Ref<Mesh> m = mi->get_mesh();
+ if (m.is_valid()) {
+
+ Ref<Shape> s = m->create_convex_shape();
+ set_shape(s);
+ }
+ }
+ }
+
+}
+
+
+void CollisionShape::_update_indicator() {
+
+ while (VisualServer::get_singleton()->mesh_get_surface_count(indicator))
+ VisualServer::get_singleton()->mesh_remove_surface(indicator,0);
+
+ if (shape.is_null())
+ return;
+
+ DVector<Vector3> points;
+ DVector<Vector3> normals;
+
+ VS::PrimitiveType pt = VS::PRIMITIVE_TRIANGLES;
+
+ if (shape->cast_to<RayShape>()) {
+
+ RayShape *rs = shape->cast_to<RayShape>();
+ points.push_back(Vector3());
+ points.push_back(Vector3(0,0,rs->get_length()));
+ pt = VS::PRIMITIVE_LINES;
+ } else if (shape->cast_to<SphereShape>()) {
+
+// VisualServer *vs=VisualServer::get_singleton();
+ SphereShape *shapeptr=shape->cast_to<SphereShape>();
+
+
+ Color col(0.4,1.0,1.0,0.5);
+
+ int lats=6;
+ int lons=12;
+ float size=shapeptr->get_radius();
+
+
+ for(int i = 1; i <= lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+ Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size;
+ Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size;
+ Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size;
+ Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size;
+
+ Vector<Vector3> line;
+ line.push_back(v1);
+ line.push_back(v2);
+ line.push_back(v3);
+ line.push_back(v4);
+
+
+ points.push_back(v1);
+ points.push_back(v2);
+ points.push_back(v3);
+
+ points.push_back(v1);
+ points.push_back(v3);
+ points.push_back(v4);
+
+ normals.push_back(v1.normalized());
+ normals.push_back(v2.normalized());
+ normals.push_back(v3.normalized());
+
+ normals.push_back(v1.normalized());
+ normals.push_back(v3.normalized());
+ normals.push_back(v4.normalized());
+
+ }
+ }
+ } else if (shape->cast_to<BoxShape>()) {
+
+ BoxShape *shapeptr=shape->cast_to<BoxShape>();
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ }
+ Vector3 normal;
+ normal[i%3]=(i>=3?-1:1);
+
+ for(int j=0;j<4;j++)
+ face_points[j]*=shapeptr->get_extents();
+
+ points.push_back(face_points[0]);
+ points.push_back(face_points[1]);
+ points.push_back(face_points[2]);
+
+ points.push_back(face_points[0]);
+ points.push_back(face_points[2]);
+ points.push_back(face_points[3]);
+
+ for(int n=0;n<6;n++)
+ normals.push_back(normal);
+
+ }
+
+ } else if (shape->cast_to<ConvexPolygonShape>()) {
+
+ ConvexPolygonShape *shapeptr=shape->cast_to<ConvexPolygonShape>();
+
+ Geometry::MeshData md;
+ QuickHull::build(Variant(shapeptr->get_points()),md);
+
+ for(int i=0;i<md.faces.size();i++) {
+
+ for(int j=2;j<md.faces[i].indices.size();j++) {
+ points.push_back(md.vertices[md.faces[i].indices[0]]);
+ points.push_back(md.vertices[md.faces[i].indices[j-1]]);
+ points.push_back(md.vertices[md.faces[i].indices[j]]);
+ normals.push_back(md.faces[i].plane.normal);
+ normals.push_back(md.faces[i].plane.normal);
+ normals.push_back(md.faces[i].plane.normal);
+ }
+ }
+ } else if (shape->cast_to<ConcavePolygonShape>()) {
+
+ ConcavePolygonShape *shapeptr=shape->cast_to<ConcavePolygonShape>();
+
+ points = shapeptr->get_faces();
+ for(int i=0;i<points.size()/3;i++) {
+
+ Vector3 n = Plane( points[i*3+0],points[i*3+1],points[i*3+2] ).normal;
+ normals.push_back(n);
+ normals.push_back(n);
+ normals.push_back(n);
+ }
+
+ } else if (shape->cast_to<CapsuleShape>()) {
+
+ CapsuleShape *shapeptr=shape->cast_to<CapsuleShape>();
+
+ DVector<Plane> planes = Geometry::build_capsule_planes(shapeptr->get_radius(), shapeptr->get_height()/2.0, 12, Vector3::AXIS_Z);
+ Geometry::MeshData md = Geometry::build_convex_mesh(planes);
+
+ for(int i=0;i<md.faces.size();i++) {
+
+ for(int j=2;j<md.faces[i].indices.size();j++) {
+ points.push_back(md.vertices[md.faces[i].indices[0]]);
+ points.push_back(md.vertices[md.faces[i].indices[j-1]]);
+ points.push_back(md.vertices[md.faces[i].indices[j]]);
+ normals.push_back(md.faces[i].plane.normal);
+ normals.push_back(md.faces[i].plane.normal);
+ normals.push_back(md.faces[i].plane.normal);
+
+ }
+ }
+
+ } else if (shape->cast_to<PlaneShape>()) {
+
+ PlaneShape *shapeptr=shape->cast_to<PlaneShape>();
+
+ Plane p = shapeptr->get_plane();
+ Vector3 n1 = p.get_any_perpendicular_normal();
+ Vector3 n2 = p.normal.cross(n1).normalized();
+
+ Vector3 pface[4]={
+ p.normal*p.d+n1*100.0+n2*100.0,
+ p.normal*p.d+n1*100.0+n2*-100.0,
+ p.normal*p.d+n1*-100.0+n2*-100.0,
+ p.normal*p.d+n1*-100.0+n2*100.0,
+ };
+
+ points.push_back(pface[0]);
+ points.push_back(pface[1]);
+ points.push_back(pface[2]);
+
+ points.push_back(pface[0]);
+ points.push_back(pface[2]);
+ points.push_back(pface[3]);
+
+ normals.push_back(p.normal);
+ normals.push_back(p.normal);
+ normals.push_back(p.normal);
+ normals.push_back(p.normal);
+ normals.push_back(p.normal);
+ normals.push_back(p.normal);
+
+ }
+
+ if (!points.size())
+ return;
+ RID material = VisualServer::get_singleton()->fixed_material_create();
+ VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_DIFFUSE,Color(0,0.6,0.7,0.3));
+ VisualServer::get_singleton()->fixed_material_set_param(material,VS::FIXED_MATERIAL_PARAM_EMISSION,0.7);
+ if (normals.size()==0)
+ VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_UNSHADED,true);
+ VisualServer::get_singleton()->material_set_flag(material,VS::MATERIAL_FLAG_DOUBLE_SIDED,true);
+ Array d;
+ d.resize(VS::ARRAY_MAX);
+ d[VS::ARRAY_VERTEX]=points;
+ if (normals.size())
+ d[VS::ARRAY_NORMAL]=normals;
+ VisualServer::get_singleton()->mesh_add_surface(indicator,pt,d);
+ VisualServer::get_singleton()->mesh_surface_set_material(indicator,0,material,true);
+
+}
+
+
+void CollisionShape::_add_to_collision_object(Object* p_cshape) {
+
+ CollisionObject *co=p_cshape->cast_to<CollisionObject>();
+ ERR_FAIL_COND(!co);
+
+ if (shape.is_valid()) {
+
+ co->add_shape(shape,get_transform());
+ if (trigger)
+ co->set_shape_as_trigger( co->get_shape_count() -1, true );
+ }
+}
+
+void CollisionShape::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+ indicator_instance = VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario());
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform());
+ if (updating_body) {
+ _update_body();
+ }
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+ if (indicator_instance.is_valid()) {
+ VisualServer::get_singleton()->free(indicator_instance);
+ indicator_instance=RID();
+ }
+ } break;
+ case NOTIFICATION_UNPARENTED: {
+ if (updating_body)
+ _update_body();
+ } break;
+ case NOTIFICATION_PARENTED: {
+ if (updating_body)
+ _update_body();
+ } break;
+ }
+}
+
+
+void CollisionShape::resource_changed(RES res) {
+
+ update_gizmo();
+
+
+}
+
+void CollisionShape::_bind_methods() {
+
+ //not sure if this should do anything
+ ObjectTypeDB::bind_method(_MD("resource_changed"),&CollisionShape::resource_changed);
+ ObjectTypeDB::bind_method(_MD("set_shape","shape"),&CollisionShape::set_shape);
+ ObjectTypeDB::bind_method(_MD("get_shape"),&CollisionShape::get_shape);
+ ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionShape::_add_to_collision_object);
+ ObjectTypeDB::bind_method(_MD("set_trigger","enable"),&CollisionShape::set_trigger);
+ ObjectTypeDB::bind_method(_MD("is_trigger"),&CollisionShape::is_trigger);
+ ObjectTypeDB::bind_method(_MD("make_convex_from_brothers"),&CollisionShape::make_convex_from_brothers);
+ ObjectTypeDB::set_method_flags("CollisionShape","make_convex_from_brothers",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR);
+
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), _SCS("set_shape"), _SCS("get_shape"));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"trigger"),_SCS("set_trigger"),_SCS("is_trigger"));
+}
+
+
+void CollisionShape::set_shape(const Ref<Shape> &p_shape) {
+
+ if (!shape.is_null())
+ shape->unregister_owner(this);
+ shape=p_shape;
+ if (!shape.is_null())
+ shape->register_owner(this);
+ update_gizmo();
+ if (updating_body)
+ _update_body();
+}
+
+Ref<Shape> CollisionShape::get_shape() const {
+
+ return shape;
+}
+
+
+void CollisionShape::set_updating_body(bool p_update) {
+ updating_body=p_update;
+}
+
+bool CollisionShape::is_updating_body() const {
+
+ return updating_body;
+}
+
+void CollisionShape::set_trigger(bool p_trigger) {
+
+ trigger=p_trigger;
+ if (updating_body)
+ _update_body();
+}
+
+bool CollisionShape::is_trigger() const{
+
+ return trigger;
+}
+
+CollisionShape::CollisionShape() {
+
+ indicator = VisualServer::get_singleton()->mesh_create();
+ updating_body=true;
+ trigger=false;
+}
+
+CollisionShape::~CollisionShape() {
+ if (!shape.is_null())
+ shape->unregister_owner(this);
+ VisualServer::get_singleton()->free(indicator);
+}
+
+#if 0
+#include "body_volume.h"
+
+#include "scene/3d/physics_body.h"
+#include "geometry.h"
+
+#define ADD_TRIANGLE( m_a, m_b, m_c, m_color)\
+{\
+ Vector<Vector3> points;\
+ points.resize(3);\
+ points[0]=m_a;\
+ points[1]=m_b;\
+ points[2]=m_c;\
+ Vector<Color> colors;\
+ colors.resize(3);\
+ colors[0]=m_color;\
+ colors[1]=m_color;\
+ colors[2]=m_color;\
+ vs->poly_add_primitive(p_indicator,points,Vector<Vector3>(),colors,Vector<Vector3>());\
+}
+
+
+void CollisionShape::_notification(int p_what) {
+
+ switch (p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+
+ if (get_root_node()->get_editor() && !indicator.is_valid()) {
+
+ indicator=VisualServer::get_singleton()->poly_create();
+ RID mat=VisualServer::get_singleton()->fixed_material_create();
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_WIREFRAME, true );
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED, true );
+ VisualServer::get_singleton()->material_set_line_width( mat, 3 );
+
+ VisualServer::get_singleton()->poly_set_material(indicator,mat,true);
+
+ update_indicator(indicator);
+ }
+
+ if (indicator.is_valid()) {
+
+ indicator_instance=VisualServer::get_singleton()->instance_create2(indicator,get_world()->get_scenario());
+ VisualServer::get_singleton()->instance_attach_object_instance_ID(indicator_instance,get_instance_ID());
+ }
+ volume_changed();
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (indicator_instance.is_valid()) {
+
+ VisualServer::get_singleton()->free(indicator_instance);
+ }
+ volume_changed();
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (indicator_instance.is_valid()) {
+
+ VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform());
+ }
+ volume_changed();
+ } break;
+ default: {}
+ }
+}
+
+void CollisionShape::volume_changed() {
+
+ if (indicator.is_valid())
+ update_indicator(indicator);
+
+ Object *parent=get_parent();
+ if (!parent)
+ return;
+ PhysicsBody *physics_body=parent->cast_to<PhysicsBody>();
+
+ ERR_EXPLAIN("CollisionShape parent is not of type PhysicsBody");
+ ERR_FAIL_COND(!physics_body);
+
+ physics_body->recompute_child_volumes();
+
+}
+
+RID CollisionShape::_get_visual_instance_rid() const {
+
+ return indicator_instance;
+
+}
+
+void CollisionShape::_bind_methods() {
+
+ ObjectTypeDB::bind_method("_get_visual_instance_rid",&CollisionShape::_get_visual_instance_rid);
+}
+
+CollisionShape::CollisionShape() {
+
+}
+
+CollisionShape::~CollisionShape() {
+
+ if (indicator.is_valid()) {
+
+ VisualServer::get_singleton()->free(indicator);
+ }
+
+}
+
+void CollisionShapeSphere::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="radius") {
+ radius=p_value;
+ volume_changed();
+ }
+
+}
+
+Variant CollisionShapeSphere::_get(const String& p_name) const {
+
+ if (p_name=="radius") {
+ return radius;
+ }
+
+ return Variant();
+}
+
+void CollisionShapeSphere::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
+}
+
+void CollisionShapeSphere::update_indicator(RID p_indicator) {
+
+ VisualServer *vs=VisualServer::get_singleton();
+
+ vs->poly_clear(p_indicator);
+ Color col(0.4,1.0,1.0,0.5);
+
+ int lats=6;
+ int lons=12;
+ float size=radius;
+
+ for(int i = 1; i <= lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+ Vector3 v4=Vector3(x0 * zr0, z0, y0 *zr0)*size;
+ Vector3 v3=Vector3(x0 * zr1, z1, y0 *zr1)*size;
+ Vector3 v2=Vector3(x1 * zr1, z1, y1 *zr1)*size;
+ Vector3 v1=Vector3(x1 * zr0, z0, y1 *zr0)*size;
+
+ Vector<Vector3> line;
+ line.push_back(v1);
+ line.push_back(v2);
+ line.push_back(v3);
+ line.push_back(v4);
+
+ Vector<Color> cols;
+ cols.push_back(col);
+ cols.push_back(col);
+ cols.push_back(col);
+ cols.push_back(col);
+
+
+ VisualServer::get_singleton()->poly_add_primitive(p_indicator,line,Vector<Vector3>(),cols,Vector<Vector3>());
+ }
+ }
+}
+
+void CollisionShapeSphere::append_to_volume(Ref<Shape> p_volume) {
+
+ p_volume->add_sphere_shape(radius,get_transform());
+}
+
+
+CollisionShapeSphere::CollisionShapeSphere() {
+
+ radius=1.0;
+}
+
+/* BOX */
+
+
+void CollisionShapeBox::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="half_extents") {
+ half_extents=p_value;
+ volume_changed();
+ }
+
+}
+
+Variant CollisionShapeBox::_get(const String& p_name) const {
+
+ if (p_name=="half_extents") {
+ return half_extents;
+ }
+
+ return Variant();
+}
+
+void CollisionShapeBox::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo(Variant::VECTOR3,"half_extents" ) );
+}
+
+
+void CollisionShapeBox::update_indicator(RID p_indicator) {
+
+ VisualServer *vs=VisualServer::get_singleton();
+
+ vs->poly_clear(p_indicator);
+ Color col(0.4,1.0,1.0,0.5);
+
+
+ for (int i=0;i<6;i++) {
+
+
+ Vector3 face_points[4];
+
+ for (int j=0;j<4;j++) {
+
+ float v[3];
+ v[0]=1.0;
+ v[1]=1-2*((j>>1)&1);
+ v[2]=v[1]*(1-2*(j&1));
+
+ for (int k=0;k<3;k++) {
+
+ if (i<3)
+ face_points[j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ else
+ face_points[3-j][(i+k)%3]=v[k]*(i>=3?-1:1);
+ }
+ }
+
+ for(int j=0;j<4;j++)
+ face_points[i]*=half_extents;
+
+ ADD_TRIANGLE(face_points[0],face_points[1],face_points[2],col);
+ ADD_TRIANGLE(face_points[2],face_points[3],face_points[0],col);
+
+ }
+}
+
+void CollisionShapeBox::append_to_volume(Ref<Shape> p_volume) {
+
+ p_volume->add_box_shape(half_extents,get_transform());
+}
+
+
+CollisionShapeBox::CollisionShapeBox() {
+
+ half_extents=Vector3(1,1,1);
+}
+
+/* CYLINDER */
+
+
+void CollisionShapeCylinder::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="radius") {
+ radius=p_value;
+ volume_changed();
+ }
+ if (p_name=="height") {
+ height=p_value;
+ volume_changed();
+ }
+
+}
+
+Variant CollisionShapeCylinder::_get(const String& p_name) const {
+
+ if (p_name=="radius") {
+ return radius;
+ }
+ if (p_name=="height") {
+ return height;
+ }
+ return Variant();
+}
+
+void CollisionShapeCylinder::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
+ p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
+}
+
+
+void CollisionShapeCylinder::update_indicator(RID p_indicator) {
+
+ VisualServer *vs=VisualServer::get_singleton();
+
+ vs->poly_clear(p_indicator);
+ Color col(0.4,1.0,1.0,0.5);
+
+ DVector<Plane> planes = Geometry::build_cylinder_planes(radius, height, 12, Vector3::AXIS_Z);
+ Geometry::MeshData md = Geometry::build_convex_mesh(planes);
+
+ for(int i=0;i<md.faces.size();i++) {
+
+ for(int j=2;j<md.faces[i].indices.size();j++) {
+ ADD_TRIANGLE(md.vertices[md.faces[i].indices[0]],md.vertices[md.faces[i].indices[j-1]],md.vertices[md.faces[i].indices[j]],col);
+ }
+ }
+
+}
+
+void CollisionShapeCylinder::append_to_volume(Ref<Shape> p_volume) {
+
+ p_volume->add_cylinder_shape(radius,height*2.0,get_transform());
+}
+
+
+CollisionShapeCylinder::CollisionShapeCylinder() {
+
+ height=1;
+ radius=1;
+}
+
+/* CAPSULE */
+
+
+void CollisionShapeCapsule::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="radius") {
+ radius=p_value;
+ volume_changed();
+ }
+
+ if (p_name=="height") {
+ height=p_value;
+ volume_changed();
+ }
+
+}
+
+Variant CollisionShapeCapsule::_get(const String& p_name) const {
+
+ if (p_name=="radius") {
+ return radius;
+ }
+ if (p_name=="height") {
+ return height;
+ }
+ return Variant();
+}
+
+void CollisionShapeCapsule::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo(Variant::REAL,"radius",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
+ p_list->push_back( PropertyInfo(Variant::REAL,"height",PROPERTY_HINT_RANGE,"0.01,16384,0.01") );
+}
+
+
+void CollisionShapeCapsule::update_indicator(RID p_indicator) {
+
+ VisualServer *vs=VisualServer::get_singleton();
+
+ vs->poly_clear(p_indicator);
+ Color col(0.4,1.0,1.0,0.5);
+
+ DVector<Plane> planes = Geometry::build_capsule_planes(radius, height, 12, 3, Vector3::AXIS_Z);
+ Geometry::MeshData md = Geometry::build_convex_mesh(planes);
+
+ for(int i=0;i<md.faces.size();i++) {
+
+ for(int j=2;j<md.faces[i].indices.size();j++) {
+ ADD_TRIANGLE(md.vertices[md.faces[i].indices[0]],md.vertices[md.faces[i].indices[j-1]],md.vertices[md.faces[i].indices[j]],col);
+ }
+ }
+
+}
+
+void CollisionShapeCapsule::append_to_volume(Ref<Shape> p_volume) {
+
+
+ p_volume->add_capsule_shape(radius,height,get_transform());
+}
+
+
+CollisionShapeCapsule::CollisionShapeCapsule() {
+
+ height=1;
+ radius=1;
+}
+#endif
diff --git a/scene/3d/body_shape.h b/scene/3d/body_shape.h
new file mode 100644
index 0000000000..d1cb229f70
--- /dev/null
+++ b/scene/3d/body_shape.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* body_shape.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_H
+#define COLLISION_SHAPE_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/shape.h"
+
+class CollisionShape : public Spatial {
+
+ OBJ_TYPE( CollisionShape, Spatial );
+ OBJ_CATEGORY("3D Physics Nodes");
+
+ RID _get_visual_instance_rid() const;
+
+ Ref<Shape> shape;
+
+ void _update_indicator();
+
+ RID material;
+ RID indicator;
+ RID indicator_instance;
+
+ void resource_changed(RES res);
+
+ bool updating_body;
+ bool trigger;
+
+ void _update_body();
+ void _add_to_collision_object(Object* p_cshape);
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void make_convex_from_brothers();
+
+ void set_shape(const Ref<Shape> &p_shape);
+ Ref<Shape> get_shape() const;
+
+ void set_updating_body(bool p_update);
+ bool is_updating_body() const;
+
+ void set_trigger(bool p_trigger);
+ bool is_trigger() const;
+
+ CollisionShape();
+ ~CollisionShape();
+};
+
+#endif // BODY_VOLUME_H
+
diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp
new file mode 100644
index 0000000000..cbc4abb7a9
--- /dev/null
+++ b/scene/3d/bone_attachment.cpp
@@ -0,0 +1,138 @@
+/*************************************************************************/
+/* bone_attachment.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 "bone_attachment.h"
+
+bool BoneAttachment::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (String(p_name)=="bone_name") {
+
+ r_ret=get_bone_name();
+ return true;
+ }
+
+ return false;
+}
+bool BoneAttachment::_set(const StringName& p_name, const Variant& p_value){
+
+ if (String(p_name)=="bone_name") {
+
+ set_bone_name(p_value);
+ return true;
+ }
+
+ return false;
+}
+void BoneAttachment::_get_property_list( List<PropertyInfo>* p_list ) const{
+
+ Skeleton *parent=NULL;
+ if(get_parent())
+ parent=get_parent()->cast_to<Skeleton>();
+
+ if (parent) {
+
+ String names;
+ for(int i=0;i<parent->get_bone_count();i++) {
+ if(i>0)
+ names+=",";
+ names+=parent->get_bone_name(i);
+ }
+
+ p_list->push_back(PropertyInfo(Variant::STRING,"bone_name",PROPERTY_HINT_ENUM,names));
+ } else {
+
+ p_list->push_back(PropertyInfo(Variant::STRING,"bone_name"));
+
+ }
+
+}
+
+
+void BoneAttachment::_check_bind() {
+
+ if (get_parent() && get_parent()->cast_to<Skeleton>()) {
+ Skeleton *sk = get_parent()->cast_to<Skeleton>();
+ int idx = sk->find_bone(bone_name);
+ if (idx!=-1) {
+ sk->bind_child_node_to_bone(idx,this);;
+ bound=true;
+ }
+ }
+}
+
+void BoneAttachment::_check_unbind() {
+
+ if (bound) {
+
+ if (get_parent() && get_parent()->cast_to<Skeleton>()) {
+ Skeleton *sk = get_parent()->cast_to<Skeleton>();
+ int idx = sk->find_bone(bone_name);
+ if (idx!=-1) {
+ sk->unbind_child_node_from_bone(idx,this);;
+ }
+ }
+ bound=false;
+ }
+}
+
+void BoneAttachment::set_bone_name(const String& p_name) {
+
+ if (is_inside_scene())
+ _check_unbind();
+
+ bone_name=p_name;
+
+ if (is_inside_scene())
+ _check_bind();
+}
+
+String BoneAttachment::get_bone_name() const{
+
+ return bone_name;
+}
+
+void BoneAttachment::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ _check_bind();
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ _check_unbind();
+ } break;
+ }
+}
+
+BoneAttachment::BoneAttachment()
+{
+ bound=false;
+
+}
diff --git a/scene/3d/bone_attachment.h b/scene/3d/bone_attachment.h
new file mode 100644
index 0000000000..5043b40fa8
--- /dev/null
+++ b/scene/3d/bone_attachment.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* bone_attachment.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 BONE_ATTACHMENT_H
+#define BONE_ATTACHMENT_H
+
+#include "scene/3d/skeleton.h"
+
+class BoneAttachment : public Spatial {
+
+ OBJ_TYPE(BoneAttachment,Spatial);
+
+ bool bound;
+ String bone_name;
+
+ void _check_bind();
+ void _check_unbind();
+protected:
+
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ bool _set(const StringName& p_name, const Variant& p_value);
+ void _get_property_list( List<PropertyInfo>* p_list ) const;
+ void _notification(int p_what);
+
+public:
+
+ void set_bone_name(const String& p_name);
+ String get_bone_name() const;
+
+ BoneAttachment();
+};
+
+#endif // BONE_ATTACHMENT_H
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
new file mode 100644
index 0000000000..ecdfc8a7f9
--- /dev/null
+++ b/scene/3d/camera.cpp
@@ -0,0 +1,727 @@
+/*************************************************************************/
+/* camera.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.h"
+
+#include "camera_matrix.h"
+#include "scene/resources/material.h"
+#include "scene/resources/surface_tool.h"
+
+
+void Camera::_update_audio_listener_state() {
+
+
+}
+
+void Camera::_request_camera_update() {
+
+ _update_camera();
+}
+
+void Camera::_update_camera_mode() {
+
+
+ force_change=true;
+ switch(mode) {
+ case PROJECTION_PERSPECTIVE: {
+
+
+ set_perspective(fov,near,far);
+
+ } break;
+ case PROJECTION_ORTHOGONAL: {
+ set_orthogonal(size,near,far);
+ } break;
+
+ }
+
+}
+
+bool Camera::_set(const StringName& p_name, const Variant& p_value) {
+
+ bool changed_all=false;
+ if (p_name=="projection") {
+
+ int proj = p_value;
+ if (proj==PROJECTION_PERSPECTIVE)
+ mode=PROJECTION_PERSPECTIVE;
+ if (proj==PROJECTION_ORTHOGONAL)
+ mode=PROJECTION_ORTHOGONAL;
+
+ changed_all=true;
+ } else if (p_name=="fov")
+ fov=p_value;
+ else if (p_name=="size")
+ size=p_value;
+ else if (p_name=="near")
+ near=p_value;
+ else if (p_name=="far")
+ far=p_value;
+ else if (p_name=="vaspect")
+ set_use_vertical_aspect(p_value);
+ else if (p_name=="current") {
+ if (p_value.operator bool()) {
+ make_current();
+ } else {
+ clear_current();
+ }
+ } else if (p_name=="visible_layers") {
+ set_visible_layers(p_value);
+ } else if (p_name=="environment") {
+ set_environment(p_value);
+ } else
+ return false;
+
+ _update_camera_mode();
+ if (changed_all)
+ _change_notify();
+ return true;
+
+}
+bool Camera::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (p_name=="projection") {
+ r_ret= mode;
+ } else if (p_name=="fov")
+ r_ret= fov;
+ else if (p_name=="size")
+ r_ret= size;
+ else if (p_name=="near")
+ r_ret= near;
+ else if (p_name=="far")
+ r_ret= far;
+ else if (p_name=="vaspect")
+ r_ret= vaspect;
+ else if (p_name=="current") {
+
+ if (is_inside_scene() && get_scene()->is_editor_hint()) {
+ r_ret=current;
+ } else {
+ r_ret=is_current();
+ }
+ } else if (p_name=="visible_layers") {
+ r_ret=get_visible_layers();
+ } else if (p_name=="environment") {
+ r_ret=get_environment();
+ } else
+ return false;
+
+ return true;
+}
+
+void Camera::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal") );
+
+ switch(mode) {
+
+ case PROJECTION_PERSPECTIVE: {
+
+ p_list->push_back( PropertyInfo( Variant::REAL, "fov" , PROPERTY_HINT_RANGE, "1,89,0.1") );
+
+ } break;
+ case PROJECTION_ORTHOGONAL: {
+
+ p_list->push_back( PropertyInfo( Variant::REAL, "size" , PROPERTY_HINT_RANGE, "1,16384,0.01" ) );
+ } break;
+
+ }
+
+ p_list->push_back( PropertyInfo( Variant::REAL, "near" , PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "far" , PROPERTY_HINT_EXP_RANGE, "0.01,4096.0,0.01") );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "vaspect") );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "current" ) );
+ p_list->push_back( PropertyInfo( Variant::INT, "visible_layers",PROPERTY_HINT_ALL_FLAGS ) );
+ p_list->push_back( PropertyInfo( Variant::OBJECT, "environment",PROPERTY_HINT_RESOURCE_TYPE,"Environment" ) );
+
+}
+
+void Camera::_update_camera() {
+
+ Transform tr = get_camera_transform();
+ VisualServer::get_singleton()->camera_set_transform( camera, tr );
+
+// here goes listener stuff
+// if (viewport_ptr && is_inside_scene() && is_current())
+// viewport_ptr->_camera_transform_changed_notify();
+
+ if (is_inside_scene() && is_current()) {
+ if (viewport_ptr) {
+ viewport_ptr->_camera_transform_changed_notify();
+ }
+ }
+
+ if (is_current() && get_world().is_valid()) {
+ get_world()->_update_camera(this);
+ }
+
+
+}
+
+void Camera::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ viewport_ptr=NULL;
+
+ { //find viewport stuff
+ Node *parent=get_parent();
+
+ while(parent) {
+
+ Viewport* viewport = parent->cast_to<Viewport>();
+
+ if (viewport) {
+ viewport_ptr=viewport;
+ break;
+ }
+ parent=parent->get_parent();
+ }
+
+ }
+
+ if (viewport_ptr)
+ viewport_ptr->cameras.insert(this);
+ if (current)
+ make_current();
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ _request_camera_update();
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ if (is_current()) {
+ clear_current();
+ current=true; //keep it true
+
+ } else {
+ current=false;
+ }
+ if (viewport_ptr)
+ viewport_ptr->cameras.erase(this);
+ viewport_ptr=NULL;
+
+ } break;
+ case NOTIFICATION_BECAME_CURRENT: {
+ if (get_world().is_valid()) {
+ get_world()->_register_camera(this);
+ }
+ } break;
+ case NOTIFICATION_LOST_CURRENT: {
+ if (get_world().is_valid()) {
+ get_world()->_remove_camera(this);
+ }
+ } break;
+
+
+ }
+
+}
+
+
+Transform Camera::get_camera_transform() const {
+
+ return get_global_transform();
+}
+
+void Camera::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) {
+
+ if (!force_change && fov==p_fovy_degrees && p_z_near==near && p_z_far==far && mode==PROJECTION_PERSPECTIVE)
+ return;
+
+ fov=p_fovy_degrees;
+ near=p_z_near;
+ far=p_z_far;
+ mode=PROJECTION_PERSPECTIVE;
+
+ VisualServer::get_singleton()->camera_set_perspective(camera,fov,near,far);
+ update_gizmo();
+ force_change=false;
+}
+void Camera::set_orthogonal(float p_size, float p_z_near, float p_z_far) {
+
+ if (!force_change && size==p_size && p_z_near==near && p_z_far==far && mode==PROJECTION_ORTHOGONAL)
+ return;
+
+ size = p_size;
+
+ near=p_z_near;
+ far=p_z_far;
+ mode=PROJECTION_ORTHOGONAL;
+ force_change=false;
+
+ VisualServer::get_singleton()->camera_set_orthogonal(camera,size,near,far);
+ update_gizmo();
+}
+
+RID Camera::get_camera() const {
+
+ return camera;
+};
+
+void Camera::make_current() {
+
+ current=true;
+
+ if (!is_inside_scene())
+ return;
+
+ if (viewport_ptr) {
+ viewport_ptr->_set_camera(this);
+ }
+
+ //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this);
+}
+
+void Camera::clear_current() {
+
+ current=false;
+ if (!is_inside_scene())
+ return;
+
+ if (viewport_ptr) {
+ if (viewport_ptr->get_camera()==this)
+ viewport_ptr->_set_camera(NULL);
+ }
+
+}
+
+bool Camera::is_current() const {
+
+ if (is_inside_scene()) {
+ if (viewport_ptr)
+ return viewport_ptr->get_camera()==this;
+ } else
+ return current;
+
+ return false;
+}
+
+
+bool Camera::_can_gizmo_scale() const {
+
+ return false;
+}
+
+
+RES Camera::_get_gizmo_geometry() const {
+
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.5,1.0,0.5) );
+ mat->set_line_width(4);
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat);
+
+ switch(mode) {
+
+ case PROJECTION_PERSPECTIVE: {
+
+
+
+ Vector3 side=Vector3( Math::sin(Math::deg2rad(fov)), 0, -Math::cos(Math::deg2rad(fov)) );
+ Vector3 nside=side;
+ nside.x=-nside.x;
+ Vector3 up=Vector3(0,side.x,0);
+
+
+#define ADD_TRIANGLE( m_a, m_b, m_c)\
+{\
+ surface_tool->add_vertex(m_a);\
+ surface_tool->add_vertex(m_b);\
+ surface_tool->add_vertex(m_b);\
+ surface_tool->add_vertex(m_c);\
+ surface_tool->add_vertex(m_c);\
+ surface_tool->add_vertex(m_a);\
+}
+
+ ADD_TRIANGLE( Vector3(), side+up, side-up );
+ ADD_TRIANGLE( Vector3(), nside+up, nside-up );
+ ADD_TRIANGLE( Vector3(), side+up, nside+up );
+ ADD_TRIANGLE( Vector3(), side-up, nside-up );
+
+ side.x*=0.25;
+ nside.x*=0.25;
+ Vector3 tup( 0, up.y*3/2,side.z);
+ ADD_TRIANGLE( tup, side+up, nside+up );
+
+ } break;
+ case PROJECTION_ORTHOGONAL: {
+
+#define ADD_QUAD( m_a, m_b, m_c, m_d)\
+{\
+ surface_tool->add_vertex(m_a);\
+ surface_tool->add_vertex(m_b);\
+ surface_tool->add_vertex(m_b);\
+ surface_tool->add_vertex(m_c);\
+ surface_tool->add_vertex(m_c);\
+ surface_tool->add_vertex(m_d);\
+ surface_tool->add_vertex(m_d);\
+ surface_tool->add_vertex(m_a);\
+}
+
+ float hsize=size*0.5;
+ Vector3 right(hsize,0,0);
+ Vector3 up(0,hsize,0);
+ Vector3 back(0,0,-1.0);
+ Vector3 front(0,0,0);
+
+ ADD_QUAD( -up-right,-up+right,up+right,up-right);
+ ADD_QUAD( -up-right+back,-up+right+back,up+right+back,up-right+back);
+ ADD_QUAD( up+right,up+right+back,up-right+back,up-right);
+ ADD_QUAD( -up+right,-up+right+back,-up-right+back,-up-right);
+
+ right.x*=0.25;
+ Vector3 tup( 0, up.y*3/2,back.z );
+ ADD_TRIANGLE( tup, right+up+back, -right+up+back );
+
+ } break;
+
+ }
+
+ return surface_tool->commit();
+
+}
+
+Vector3 Camera::project_ray_normal(const Point2& p_pos) const {
+
+ Vector3 ray = project_local_ray_normal(p_pos);
+ return get_camera_transform().basis.xform(ray).normalized();
+};
+
+Vector3 Camera::project_local_ray_normal(const Point2& p_pos) const {
+
+ if (!is_inside_scene()) {
+ ERR_EXPLAIN("Camera is not inside scene.");
+ ERR_FAIL_COND_V(!is_inside_scene(),Vector3());
+ }
+
+ Size2 viewport_size = viewport_ptr->get_visible_rect().size;
+
+ Vector3 ray;
+
+ if (mode==PROJECTION_ORTHOGONAL) {
+
+ ray=Vector3(0,0,-1);
+ } else {
+ CameraMatrix cm;
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+ float screen_w,screen_h;
+ cm.get_viewport_size(screen_w,screen_h);
+ ray=Vector3( ((p_pos.x/viewport_size.width)*2.0-1.0)*screen_w, ((1.0-(p_pos.y/viewport_size.height))*2.0-1.0)*screen_h,-near).normalized();
+ }
+
+
+ return ray;
+};
+
+
+Vector3 Camera::project_ray_origin(const Point2& p_pos) const {
+
+ if (!is_inside_scene()) {
+ ERR_EXPLAIN("Camera is not inside scene.");
+ ERR_FAIL_COND_V(!is_inside_scene(),Vector3());
+ }
+
+ Size2 viewport_size = viewport_ptr->get_visible_rect().size;
+
+
+ ERR_FAIL_COND_V( viewport_size.y == 0, Vector3() );
+// float aspect = viewport_size.x / viewport_size.y;
+
+ if (mode == PROJECTION_PERSPECTIVE) {
+
+ return get_camera_transform().origin;
+ } else {
+
+ Vector2 pos = p_pos / viewport_size;
+ float vsize,hsize;
+ if (vaspect) {
+ vsize = size/viewport_size.get_aspect();
+ hsize = size;
+ } else {
+ hsize = size*viewport_size.get_aspect();
+ vsize = size;
+
+ }
+
+ Vector3 ray;
+ ray.x = pos.x * (hsize) - hsize/2;
+ ray.y = (1.0 - pos.y) * (vsize) - vsize/2;
+ ray.z = -near;
+ ray = get_camera_transform().xform(ray);
+ return ray;
+ };
+};
+
+Point2 Camera::unproject_position(const Vector3& p_pos) const {
+
+ if (!is_inside_scene()) {
+ ERR_EXPLAIN("Camera is not inside scene.");
+ ERR_FAIL_COND_V(!is_inside_scene(),Vector2());
+ }
+
+ Size2 viewport_size = viewport_ptr->get_visible_rect().size;
+
+ CameraMatrix cm;
+
+
+ if (mode==PROJECTION_ORTHOGONAL)
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+ else
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+
+ Plane p(get_camera_transform().xform_inv(p_pos),1.0);
+
+ p=cm.xform4(p);
+ p.normal/=p.d;
+
+
+ Point2 res;
+ res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x;
+ res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y;
+
+ return res;
+
+}
+
+Vector3 Camera::project_position(const Point2& p_point) const {
+
+ if (!is_inside_scene()) {
+ ERR_EXPLAIN("Camera is not inside scene.");
+ ERR_FAIL_COND_V(!is_inside_scene(),Vector3());
+ }
+
+ Size2 viewport_size = viewport_ptr->get_visible_rect().size;
+
+ CameraMatrix cm;
+
+ if (mode==PROJECTION_ORTHOGONAL)
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+ else
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+
+ Size2 vp_size;
+ cm.get_viewport_size(vp_size.x,vp_size.y);
+
+ Vector2 point;
+ point.x = (p_point.x/viewport_size.x) * 2.0 - 1.0;
+ point.y = (p_point.y/viewport_size.y) * 2.0 - 1.0;
+ point*=vp_size;
+
+ Vector3 p(point.x,point.y,-near);
+
+
+ return get_camera_transform().xform(p);
+}
+
+/*
+void Camera::_camera_make_current(Node *p_camera) {
+
+
+ if (p_camera==this) {
+ VisualServer::get_singleton()->viewport_attach_camera(viewport_id,camera);
+ active=true;
+ } else {
+ if (active && p_camera==NULL) {
+ //detech camera because no one else will claim it
+ VisualServer::get_singleton()->viewport_attach_camera(viewport_id,RID());
+ }
+ active=false;
+ }
+}
+*/
+
+void Camera::set_environment(const Ref<Environment>& p_environment) {
+
+ environment=p_environment;
+ if (environment.is_valid())
+ VS::get_singleton()->camera_set_environment(camera,environment->get_rid());
+ else
+ VS::get_singleton()->camera_set_environment(camera,RID());
+}
+
+Ref<Environment> Camera::get_environment() const {
+
+ return environment;
+}
+
+
+
+void Camera::_bind_methods() {
+
+ ObjectTypeDB::bind_method( _MD("project_ray_normal","screen_point"), &Camera::project_ray_normal);
+ ObjectTypeDB::bind_method( _MD("project_local_ray_normal","screen_point"), &Camera::project_local_ray_normal);
+ ObjectTypeDB::bind_method( _MD("project_ray_origin","screen_point"), &Camera::project_ray_origin);
+ ObjectTypeDB::bind_method( _MD("unproject_position","world_point"), &Camera::unproject_position);
+ ObjectTypeDB::bind_method( _MD("project_position","screen_point"), &Camera::project_position);
+ ObjectTypeDB::bind_method( _MD("set_perspective","fov","z_near","z_far"),&Camera::set_perspective );
+ ObjectTypeDB::bind_method( _MD("set_orthogonal","size","z_near","z_far"),&Camera::set_orthogonal );
+ ObjectTypeDB::bind_method( _MD("make_current"),&Camera::make_current );
+ ObjectTypeDB::bind_method( _MD("clear_current"),&Camera::clear_current );
+ ObjectTypeDB::bind_method( _MD("is_current"),&Camera::is_current );
+ ObjectTypeDB::bind_method( _MD("get_camera_transform"),&Camera::get_camera_transform );
+ ObjectTypeDB::bind_method( _MD("get_fov"),&Camera::get_fov );
+ ObjectTypeDB::bind_method( _MD("get_size"),&Camera::get_size );
+ ObjectTypeDB::bind_method( _MD("get_zfar"),&Camera::get_zfar );
+ ObjectTypeDB::bind_method( _MD("get_znear"),&Camera::get_znear );
+ ObjectTypeDB::bind_method( _MD("get_projection"),&Camera::get_projection );
+ ObjectTypeDB::bind_method( _MD("set_visible_layers","mask"),&Camera::set_visible_layers );
+ ObjectTypeDB::bind_method( _MD("get_visible_layers"),&Camera::get_visible_layers );
+ ObjectTypeDB::bind_method( _MD("look_at","target","up"),&Camera::look_at );
+ ObjectTypeDB::bind_method( _MD("look_at_from_pos","pos","target","up"),&Camera::look_at_from_pos );
+ ObjectTypeDB::bind_method(_MD("set_environment","env:Environment"),&Camera::set_environment);
+ ObjectTypeDB::bind_method(_MD("get_environment:Environment"),&Camera::get_environment);
+ ObjectTypeDB::bind_method(_MD("set_use_vertical_aspect","enable"),&Camera::set_use_vertical_aspect);
+ ObjectTypeDB::bind_method(_MD("is_using_vertical_aspect"),&Camera::is_using_vertical_aspect);
+ //ObjectTypeDB::bind_method( _MD("_camera_make_current"),&Camera::_camera_make_current );
+
+ BIND_CONSTANT( PROJECTION_PERSPECTIVE );
+ BIND_CONSTANT( PROJECTION_ORTHOGONAL );
+}
+
+float Camera::get_fov() const {
+
+ return fov;
+}
+
+float Camera::get_size() const {
+
+ return size;
+}
+
+float Camera::get_znear() const {
+
+ return near;
+}
+
+float Camera::get_zfar() const {
+
+ return far;
+}
+
+
+Camera::Projection Camera::get_projection() const {
+
+ return mode;
+}
+
+void Camera::set_visible_layers(uint32_t p_layers) {
+
+ layers=p_layers;
+ VisualServer::get_singleton()->camera_set_visible_layers(camera,layers);
+}
+
+uint32_t Camera::get_visible_layers() const{
+
+ return layers;
+}
+
+
+Vector<Plane> Camera::get_frustum() const {
+
+ ERR_FAIL_COND_V(!is_inside_world(),Vector<Plane>());
+
+ Size2 viewport_size = viewport_ptr->get_visible_rect().size;
+ CameraMatrix cm;
+ if (mode==PROJECTION_PERSPECTIVE)
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+ else
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+
+ return cm.get_projection_planes(get_global_transform());
+
+}
+
+
+void Camera::set_use_vertical_aspect(bool p_enable) {
+
+ vaspect=p_enable;
+ VisualServer::get_singleton()->camera_set_use_vertical_aspect(camera,p_enable);
+}
+
+
+bool Camera::is_using_vertical_aspect() const{
+
+ return vaspect;
+}
+
+void Camera::look_at(const Vector3& p_target, const Vector3& p_up_normal) {
+
+ Transform lookat;
+ lookat.origin=get_global_transform().origin;
+ lookat=lookat.looking_at(p_target,p_up_normal);
+ set_global_transform(lookat);
+}
+
+void Camera::look_at_from_pos(const Vector3& p_pos,const Vector3& p_target, const Vector3& p_up_normal) {
+
+ Transform lookat;
+ lookat.origin=p_pos;
+ lookat=lookat.looking_at(p_target,p_up_normal);
+ set_global_transform(lookat);
+
+}
+
+
+Camera::Camera() {
+
+ camera = VisualServer::get_singleton()->camera_create();
+ size=1;
+ fov=0;
+ near=0;
+ far=0;
+ current=false;
+ viewport_ptr=NULL;
+ force_change=false;
+ mode=PROJECTION_PERSPECTIVE;
+ set_perspective(60.0,0.1,100.0);
+ vaspect=false;
+ layers=0xFFFFFFFF;
+ //active=false;
+}
+
+
+Camera::~Camera() {
+
+ VisualServer::get_singleton()->free(camera);
+
+}
+
+
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
new file mode 100644
index 0000000000..a8599497ac
--- /dev/null
+++ b/scene/3d/camera.h
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* camera.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_H
+#define CAMERA_H
+
+
+#include "scene/3d/spatial.h"
+#include "scene/main/viewport.h"
+#include "scene/resources/environment.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class Camera : public Spatial {
+
+ OBJ_TYPE( Camera, Spatial );
+public:
+ enum Projection {
+
+ PROJECTION_PERSPECTIVE,
+ PROJECTION_ORTHOGONAL
+ };
+
+private:
+
+ bool force_change;
+ bool current;
+
+ Projection mode;
+
+ float fov;
+ float size;
+ float near,far;
+ bool vaspect;
+
+ RID camera;
+ RID scenario_id;
+
+ uint32_t layers;
+
+ Viewport *viewport_ptr;
+ Ref<Environment> environment;
+
+ virtual bool _can_gizmo_scale() const;
+ virtual RES _get_gizmo_geometry() const;
+
+
+
+ //void _camera_make_current(Node *p_camera);
+friend class Viewport;
+ void _update_audio_listener_state();
+protected:
+
+ void _update_camera();
+ virtual void _request_camera_update();
+ void _update_camera_mode();
+
+ 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:
+
+ enum {
+
+ NOTIFICATION_BECAME_CURRENT=50,
+ NOTIFICATION_LOST_CURRENT=51
+ };
+
+ void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far);
+ void set_orthogonal(float p_size, float p_z_near, float p_z_far);
+
+ void make_current();
+ void clear_current();
+ bool is_current() const;
+
+ RID get_camera() const;
+
+ float get_fov() const;
+ float get_size() const;
+ float get_zfar() const;
+ float get_znear() const;
+ Projection get_projection() const;
+
+ virtual Transform get_camera_transform() const;
+
+ Vector3 project_ray_normal(const Point2& p_point) const;
+ Vector3 project_ray_origin(const Point2& p_point) const;
+ Vector3 project_local_ray_normal(const Point2& p_point) const;
+ Point2 unproject_position(const Vector3& p_pos) const;
+ Vector3 project_position(const Point2& p_point) const;
+
+ void set_visible_layers(uint32_t p_layers);
+ uint32_t get_visible_layers() const;
+
+ Vector<Plane> get_frustum() const;
+
+ void set_environment(const Ref<Environment>& p_environment);
+ Ref<Environment> get_environment() const;
+
+ void set_use_vertical_aspect(bool p_enable);
+ bool is_using_vertical_aspect() const;
+
+ void look_at(const Vector3& p_target, const Vector3& p_up_normal);
+ void look_at_from_pos(const Vector3& p_pos,const Vector3& p_target, const Vector3& p_up_normal);
+
+
+ Camera();
+ ~Camera();
+
+};
+
+
+VARIANT_ENUM_CAST( Camera::Projection );
+
+#endif
diff --git a/scene/3d/car_body.cpp b/scene/3d/car_body.cpp
new file mode 100644
index 0000000000..a21598b07c
--- /dev/null
+++ b/scene/3d/car_body.cpp
@@ -0,0 +1,741 @@
+/*************************************************************************/
+/* car_body.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 "car_body.h"
+
+#define DEG2RADMUL (Math_PI/180.0)
+#define RAD2DEGMUL (180.0/Math_PI)
+
+void CarWheel::_notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ if (!get_parent())
+ return;
+ CarBody *cb = get_parent()->cast_to<CarBody>();
+ if (!cb)
+ return;
+ body=cb;
+ local_xform=get_transform();
+ cb->wheels.push_back(this);
+ }
+ if (p_what==NOTIFICATION_EXIT_SCENE) {
+
+ if (!get_parent())
+ return;
+ CarBody *cb = get_parent()->cast_to<CarBody>();
+ if (!cb)
+ return;
+ cb->wheels.erase(this);
+ body=NULL;
+ }
+}
+
+void CarWheel::set_side_friction(real_t p_friction) {
+
+ side_friction=p_friction;
+}
+void CarWheel::set_forward_friction(real_t p_friction) {
+
+ forward_friction=p_friction;
+}
+void CarWheel::set_travel(real_t p_travel) {
+
+ travel=p_travel;
+ update_gizmo();
+
+}
+void CarWheel::set_radius(real_t p_radius) {
+
+ radius=p_radius;
+ update_gizmo();
+
+}
+void CarWheel::set_resting_frac(real_t p_frac) {
+
+ resting_frac=p_frac;
+}
+void CarWheel::set_damping_frac(real_t p_frac) {
+
+ damping_frac=p_frac;
+}
+void CarWheel::set_num_rays(real_t p_rays) {
+
+ num_rays=p_rays;
+}
+
+real_t CarWheel::get_side_friction() const{
+
+ return side_friction;
+}
+real_t CarWheel::get_forward_friction() const{
+
+ return forward_friction;
+}
+real_t CarWheel::get_travel() const{
+
+ return travel;
+}
+real_t CarWheel::get_radius() const{
+
+ return radius;
+}
+real_t CarWheel::get_resting_frac() const{
+
+ return resting_frac;
+}
+real_t CarWheel::get_damping_frac() const{
+
+ return damping_frac;
+}
+
+int CarWheel::get_num_rays() const{
+
+ return num_rays;
+}
+
+
+void CarWheel::update(real_t dt) {
+
+
+ if (dt <= 0.0f)
+ return;
+
+ float origAngVel = angVel;
+
+ if (locked)
+ {
+ angVel = 0;
+ torque = 0;
+ }
+ else
+ {
+
+ float wheelMass = 0.03f * body->mass;
+ float inertia = 0.5f * (radius * radius) * wheelMass;
+
+ angVel += torque * dt / inertia;
+ torque = 0;
+
+ // prevent friction from reversing dir - todo do this better
+ // by limiting the torque
+ if (((origAngVel > angVelForGrip) && (angVel < angVelForGrip)) ||
+ ((origAngVel < angVelForGrip) && (angVel > angVelForGrip)))
+ angVel = angVelForGrip;
+
+ angVel += driveTorque * dt / inertia;
+ driveTorque = 0;
+
+ float maxAngVel = 200;
+ print_line("angvel: "+rtos(angVel));
+ angVel = CLAMP(angVel, -maxAngVel, maxAngVel);
+
+ axisAngle += Math::rad2deg(dt * angVel);
+ }
+}
+
+bool CarWheel::add_forces(PhysicsDirectBodyState *s) {
+
+
+ Vector3 force;
+
+ PhysicsDirectSpaceState *space = s->get_space_state();
+
+ Transform world = s->get_transform() * local_xform;
+
+ // OpenGl has differnet row/column order for matrixes than XNA has ..
+ //Vector3 wheelFwd = world.get_basis().get_axis(Vector3::AXIS_Z);
+ //Vector3 wheelFwd = RotationMatrix(mSteerAngle, worldAxis) * carBody.GetOrientation().GetCol(0);
+ Vector3 wheelUp = world.get_basis().get_axis(Vector3::AXIS_Y);
+ Vector3 wheelFwd = Matrix3(wheelUp,Math::deg2rad(steerAngle)).xform( world.get_basis().get_axis(Vector3::AXIS_Z) );
+ Vector3 wheelLeft = -wheelUp.cross(wheelFwd).normalized();
+ Vector3 worldPos = world.origin;
+ Vector3 worldAxis = wheelUp;
+
+ // start of ray
+ float rayLen = 2.0f * radius + travel;
+ Vector3 wheelRayEnd = worldPos - radius * worldAxis;
+ Vector3 wheelRayBegin = wheelRayEnd + rayLen * worldAxis;
+ //wheelRayEnd = -rayLen * worldAxis;
+
+ //Assert(PhysicsSystem.CurrentPhysicsSystem);
+
+
+ ///Assert(collSystem);
+ ///
+ const int maxNumRays = 32;
+
+ int numRaysUse = MIN(num_rays, maxNumRays);
+
+ // adjust the start position of the ray - divide the wheel into numRays+2
+ // rays, but don't use the first/last.
+ float deltaFwd = (2.0f * radius) / (numRaysUse + 1);
+ float deltaFwdStart = deltaFwd;
+
+ float fracs[maxNumRays];
+ Vector3 segmentEnds[maxNumRays];
+ Vector3 groundPositions[maxNumRays];
+ Vector3 groundNormals[maxNumRays];
+
+
+ lastOnFloor = false;
+ int bestIRay = 0;
+ int iRay;
+
+
+ for (iRay = 0; iRay < numRaysUse; ++iRay)
+ {
+ fracs[iRay] = 1e20;
+ // work out the offset relative to the middle ray
+ float distFwd = (deltaFwdStart + iRay * deltaFwd) - radius;
+ float zOffset = radius * (1.0f - (float)Math::cos( Math::deg2rad( 90.0f * (distFwd / radius))));
+
+ segmentEnds[iRay] = wheelRayEnd + distFwd * wheelFwd + zOffset * wheelUp;
+
+
+ PhysicsDirectSpaceState::RayResult rr;
+
+ bool collided = space->intersect_ray(wheelRayBegin,segmentEnds[iRay],rr,body->exclude);
+
+
+ if (collided){
+ lastOnFloor = true;
+ groundPositions[iRay]=rr.position;
+ groundNormals[iRay]=rr.normal;
+ fracs[iRay] = ((wheelRayBegin-rr.position).length() / (wheelRayBegin-wheelRayEnd).length());
+ if (fracs[iRay] < fracs[bestIRay])
+ bestIRay = iRay;
+ }
+ }
+
+
+ if (!lastOnFloor)
+ return false;
+
+ //Assert(bestIRay < numRays);
+
+ // use the best one
+ Vector3 groundPos = groundPositions[bestIRay];
+ float frac = fracs[bestIRay];
+
+ // const Vector3 groundNormal = (worldPos - segments[bestIRay].GetEnd()).NormaliseSafe();
+ // const Vector3 groundNormal = groundNormals[bestIRay];
+
+
+ Vector3 groundNormal = worldAxis;
+
+ if (numRaysUse > 1)
+ {
+ for (iRay = 0; iRay < numRaysUse; ++iRay)
+ {
+ if (fracs[iRay] <= 1.0f)
+ {
+ groundNormal += (1.0f - fracs[iRay]) * (worldPos - segmentEnds[iRay]);
+ }
+ }
+
+ groundNormal.normalize();
+
+ }
+ else
+ {
+ groundNormal = groundNormals[bestIRay];
+ }
+
+
+
+ float spring = (body->mass/body->wheels.size()) * s->get_total_gravity().length() / (resting_frac * travel);
+
+ float displacement = rayLen * (1.0f - frac);
+ displacement = CLAMP(displacement, 0, travel);
+
+
+
+ float displacementForceMag = displacement * spring;
+
+ // reduce force when suspension is par to ground
+ displacementForceMag *= groundNormals[bestIRay].dot(worldAxis);
+
+ // apply damping
+ float damping = 2.0f * (float)Math::sqrt(spring * body->mass);
+ damping /= body->wheels.size(); // assume wheels act together
+ damping *= damping_frac; // a bit bouncy
+
+ float upSpeed = (displacement - lastDisplacement) / s->get_step();
+
+ float dampingForceMag = upSpeed * damping;
+
+ float totalForceMag = displacementForceMag + dampingForceMag;
+
+ if (totalForceMag < 0.0f) totalForceMag = 0.0f;
+
+ Vector3 extraForce = totalForceMag * worldAxis;
+
+
+ force += extraForce;
+ // side-slip friction and drive force. Work out wheel- and floor-relative coordinate frame
+ Vector3 groundUp = groundNormal;
+ Vector3 groundLeft = groundNormal.cross(wheelFwd).normalized();
+
+ Vector3 groundFwd = groundLeft.cross(groundUp);
+
+ Vector3 wheelPointVel = s->get_linear_velocity() +
+ (s->get_angular_velocity()).cross(s->get_transform().basis.xform(local_xform.origin));// * mPos);
+
+ Vector3 rimVel = -angVel * wheelLeft.cross(groundPos - worldPos);
+ wheelPointVel += rimVel;
+
+ // if sitting on another body then adjust for its velocity.
+ /*if (worldBody != null)
+ {
+ Vector3 worldVel = worldBody.Velocity +
+ Vector3.Cross(worldBody.AngularVelocity, groundPos - worldBody.Position);
+
+ wheelPointVel -= worldVel;
+ }*/
+
+ // sideways forces
+ float noslipVel = 0.2f;
+ float slipVel = 0.4f;
+ float slipFactor = 0.7f;
+
+ float smallVel = 3;
+ float friction = side_friction;
+
+ float sideVel = wheelPointVel.dot(groundLeft);
+
+ if ((sideVel > slipVel) || (sideVel < -slipVel))
+ friction *= slipFactor;
+ else
+ if ((sideVel > noslipVel) || (sideVel < -noslipVel))
+ friction *= 1.0f - (1.0f - slipFactor) * (Math::absf(sideVel) - noslipVel) / (slipVel - noslipVel);
+
+ if (sideVel < 0.0f)
+ friction *= -1.0f;
+
+ if (Math::absf(sideVel) < smallVel)
+ friction *= Math::absf(sideVel) / smallVel;
+
+ float sideForce = -friction * totalForceMag;
+
+ extraForce = sideForce * groundLeft;
+ force += extraForce;
+ // fwd/back forces
+ friction = forward_friction;
+ float fwdVel = wheelPointVel.dot(groundFwd);
+
+ if ((fwdVel > slipVel) || (fwdVel < -slipVel))
+ friction *= slipFactor;
+ else
+ if ((fwdVel > noslipVel) || (fwdVel < -noslipVel))
+ friction *= 1.0f - (1.0f - slipFactor) * (Math::absf(fwdVel) - noslipVel) / (slipVel - noslipVel);
+
+ if (fwdVel < 0.0f)
+ friction *= -1.0f;
+
+ if (Math::absf(fwdVel) < smallVel)
+ friction *= Math::absf(fwdVel) / smallVel;
+
+ float fwdForce = -friction * totalForceMag;
+
+ extraForce = fwdForce * groundFwd;
+ force += extraForce;
+
+
+ //if (!force.IsSensible())
+ //{
+ // TRACE_FILE_IF(ONCE_1)
+ // TRACE("Bad force in car wheel\n");
+ // return true;
+ //}
+
+ // fwd force also spins the wheel
+ Vector3 wheelCentreVel = s->get_linear_velocity() +
+ (s->get_angular_velocity()).cross(s->get_transform().basis.xform(local_xform.origin));
+
+ angVelForGrip = wheelCentreVel.dot(groundFwd) / radius;
+ torque += -fwdForce * radius;
+
+ // add force to car
+// carBody.AddWorldForce(force, groundPos);
+
+ s->add_force(force,(groundPos-s->get_transform().origin));
+
+ // add force to the world
+ /*
+ if (worldBody != null && !worldBody.Immovable)
+ {
+ // todo get the position in the right place...
+ // also limit the velocity that this force can produce by looking at the
+ // mass/inertia of the other object
+ float maxOtherBodyAcc = 500.0f;
+ float maxOtherBodyForce = maxOtherBodyAcc * worldBody.Mass;
+
+ if (force.LengthSquared() > (maxOtherBodyForce * maxOtherBodyForce))
+ force *= maxOtherBodyForce / force.Length();
+
+ worldBody.AddWorldForce(-force, groundPos);
+ }*/
+
+ Transform wheel_xf = local_xform;
+ wheel_xf.origin += wheelUp * displacement;
+ wheel_xf.basis = wheel_xf.basis * Matrix3(Vector3(0,1,0),Math::deg2rad(steerAngle));
+ //wheel_xf.basis = wheel_xf.basis * Matrix3(wheel_xf.basis[0],-Math::deg2rad(axisAngle));
+
+ set_transform(wheel_xf);
+ lastDisplacement=displacement;
+ return true;
+
+}
+
+void CarWheel::set_type_drive(bool p_enable) {
+
+ type_drive=p_enable;
+}
+
+bool CarWheel::is_type_drive() const {
+
+ return type_drive;
+}
+
+void CarWheel::set_type_steer(bool p_enable) {
+
+ type_steer=p_enable;
+}
+
+bool CarWheel::is_type_steer() const {
+
+ return type_steer;
+}
+
+
+void CarWheel::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_side_friction","friction"),&CarWheel::set_side_friction);
+ ObjectTypeDB::bind_method(_MD("set_forward_friction","friction"),&CarWheel::set_forward_friction);
+ ObjectTypeDB::bind_method(_MD("set_travel","distance"),&CarWheel::set_travel);
+ ObjectTypeDB::bind_method(_MD("set_radius","radius"),&CarWheel::set_radius);
+ ObjectTypeDB::bind_method(_MD("set_resting_frac","frac"),&CarWheel::set_resting_frac);
+ ObjectTypeDB::bind_method(_MD("set_damping_frac","frac"),&CarWheel::set_damping_frac);
+ ObjectTypeDB::bind_method(_MD("set_num_rays","amount"),&CarWheel::set_num_rays);
+
+ ObjectTypeDB::bind_method(_MD("get_side_friction"),&CarWheel::get_side_friction);
+ ObjectTypeDB::bind_method(_MD("get_forward_friction"),&CarWheel::get_forward_friction);
+ ObjectTypeDB::bind_method(_MD("get_travel"),&CarWheel::get_travel);
+ ObjectTypeDB::bind_method(_MD("get_radius"),&CarWheel::get_radius);
+ ObjectTypeDB::bind_method(_MD("get_resting_frac"),&CarWheel::get_resting_frac);
+ ObjectTypeDB::bind_method(_MD("get_damping_frac"),&CarWheel::get_damping_frac);
+ ObjectTypeDB::bind_method(_MD("get_num_rays"),&CarWheel::get_num_rays);
+
+ ObjectTypeDB::bind_method(_MD("set_type_drive","enable"),&CarWheel::set_type_drive);
+ ObjectTypeDB::bind_method(_MD("is_type_drive"),&CarWheel::is_type_drive);
+
+ ObjectTypeDB::bind_method(_MD("set_type_steer","enable"),&CarWheel::set_type_steer);
+ ObjectTypeDB::bind_method(_MD("is_type_steer"),&CarWheel::is_type_steer);
+
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"type/drive"),_SCS("set_type_drive"),_SCS("is_type_drive"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"type/steer"),_SCS("set_type_steer"),_SCS("is_type_steer"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/side_friction",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_side_friction"),_SCS("get_side_friction"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/forward_friction",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_forward_friction"),_SCS("get_forward_friction"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/travel",PROPERTY_HINT_RANGE,"0.01,1024,0.01"),_SCS("set_travel"),_SCS("get_travel"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/radius",PROPERTY_HINT_RANGE,"0.01,1024,0.01"),_SCS("set_radius"),_SCS("get_radius"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/resting_frac",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_resting_frac"),_SCS("get_resting_frac"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/damping_frac",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_damping_frac"),_SCS("get_damping_frac"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/num_rays",PROPERTY_HINT_RANGE,"1,32,1"),_SCS("set_num_rays"),_SCS("get_num_rays"));
+
+}
+
+CarWheel::CarWheel() {
+
+ side_friction=4.7;
+ forward_friction=5.0;
+ travel=0.2;
+ radius=0.4;
+ resting_frac=0.45;
+ damping_frac=0.3;
+ num_rays=1;
+
+ angVel = 0.0f;
+ steerAngle = 0.0f;
+ torque = 0.0f;
+ driveTorque = 0.0f;
+ axisAngle = 0.0f;
+ upSpeed = 0.0f;
+ locked = false;
+ lastDisplacement = 0.0f;
+ lastOnFloor = false;
+ angVelForGrip = 0.0f;
+ angVelForGrip=0;
+
+ type_drive=false;
+ type_steer=false;
+
+}
+
+///
+
+
+void CarBody::set_max_steer_angle(real_t p_angle) {
+
+ max_steer_angle=p_angle;
+}
+void CarBody::set_steer_rate(real_t p_rate) {
+
+ steer_rate=p_rate;
+}
+void CarBody::set_drive_torque(real_t p_torque) {
+
+ drive_torque=p_torque;
+}
+
+real_t CarBody::get_max_steer_angle() const{
+
+ return max_steer_angle;
+}
+real_t CarBody::get_steer_rate() const{
+
+ return steer_rate;
+}
+real_t CarBody::get_drive_torque() const{
+
+ return drive_torque;
+}
+
+
+void CarBody::set_target_steering(float p_steering) {
+
+ target_steering=p_steering;
+}
+
+void CarBody::set_target_accelerate(float p_accelerate) {
+ target_accelerate=p_accelerate;
+}
+
+void CarBody::set_hand_brake(float p_amont) {
+
+ hand_brake=p_amont;
+}
+
+real_t CarBody::get_target_steering() const {
+
+ return target_steering;
+}
+real_t CarBody::get_target_accelerate() const {
+
+ return target_accelerate;
+}
+real_t CarBody::get_hand_brake() const {
+
+ return hand_brake;
+}
+
+
+void CarBody::_direct_state_changed(Object *p_state) {
+
+ PhysicsDirectBodyState *state=p_state->cast_to<PhysicsDirectBodyState>();
+
+ float dt = state->get_step();
+ AABB aabb;
+ int drive_total=0;
+ for(int i=0;i<wheels.size();i++) {
+ CarWheel *w=wheels[i];
+ if (i==0) {
+ aabb.pos=w->local_xform.origin;
+ } else {
+ aabb.expand_to(w->local_xform.origin);
+ }
+ if (w->type_drive)
+ drive_total++;
+
+ }
+ // control inputs
+ float deltaAccelerate = dt * 4.0f;
+
+ float dAccelerate = target_accelerate - accelerate;
+ dAccelerate = CLAMP(dAccelerate, -deltaAccelerate, deltaAccelerate);
+ accelerate += dAccelerate;
+
+ float deltaSteering = dt * steer_rate;
+ float dSteering = target_steering - steering;
+ dSteering = CLAMP(dSteering, -deltaSteering, deltaSteering);
+ steering += dSteering;
+
+ // apply these inputs
+ float maxTorque = drive_torque;
+
+ float torque_div = drive_total/2;
+ if (torque_div>0)
+ maxTorque/=torque_div;
+
+
+ float alpha = ABS(max_steer_angle * steering);
+ float angleSgn = steering > 0.0f ? 1.0f : -1.0f;
+
+ int wheels_on_floor=0;
+
+ for(int i=0;i<wheels.size();i++) {
+
+ CarWheel *w=wheels[i];
+ if (w->type_drive)
+ w->driveTorque+=maxTorque * accelerate;
+ w->locked = !w->type_steer && (hand_brake > 0.5f);
+
+ if (w->type_steer) {
+ //steering
+
+ bool inner = (steering > 0 && w->local_xform.origin.x > 0) || (steering < 0 && w->local_xform.origin.x < 0);
+
+ if (inner || alpha==0.0) {
+
+ w->steerAngle = (angleSgn * alpha);
+ } else {
+ float dx = aabb.size.z;
+ float dy = aabb.size.x;
+
+ float beta = Math::atan2(dy, dx + (dy / (float)Math::tan(Math::deg2rad(alpha))));
+ beta = Math::rad2deg(beta);
+ w->steerAngle = (angleSgn * beta);
+
+ }
+ }
+
+ if (w->add_forces(state))
+ wheels_on_floor++;
+ w->update(dt);
+
+
+ }
+
+ print_line("onfloor: "+itos(wheels_on_floor));
+
+
+ set_ignore_transform_notification(true);
+ 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_ignore_transform_notification(false);
+
+
+}
+
+void CarBody::set_mass(real_t p_mass) {
+
+ mass=p_mass;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass);
+}
+
+real_t CarBody::get_mass() const{
+
+ return mass;
+}
+
+
+void CarBody::set_friction(real_t p_friction) {
+
+ friction=p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction);
+}
+
+real_t CarBody::get_friction() const{
+
+ return friction;
+}
+
+
+void CarBody::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_max_steer_angle","value"),&CarBody::set_max_steer_angle);
+ ObjectTypeDB::bind_method(_MD("set_steer_rate","rate"),&CarBody::set_steer_rate);
+ ObjectTypeDB::bind_method(_MD("set_drive_torque","value"),&CarBody::set_drive_torque);
+
+ ObjectTypeDB::bind_method(_MD("get_max_steer_angle"),&CarBody::get_max_steer_angle);
+ ObjectTypeDB::bind_method(_MD("get_steer_rate"),&CarBody::get_steer_rate);
+ ObjectTypeDB::bind_method(_MD("get_drive_torque"),&CarBody::get_drive_torque);
+
+ ObjectTypeDB::bind_method(_MD("set_target_steering","amount"),&CarBody::set_target_steering);
+ ObjectTypeDB::bind_method(_MD("set_target_accelerate","amount"),&CarBody::set_target_accelerate);
+ ObjectTypeDB::bind_method(_MD("set_hand_brake","amount"),&CarBody::set_hand_brake);
+
+ ObjectTypeDB::bind_method(_MD("get_target_steering"),&CarBody::get_target_steering);
+ ObjectTypeDB::bind_method(_MD("get_target_accelerate"),&CarBody::get_target_accelerate);
+ ObjectTypeDB::bind_method(_MD("get_hand_brake"),&CarBody::get_hand_brake);
+
+ ObjectTypeDB::bind_method(_MD("set_mass","mass"),&CarBody::set_mass);
+ ObjectTypeDB::bind_method(_MD("get_mass"),&CarBody::get_mass);
+
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&CarBody::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&CarBody::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&CarBody::_direct_state_changed);
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/mass",PROPERTY_HINT_RANGE,"0.01,65536,0.01"),_SCS("set_mass"),_SCS("get_mass"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"body/friction",PROPERTY_HINT_RANGE,"0.01,1,0.01"),_SCS("set_friction"),_SCS("get_friction"));
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/max_steer_angle",PROPERTY_HINT_RANGE,"1,90,1"),_SCS("set_max_steer_angle"),_SCS("get_max_steer_angle"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/drive_torque",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_drive_torque"),_SCS("get_drive_torque"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"config/steer_rate",PROPERTY_HINT_RANGE,"0.01,64,0.01"),_SCS("set_steer_rate"),_SCS("get_steer_rate"));
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/target_steering",PROPERTY_HINT_RANGE,"-1,1,0.01"),_SCS("set_target_steering"),_SCS("get_target_steering"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/target_accelerate",PROPERTY_HINT_RANGE,"-1,1,0.01"),_SCS("set_target_accelerate"),_SCS("get_target_accelerate"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"drive/hand_brake",PROPERTY_HINT_RANGE,"0,1,0.01"),_SCS("set_hand_brake"),_SCS("get_hand_brake"));
+
+}
+
+CarBody::CarBody() : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
+
+ forward_drive=true;
+ backward_drive=true;
+ max_steer_angle=30;
+ steer_rate=1;
+ drive_torque=520;
+
+ target_steering=0;
+ target_accelerate=0;
+ hand_brake=0;
+
+ steering=0;
+ accelerate=0;
+
+ mass=1;
+ friction=1;
+
+ ccd=false;
+// can_sleep=true;
+
+
+
+
+ exclude.insert(get_rid());
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed");
+
+
+}
diff --git a/scene/3d/car_body.h b/scene/3d/car_body.h
new file mode 100644
index 0000000000..87eb047bcf
--- /dev/null
+++ b/scene/3d/car_body.h
@@ -0,0 +1,170 @@
+/*************************************************************************/
+/* car_body.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 CAR_BODY_H
+#define CAR_BODY_H
+
+#include "scene/3d/physics_body.h"
+
+
+class CarBody;
+
+class CarWheel : public Spatial {
+
+ OBJ_TYPE(CarWheel,Spatial);
+
+friend class CarBody;
+ real_t side_friction;
+ real_t forward_friction;
+ real_t travel;
+ real_t radius;
+ real_t resting_frac;
+ real_t damping_frac;
+ int num_rays;
+ Transform local_xform;
+
+ CarBody *body;
+
+ float angVel;
+ float steerAngle;
+ float torque;
+ float driveTorque;
+ float axisAngle;
+ float upSpeed; // speed relative to the car
+ bool locked;
+ // last frame stuff
+ float lastDisplacement;
+ float angVelForGrip;
+ bool lastOnFloor;
+
+ bool type_drive;
+ bool type_steer;
+
+
+protected:
+ void update(real_t dt);
+ bool add_forces(PhysicsDirectBodyState *s);
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_side_friction(real_t p_friction);
+ void set_forward_friction(real_t p_friction);
+ void set_travel(real_t p_travel);
+ void set_radius(real_t p_radius);
+ void set_resting_frac(real_t p_frac);
+ void set_damping_frac(real_t p_frac);
+ void set_num_rays(real_t p_rays);
+
+ real_t get_side_friction() const;
+ real_t get_forward_friction() const;
+ real_t get_travel() const;
+ real_t get_radius() const;
+ real_t get_resting_frac() const;
+ real_t get_damping_frac() const;
+ int get_num_rays() const;
+
+ void set_type_drive(bool p_enable);
+ bool is_type_drive() const;
+
+ void set_type_steer(bool p_enable);
+ bool is_type_steer() const;
+
+ CarWheel();
+
+};
+
+
+
+class CarBody : public PhysicsBody {
+
+ OBJ_TYPE(CarBody,PhysicsBody);
+
+ real_t mass;
+ real_t friction;
+
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+ bool ccd;
+
+ real_t max_steer_angle;
+ real_t steer_rate;
+ int wheel_num_rays;
+ real_t drive_torque;
+
+// control stuff
+ real_t target_steering;
+ real_t target_accelerate;
+
+ bool forward_drive;
+ bool backward_drive;
+
+ real_t steering;
+ real_t accelerate;
+ real_t hand_brake;
+ Set<RID> exclude;
+
+
+friend class CarWheel;
+ Vector<CarWheel*> wheels;
+
+ static void _bind_methods();
+
+ void _direct_state_changed(Object *p_state);
+public:
+
+
+ void set_mass(real_t p_mass);
+ real_t get_mass() const;
+
+ void set_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_max_steer_angle(real_t p_angle);
+ void set_steer_rate(real_t p_rate);
+ void set_drive_torque(real_t p_torque);
+
+ real_t get_max_steer_angle() const;
+ real_t get_steer_rate() const;
+ real_t get_drive_torque() const;
+
+
+ void set_target_steering(float p_steering);
+ void set_target_accelerate(float p_accelerate);
+ void set_hand_brake(float p_amont);
+
+ real_t get_target_steering() const;
+ real_t get_target_accelerate() const;
+ real_t get_hand_brake() const;
+
+
+ CarBody();
+};
+
+#endif // CAR_BODY_H
diff --git a/scene/3d/character_camera.cpp b/scene/3d/character_camera.cpp
new file mode 100644
index 0000000000..e3c071d42f
--- /dev/null
+++ b/scene/3d/character_camera.cpp
@@ -0,0 +1,718 @@
+/*************************************************************************/
+/* character_camera.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 "character_camera.h"
+
+#include "physics_body.h"
+#if 0
+void CharacterCamera::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="type")
+ set_camera_type((CameraType)((int)(p_value)));
+ else if (p_name=="orbit")
+ set_orbit(p_value);
+ else if (p_name=="height")
+ set_height(p_value);
+ else if (p_name=="inclination")
+ set_inclination(p_value);
+ else if (p_name=="max_orbit_x")
+ set_max_orbit_x(p_value);
+ else if (p_name=="min_orbit_x")
+ set_min_orbit_x(p_value);
+ else if (p_name=="max_distance")
+ set_max_distance(p_value);
+ else if (p_name=="min_distance")
+ set_min_distance(p_value);
+ else if (p_name=="distance")
+ set_distance(p_value);
+ else if (p_name=="clip")
+ set_clip(p_value);
+ else if (p_name=="autoturn")
+ set_autoturn(p_value);
+ else if (p_name=="autoturn_tolerance")
+ set_autoturn_tolerance(p_value);
+ else if (p_name=="autoturn_speed")
+ set_autoturn_speed(p_value);
+
+}
+Variant CharacterCamera::_get(const String& p_name) const {
+
+ if (p_name=="type")
+ return get_camera_type();
+ else if (p_name=="orbit")
+ return get_orbit();
+ else if (p_name=="height")
+ return get_height();
+ else if (p_name=="inclination")
+ return get_inclination();
+ else if (p_name=="max_orbit_x")
+ return get_max_orbit_x();
+ else if (p_name=="min_orbit_x")
+ return get_min_orbit_x();
+ else if (p_name=="max_distance")
+ return get_max_distance();
+ else if (p_name=="min_distance")
+ return get_min_distance();
+ else if (p_name=="distance")
+ return get_distance();
+ else if (p_name=="clip")
+ return has_clip();
+ else if (p_name=="autoturn")
+ return has_autoturn();
+ else if (p_name=="autoturn_tolerance")
+ return get_autoturn_tolerance();
+ else if (p_name=="autoturn_speed")
+ return get_autoturn_speed();
+
+ return Variant();
+}
+
+void CharacterCamera::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::INT, "type", PROPERTY_HINT_ENUM, "Fixed,Follow") );
+ p_list->push_back( PropertyInfo( Variant::VECTOR2, "orbit" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "inclination", PROPERTY_HINT_RANGE,"-90,90,0.01" ) ); ;
+ p_list->push_back( PropertyInfo( Variant::REAL, "max_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "min_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "min_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "max_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "distance", PROPERTY_HINT_RANGE,"0.01,1024,0,01") );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "clip") );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "autoturn") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "autoturn_tolerance", PROPERTY_HINT_RANGE,"1,90,0.01") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "autoturn_speed", PROPERTY_HINT_RANGE,"1,90,0.01") );
+
+
+}
+
+void CharacterCamera::_compute_camera() {
+
+
+ // update the transform with the next proposed transform (camera is 1 logic frame delayed)
+
+ /*
+ float time = get_root_node()->get_frame_time();
+ Vector3 oldp = accepted.get_origin();
+ Vector3 newp = proposed.get_origin();
+
+ float frame_dist = time *
+ if (oldp.distance_to(newp) >
+ */
+
+ float time = get_root_node()->get_frame_time();
+
+ if (true) {
+
+ if (clip_ray[0].clipped && clip_ray[1].clipped && clip_ray[2].clipped) {
+ //all have been clipped
+ proposed.origin=clip_ray[1].clip_pos;
+
+
+ } else {
+
+ Vector3 rel=proposed.origin-target_pos;
+
+ if (clip_ray[0].clipped && !clip_ray[2].clipped) {
+
+ float distance = target_pos.distance_to(clip_ray[0].clip_pos);
+ real_t amount = 1.0-(distance/clip_len);
+ amount = CLAMP(amount,0,1);
+
+
+ rel=Matrix3(Vector3(0,1,0)),
+ rotate_orbit(Vector2(0,autoturn_speed*time*amount));
+ }
+ if (clip_ray[2].clipped && !clip_ray[0].clipped) {
+
+ float distance = target_pos.distance_to(clip_ray[2].clip_pos);
+ real_t amount = 1.0-(distance/clip_len);
+ amount = CLAMP(amount,0,1);
+
+ rotate_orbit(Vector2(0,-autoturn_speed*time*amount));
+ }
+
+ }
+ }
+
+
+ Transform final;
+
+ static float pos_ratio = 0.9;
+ static float rot_ratio = 10;
+
+ Vector3 vec1 = accepted.origin;
+ Vector3 vec2 = proposed.origin;
+ final.origin = vec2.linear_interpolate(vec1, pos_ratio * time);;
+
+ Quat q1 = accepted.basis;
+ Quat q2 = proposed.basis;
+ final.basis = q1.slerp(q2, rot_ratio * time);
+
+ accepted=final;
+
+ _update_camera();
+
+ // calculate the next proposed transform
+
+
+ Vector3 new_pos;
+ Vector3 character_pos = get_global_transform().origin;
+ character_pos.y+=height; // height compensate
+
+ if(type==CAMERA_FOLLOW) {
+
+
+
+ /* calculate some variables */
+
+ Vector3 rel = follow_pos - character_pos;
+
+ float l = rel.length();
+ Vector3 rel_n = (l > 0) ? (rel/l) : Vector3();
+#if 1
+ float ang = Math::acos(rel_n.dot( Vector3(0,1,0) ));
+
+ Vector3 tangent = rel_n;
+ tangent.y=0; // get rid of y
+ if (tangent.length_squared() < CMP_EPSILON2)
+ tangent=Vector3(0,0,1); // use Z as tangent if rel is parallel to y
+ else
+ tangent.normalize();
+
+ /* now start applying the rules */
+
+ //clip distance
+ if (l > max_distance)
+ l=max_distance;
+ if (l < min_distance)
+ l=min_distance;
+
+ //fix angle
+
+ float ang_min = Math_PI * 0.5 + Math::deg2rad(min_orbit_x);
+ float ang_max = Math_PI * 0.5 + Math::deg2rad(max_orbit_x);
+
+ if (ang<ang_min)
+ ang=ang_min;
+ if (ang>ang_max)
+ ang=ang_max;
+
+ /* finally, rebuild the validated camera position */
+
+ new_pos=Vector3(0,Math::cos(ang),0);
+ new_pos+=tangent*Math::sin(ang);
+ new_pos*=l;
+ new_pos+=character_pos;
+#else
+ if (l > max_distance)
+ l=max_distance;
+ if (l < min_distance)
+ l=min_distance;
+
+ new_pos = character_pos + rel_n * l;
+
+
+#endif
+ follow_pos=new_pos;
+
+ } else if (type==CAMERA_FIXED) {
+
+
+ if (distance<min_distance)
+ distance=min_distance;
+ if (distance>max_distance)
+ distance=max_distance;
+
+ if (orbit.x<min_orbit_x)
+ orbit.x=min_orbit_x;
+ if (orbit.x>max_orbit_x)
+ orbit.x=max_orbit_x;
+
+ Matrix3 m;
+ m.rotate(Vector3(0,1,0),Math::deg2rad(orbit.y));
+ m.rotate(Vector3(1,0,0),Math::deg2rad(orbit.x));
+
+ new_pos = (m.get_axis(2) * distance) + character_pos;
+
+ if (use_lookat_target) {
+
+ Transform t = get_global_transform();
+ Vector3 y = t.basis.get_axis(1).normalized();
+ Vector3 z = lookat_target - character_pos;
+ z= (z - y * y.dot(z)).normalized();
+ orbit.y = -Math::rad2deg(Math::atan2(z.x,z.z)) + 180;
+
+ /*
+ Transform t = get_global_transform();
+ Vector3 y = t.basis.get_axis(1).normalized();
+ Vector3 z = lookat_target - t.origin;
+ z= (z - y * y.dot(z)).normalized();
+ Vector3 x = z.cross(y).normalized();
+ Transform t2;
+ t2.basis.set_axis(0,x);
+ t2.basis.set_axis(1,y);
+ t2.basis.set_axis(2,z);
+ t2.origin=t.origin;
+
+ Vector3 local = t2.xform_inv(camera_pos);
+
+ float ang = Math::atan2(local.x,local.y);
+ */
+
+ /*
+
+ Vector3 vec1 = lookat_target - new_pos;
+ vec1.normalize();
+ Vector3 vec2 = character_pos - new_pos;
+ vec2.normalize();
+
+ float dot = vec1.dot(vec2);
+ printf("dot %f\n", dot);
+ if ( dot < 0.5) {
+
+ rotate_orbit(Vector2(0, 90));
+ };
+ */
+
+
+ };
+ }
+
+ Vector3 target;
+ if (use_lookat_target) {
+
+ target = lookat_target;
+ } else {
+ target = character_pos;
+ };
+
+ proposed.set_look_at(new_pos,target,Vector3(0,1,0));
+ proposed = proposed * Transform(Matrix3(Vector3(1,0,0),Math::deg2rad(inclination)),Vector3()); //inclination
+
+
+ Vector<RID> exclude;
+ exclude.push_back(target_body);
+
+
+
+ Vector3 rel = new_pos-target;
+
+ for(int i=0;i<3;i++) {
+
+ PhysicsServer::get_singleton()->query_intersection(clip_ray[i].query,get_world().get_space(),exclude);
+ PhysicsServer::get_singleton()->query_intersection_segment(clip_ray[i].query,target,target+Matrix3(Vector3(0,1,0),Math::deg2rad(autoturn_tolerance*(i-1.0))).xform(rel));
+ clip_ray[i].clipped=false;
+ clip_ray[i].clip_pos=Vector3();
+ }
+ target_pos=target;
+ clip_len=rel.length();
+
+
+
+}
+
+void CharacterCamera::set_use_lookat_target(bool p_use, const Vector3 &p_lookat) {
+
+ use_lookat_target = p_use;
+ lookat_target = p_lookat;
+};
+
+
+void CharacterCamera::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_PROCESS: {
+
+
+ _compute_camera();
+ } break;
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (type==CAMERA_FOLLOW) {
+
+ set_orbit(orbit);
+ set_distance(distance);
+ }
+
+ accepted=get_global_transform();
+ proposed=accepted;
+
+ target_body = RID();
+
+ Node* parent = get_parent();
+ while (parent) {
+ PhysicsBody* p = parent->cast_to<PhysicsBody>();
+ if (p) {
+ target_body = p->get_body();
+ break;
+ };
+ parent = parent->get_parent();
+ };
+
+ } break;
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (type==CAMERA_FOLLOW) {
+ distance=get_distance();
+ orbit=get_orbit();
+
+ }
+ } break;
+
+ case NOTIFICATION_BECAME_CURRENT: {
+
+ set_process(true);
+ } break;
+ case NOTIFICATION_LOST_CURRENT: {
+
+ set_process(false);
+ } break;
+ }
+
+}
+
+
+void CharacterCamera::set_camera_type(CameraType p_camera_type) {
+
+
+ if (p_camera_type==type)
+ return;
+
+ type=p_camera_type;
+
+ // do conversions
+}
+
+CharacterCamera::CameraType CharacterCamera::get_camera_type() const {
+
+ return type;
+
+}
+
+void CharacterCamera::set_orbit(const Vector2& p_orbit) {
+
+ orbit=p_orbit;
+
+ if(type == CAMERA_FOLLOW && is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ float d = char_pos.distance_to(follow_pos);
+
+ Matrix3 m;
+ m.rotate(Vector3(0,1,0),orbit.y);
+ m.rotate(Vector3(1,0,0),orbit.x);
+
+ follow_pos=char_pos + m.get_axis(2) * d;
+
+ }
+
+}
+void CharacterCamera::set_orbit_x(float p_x) {
+
+ orbit.x=p_x;
+ if(type == CAMERA_FOLLOW && is_inside_scene())
+ set_orbit(Vector2( p_x, get_orbit().y ));
+}
+void CharacterCamera::set_orbit_y(float p_y) {
+
+
+ orbit.y=p_y;
+ if(type == CAMERA_FOLLOW && is_inside_scene())
+ set_orbit(Vector2( get_orbit().x, p_y ));
+
+}
+Vector2 CharacterCamera::get_orbit() const {
+
+
+ if (type == CAMERA_FOLLOW && is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos).normalized();
+ Vector2 ret_orbit;
+ ret_orbit.x = Math::acos( Vector3(0,1,0).dot( rel ) ) - Math_PI * 0.5;
+ ret_orbit.y = Math::atan2(rel.x,rel.z);
+ return ret_orbit;
+ }
+ return orbit;
+}
+
+void CharacterCamera::rotate_orbit(const Vector2& p_relative) {
+
+ if (type == CAMERA_FOLLOW && is_inside_scene()) {
+
+ Matrix3 m;
+ m.rotate(Vector3(0,1,0),Math::deg2rad(p_relative.y));
+ m.rotate(Vector3(1,0,0),Math::deg2rad(p_relative.x));
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos);
+ rel = m.xform(rel);
+ follow_pos=char_pos+rel;
+
+ }
+
+ orbit+=p_relative;
+}
+
+void CharacterCamera::set_height(float p_height) {
+
+
+ height=p_height;
+}
+
+float CharacterCamera::get_height() const {
+
+ return height;
+
+}
+
+void CharacterCamera::set_max_orbit_x(float p_max) {
+
+ max_orbit_x=p_max;
+}
+
+float CharacterCamera::get_max_orbit_x() const {
+
+ return max_orbit_x;
+}
+
+void CharacterCamera::set_min_orbit_x(float p_min) {
+
+ min_orbit_x=p_min;
+}
+
+float CharacterCamera::get_min_orbit_x() const {
+
+ return min_orbit_x;
+}
+
+float CharacterCamera::get_min_distance() const {
+
+ return min_distance;
+}
+float CharacterCamera::get_max_distance() const {
+
+ return max_distance;
+}
+
+void CharacterCamera::set_min_distance(float p_min) {
+
+ min_distance=p_min;
+}
+
+void CharacterCamera::set_max_distance(float p_max) {
+
+ max_distance = p_max;
+}
+
+
+void CharacterCamera::set_distance(float p_distance) {
+
+ if (type == CAMERA_FOLLOW && is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos).normalized();
+ rel*=p_distance;
+ follow_pos=char_pos+rel;
+
+ }
+
+ distance=p_distance;
+}
+
+float CharacterCamera::get_distance() const {
+
+ if (type == CAMERA_FOLLOW && is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ return (follow_pos - char_pos).length();
+
+ }
+
+ return distance;
+}
+
+void CharacterCamera::set_clip(bool p_enabled) {
+
+
+ clip=p_enabled;
+}
+
+bool CharacterCamera::has_clip() const {
+
+ return clip;
+
+}
+
+
+void CharacterCamera::set_autoturn(bool p_enabled) {
+
+
+ autoturn=p_enabled;
+}
+
+bool CharacterCamera::has_autoturn() const {
+
+ return autoturn;
+
+}
+
+void CharacterCamera::set_autoturn_tolerance(float p_degrees) {
+
+
+ autoturn_tolerance=p_degrees;
+}
+float CharacterCamera::get_autoturn_tolerance() const {
+
+
+ return autoturn_tolerance;
+}
+
+void CharacterCamera::set_inclination(float p_degrees) {
+
+
+ inclination=p_degrees;
+}
+float CharacterCamera::get_inclination() const {
+
+
+ return inclination;
+}
+
+
+void CharacterCamera::set_autoturn_speed(float p_speed) {
+
+
+ autoturn_speed=p_speed;
+}
+float CharacterCamera::get_autoturn_speed() const {
+
+ return autoturn_speed;
+
+}
+
+
+
+
+
+void CharacterCamera::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_camera_type","type"),&CharacterCamera::set_camera_type);
+ ObjectTypeDB::bind_method(_MD("get_camera_type"),&CharacterCamera::get_camera_type);
+ ObjectTypeDB::bind_method(_MD("set_orbit","orbit"),&CharacterCamera::set_orbit);
+ ObjectTypeDB::bind_method(_MD("get_orbit"),&CharacterCamera::get_orbit);
+ ObjectTypeDB::bind_method(_MD("set_orbit_x","x"),&CharacterCamera::set_orbit_x);
+ ObjectTypeDB::bind_method(_MD("set_orbit_y","y"),&CharacterCamera::set_orbit_y);
+ ObjectTypeDB::bind_method(_MD("set_min_orbit_x","x"),&CharacterCamera::set_min_orbit_x);
+ ObjectTypeDB::bind_method(_MD("get_min_orbit_x"),&CharacterCamera::get_min_orbit_x);
+ ObjectTypeDB::bind_method(_MD("set_max_orbit_x","x"),&CharacterCamera::set_max_orbit_x);
+ ObjectTypeDB::bind_method(_MD("get_max_orbit_x"),&CharacterCamera::get_max_orbit_x);
+ ObjectTypeDB::bind_method(_MD("rotate_orbit"),&CharacterCamera::rotate_orbit);
+ ObjectTypeDB::bind_method(_MD("set_distance","distance"),&CharacterCamera::set_distance);
+ ObjectTypeDB::bind_method(_MD("get_distance"),&CharacterCamera::get_distance);
+ ObjectTypeDB::bind_method(_MD("set_clip","enable"),&CharacterCamera::set_clip);
+ ObjectTypeDB::bind_method(_MD("has_clip"),&CharacterCamera::has_clip);
+ ObjectTypeDB::bind_method(_MD("set_autoturn","enable"),&CharacterCamera::set_autoturn);
+ ObjectTypeDB::bind_method(_MD("has_autoturn"),&CharacterCamera::has_autoturn);
+ ObjectTypeDB::bind_method(_MD("set_autoturn_tolerance","degrees"),&CharacterCamera::set_autoturn_tolerance);
+ ObjectTypeDB::bind_method(_MD("get_autoturn_tolerance"),&CharacterCamera::get_autoturn_tolerance);
+ ObjectTypeDB::bind_method(_MD("set_autoturn_speed","speed"),&CharacterCamera::set_autoturn_speed);
+ ObjectTypeDB::bind_method(_MD("get_autoturn_speed"),&CharacterCamera::get_autoturn_speed);
+ ObjectTypeDB::bind_method(_MD("set_use_lookat_target","use","lookat"),&CharacterCamera::set_use_lookat_target, DEFVAL(Vector3()));
+
+ ObjectTypeDB::bind_method(_MD("_ray_collision"),&CharacterCamera::_ray_collision);
+
+ BIND_CONSTANT( CAMERA_FIXED );
+ BIND_CONSTANT( CAMERA_FOLLOW );
+}
+
+void CharacterCamera::_ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx) {
+
+
+ clip_ray[p_idx].clip_pos=p_point;
+ clip_ray[p_idx].clipped=true;
+};
+
+Transform CharacterCamera::get_camera_transform() const {
+
+ return accepted;
+}
+
+
+CharacterCamera::CharacterCamera() {
+
+
+ type=CAMERA_FOLLOW;
+ height=1;
+
+ orbit=Vector2(0,0);
+
+ distance=3;
+ min_distance=2;
+ max_distance=5;
+
+ autoturn=false;
+ autoturn_tolerance=15;
+ autoturn_speed=20;
+
+ min_orbit_x=-50;
+ max_orbit_x=70;
+ inclination=0;
+
+ clip=false;
+ use_lookat_target = false;
+
+ for(int i=0;i<3;i++) {
+ clip_ray[i].query=PhysicsServer::get_singleton()->query_create(this, "_ray_collision", i, true);
+ clip_ray[i].clipped=false;
+ }
+
+
+}
+
+CharacterCamera::~CharacterCamera() {
+
+ for(int i=0;i<3;i++) {
+ PhysicsServer::get_singleton()->free(clip_ray[i].query);
+ }
+
+
+}
+#endif
diff --git a/scene/3d/character_camera.h b/scene/3d/character_camera.h
new file mode 100644
index 0000000000..f3bdef54a6
--- /dev/null
+++ b/scene/3d/character_camera.h
@@ -0,0 +1,167 @@
+/*************************************************************************/
+/* character_camera.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 CHARACTER_CAMERA_H
+#define CHARACTER_CAMERA_H
+
+
+#include "scene/3d/camera.h"
+#if 0
+class CharacterCamera : public Camera {
+
+ OBJ_TYPE( CharacterCamera, Camera );
+public:
+
+ enum CameraType {
+ CAMERA_FIXED,
+ CAMERA_FOLLOW
+ };
+
+private:
+
+
+ CameraType type;
+
+ //used for follow
+ Vector3 follow_pos;
+ //used for fixed
+ Vector2 orbit;
+ float distance;
+
+ float height;
+
+ float min_distance;
+ float max_distance;
+
+ float max_orbit_x;
+ float min_orbit_x;
+
+ float inclination;
+
+ bool clip;
+ bool autoturn;
+ float autoturn_tolerance;
+ float autoturn_speed;
+
+
+
+ struct ClipRay {
+ RID query;
+ bool clipped;
+ Vector3 clip_pos;
+ };
+
+ ClipRay clip_ray[3];
+ Vector3 target_pos;
+ float clip_len;
+
+
+ Transform accepted;
+ Vector3 proposed_pos;
+
+ bool use_lookat_target;
+ Vector3 lookat_target;
+
+ void _compute_camera();
+
+ RID ray_query;
+ RID left_turn_query;
+ RID right_turn_query;
+ RID target_body;
+
+protected:
+
+ virtual void _request_camera_update() {} //ignore
+
+ 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();
+
+ void _ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx);
+
+public:
+
+
+ void set_camera_type(CameraType p_camera_type);
+ CameraType get_camera_type() const;
+
+ void set_orbit(const Vector2& p_orbit);
+ void set_orbit_x(float p_x);
+ void set_orbit_y(float p_y);
+ Vector2 get_orbit() const;
+
+ void set_height(float p_height);
+ float get_height() const;
+
+ void set_inclination(float p_degrees);
+ float get_inclination() const;
+
+ void set_max_orbit_x(float p_max);
+ float get_max_orbit_x() const;
+
+ void set_min_orbit_x(float p_min);
+ float get_min_orbit_x() const;
+
+ void rotate_orbit(const Vector2& p_relative);
+
+ void set_distance(float p_distance);
+ float get_distance() const;
+
+ float get_min_distance() const;
+ float get_max_distance() const;
+ void set_min_distance(float p_min);
+ void set_max_distance(float p_max);
+
+
+ void set_clip(bool p_enabled);
+ bool has_clip() const;
+
+ void set_autoturn(bool p_enabled);
+ bool has_autoturn() const;
+
+ void set_autoturn_tolerance(float p_degrees);
+ float get_autoturn_tolerance() const;
+
+ void set_autoturn_speed(float p_speed);
+ float get_autoturn_speed() const;
+
+ void set_use_lookat_target(bool p_use, const Vector3 &p_lookat = Vector3());
+
+ virtual Transform get_camera_transform() const;
+
+ CharacterCamera();
+ ~CharacterCamera();
+};
+
+VARIANT_ENUM_CAST( CharacterCamera::CameraType );
+
+#endif
+#endif // CHARACTER_CAMERA_H
diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp
new file mode 100644
index 0000000000..7ad10d3222
--- /dev/null
+++ b/scene/3d/collision_object.cpp
@@ -0,0 +1,284 @@
+/*************************************************************************/
+/* collision_object.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.h"
+#include "servers/physics_server.h"
+
+void CollisionObject::_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 CollisionObject::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ RID space = get_world()->get_space();
+ if (area) {
+ PhysicsServer::get_singleton()->area_set_space(rid,space);
+ } else
+ PhysicsServer::get_singleton()->body_set_space(rid,space);
+
+ //get space
+ }
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (area)
+ PhysicsServer::get_singleton()->area_set_transform(rid,get_global_transform());
+ else
+ PhysicsServer::get_singleton()->body_set_state(rid,PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform());
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ if (area) {
+ PhysicsServer::get_singleton()->area_set_space(rid,RID());
+ } else
+ PhysicsServer::get_singleton()->body_set_space(rid,RID());
+
+ } break;
+ }
+}
+
+void CollisionObject::_update_shapes() {
+
+ if (!rid.is_valid())
+ return;
+
+ if (area)
+ PhysicsServer::get_singleton()->area_clear_shapes(rid);
+ else
+ PhysicsServer::get_singleton()->body_clear_shapes(rid);
+
+ for(int i=0;i<shapes.size();i++) {
+
+ if (shapes[i].shape.is_null())
+ continue;
+ if (area)
+ PhysicsServer::get_singleton()->area_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform);
+ else {
+ PhysicsServer::get_singleton()->body_add_shape(rid,shapes[i].shape->get_rid(),shapes[i].xform);
+ if (shapes[i].trigger)
+ PhysicsServer::get_singleton()->body_set_shape_as_trigger(rid,i,shapes[i].trigger);
+ }
+ }
+}
+
+
+bool CollisionObject::_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 CollisionObject::_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 CollisionObject::_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,"Shape",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 CollisionObject::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("add_shape","shape:Shape","transform"),&CollisionObject::add_shape,DEFVAL(Transform()));
+ ObjectTypeDB::bind_method(_MD("get_shape_count"),&CollisionObject::get_shape_count);
+ ObjectTypeDB::bind_method(_MD("set_shape","shape_idx","shape:Shape"),&CollisionObject::set_shape);
+ ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject::set_shape_transform);
+// ObjectTypeDB::bind_method(_MD("set_shape_transform","shape_idx","transform"),&CollisionObject::set_shape_transform);
+ ObjectTypeDB::bind_method(_MD("set_shape_as_trigger","shape_idx","enable"),&CollisionObject::set_shape_as_trigger);
+ ObjectTypeDB::bind_method(_MD("is_shape_set_as_trigger","shape_idx"),&CollisionObject::is_shape_set_as_trigger);
+ ObjectTypeDB::bind_method(_MD("get_shape:Shape","shape_idx"),&CollisionObject::get_shape);
+ ObjectTypeDB::bind_method(_MD("get_shape_transform","shape_idx"),&CollisionObject::get_shape_transform);
+ ObjectTypeDB::bind_method(_MD("remove_shape","shape_idx"),&CollisionObject::remove_shape);
+ ObjectTypeDB::bind_method(_MD("clear_shapes"),&CollisionObject::clear_shapes);
+ ObjectTypeDB::bind_method(_MD("get_rid"),&CollisionObject::get_rid);
+
+}
+
+
+void CollisionObject::add_shape(const Ref<Shape>& p_shape, const Transform& p_transform) {
+
+ ShapeData sdata;
+ sdata.shape=p_shape;
+ sdata.xform=p_transform;
+ shapes.push_back(sdata);
+ _update_shapes();
+
+}
+int CollisionObject::get_shape_count() const {
+
+ return shapes.size();
+
+}
+void CollisionObject::set_shape(int p_shape_idx, const Ref<Shape>& p_shape) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes[p_shape_idx].shape=p_shape;
+ _update_shapes();
+}
+
+void CollisionObject::set_shape_transform(int p_shape_idx, const Transform& p_transform) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes[p_shape_idx].xform=p_transform;
+
+ _update_shapes();
+}
+
+Ref<Shape> CollisionObject::get_shape(int p_shape_idx) const {
+
+ ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Ref<Shape>());
+ return shapes[p_shape_idx].shape;
+
+}
+Transform CollisionObject::get_shape_transform(int p_shape_idx) const {
+
+ ERR_FAIL_INDEX_V(p_shape_idx,shapes.size(),Transform());
+ return shapes[p_shape_idx].xform;
+
+}
+void CollisionObject::remove_shape(int p_shape_idx) {
+
+ ERR_FAIL_INDEX(p_shape_idx,shapes.size());
+ shapes.remove(p_shape_idx);
+
+ _update_shapes();
+}
+
+void CollisionObject::clear_shapes() {
+
+ shapes.clear();
+
+ _update_shapes();
+}
+
+void CollisionObject::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()) {
+
+ PhysicsServer::get_singleton()->body_set_shape_as_trigger(rid,p_shape_idx,p_trigger);
+
+ }
+}
+
+bool CollisionObject::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;
+}
+
+CollisionObject::CollisionObject(RID p_rid, bool p_area) {
+
+ rid=p_rid;
+ area=p_area;
+ if (p_area) {
+ PhysicsServer::get_singleton()->area_attach_object_instance_ID(rid,get_instance_ID());
+ } else {
+ PhysicsServer::get_singleton()->body_attach_object_instance_ID(rid,get_instance_ID());
+ }
+// set_transform_notify(true);
+
+}
+
+
+CollisionObject::CollisionObject() {
+
+
+ //owner=
+
+ //set_transform_notify(true);
+}
+
+CollisionObject::~CollisionObject() {
+
+ PhysicsServer::get_singleton()->free(rid);
+}
diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h
new file mode 100644
index 0000000000..54dc6508ab
--- /dev/null
+++ b/scene/3d/collision_object.h
@@ -0,0 +1,90 @@
+/*************************************************************************/
+/* collision_object.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_H
+#define COLLISION_OBJECT_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/shape.h"
+
+class CollisionObject : public Spatial {
+
+ OBJ_TYPE( CollisionObject, Spatial );
+
+ bool area;
+ RID rid;
+
+ struct ShapeData {
+ Transform xform;
+ Ref<Shape> shape;
+ bool trigger;
+
+ ShapeData() {
+ trigger=false;
+ }
+
+ };
+
+
+ Vector<ShapeData> shapes;
+
+ void _update_shapes();
+
+friend class CollisionShape;
+friend class CollisionPolygon;
+ void _update_shapes_from_children();
+protected:
+
+ CollisionObject(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<Shape>& p_shape, const Transform& p_transform=Transform());
+ int get_shape_count() const;
+ void set_shape(int p_shape_idx, const Ref<Shape>& p_shape);
+ void set_shape_transform(int p_shape_idx, const Transform& p_transform);
+ Ref<Shape> get_shape(int p_shape_idx) const;
+ Transform get_shape_transform(int p_shape_idx) const;
+ void remove_shape(int p_shape_idx);
+ void clear_shapes();
+ void set_shape_as_trigger(int p_shape_idx, bool p_trigger);
+ bool is_shape_set_as_trigger(int p_shape_idx) const;
+
+ _FORCE_INLINE_ RID get_rid() const { return rid; }
+
+ CollisionObject();
+ ~CollisionObject();
+};
+
+#endif // COLLISION_OBJECT__H
diff --git a/scene/3d/editable_shape.cpp b/scene/3d/editable_shape.cpp
new file mode 100644
index 0000000000..ab3f832028
--- /dev/null
+++ b/scene/3d/editable_shape.cpp
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* editable_shape.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 "editable_shape.h"
+
+
+void EditableShape::_notification(int p_what) {
+
+
+
+}
+
+
+void EditableShape::set_bsp_tree(const BSP_Tree& p_bsp) {
+
+ bsp=p_bsp;
+}
+
+void EditableShape::set_shape(const Ref<Shape>& p_shape) {
+
+ shape=p_shape;
+}
+
+
+
+EditableShape::EditableShape()
+{
+}
+
+
+
+/////////////////////////
+
+
+void EditableSphere::set_radius(float p_radius) {
+
+ radius=p_radius;
+ update_gizmo();
+ _change_notify("params/radius");
+}
+
+
+float EditableSphere::get_radius() const{
+
+ return radius;
+}
+
+
+void EditableSphere::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_radius","radius"),&EditableSphere::set_radius);
+ ObjectTypeDB::bind_method(_MD("get_radius"),&EditableSphere::get_radius);
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"params/radius",PROPERTY_HINT_EXP_RANGE,"0.001,16384,0.001"),_SCS("set_radius"),_SCS("get_radius"));
+}
+
+EditableSphere::EditableSphere() {
+
+ radius=1.0;
+}
diff --git a/scene/3d/editable_shape.h b/scene/3d/editable_shape.h
new file mode 100644
index 0000000000..9accea575c
--- /dev/null
+++ b/scene/3d/editable_shape.h
@@ -0,0 +1,73 @@
+/*************************************************************************/
+/* editable_shape.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 EDITABLE_SHAPE_H
+#define EDITABLE_SHAPE_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/shape.h"
+
+class EditableShape : public Spatial {
+
+ OBJ_TYPE(EditableShape,Spatial);
+
+ //can hold either of those
+ BSP_Tree bsp;
+ Ref<Shape> shape;
+
+ void _update_parent();
+protected:
+
+ void _notification(int p_what);
+
+ void set_bsp_tree(const BSP_Tree& p_bsp);
+ void set_shape(const Ref<Shape>& p_shape);
+public:
+ EditableShape();
+};
+
+
+class EditableSphere : public EditableShape {
+
+ OBJ_TYPE( EditableSphere, EditableShape );
+
+
+ float radius;
+protected:
+
+ static void _bind_methods();
+public:
+
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ EditableSphere();
+};
+
+
+#endif // EDITABLE_SHAPE_H
diff --git a/scene/3d/follow_camera.cpp b/scene/3d/follow_camera.cpp
new file mode 100644
index 0000000000..20a1654b92
--- /dev/null
+++ b/scene/3d/follow_camera.cpp
@@ -0,0 +1,778 @@
+/*************************************************************************/
+/* follow_camera.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 "follow_camera.h"
+
+
+#include "physics_body.h"
+#include "scene/resources/surface_tool.h"
+
+void FollowCamera::_set_initial_orbit(const Vector2& p_orbit) {
+
+ initial_orbit=p_orbit;
+ set_orbit(p_orbit);
+}
+
+
+
+void FollowCamera::_clear_queries() {
+
+ if (!queries_active)
+ return;
+#if 0
+ for(int i=0;i<3;i++)
+ PhysicsServer::get_singleton()->query_clear(clip_ray[i].query);
+#endif
+ queries_active=false;
+
+}
+
+void FollowCamera::_compute_camera() {
+
+ // update the transform with the next proposed transform (camera is 1 logic frame delayed)
+
+ /*
+ float time = get_root_node()->get_frame_time();
+ Vector3 oldp = accepted.get_origin();
+ Vector3 newp = proposed.get_origin();
+
+ float frame_dist = time *
+ if (oldp.distance_to(newp) >
+ */
+
+ float time = get_process_delta_time();
+ bool noblend=false;
+
+ if (clip) {
+
+ if ((clip_ray[0].clipped==clip_ray[2].clipped || fullclip) && clip_ray[1].clipped) {
+ //all have been clipped
+ proposed_pos=clip_ray[1].clip_pos-extraclip*(proposed_pos-target_pos).normalized();
+ if (clip_ray[0].clipped)
+ fullclip=true;
+ noblend=true;
+
+
+ } else {
+
+
+ //Vector3 rel=follow_pos-target_pos;
+
+ if (clip_ray[0].clipped && !clip_ray[2].clipped) {
+
+ float distance = target_pos.distance_to(clip_ray[0].clip_pos);
+ real_t amount = 1.0-(distance/clip_len);
+ amount = CLAMP(amount,0,1)*autoturn_speed*time;
+ if (clip_ray[1].clipped)
+ amount*=2.0;
+ //rotate_rel=Matrix3(Vector3(0,1,0),amount).xform(rel);
+ rotate_orbit(Vector2(0,amount));
+
+ } else if (clip_ray[2].clipped && !clip_ray[0].clipped) {
+
+ float distance = target_pos.distance_to(clip_ray[2].clip_pos);
+ real_t amount = 1.0-(distance/clip_len);
+ amount = CLAMP(amount,0,1)*autoturn_speed*time;
+ if (clip_ray[1].clipped)
+ amount*=2.0;
+ rotate_orbit(Vector2(0,-amount));
+ }
+
+ fullclip=false;
+
+ }
+ }
+
+
+ Vector3 base_pos = get_global_transform().origin;
+ Vector3 pull_from = base_pos;
+ pull_from.y+=height; // height compensate
+
+
+
+ Vector3 camera_target;
+ if (use_lookat_target) {
+
+ camera_target = lookat_target;
+ } else {
+ camera_target = base_pos;
+ };
+
+ Transform proposed;
+ proposed.set_look_at(proposed_pos,camera_target,up_vector);
+ proposed = proposed * Transform(Matrix3(Vector3(1,0,0),Math::deg2rad(inclination)),Vector3()); //inclination
+
+
+ accepted=proposed;
+ if (smooth && !noblend) {
+
+
+ Vector3 vec1 = accepted.origin;
+ Vector3 vec2 = final.origin;
+ final.origin = vec2.linear_interpolate(vec1, MIN(1,smooth_pos_ratio * time));;
+
+ Quat q1 = accepted.basis;
+ Quat q2 = final.basis;
+ final.basis = q2.slerp(q1, MIN(1,smooth_rot_ratio * time));
+
+ } else {
+ final=accepted;
+ }
+
+ _update_camera();
+
+ // calculate the next proposed transform
+
+
+ Vector3 new_pos;
+
+ { /*follow code*/
+
+
+
+ /* calculate some variables */
+
+ Vector3 rel = follow_pos - pull_from;
+
+ float l = rel.length();
+ Vector3 rel_n = (l > 0) ? (rel/l) : Vector3();
+ float ang = Math::acos(rel_n.dot( Vector3(0,1,0) ));
+
+ Vector3 tangent = rel_n;
+ tangent.y=0; // get rid of y
+ if (tangent.length_squared() < CMP_EPSILON2)
+ tangent=Vector3(0,0,1); // use Z as tangent if rel is parallel to y
+ else
+ tangent.normalize();
+
+ /* now start applying the rules */
+
+ //clip distance
+ if (l > max_distance)
+ l=max_distance;
+ if (l < min_distance)
+ l=min_distance;
+
+ //fix angle
+
+ float ang_min = Math_PI * 0.5 + Math::deg2rad(min_orbit_x);
+ float ang_max = Math_PI * 0.5 + Math::deg2rad(max_orbit_x);
+
+ if (ang<ang_min)
+ ang=ang_min;
+ if (ang>ang_max)
+ ang=ang_max;
+
+ /* finally, rebuild the validated camera position */
+
+ new_pos=Vector3(0,Math::cos(ang),0);
+ new_pos+=tangent*Math::sin(ang);
+ new_pos*=l;
+ new_pos+=pull_from;
+ follow_pos=new_pos;
+
+ }
+
+ proposed_pos=new_pos;
+
+ Vector3 rel = new_pos-camera_target;
+
+
+ if (clip) {
+
+ Vector<RID> exclude;
+ exclude.push_back(target_body);
+
+ for(int i=0;i<3;i++) {
+
+ clip_ray[i].clipped=false;
+ clip_ray[i].clip_pos=Vector3();
+ clip_ray[i].cast_pos=camera_target;
+
+ Vector3 cast_to = camera_target+Matrix3(Vector3(0,1,0),Math::deg2rad(autoturn_tolerance*(i-1.0))).xform(rel);
+
+
+ if (i!=1) {
+
+ Vector3 side = rel.cross(Vector3(0,1,0)).normalized()*(i-1.0);
+ clip_ray[i].cast_pos+side*target_width+rel.normalized()*target_width;
+
+ Vector3 d = -rel;
+ d.rotate(Vector3(0,1,0),Math::deg2rad(get_fov())*(i-1.0));
+ Plane p(new_pos,new_pos+d,new_pos+Vector3(0,1,0)); //fov clipping plane, build a face and use it as plane, facing doesn't matter
+ Vector3 intersect;
+ if (p.intersects_segment(clip_ray[i].cast_pos,cast_to,&intersect))
+ cast_to=intersect;
+
+ } else {
+
+ cast_to+=rel.normalized()*extraclip;
+ }
+
+ // PhysicsServer::get_singleton()->query_intersection(clip_ray[i].query,get_world()->get_space(),exclude);
+ // PhysicsServer::get_singleton()->query_intersection_segment(clip_ray[i].query,clip_ray[i].cast_pos,cast_to);
+
+
+
+
+ }
+
+ queries_active=true;
+ } else {
+
+ _clear_queries();
+ }
+ target_pos=camera_target;
+ clip_len=rel.length();
+
+}
+
+void FollowCamera::set_use_lookat_target(bool p_use, const Vector3 &p_lookat) {
+
+ use_lookat_target = p_use;
+ lookat_target = p_lookat;
+};
+
+
+void FollowCamera::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_PROCESS: {
+
+
+ _compute_camera();
+ } break;
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ set_orbit(orbit);
+ set_distance(distance);
+
+ accepted=final=get_global_transform();
+ proposed_pos=accepted.origin;
+
+ target_body = RID();
+/*
+ Node* parent = get_parent();
+ while (parent) {
+ PhysicsBody* p = parent->cast_to<PhysicsBody>();
+ if (p) {
+ target_body = p->get_body();
+ break;
+ };
+ parent = parent->get_parent();
+ };
+*/
+ set_process(true);
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ distance=get_distance();
+ orbit=get_orbit();
+ _clear_queries();
+
+ } break;
+ case NOTIFICATION_BECAME_CURRENT: {
+
+ set_process(true);
+ } break;
+ case NOTIFICATION_LOST_CURRENT: {
+
+ set_process(false);
+ _clear_queries();
+
+ } break;
+ }
+
+}
+
+
+
+void FollowCamera::set_orbit(const Vector2& p_orbit) {
+
+ orbit=p_orbit;
+
+ if(is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ float d = char_pos.distance_to(follow_pos);
+
+ Matrix3 m;
+ m.rotate(Vector3(0,1,0),orbit.y);
+ m.rotate(Vector3(1,0,0),orbit.x);
+
+ follow_pos=char_pos + m.get_axis(2) * d;
+
+ }
+
+ update_gizmo();
+
+}
+void FollowCamera::set_orbit_x(float p_x) {
+
+ orbit.x=p_x;
+ if(is_inside_scene())
+ set_orbit(Vector2( p_x, get_orbit().y ));
+}
+void FollowCamera::set_orbit_y(float p_y) {
+
+
+ orbit.y=p_y;
+ if(is_inside_scene())
+ set_orbit(Vector2( get_orbit().x, p_y ));
+
+}
+Vector2 FollowCamera::get_orbit() const {
+
+
+ if (is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos).normalized();
+ Vector2 ret_orbit;
+ ret_orbit.x = Math::acos( Vector3(0,1,0).dot( rel ) ) - Math_PI * 0.5;
+ ret_orbit.y = Math::atan2(rel.x,rel.z);
+ return ret_orbit;
+ }
+ return orbit;
+}
+
+void FollowCamera::rotate_orbit(const Vector2& p_relative) {
+
+ if (is_inside_scene()) {
+
+ Matrix3 m;
+ m.rotate(Vector3(0,1,0),Math::deg2rad(p_relative.y));
+ m.rotate(Vector3(1,0,0),Math::deg2rad(p_relative.x));
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos);
+ rel = m.xform(rel);
+ follow_pos=char_pos+rel;
+
+ }
+
+ orbit+=p_relative;
+ update_gizmo();
+}
+
+void FollowCamera::set_height(float p_height) {
+
+
+ height=p_height;
+ update_gizmo();
+}
+
+float FollowCamera::get_height() const {
+
+ return height;
+
+}
+
+void FollowCamera::set_max_orbit_x(float p_max) {
+
+ max_orbit_x=p_max;
+ update_gizmo();
+}
+
+float FollowCamera::get_max_orbit_x() const {
+
+ return max_orbit_x;
+}
+
+void FollowCamera::set_min_orbit_x(float p_min) {
+
+ min_orbit_x=p_min;
+ update_gizmo();
+}
+
+float FollowCamera::get_min_orbit_x() const {
+
+ return min_orbit_x;
+}
+
+float FollowCamera::get_min_distance() const {
+
+ return min_distance;
+}
+float FollowCamera::get_max_distance() const {
+
+ return max_distance;
+}
+
+void FollowCamera::set_min_distance(float p_min) {
+
+ min_distance=p_min;
+ update_gizmo();
+}
+
+void FollowCamera::set_max_distance(float p_max) {
+
+ max_distance = p_max;
+ update_gizmo();
+}
+
+
+void FollowCamera::set_distance(float p_distance) {
+
+ if (is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ Vector3 rel = (follow_pos - char_pos).normalized();
+ rel*=p_distance;
+ follow_pos=char_pos+rel;
+
+ }
+
+ distance=p_distance;
+}
+
+float FollowCamera::get_distance() const {
+
+ if (is_inside_scene()) {
+
+ Vector3 char_pos = get_global_transform().origin;
+ char_pos.y+=height;
+ return (follow_pos - char_pos).length();
+
+ }
+
+ return distance;
+}
+
+void FollowCamera::set_clip(bool p_enabled) {
+
+
+ clip=p_enabled;
+
+ if (!p_enabled)
+ _clear_queries();
+}
+
+bool FollowCamera::has_clip() const {
+
+ return clip;
+
+}
+
+
+void FollowCamera::set_autoturn(bool p_enabled) {
+
+
+ autoturn=p_enabled;
+}
+
+bool FollowCamera::has_autoturn() const {
+
+ return autoturn;
+
+}
+
+void FollowCamera::set_autoturn_tolerance(float p_degrees) {
+
+
+ autoturn_tolerance=p_degrees;
+}
+float FollowCamera::get_autoturn_tolerance() const {
+
+
+ return autoturn_tolerance;
+}
+
+void FollowCamera::set_inclination(float p_degrees) {
+
+
+ inclination=p_degrees;
+}
+float FollowCamera::get_inclination() const {
+
+
+ return inclination;
+}
+
+
+void FollowCamera::set_autoturn_speed(float p_speed) {
+
+
+ autoturn_speed=p_speed;
+}
+
+float FollowCamera::get_autoturn_speed() const {
+
+ return autoturn_speed;
+
+}
+
+
+RES FollowCamera::_get_gizmo_geometry() const {
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.5,1.0,0.3) );
+ mat->set_line_width(4);
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat);
+
+
+ int steps=16;
+
+ Vector3 base_up = Matrix3(Vector3(1,0,0),Math::deg2rad(max_orbit_x)).get_axis(2);
+ Vector3 base_down = Matrix3(Vector3(1,0,0),Math::deg2rad(min_orbit_x)).get_axis(2);
+
+ Vector3 ofs(0,height,0);
+
+ for(int i=0;i<steps;i++) {
+
+
+ Matrix3 rot(Vector3(0,1,0),Math_PI*2*float(i)/steps);
+ Matrix3 rot2(Vector3(0,1,0),Math_PI*2*float(i+1)/steps);
+
+ Vector3 up = rot.xform(base_up);
+ Vector3 up2 = rot2.xform(base_up);
+
+ Vector3 down = rot.xform(base_down);
+ Vector3 down2 = rot2.xform(base_down);
+
+ surface_tool->add_vertex(ofs+up*min_distance);
+ surface_tool->add_vertex(ofs+up*max_distance);
+ surface_tool->add_vertex(ofs+up*min_distance);
+ surface_tool->add_vertex(ofs+up2*min_distance);
+ surface_tool->add_vertex(ofs+up*max_distance);
+ surface_tool->add_vertex(ofs+up2*max_distance);
+
+ surface_tool->add_vertex(ofs+down*min_distance);
+ surface_tool->add_vertex(ofs+down*max_distance);
+ surface_tool->add_vertex(ofs+down*min_distance);
+ surface_tool->add_vertex(ofs+down2*min_distance);
+ surface_tool->add_vertex(ofs+down*max_distance);
+ surface_tool->add_vertex(ofs+down2*max_distance);
+
+ int substeps = 8;
+
+ for(int j=0;j<substeps;j++) {
+
+ Vector3 a = up.linear_interpolate(down,float(j)/substeps).normalized()*max_distance;
+ Vector3 b = up.linear_interpolate(down,float(j+1)/substeps).normalized()*max_distance;
+ Vector3 am = up.linear_interpolate(down,float(j)/substeps).normalized()*min_distance;
+ Vector3 bm = up.linear_interpolate(down,float(j+1)/substeps).normalized()*min_distance;
+
+ surface_tool->add_vertex(ofs+a);
+ surface_tool->add_vertex(ofs+b);
+ surface_tool->add_vertex(ofs+am);
+ surface_tool->add_vertex(ofs+bm);
+
+ }
+ }
+
+
+ return surface_tool->commit();
+
+
+}
+
+
+void FollowCamera::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_set_initial_orbit","orbit"),&FollowCamera::_set_initial_orbit);
+ ObjectTypeDB::bind_method(_MD("set_orbit","orbit"),&FollowCamera::set_orbit);
+ ObjectTypeDB::bind_method(_MD("get_orbit"),&FollowCamera::get_orbit);
+ ObjectTypeDB::bind_method(_MD("set_orbit_x","x"),&FollowCamera::set_orbit_x);
+ ObjectTypeDB::bind_method(_MD("set_orbit_y","y"),&FollowCamera::set_orbit_y);
+ ObjectTypeDB::bind_method(_MD("set_min_orbit_x","x"),&FollowCamera::set_min_orbit_x);
+ ObjectTypeDB::bind_method(_MD("get_min_orbit_x"),&FollowCamera::get_min_orbit_x);
+ ObjectTypeDB::bind_method(_MD("set_max_orbit_x","x"),&FollowCamera::set_max_orbit_x);
+ ObjectTypeDB::bind_method(_MD("get_max_orbit_x"),&FollowCamera::get_max_orbit_x);
+ ObjectTypeDB::bind_method(_MD("set_height","height"),&FollowCamera::set_height);
+ ObjectTypeDB::bind_method(_MD("get_height"),&FollowCamera::get_height);
+ ObjectTypeDB::bind_method(_MD("set_inclination","inclination"),&FollowCamera::set_inclination);
+ ObjectTypeDB::bind_method(_MD("get_inclination"),&FollowCamera::get_inclination);
+
+ ObjectTypeDB::bind_method(_MD("rotate_orbit"),&FollowCamera::rotate_orbit);
+ ObjectTypeDB::bind_method(_MD("set_distance","distance"),&FollowCamera::set_distance);
+ ObjectTypeDB::bind_method(_MD("get_distance"),&FollowCamera::get_distance);
+ ObjectTypeDB::bind_method(_MD("set_max_distance","max_distance"),&FollowCamera::set_max_distance);
+ ObjectTypeDB::bind_method(_MD("get_max_distance"),&FollowCamera::get_max_distance);
+ ObjectTypeDB::bind_method(_MD("set_min_distance","min_distance"),&FollowCamera::set_min_distance);
+ ObjectTypeDB::bind_method(_MD("get_min_distance"),&FollowCamera::get_min_distance);
+ ObjectTypeDB::bind_method(_MD("set_clip","enable"),&FollowCamera::set_clip);
+ ObjectTypeDB::bind_method(_MD("has_clip"),&FollowCamera::has_clip);
+ ObjectTypeDB::bind_method(_MD("set_autoturn","enable"),&FollowCamera::set_autoturn);
+ ObjectTypeDB::bind_method(_MD("has_autoturn"),&FollowCamera::has_autoturn);
+ ObjectTypeDB::bind_method(_MD("set_autoturn_tolerance","degrees"),&FollowCamera::set_autoturn_tolerance);
+ ObjectTypeDB::bind_method(_MD("get_autoturn_tolerance"),&FollowCamera::get_autoturn_tolerance);
+ ObjectTypeDB::bind_method(_MD("set_autoturn_speed","speed"),&FollowCamera::set_autoturn_speed);
+ ObjectTypeDB::bind_method(_MD("get_autoturn_speed"),&FollowCamera::get_autoturn_speed);
+ ObjectTypeDB::bind_method(_MD("set_smoothing","enable"),&FollowCamera::set_smoothing);
+ ObjectTypeDB::bind_method(_MD("has_smoothing"),&FollowCamera::has_smoothing);
+ ObjectTypeDB::bind_method(_MD("set_rotation_smoothing","amount"),&FollowCamera::set_rotation_smoothing);
+ ObjectTypeDB::bind_method(_MD("get_rotation_smoothing"),&FollowCamera::get_rotation_smoothing);
+ ObjectTypeDB::bind_method(_MD("set_translation_smoothing","amount"),&FollowCamera::set_translation_smoothing);
+ ObjectTypeDB::bind_method(_MD("get_translation_smoothing"),&FollowCamera::get_translation_smoothing);
+ ObjectTypeDB::bind_method(_MD("set_use_lookat_target","use","lookat"),&FollowCamera::set_use_lookat_target, DEFVAL(Vector3()));
+ ObjectTypeDB::bind_method(_MD("set_up_vector","vector"),&FollowCamera::set_up_vector);
+ ObjectTypeDB::bind_method(_MD("get_up_vector"),&FollowCamera::get_up_vector);
+
+ ObjectTypeDB::bind_method(_MD("_ray_collision"),&FollowCamera::_ray_collision);
+
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "orbit" ), _SCS("_set_initial_orbit"),_SCS("get_orbit") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "height", PROPERTY_HINT_RANGE,"-1024,1024,0.01" ), _SCS("set_height"), _SCS("get_height") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "inclination", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_inclination"), _SCS("get_inclination") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "max_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_max_orbit_x"), _SCS("get_max_orbit_x") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "min_orbit_x", PROPERTY_HINT_RANGE,"-90,90,0.01" ), _SCS("set_min_orbit_x"), _SCS("get_min_orbit_x") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "min_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ), _SCS("set_min_distance"), _SCS("get_min_distance") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "max_distance", PROPERTY_HINT_RANGE,"0,100,0.01" ), _SCS("set_max_distance"), _SCS("get_max_distance") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "distance", PROPERTY_HINT_RANGE,"0.01,1024,0,01"), _SCS("set_distance"), _SCS("get_distance") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "clip"), _SCS("set_clip"), _SCS("has_clip") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "autoturn"), _SCS("set_autoturn"), _SCS("has_autoturn") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "autoturn_tolerance", PROPERTY_HINT_RANGE,"1,90,0.01") , _SCS("set_autoturn_tolerance"), _SCS("get_autoturn_tolerance") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "autoturn_speed", PROPERTY_HINT_RANGE,"1,90,0.01"), _SCS("set_autoturn_speed"), _SCS("get_autoturn_speed") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "smoothing"), _SCS("set_smoothing"), _SCS("has_smoothing") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "translation_smooth", PROPERTY_HINT_RANGE,"0.01,128,0.01"), _SCS("set_translation_smoothing"), _SCS("get_translation_smoothing") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "rotation_smooth", PROPERTY_HINT_RANGE,"0.01,128,0.01"), _SCS("set_rotation_smoothing"), _SCS("get_rotation_smoothing") );
+
+
+}
+
+void FollowCamera::_ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx) {
+
+ clip_ray[p_idx].clip_pos=p_point;
+ clip_ray[p_idx].clipped=true;
+
+};
+
+Transform FollowCamera::get_camera_transform() const {
+
+ return final;
+}
+
+void FollowCamera::set_smoothing(bool p_enable) {
+
+ smooth=p_enable;
+}
+
+bool FollowCamera::has_smoothing() const {
+
+ return smooth;
+}
+
+void FollowCamera::set_translation_smoothing(float p_amount) {
+
+ smooth_pos_ratio=p_amount;
+}
+float FollowCamera::get_translation_smoothing() const {
+
+ return smooth_pos_ratio;
+}
+
+void FollowCamera::set_rotation_smoothing(float p_amount) {
+
+ smooth_rot_ratio=p_amount;
+
+}
+
+void FollowCamera::set_up_vector(const Vector3& p_up) {
+
+ up_vector=p_up;
+}
+
+Vector3 FollowCamera::get_up_vector() const{
+
+ return up_vector;
+}
+
+float FollowCamera::get_rotation_smoothing() const {
+
+ return smooth_pos_ratio;
+
+}
+
+
+FollowCamera::FollowCamera() {
+
+
+ height=1;
+
+ orbit=Vector2(0,0);
+ up_vector=Vector3(0,1,0);
+
+ distance=3;
+ min_distance=2;
+ max_distance=5;
+
+ autoturn=true;
+ autoturn_tolerance=10;
+ autoturn_speed=80;
+
+ min_orbit_x=-50;
+ max_orbit_x=70;
+ inclination=0;
+ target_width=0.3;
+
+ clip=true;
+ use_lookat_target = false;
+ extraclip=0.3;
+ fullclip=false;
+
+ smooth=true;
+ smooth_rot_ratio=10;
+ smooth_pos_ratio=10;
+
+
+ for(int i=0;i<3;i++) {
+// clip_ray[i].query=PhysicsServer::get_singleton()->query_create(this, "_ray_collision", i, true);
+ clip_ray[i].clipped=false;
+ }
+
+ queries_active=false;
+
+
+}
+
+FollowCamera::~FollowCamera() {
+
+ for(int i=0;i<3;i++) {
+ PhysicsServer::get_singleton()->free(clip_ray[i].query);
+ }
+
+
+}
diff --git a/scene/3d/follow_camera.h b/scene/3d/follow_camera.h
new file mode 100644
index 0000000000..10912eb606
--- /dev/null
+++ b/scene/3d/follow_camera.h
@@ -0,0 +1,180 @@
+/*************************************************************************/
+/* follow_camera.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 FOLLOW_CAMERA_H
+#define FOLLOW_CAMERA_H
+
+#include "scene/3d/camera.h"
+
+class FollowCamera : public Camera {
+
+ OBJ_TYPE( FollowCamera, Camera );
+
+private:
+
+
+ //used for follow
+ Vector3 follow_pos;
+ //used for fixed
+ Vector2 initial_orbit;
+ Vector2 orbit;
+ float distance;
+
+ float height;
+ float target_width;
+
+ float min_distance;
+ float max_distance;
+
+ float max_orbit_x;
+ float min_orbit_x;
+
+ float inclination;
+ float extraclip;
+ bool fullclip;
+
+ bool clip;
+ bool autoturn;
+ float autoturn_tolerance;
+ float autoturn_speed;
+
+ bool smooth;
+ float smooth_rot_ratio;
+ float smooth_pos_ratio;
+
+
+
+ struct ClipRay {
+ RID query;
+ bool clipped;
+ Vector3 cast_pos;
+ Vector3 clip_pos;
+ };
+
+ ClipRay clip_ray[3];
+ Vector3 target_pos;
+ float clip_len;
+
+ Vector3 up_vector;
+
+
+ virtual RES _get_gizmo_geometry() const;
+
+ Transform ted;
+ Vector3 proposed_pos;
+ Transform accepted;
+ Transform final;
+ RID target_body;
+
+ bool use_lookat_target;
+ Vector3 lookat_target;
+
+ void _compute_camera();
+
+ bool queries_active;
+ void _clear_queries();
+
+ void _set_initial_orbit(const Vector2& p_orbit);
+
+protected:
+
+ virtual void _request_camera_update() {} //ignore
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+ void _ray_collision(Vector3 p_point, Vector3 p_normal, int p_subindex, ObjectID p_against,int p_idx);
+
+public:
+
+
+ void set_orbit(const Vector2& p_orbit);
+ void set_orbit_x(float p_x);
+ void set_orbit_y(float p_y);
+ Vector2 get_orbit() const;
+
+ void set_height(float p_height);
+ float get_height() const;
+
+ void set_inclination(float p_degrees);
+ float get_inclination() const;
+
+ void set_max_orbit_x(float p_max);
+ float get_max_orbit_x() const;
+
+ void set_min_orbit_x(float p_min);
+ float get_min_orbit_x() const;
+
+ void rotate_orbit(const Vector2& p_relative);
+
+ void set_distance(float p_distance);
+ float get_distance() const;
+
+ float get_min_distance() const;
+ float get_max_distance() const;
+ void set_min_distance(float p_min);
+ void set_max_distance(float p_max);
+
+ /** FINISH THIS AND CLEAN IT UP */
+
+ void set_clip(bool p_enabled);
+ bool has_clip() const;
+
+ void set_autoturn(bool p_enabled);
+ bool has_autoturn() const;
+
+ void set_autoturn_tolerance(float p_degrees);
+ float get_autoturn_tolerance() const;
+
+ void set_autoturn_speed(float p_speed);
+ float get_autoturn_speed() const;
+
+ void set_smoothing(bool p_enable);
+ bool has_smoothing() const;
+
+ void set_translation_smoothing(float p_amount);
+ float get_translation_smoothing() const;
+
+ void set_rotation_smoothing(float p_amount);
+ float get_rotation_smoothing() const;
+
+ void set_use_lookat_target(bool p_use, const Vector3 &p_lookat = Vector3());
+
+ void set_up_vector(const Vector3& p_up);
+ Vector3 get_up_vector() const;
+
+ virtual Transform get_camera_transform() const;
+
+ FollowCamera();
+ ~FollowCamera();
+};
+
+
+
+#endif // FOLLOW_CAMERA_H
diff --git a/scene/3d/interpolated_camera.cpp b/scene/3d/interpolated_camera.cpp
new file mode 100644
index 0000000000..4d8c9cf7a5
--- /dev/null
+++ b/scene/3d/interpolated_camera.cpp
@@ -0,0 +1,157 @@
+/*************************************************************************/
+/* interpolated_camera.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 "interpolated_camera.h"
+
+
+void InterpolatedCamera::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (get_scene()->is_editor_hint() && enabled)
+ set_fixed_process(false);
+
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+ if (!enabled)
+ break;
+ if (has_node(target)) {
+
+ Spatial *node = get_node(target)->cast_to<Spatial>();
+ if (!node)
+ break;
+
+ float delta = speed*get_process_delta_time();
+ Transform target_xform = node->get_global_transform();
+ Transform local_transform = get_transform();
+ local_transform = local_transform.interpolate_with(target_xform,delta);
+ set_global_transform(local_transform);
+
+ if (node->cast_to<Camera>()) {
+ Camera *cam = node->cast_to<Camera>();
+ if (cam->get_projection()==get_projection()) {
+
+ float new_near = Math::lerp(get_znear(),cam->get_znear(),delta);
+ float new_far = Math::lerp(get_zfar(),cam->get_zfar(),delta);
+
+ if (cam->get_projection()==PROJECTION_ORTHOGONAL) {
+
+ float size = Math::lerp(get_size(),cam->get_size(),delta);
+ set_orthogonal(size,new_near,new_far);
+ } else {
+
+ float fov = Math::lerp(get_fov(),cam->get_fov(),delta);
+ set_perspective(fov,new_near,new_far);
+ }
+ }
+ }
+
+
+ }
+
+ } break;
+ }
+}
+
+void InterpolatedCamera::_set_target(const Object *p_target) {
+
+ ERR_FAIL_NULL(p_target);
+ set_target(p_target->cast_to<Spatial>());
+}
+
+void InterpolatedCamera::set_target(const Spatial *p_target) {
+
+ ERR_FAIL_NULL(p_target);
+ target=get_path_to(p_target);
+}
+
+
+void InterpolatedCamera::set_target_path(const NodePath& p_path){
+
+ target=p_path;
+}
+
+NodePath InterpolatedCamera::get_target_path() const{
+
+ return target;
+}
+
+void InterpolatedCamera::set_interpolation_enabled(bool p_enable) {
+
+ if (enabled==p_enable)
+ return;
+ enabled=p_enable;
+ if (p_enable) {
+ if (is_inside_scene() && get_scene()->is_editor_hint())
+ return;
+ set_process(true);
+ } else
+ set_process(false);
+}
+
+bool InterpolatedCamera::is_interpolation_enabled() const {
+
+ return enabled;
+}
+
+void InterpolatedCamera::set_speed(real_t p_speed) {
+
+ speed=p_speed;
+}
+
+real_t InterpolatedCamera::get_speed() const {
+
+ return speed;
+}
+
+
+void InterpolatedCamera::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_target_path","target_path"),&InterpolatedCamera::set_target_path);
+ ObjectTypeDB::bind_method(_MD("get_target_path"),&InterpolatedCamera::get_target_path);
+ ObjectTypeDB::bind_method(_MD("set_target","target"),&InterpolatedCamera::_set_target);
+
+ ObjectTypeDB::bind_method(_MD("set_speed","speed"),&InterpolatedCamera::set_speed);
+ ObjectTypeDB::bind_method(_MD("get_speed"),&InterpolatedCamera::get_speed);
+
+ ObjectTypeDB::bind_method(_MD("set_interpolation_enabled","target_path"),&InterpolatedCamera::set_interpolation_enabled);
+ ObjectTypeDB::bind_method(_MD("is_interpolation_enabled"),&InterpolatedCamera::is_interpolation_enabled);
+
+ ADD_PROPERTY( PropertyInfo(Variant::NODE_PATH,"target"), _SCS("set_target_path"), _SCS("get_target_path") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"speed"), _SCS("set_speed"), _SCS("get_speed") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"), _SCS("set_interpolation_enabled"), _SCS("is_interpolation_enabled") );
+}
+
+InterpolatedCamera::InterpolatedCamera() {
+
+ enabled=false;
+ speed=1;
+
+}
diff --git a/scene/3d/interpolated_camera.h b/scene/3d/interpolated_camera.h
new file mode 100644
index 0000000000..da0e3d562b
--- /dev/null
+++ b/scene/3d/interpolated_camera.h
@@ -0,0 +1,63 @@
+/*************************************************************************/
+/* interpolated_camera.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 INTERPOLATED_CAMERA_H
+#define INTERPOLATED_CAMERA_H
+
+#include "scene/3d/camera.h"
+
+class InterpolatedCamera : public Camera {
+
+ OBJ_TYPE(InterpolatedCamera,Camera);
+
+ bool enabled;
+ real_t speed;
+ NodePath target;
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+ void _set_target(const Object *p_target);
+
+public:
+
+ void set_target(const Spatial *p_target);
+ void set_target_path(const NodePath& p_path);
+ NodePath get_target_path() const;
+
+ void set_speed(real_t p_speed);
+ real_t get_speed() const;
+
+ void set_interpolation_enabled(bool p_enable);
+ bool is_interpolation_enabled() const;
+
+
+ InterpolatedCamera();
+};
+
+#endif // INTERPOLATED_CAMERA_H
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
new file mode 100644
index 0000000000..94c56850ef
--- /dev/null
+++ b/scene/3d/light.cpp
@@ -0,0 +1,585 @@
+/*************************************************************************/
+/* light.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 "light.h"
+
+#include "globals.h"
+#include "scene/resources/surface_tool.h"
+
+
+static const char* _light_param_names[VS::LIGHT_PARAM_MAX]={
+ "params/spot_attenuation",
+ "params/spot_angle",
+ "params/radius",
+ "params/energy",
+ "params/attenuation",
+ "shadow/darkening",
+ "shadow/z_offset",
+ "shadow/z_slope_scale"
+};
+
+void Light::set_parameter(Parameter p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param, PARAM_MAX);
+ vars[p_param]=p_value;
+ VisualServer::get_singleton()->light_set_param(light,(VisualServer::LightParam)p_param,p_value);
+ if (p_param==PARAM_RADIUS || p_param==PARAM_SPOT_ANGLE)
+ update_gizmo();
+ _change_notify(_light_param_names[p_param]);
+// _change_notify(_param_names[p_param]);
+}
+
+float Light::get_parameter(Parameter p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0);
+ return vars[p_param];
+
+}
+
+void Light::set_color(LightColor p_color, const Color& p_value) {
+
+ ERR_FAIL_INDEX(p_color, 3);
+ colors[p_color]=p_value;
+ VisualServer::get_singleton()->light_set_color(light,(VisualServer::LightColor)p_color,p_value);
+ //_change_notify(_color_names[p_color]);
+
+}
+Color Light::get_color(LightColor p_color) const {
+
+ ERR_FAIL_INDEX_V(p_color, 3, Color());
+ return colors[p_color];
+
+}
+
+
+void Light::set_project_shadows(bool p_enabled) {
+
+ shadows=p_enabled;
+ VisualServer::get_singleton()->light_set_shadow(light, p_enabled);
+ _change_notify("shadow");
+}
+bool Light::has_project_shadows() const {
+
+ return shadows;
+}
+
+void Light::set_projector(const Ref<Texture>& p_projector) {
+
+ projector=p_projector;
+ VisualServer::get_singleton()->light_set_projector(light, projector.is_null()?RID():projector->get_rid());
+}
+
+Ref<Texture> Light::get_projector() const {
+
+ return projector;
+}
+
+
+bool Light::_can_gizmo_scale() const {
+
+ return false;
+}
+
+
+static void _make_sphere(int p_lats, int p_lons, float p_radius, Ref<SurfaceTool> p_tool) {
+
+
+ p_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for(int i = 1; i <= p_lats; i++) {
+ double lat0 = Math_PI * (-0.5 + (double) (i - 1) / p_lats);
+ double z0 = Math::sin(lat0);
+ double zr0 = Math::cos(lat0);
+
+ double lat1 = Math_PI * (-0.5 + (double) i / p_lats);
+ double z1 = Math::sin(lat1);
+ double zr1 = Math::cos(lat1);
+
+ for(int j = p_lons; j >= 1; j--) {
+
+ double lng0 = 2 * Math_PI * (double) (j - 1) / p_lons;
+ double x0 = Math::cos(lng0);
+ double y0 = Math::sin(lng0);
+
+ double lng1 = 2 * Math_PI * (double) (j) / p_lons;
+ double x1 = Math::cos(lng1);
+ double y1 = Math::sin(lng1);
+
+
+ Vector3 v[4]={
+ Vector3(x1 * zr0, z0, y1 *zr0),
+ Vector3(x1 * zr1, z1, y1 *zr1),
+ Vector3(x0 * zr1, z1, y0 *zr1),
+ Vector3(x0 * zr0, z0, y0 *zr0)
+ };
+
+#define ADD_POINT(m_idx) \
+ p_tool->add_normal(v[m_idx]);\
+ p_tool->add_vertex(v[m_idx]*p_radius);
+
+ ADD_POINT(0);
+ ADD_POINT(1);
+ ADD_POINT(2);
+
+ ADD_POINT(2);
+ ADD_POINT(3);
+ ADD_POINT(0);
+ }
+ }
+
+}
+
+RES Light::_get_gizmo_geometry() const {
+
+
+ Ref<FixedMaterial> mat_area( memnew( FixedMaterial ));
+
+ mat_area->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.7,0.6,0.0,0.05) );
+ mat_area->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.7,0.7,0.7) );
+ mat_area->set_blend_mode( Material::BLEND_MODE_ADD );
+ mat_area->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat_area->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ Ref<FixedMaterial> mat_light( memnew( FixedMaterial ));
+
+ mat_light->set_parameter( FixedMaterial::PARAM_DIFFUSE, Color(1.0,1.0,0.8,0.9) );
+ mat_light->set_flag(Material::FLAG_UNSHADED,true);
+
+ Ref< Mesh > mesh;
+
+ Ref<SurfaceTool> surftool( memnew( SurfaceTool ));
+
+ switch(type) {
+
+ case VisualServer::LIGHT_DIRECTIONAL: {
+
+
+ mat_area->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.9,0.8,0.1,0.8) );
+ mat_area->set_blend_mode( Material::BLEND_MODE_MIX);
+ mat_area->set_flag(Material::FLAG_DOUBLE_SIDED,false);
+ mat_area->set_flag(Material::FLAG_UNSHADED,true);
+
+ _make_sphere( 5,5,0.6, surftool );
+ surftool->set_material(mat_light);
+ mesh=surftool->commit(mesh);
+
+ // float radius=1;
+
+ surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ const int arrow_points=5;
+ Vector3 arrow[arrow_points]={
+ Vector3(0,0,2),
+ Vector3(1,1,2),
+ Vector3(1,1,-1),
+ Vector3(2,2,-1),
+ Vector3(0,0,-3)
+ };
+
+ int arrow_sides=4;
+
+
+ for(int i = 0; i < arrow_sides ; i++) {
+
+
+ Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/arrow_sides);
+ Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/arrow_sides);
+
+
+ for(int j=0;j<arrow_points-1;j++) {
+
+ Vector3 points[4]={
+ ma.xform(arrow[j]),
+ mb.xform(arrow[j]),
+ mb.xform(arrow[j+1]),
+ ma.xform(arrow[j+1]),
+ };
+
+ Vector3 n = Plane(points[0],points[1],points[2]).normal;
+
+ surftool->add_normal(n);
+ surftool->add_vertex(points[0]);
+ surftool->add_normal(n);
+ surftool->add_vertex(points[1]);
+ surftool->add_normal(n);
+ surftool->add_vertex(points[2]);
+
+ surftool->add_normal(n);
+ surftool->add_vertex(points[0]);
+ surftool->add_normal(n);
+ surftool->add_vertex(points[2]);
+ surftool->add_normal(n);
+ surftool->add_vertex(points[3]);
+
+
+ }
+
+
+ }
+
+ surftool->set_material(mat_area);
+ mesh=surftool->commit(mesh);
+
+
+
+ } break;
+ case VisualServer::LIGHT_OMNI: {
+
+
+ _make_sphere( 20,20,vars[PARAM_RADIUS], surftool );
+ surftool->set_material(mat_area);
+ mesh=surftool->commit(mesh);
+ _make_sphere(5,5, 0.1, surftool );
+ surftool->set_material(mat_light);
+ mesh=surftool->commit(mesh);
+ } break;
+
+ case VisualServer::LIGHT_SPOT: {
+
+ _make_sphere( 5,5,0.1, surftool );
+ surftool->set_material(mat_light);
+ mesh=surftool->commit(mesh);
+
+ // make cone
+ int points=24;
+ float len=vars[PARAM_RADIUS];
+ float size=Math::tan(Math::deg2rad(vars[PARAM_SPOT_ANGLE]))*len;
+
+ surftool->begin(Mesh::PRIMITIVE_TRIANGLES);
+
+ for(int i = 0; i < points; i++) {
+
+ float x0=Math::sin(i * Math_PI * 2 / points);
+ float y0=Math::cos(i * Math_PI * 2 / points);
+ float x1=Math::sin((i+1) * Math_PI * 2 / points);
+ float y1=Math::cos((i+1) * Math_PI * 2 / points);
+
+ Vector3 v1=Vector3(x0*size,y0*size,-len).normalized()*len;
+ Vector3 v2=Vector3(x1*size,y1*size,-len).normalized()*len;
+
+ Vector3 v3=Vector3(0,0,0);
+ Vector3 v4=Vector3(0,0,v1.z);
+
+ Vector3 n = Plane(v1,v2,v3).normal;
+
+
+ surftool->add_normal(n);
+ surftool->add_vertex(v1);
+ surftool->add_normal(n);
+ surftool->add_vertex(v2);
+ surftool->add_normal(n);
+ surftool->add_vertex(v3);
+
+ n=Vector3(0,0,-1);
+
+ surftool->add_normal(n);
+ surftool->add_vertex(v1);
+ surftool->add_normal(n);
+ surftool->add_vertex(v2);
+ surftool->add_normal(n);
+ surftool->add_vertex(v4);
+
+
+ }
+
+ surftool->set_material(mat_area);
+ mesh=surftool->commit(mesh);
+
+
+ } break;
+ }
+
+ return mesh;
+}
+
+
+AABB Light::get_aabb() const {
+
+ if (type==VisualServer::LIGHT_DIRECTIONAL) {
+
+ return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) );
+
+ } else if (type==VisualServer::LIGHT_OMNI) {
+
+ return AABB( Vector3(-1,-1,-1) * vars[PARAM_RADIUS], Vector3(2, 2, 2 ) * vars[PARAM_RADIUS]);
+
+ } else if (type==VisualServer::LIGHT_SPOT) {
+
+ float len=vars[PARAM_RADIUS];
+ float size=Math::tan(Math::deg2rad(vars[PARAM_SPOT_ANGLE]))*len;
+ return AABB( Vector3( -size,-size,-len ), Vector3( size*2, size*2, len ) );
+ }
+
+ return AABB();
+}
+
+DVector<Face3> Light::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+}
+
+
+void Light::set_operator(Operator p_op) {
+ ERR_FAIL_INDEX(p_op,2);
+ op=p_op;
+ VisualServer::get_singleton()->light_set_operator(light,VS::LightOp(op));
+
+}
+
+Light::Operator Light::get_operator() const {
+
+ return op;
+}
+
+void Light::approximate_opengl_attenuation(float p_constant, float p_linear, float p_quadratic,float p_radius_treshold) {
+
+ //this is horrible and must never be used
+
+ float a = p_quadratic * p_radius_treshold;
+ float b = p_linear * p_radius_treshold;
+ float c = p_constant * p_radius_treshold -1;
+
+ float radius=10000;
+
+ if(a == 0) { // solve linear
+ float d = Math::abs(-c/b);
+ if(d<radius)
+ radius=d;
+
+
+ } else { // solve quadratic
+ // now ad^2 + bd + c = 0, solve quadratic equation:
+
+ float denominator = 2*a;
+
+ if(denominator != 0) {
+
+
+ float root = b*b - 4*a*c;
+
+ if(root >=0) {
+
+ root = sqrt(root);
+
+ float solution1 = fabs( (-b + root) / denominator);
+ float solution2 = fabs( (-b - root) / denominator);
+
+ if(solution1 > radius)
+ solution1 = radius;
+
+ if(solution2 > radius)
+ solution2 = radius;
+
+ radius = (solution1 > solution2 ? solution1 : solution2);
+ }
+ }
+ }
+
+ float energy=1.0;
+
+ if (p_constant>0)
+ energy=1.0/p_constant; //energy is this
+ else
+ energy=8.0; // some high number..
+
+
+ if (radius==10000)
+ radius=100; //bug?
+
+ set_parameter(PARAM_RADIUS,radius);
+ set_parameter(PARAM_ENERGY,energy);
+
+}
+
+void Light::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_parameter","variable","value"), &Light::set_parameter );
+ ObjectTypeDB::bind_method(_MD("get_parameter"), &Light::get_parameter );
+ ObjectTypeDB::bind_method(_MD("set_color","color","value"), &Light::set_color );
+ ObjectTypeDB::bind_method(_MD("get_color"), &Light::get_color );
+ ObjectTypeDB::bind_method(_MD("set_project_shadows","enable"), &Light::set_project_shadows );
+ ObjectTypeDB::bind_method(_MD("has_project_shadows"), &Light::has_project_shadows );
+ ObjectTypeDB::bind_method(_MD("set_projector","projector:Texture"), &Light::set_projector );
+ ObjectTypeDB::bind_method(_MD("get_projector:Texture"), &Light::get_projector );
+ ObjectTypeDB::bind_method(_MD("set_operator","operator"), &Light::set_operator );
+ ObjectTypeDB::bind_method(_MD("get_operator"), &Light::get_operator );
+
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/energy", PROPERTY_HINT_EXP_RANGE, "0,64,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ENERGY );
+ /*
+ if (type == VisualServer::LIGHT_OMNI || type == VisualServer::LIGHT_SPOT) {
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_RANGE, "0.01,4096,0.01"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_RANGE, "0,8,0.01"));
+ }
+
+ if (type == VisualServer::LIGHT_SPOT) {
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/spot_angle", PROPERTY_HINT_RANGE, "0.01,90.0,0.01"));
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "params/spot_attenuation", PROPERTY_HINT_RANGE, "0,8,0.01"));
+
+ }*/
+
+ ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/ambient"), _SCS("set_color"), _SCS("get_color"),COLOR_AMBIENT);
+ ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/diffuse"), _SCS("set_color"), _SCS("get_color"),COLOR_DIFFUSE);
+ ADD_PROPERTYI( PropertyInfo( Variant::COLOR, "colors/specular"), _SCS("set_color"), _SCS("get_color"),COLOR_SPECULAR);
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "shadow/shadow"), _SCS("set_project_shadows"), _SCS("has_project_shadows"));
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/darkening", PROPERTY_HINT_RANGE, "0,64,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_DARKENING );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/z_offset", PROPERTY_HINT_RANGE, "0,128,0.001"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_Z_OFFSET);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/z_slope_scale", PROPERTY_HINT_RANGE, "0,128,0.001"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_Z_SLOPE_SCALE);
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "projector",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_projector"), _SCS("get_projector"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "operator",PROPERTY_HINT_ENUM,"Add,Sub"), _SCS("set_operator"), _SCS("get_operator"));
+
+
+ BIND_CONSTANT( PARAM_RADIUS );
+ BIND_CONSTANT( PARAM_ENERGY );
+ BIND_CONSTANT( PARAM_ATTENUATION );
+ BIND_CONSTANT( PARAM_SPOT_ANGLE );
+ BIND_CONSTANT( PARAM_SPOT_ATTENUATION );
+ BIND_CONSTANT( PARAM_SHADOW_DARKENING );
+ BIND_CONSTANT( PARAM_SHADOW_Z_OFFSET );
+
+ BIND_CONSTANT( COLOR_AMBIENT );
+ BIND_CONSTANT( COLOR_DIFFUSE );
+ BIND_CONSTANT( COLOR_SPECULAR );
+
+}
+
+
+Light::Light(VisualServer::LightType p_type) {
+
+ type=p_type;
+ light=VisualServer::get_singleton()->light_create(p_type);
+
+ set_parameter(PARAM_SPOT_ATTENUATION,1.0);
+ set_parameter(PARAM_SPOT_ANGLE,30.0);
+ set_parameter(PARAM_RADIUS,2.0);
+ set_parameter(PARAM_ENERGY,1.0);
+ set_parameter(PARAM_ATTENUATION,1.0);
+ set_parameter(PARAM_SHADOW_DARKENING,0.0);
+ set_parameter(PARAM_SHADOW_Z_OFFSET,0.05);
+ set_parameter(PARAM_SHADOW_Z_SLOPE_SCALE,0);
+
+ set_color( COLOR_AMBIENT, Color(0,0,0));
+ set_color( COLOR_DIFFUSE, Color(1,1,1));
+ set_color( COLOR_SPECULAR, Color(1,1,1));
+
+ op=OPERATOR_ADD;
+ set_project_shadows( false );
+ set_base(light);
+
+}
+
+
+Light::Light() {
+
+ type=VisualServer::LIGHT_DIRECTIONAL;
+ ERR_PRINT("Light shouldn't be instanced dircetly, use the subtypes.");
+}
+
+
+Light::~Light() {
+
+ if (light.is_valid())
+ VisualServer::get_singleton()->free(light);
+}
+/////////////////////////////////////////
+
+
+void DirectionalLight::set_shadow_mode(ShadowMode p_mode) {
+
+ shadow_mode=p_mode;
+ VS::get_singleton()->light_directional_set_shadow_mode(light,(VS::LightDirectionalShadowMode)p_mode);
+
+}
+
+DirectionalLight::ShadowMode DirectionalLight::get_shadow_mode() const{
+
+ return shadow_mode;
+}
+
+void DirectionalLight::set_shadow_param(ShadowParam p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,3);
+ shadow_param[p_param]=p_value;
+ VS::get_singleton()->light_directional_set_shadow_param(light,VS::LightDirectionalShadowParam(p_param),p_value);
+}
+
+float DirectionalLight::get_shadow_param(ShadowParam p_param) const {
+ ERR_FAIL_INDEX_V(p_param,3,0);
+ return shadow_param[p_param];
+}
+
+void DirectionalLight::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_shadow_mode","mode"),&DirectionalLight::set_shadow_mode);
+ ObjectTypeDB::bind_method(_MD("get_shadow_mode"),&DirectionalLight::get_shadow_mode);
+ ObjectTypeDB::bind_method(_MD("set_shadow_param","param","value"),&DirectionalLight::set_shadow_param);
+ ObjectTypeDB::bind_method(_MD("get_shadow_param","param"),&DirectionalLight::get_shadow_param);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"shadow/mode",PROPERTY_HINT_ENUM,"Orthogonal,Perspective,PSSM"),_SCS("set_shadow_mode"),_SCS("get_shadow_mode"));
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/max_distance",PROPERTY_HINT_EXP_RANGE,"0.00,99999,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_MAX_DISTANCE);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/split_weight",PROPERTY_HINT_RANGE,"0.01,1.0,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_PSSM_SPLIT_WEIGHT);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"shadow/zoffset_scale",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_shadow_param"),_SCS("get_shadow_param"), SHADOW_PARAM_PSSM_ZOFFSET_SCALE);
+
+ BIND_CONSTANT( SHADOW_ORTHOGONAL );
+ BIND_CONSTANT( SHADOW_PERSPECTIVE );
+ BIND_CONSTANT( SHADOW_PARALLEL_SPLIT );
+ BIND_CONSTANT( SHADOW_PARAM_MAX_DISTANCE );
+ BIND_CONSTANT( SHADOW_PARAM_PSSM_SPLIT_WEIGHT );
+ BIND_CONSTANT( SHADOW_PARAM_PSSM_ZOFFSET_SCALE );
+
+}
+
+
+DirectionalLight::DirectionalLight() : Light( VisualServer::LIGHT_DIRECTIONAL ) {
+
+ shadow_mode=SHADOW_ORTHOGONAL;
+ shadow_param[SHADOW_PARAM_MAX_DISTANCE]=0;
+ shadow_param[SHADOW_PARAM_PSSM_SPLIT_WEIGHT]=0.5;
+ shadow_param[SHADOW_PARAM_PSSM_ZOFFSET_SCALE]=2.0;
+
+}
+
+
+void OmniLight::_bind_methods() {
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_EXP_RANGE, "0.2,4096,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_RADIUS );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION );
+
+}
+
+void SpotLight::_bind_methods() {
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/radius", PROPERTY_HINT_EXP_RANGE, "0.2,4096,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_RADIUS );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION );
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/spot_angle", PROPERTY_HINT_RANGE, "0.01,89.9,0.01"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SPOT_ANGLE );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_ATTENUATION );
+
+}
+
+
diff --git a/scene/3d/light.h b/scene/3d/light.h
new file mode 100644
index 0000000000..03bf336303
--- /dev/null
+++ b/scene/3d/light.h
@@ -0,0 +1,195 @@
+/*************************************************************************/
+/* light.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 LIGHT_H
+#define LIGHT_H
+
+
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/texture.h"
+#include "servers/visual_server.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class Light : public VisualInstance {
+
+ OBJ_TYPE( Light, VisualInstance );
+ OBJ_CATEGORY("3D Light Nodes");
+
+public:
+
+ enum Parameter {
+ PARAM_RADIUS=VisualServer::LIGHT_PARAM_RADIUS,
+ PARAM_ENERGY=VisualServer::LIGHT_PARAM_ENERGY,
+ PARAM_ATTENUATION=VisualServer::LIGHT_PARAM_ATTENUATION,
+ PARAM_SPOT_ANGLE=VisualServer::LIGHT_PARAM_SPOT_ANGLE,
+ PARAM_SPOT_ATTENUATION=VisualServer::LIGHT_PARAM_ATTENUATION,
+ PARAM_SHADOW_DARKENING=VisualServer::LIGHT_PARAM_SHADOW_DARKENING,
+ PARAM_SHADOW_Z_OFFSET=VisualServer::LIGHT_PARAM_SHADOW_Z_OFFSET,
+ PARAM_SHADOW_Z_SLOPE_SCALE=VisualServer::LIGHT_PARAM_SHADOW_Z_SLOPE_SCALE,
+ PARAM_MAX=VisualServer::LIGHT_PARAM_MAX
+ };
+
+
+ enum LightColor {
+
+ COLOR_AMBIENT=VisualServer::LIGHT_COLOR_AMBIENT,
+ COLOR_DIFFUSE=VisualServer::LIGHT_COLOR_DIFFUSE,
+ COLOR_SPECULAR=VisualServer::LIGHT_COLOR_SPECULAR
+ };
+
+
+ enum Operator {
+
+ OPERATOR_ADD,
+ OPERATOR_SUB
+ };
+private:
+
+
+ Ref<Texture> projector;
+ float vars[PARAM_MAX];
+ Color colors[3];
+
+
+ VisualServer::LightType type;
+ bool shadows;
+ Operator op;
+
+// bind helpers
+
+protected:
+
+ RID light;
+
+ virtual bool _can_gizmo_scale() const;
+ virtual RES _get_gizmo_geometry() const;
+
+ static void _bind_methods();
+
+
+ Light(VisualServer::LightType p_type);
+public:
+
+ VS::LightType get_light_type() const { return type; }
+
+ void set_parameter(Parameter p_var, float p_value);
+ float get_parameter(Parameter p_var) const;
+
+ void set_color(LightColor p_color,const Color& p_value);
+ Color get_color(LightColor p_color) const;
+
+ void set_project_shadows(bool p_enabled);
+ bool has_project_shadows() const;
+
+ void set_projector(const Ref<Texture>& p_projector);
+ Ref<Texture> get_projector() const;
+
+ void set_operator(Operator p_op);
+ Operator get_operator() const;
+
+ virtual AABB get_aabb() const;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void approximate_opengl_attenuation(float p_constant, float p_linear, float p_quadratic, float p_radius_treshold=0.5);
+
+ Light();
+ ~Light();
+
+};
+
+VARIANT_ENUM_CAST( Light::Parameter );
+VARIANT_ENUM_CAST( Light::LightColor );
+VARIANT_ENUM_CAST( Light::Operator );
+
+
+class DirectionalLight : public Light {
+
+ OBJ_TYPE( DirectionalLight, Light );
+
+public:
+
+ enum ShadowMode {
+ SHADOW_ORTHOGONAL,
+ SHADOW_PERSPECTIVE,
+ SHADOW_PARALLEL_SPLIT
+ };
+ enum ShadowParam {
+ SHADOW_PARAM_MAX_DISTANCE,
+ SHADOW_PARAM_PSSM_SPLIT_WEIGHT,
+ SHADOW_PARAM_PSSM_ZOFFSET_SCALE
+ };
+
+private:
+ ShadowMode shadow_mode;
+ float shadow_param[3];
+protected:
+ static void _bind_methods();
+public:
+
+ void set_shadow_mode(ShadowMode p_mode);
+ ShadowMode get_shadow_mode() const;
+
+ void set_shadow_max_distance(float p_distance);
+ float get_shadow_max_distance() const;
+ void set_shadow_param(ShadowParam p_param, float p_value);
+ float get_shadow_param(ShadowParam p_param) const;
+
+ DirectionalLight();
+};
+
+VARIANT_ENUM_CAST( DirectionalLight::ShadowMode );
+VARIANT_ENUM_CAST( DirectionalLight::ShadowParam );
+
+
+class OmniLight : public Light {
+
+ OBJ_TYPE( OmniLight, Light );
+protected:
+ static void _bind_methods();
+
+public:
+
+
+ OmniLight() : Light( VisualServer::LIGHT_OMNI ) {}
+};
+
+class SpotLight : public Light {
+
+ OBJ_TYPE( SpotLight, Light );
+protected:
+ static void _bind_methods();
+public:
+
+
+ SpotLight() : Light( VisualServer::LIGHT_SPOT ) {}
+};
+
+
+#endif
diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp
new file mode 100644
index 0000000000..6387f5fdbc
--- /dev/null
+++ b/scene/3d/mesh_instance.cpp
@@ -0,0 +1,218 @@
+/*************************************************************************/
+/* mesh_instance.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 "mesh_instance.h"
+
+#include "skeleton.h"
+#include "physics_body.h"
+
+
+
+bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) {
+
+ //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else.
+ //add to it that it's probably found on first call to _set anyway.
+
+ if (!get_instance().is_valid())
+ return false;
+
+
+ Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name);
+ if (!E)
+ return false;
+
+ E->get().value=p_value;
+ VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value);
+
+ return true;
+}
+
+bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const {
+
+
+ if (!get_instance().is_valid())
+ return false;
+
+ const Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name);
+ if (!E)
+ return false;
+
+ r_ret = E->get().value;
+
+ return true;
+}
+
+void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ List<String> ls;
+ for(const Map<StringName,MorphTrack>::Element *E=morph_tracks.front();E;E=E->next()) {
+
+ ls.push_back(E->key());
+ }
+
+ ls.sort();;
+
+ for(List<String>::Element *E=ls.front();E;E=E->next()) {
+ p_list->push_back( PropertyInfo(Variant::REAL,E->get(),PROPERTY_HINT_RANGE,"0,1,0.01"));
+ }
+}
+
+
+
+
+void MeshInstance::set_mesh(const Ref<Mesh>& p_mesh) {
+
+ mesh=p_mesh;
+
+ morph_tracks.clear();
+ if (mesh.is_valid()) {
+
+
+ for(int i=0;i<mesh->get_morph_target_count();i++) {
+
+ MorphTrack mt;
+ mt.idx=i;
+ mt.value=0;
+ morph_tracks["morph/"+String(mesh->get_morph_target_name(i))]=mt;
+ }
+ set_base(mesh->get_rid());
+ } else {
+
+ set_base(RID());
+ }
+
+ _change_notify("mesh");
+}
+Ref<Mesh> MeshInstance::get_mesh() const {
+
+ return mesh;
+}
+
+
+AABB MeshInstance::get_aabb() const {
+
+ if (!mesh.is_null())
+ return mesh->get_aabb();
+
+ return AABB();
+}
+
+DVector<Face3> MeshInstance::get_faces(uint32_t p_usage_flags) const {
+
+ if (!(p_usage_flags&(FACES_SOLID|FACES_ENCLOSING)))
+ return DVector<Face3>();
+
+ if (mesh.is_null())
+ return DVector<Face3>();
+
+ return mesh->get_faces();
+}
+
+
+Node* MeshInstance::create_trimesh_collision_node() {
+
+ if (mesh.is_null())
+ return NULL;
+
+ Ref<Shape> shape = mesh->create_trimesh_shape();
+ if (shape.is_null())
+ return NULL;
+
+ StaticBody * static_body = memnew( StaticBody );
+ static_body->add_shape( shape );
+ return static_body;
+
+ return NULL;
+}
+
+void MeshInstance::create_trimesh_collision() {
+
+
+ StaticBody* static_body = create_trimesh_collision_node()->cast_to<StaticBody>();
+ ERR_FAIL_COND(!static_body);
+ static_body->set_name( String(get_name()) + "_col" );
+
+ add_child(static_body);
+ if (get_owner())
+ static_body->set_owner( get_owner() );
+
+}
+
+Node* MeshInstance::create_convex_collision_node() {
+
+ if (mesh.is_null())
+ return NULL;
+
+ Ref<Shape> shape = mesh->create_convex_shape();
+ if (shape.is_null())
+ return NULL;
+
+ StaticBody * static_body = memnew( StaticBody );
+ static_body->add_shape( shape );
+ return static_body;
+
+ return NULL;
+}
+
+void MeshInstance::create_convex_collision() {
+
+
+ StaticBody* static_body = create_convex_collision_node()->cast_to<StaticBody>();
+ ERR_FAIL_COND(!static_body);
+ static_body->set_name( String(get_name()) + "_col" );
+
+ add_child(static_body);
+ if (get_owner())
+ static_body->set_owner( get_owner() );
+
+}
+
+void MeshInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_mesh","mesh:Mesh"),&MeshInstance::set_mesh);
+ ObjectTypeDB::bind_method(_MD("get_mesh:Mesh"),&MeshInstance::get_mesh);
+ ObjectTypeDB::bind_method(_MD("get_aabb"),&MeshInstance::get_aabb);
+ ObjectTypeDB::bind_method(_MD("create_trimesh_collision"),&MeshInstance::create_trimesh_collision);
+ ObjectTypeDB::set_method_flags("MeshInstance","create_trimesh_collision",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR);
+ ObjectTypeDB::bind_method(_MD("create_convex_collision"),&MeshInstance::create_convex_collision);
+ ObjectTypeDB::set_method_flags("MeshInstance","create_convex_collision",METHOD_FLAGS_DEFAULT|METHOD_FLAG_EDITOR);
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "mesh/mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh" ), _SCS("set_mesh"), _SCS("get_mesh"));
+
+
+}
+
+MeshInstance::MeshInstance()
+{
+}
+
+
+MeshInstance::~MeshInstance() {
+
+}
+
+
diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h
new file mode 100644
index 0000000000..0e07109502
--- /dev/null
+++ b/scene/3d/mesh_instance.h
@@ -0,0 +1,80 @@
+/*************************************************************************/
+/* mesh_instance.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 MESH_INSTANCE_H
+#define MESH_INSTANCE_H
+
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/mesh.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class MeshInstance : public GeometryInstance {
+
+ OBJ_TYPE( MeshInstance, GeometryInstance );
+
+ Ref<Mesh> mesh;
+
+ struct MorphTrack {
+
+ int idx;
+ float value;
+ MorphTrack() { idx=0; value=0; }
+ };
+
+ Map<StringName,MorphTrack> morph_tracks;
+
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+
+ static void _bind_methods();
+public:
+
+ void set_mesh(const Ref<Mesh>& p_mesh);
+ Ref<Mesh> get_mesh() const;
+
+ Node* create_trimesh_collision_node();
+ void create_trimesh_collision();
+
+ Node* create_convex_collision_node();
+ void create_convex_collision();
+
+ virtual AABB get_aabb() const;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ MeshInstance();
+ ~MeshInstance();
+};
+
+#endif
diff --git a/scene/3d/multimesh_instance.cpp b/scene/3d/multimesh_instance.cpp
new file mode 100644
index 0000000000..b4e58aca40
--- /dev/null
+++ b/scene/3d/multimesh_instance.cpp
@@ -0,0 +1,81 @@
+/*************************************************************************/
+/* multimesh_instance.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 "multimesh_instance.h"
+
+
+
+
+
+void MultiMeshInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_multimesh","multimesh"),&MultiMeshInstance::set_multimesh);
+ ObjectTypeDB::bind_method(_MD("get_multimesh"),&MultiMeshInstance::get_multimesh);
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"multimesh",PROPERTY_HINT_RESOURCE_TYPE,"MultiMesh"), _SCS("set_multimesh"), _SCS("get_multimesh"));
+
+
+}
+
+void MultiMeshInstance::set_multimesh(const Ref<MultiMesh>& p_multimesh) {
+
+ multimesh=p_multimesh;
+ if (multimesh.is_valid())
+ set_base(multimesh->get_rid());
+ else
+ set_base(RID());
+
+}
+
+Ref<MultiMesh> MultiMeshInstance::get_multimesh() const {
+
+ return multimesh;
+}
+
+
+
+DVector<Face3> MultiMeshInstance::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+}
+
+AABB MultiMeshInstance::get_aabb() const {
+
+ if (multimesh.is_null())
+ return AABB();
+ else
+ return multimesh->get_aabb();
+}
+
+MultiMeshInstance::MultiMeshInstance() {
+
+}
+
+MultiMeshInstance::~MultiMeshInstance() {
+
+
+}
diff --git a/scene/3d/multimesh_instance.h b/scene/3d/multimesh_instance.h
new file mode 100644
index 0000000000..fd50140bae
--- /dev/null
+++ b/scene/3d/multimesh_instance.h
@@ -0,0 +1,64 @@
+/*************************************************************************/
+/* multimesh_instance.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 MULTIMESH_INSTANCE_H
+#define MULTIMESH_INSTANCE_H
+
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/multimesh.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class MultiMeshInstance : public GeometryInstance {
+ OBJ_TYPE( MultiMeshInstance, GeometryInstance );
+
+
+ Ref<MultiMesh> multimesh;
+protected:
+
+
+ static void _bind_methods();
+ // bind helpers
+
+public:
+
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_multimesh(const Ref<MultiMesh>& p_multimesh);
+ Ref<MultiMesh> get_multimesh() const;
+
+ virtual AABB get_aabb() const;
+
+ MultiMeshInstance();
+ ~MultiMeshInstance();
+};
+
+#endif // MULTIMESH_INSTANCE_H
+
diff --git a/scene/3d/optimized_spatial_scene.cpp b/scene/3d/optimized_spatial_scene.cpp
new file mode 100644
index 0000000000..12c847f71c
--- /dev/null
+++ b/scene/3d/optimized_spatial_scene.cpp
@@ -0,0 +1,33 @@
+/*************************************************************************/
+/* optimized_spatial_scene.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 "optimized_spatial_scene.h"
+
+OptimizedSpatialScene::OptimizedSpatialScene()
+{
+}
diff --git a/scene/3d/optimized_spatial_scene.h b/scene/3d/optimized_spatial_scene.h
new file mode 100644
index 0000000000..36ae1ec213
--- /dev/null
+++ b/scene/3d/optimized_spatial_scene.h
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* optimized_spatial_scene.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 OPTIMIZED_SPATIAL_SCENE_H
+#define OPTIMIZED_SPATIAL_SCENE_H
+
+#include "scene/3d/spatial.h"
+
+class OptimizedSpatialScene : public Spatial {
+
+ OBJ_TYPE( OptimizedSpatialScene, Spatial );
+public:
+ OptimizedSpatialScene();
+};
+
+#endif // OPTIMIZED_SPATIAL_SCENE_H
diff --git a/scene/3d/particles.cpp b/scene/3d/particles.cpp
new file mode 100644
index 0000000000..d74768f0ab
--- /dev/null
+++ b/scene/3d/particles.cpp
@@ -0,0 +1,559 @@
+/*************************************************************************/
+/* particles.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.h"
+#include "servers/visual_server.h"
+#include "scene/resources/surface_tool.h"
+
+/*
+static const char* _var_names[Particles::VAR_MAX]={
+ "vars/lifetime",
+ "vars/spread",
+ "vars/gravity",
+ "vars/linear_vel",
+ "vars/angular_vel",
+ "vars/linear_accel",
+ "vars/radial_accel",
+ "vars/tan_accel",
+ "vars/initial_size",
+ "vars/final_size",
+ "vars/initial_angle",
+ "vars/height",
+ "vars/height_speed_scale",
+};
+*/
+static const char* _rand_names[Particles::VAR_MAX]={
+ "rand/lifetime",
+ "rand/spread",
+ "rand/gravity",
+ "rand/linear_vel",
+ "rand/angular_vel",
+ "rand/linear_accel",
+ "rand/radial_accel",
+ "rand/tan_accel",
+ "rand/damping",
+ "rand/initial_size",
+ "rand/final_size",
+ "rand/initial_angle",
+ "rand/height",
+ "rand/height_speed_scale",
+};
+
+static const Particles::Variable _var_indices[Particles::VAR_MAX]={
+ Particles::VAR_LIFETIME,
+ Particles::VAR_SPREAD,
+ Particles::VAR_GRAVITY,
+ Particles::VAR_LINEAR_VELOCITY,
+ Particles::VAR_ANGULAR_VELOCITY,
+ Particles::VAR_LINEAR_ACCELERATION,
+ Particles::VAR_DRAG,
+ Particles::VAR_TANGENTIAL_ACCELERATION,
+ Particles::VAR_DAMPING,
+ Particles::VAR_INITIAL_SIZE,
+ Particles::VAR_FINAL_SIZE,
+ Particles::VAR_INITIAL_ANGLE,
+ Particles::VAR_HEIGHT,
+ Particles::VAR_HEIGHT_SPEED_SCALE,
+};
+
+
+
+AABB Particles::get_aabb() const {
+
+ return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) );
+}
+DVector<Face3> Particles::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+}
+
+
+void Particles::set_amount(int p_amount) {
+
+ ERR_FAIL_INDEX(p_amount,4096);
+ amount=p_amount;
+ VisualServer::get_singleton()->particles_set_amount(particles,p_amount);
+}
+int Particles::get_amount() const {
+
+ return amount;
+}
+
+void Particles::set_emitting(bool p_emitting) {
+
+ emitting=p_emitting;
+ VisualServer::get_singleton()->particles_set_emitting(particles,p_emitting);
+
+ setup_timer();
+}
+bool Particles::is_emitting() const {
+
+ return emitting;
+}
+
+void Particles::set_visibility_aabb(const AABB& p_aabb) {
+
+ visibility_aabb=p_aabb;
+ VisualServer::get_singleton()->particles_set_visibility_aabb(particles,p_aabb);
+ update_gizmo();
+
+}
+AABB Particles::get_visibility_aabb() const {
+
+ return visibility_aabb;
+}
+
+
+void Particles::set_emission_points(const DVector<Vector3>& p_points) {
+
+ using_points = p_points.size();
+ VisualServer::get_singleton()->particles_set_emission_points(particles,p_points);
+}
+
+DVector<Vector3> Particles::get_emission_points() const {
+
+ if (!using_points)
+ return DVector<Vector3>();
+
+ return VisualServer::get_singleton()->particles_get_emission_points(particles);
+
+}
+
+void Particles::set_emission_half_extents(const Vector3& p_half_extents) {
+
+ emission_half_extents=p_half_extents;
+ VisualServer::get_singleton()->particles_set_emission_half_extents(particles,p_half_extents);
+
+}
+
+Vector3 Particles::get_emission_half_extents() const {
+
+ return emission_half_extents;
+}
+
+void Particles::set_emission_base_velocity(const Vector3& p_base_velocity) {
+
+ emission_base_velocity=p_base_velocity;
+ VisualServer::get_singleton()->particles_set_emission_base_velocity(particles,p_base_velocity);
+
+}
+
+Vector3 Particles::get_emission_base_velocity() const {
+
+ return emission_base_velocity;
+}
+
+void Particles::set_gravity_normal(const Vector3& p_normal) {
+
+ gravity_normal=p_normal;
+ VisualServer::get_singleton()->particles_set_gravity_normal(particles,p_normal);
+}
+
+Vector3 Particles::get_gravity_normal() const {
+
+ return gravity_normal;
+
+}
+
+void Particles::set_variable(Variable p_variable,float p_value) {
+
+ ERR_FAIL_INDEX(p_variable,VAR_MAX);
+ var[p_variable]=p_value;
+ VisualServer::get_singleton()->particles_set_variable(particles,(VS::ParticleVariable)p_variable,p_value);
+ if (p_variable==VAR_SPREAD)
+ update_gizmo();
+}
+
+float Particles::get_variable(Variable p_variable) const {
+
+ ERR_FAIL_INDEX_V(p_variable,VAR_MAX,-1);
+ return var[p_variable];
+
+}
+
+void Particles::set_randomness(Variable p_variable,float p_randomness) {
+
+ ERR_FAIL_INDEX(p_variable,VAR_MAX);
+ var_random[p_variable]=p_randomness;
+ VisualServer::get_singleton()->particles_set_randomness(particles,(VS::ParticleVariable)p_variable,p_randomness);
+
+}
+float Particles::get_randomness(Variable p_variable) const {
+
+ ERR_FAIL_INDEX_V(p_variable,VAR_MAX,-1);
+ return var_random[p_variable];
+
+}
+
+void Particles::set_color_phase_pos(int p_phase, float p_pos) {
+
+ ERR_FAIL_INDEX(p_phase,VS::MAX_PARTICLE_COLOR_PHASES);
+ color_phase[p_phase].pos=p_pos;
+ VisualServer::get_singleton()->particles_set_color_phase_pos(particles,p_phase,p_pos);
+
+}
+float Particles::get_color_phase_pos(int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase,VS::MAX_PARTICLE_COLOR_PHASES,-1);
+ return color_phase[p_phase].pos;
+}
+
+void Particles::set_color_phase_color(int p_phase, const Color& p_color) {
+
+ ERR_FAIL_INDEX(p_phase,VS::MAX_PARTICLE_COLOR_PHASES);
+ color_phase[p_phase].color=p_color;
+ VisualServer::get_singleton()->particles_set_color_phase_color(particles,p_phase,p_color);
+
+}
+Color Particles::get_color_phase_color(int p_phase) const {
+
+ ERR_FAIL_INDEX_V(p_phase,VS::MAX_PARTICLE_COLOR_PHASES,Color());
+ return color_phase[p_phase].color;
+
+}
+
+void Particles::set_material(const Ref<Material>& p_material) {
+
+ material=p_material;
+ if(material.is_null()) {
+ VisualServer::get_singleton()->particles_set_material(particles,RID());
+ } else {
+ VisualServer::get_singleton()->particles_set_material(particles,material->get_rid());
+ }
+
+}
+
+void Particles::setup_timer() {
+
+ if (emitting && emit_timeout > 0) {
+
+ timer->set_wait_time(emit_timeout);
+ timer->start();
+ timer->set_one_shot(true);
+ };
+};
+
+void Particles::set_emit_timeout(float p_timeout) {
+
+ emit_timeout = p_timeout;
+ setup_timer();
+};
+
+float Particles::get_emit_timeout() const {
+
+ return emit_timeout;
+};
+
+
+Ref<Material> Particles::get_material() const {
+
+ return material;
+}
+
+void Particles::set_height_from_velocity(bool p_enable) {
+
+ height_from_velocity=p_enable;
+ VisualServer::get_singleton()->particles_set_height_from_velocity(particles,height_from_velocity);
+}
+
+bool Particles::has_height_from_velocity() const {
+
+ return height_from_velocity;
+}
+
+void Particles::set_color_phases(int p_phases) {
+
+ color_phase_count=p_phases;
+ VisualServer::get_singleton()->particles_set_color_phases(particles,p_phases);
+}
+
+int Particles::get_color_phases() const{
+
+ return color_phase_count;
+}
+
+bool Particles::_can_gizmo_scale() const {
+
+ return false;
+}
+
+void Particles::set_use_local_coordinates(bool p_use) {
+
+ local_coordinates=p_use;
+ VisualServer::get_singleton()->particles_set_use_local_coordinates(particles,local_coordinates);
+}
+
+bool Particles::is_using_local_coordinates() const{
+
+ return local_coordinates;
+}
+
+
+RES Particles::_get_gizmo_geometry() const {
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.0,0.6,0.7,0.2) );
+ mat->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.5,0.7,0.8) );
+ mat->set_blend_mode( Material::BLEND_MODE_ADD );
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+
+ surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ surface_tool->set_material(mat);
+
+ int sides=16;
+ int sections=24;
+
+// float len=1;
+ float deg=Math::deg2rad(var[VAR_SPREAD]*180);
+ if (deg==180)
+ deg=179.5;
+
+ Vector3 to=Vector3(0,0,-1);
+
+ for(int j=0;j<sections;j++) {
+
+ Vector3 p1=Matrix3(Vector3(1,0,0),deg*j/sections).xform(to);
+ Vector3 p2=Matrix3(Vector3(1,0,0),deg*(j+1)/sections).xform(to);
+
+ for(int i=0;i<sides;i++) {
+
+ Vector3 p1r = Matrix3(Vector3(0,0,1),Math_PI*2*float(i)/sides).xform(p1);
+ Vector3 p1s = Matrix3(Vector3(0,0,1),Math_PI*2*float(i+1)/sides).xform(p1);
+ Vector3 p2s = Matrix3(Vector3(0,0,1),Math_PI*2*float(i+1)/sides).xform(p2);
+ Vector3 p2r = Matrix3(Vector3(0,0,1),Math_PI*2*float(i)/sides).xform(p2);
+
+ surface_tool->add_normal(p1r.normalized());
+ surface_tool->add_vertex(p1r);
+ surface_tool->add_normal(p1s.normalized());
+ surface_tool->add_vertex(p1s);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+
+ surface_tool->add_normal(p1r.normalized());
+ surface_tool->add_vertex(p1r);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+ surface_tool->add_normal(p2r.normalized());
+ surface_tool->add_vertex(p2r);
+
+ if (j==sections-1) {
+
+ surface_tool->add_normal(p2r.normalized());
+ surface_tool->add_vertex(p2r);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+ surface_tool->add_normal(Vector3(0,0,1));
+ surface_tool->add_vertex(Vector3());
+ }
+ }
+ }
+
+
+ Ref<Mesh> mesh = surface_tool->commit();
+
+ Ref<FixedMaterial> mat_aabb( memnew( FixedMaterial ));
+
+ mat_aabb->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.8,0.8,0.9,0.7) );
+ mat_aabb->set_line_width(3);
+ mat_aabb->set_flag( Material::FLAG_UNSHADED, true );
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat_aabb);
+
+ for(int i=0;i<12;i++) {
+
+ Vector3 f,t;
+ visibility_aabb.get_edge(i,f,t);
+ surface_tool->add_vertex(f);
+ surface_tool->add_vertex(t);
+ }
+
+ return surface_tool->commit(mesh);
+
+}
+
+
+void Particles::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_amount","amount"),&Particles::set_amount);
+ ObjectTypeDB::bind_method(_MD("get_amount"),&Particles::get_amount);
+ ObjectTypeDB::bind_method(_MD("set_emitting","enabled"),&Particles::set_emitting);
+ ObjectTypeDB::bind_method(_MD("is_emitting"),&Particles::is_emitting);
+ ObjectTypeDB::bind_method(_MD("set_visibility_aabb","aabb"),&Particles::set_visibility_aabb);
+ ObjectTypeDB::bind_method(_MD("get_visibility_aabb"),&Particles::get_visibility_aabb);
+ ObjectTypeDB::bind_method(_MD("set_emission_half_extents","half_extents"),&Particles::set_emission_half_extents);
+ ObjectTypeDB::bind_method(_MD("get_emission_half_extents"),&Particles::get_emission_half_extents);
+ ObjectTypeDB::bind_method(_MD("set_emission_base_velocity","base_velocity"),&Particles::set_emission_base_velocity);
+ ObjectTypeDB::bind_method(_MD("get_emission_base_velocity"),&Particles::get_emission_base_velocity);
+ ObjectTypeDB::bind_method(_MD("set_emission_points","points"),&Particles::set_emission_points);
+ ObjectTypeDB::bind_method(_MD("get_emission_points"),&Particles::get_emission_points);
+ ObjectTypeDB::bind_method(_MD("set_gravity_normal","normal"),&Particles::set_gravity_normal);
+ ObjectTypeDB::bind_method(_MD("get_gravity_normal"),&Particles::get_gravity_normal);
+ ObjectTypeDB::bind_method(_MD("set_variable","variable","value"),&Particles::set_variable);
+ ObjectTypeDB::bind_method(_MD("get_variable","variable"),&Particles::get_variable);
+ ObjectTypeDB::bind_method(_MD("set_randomness","variable","randomness"),&Particles::set_randomness);
+ ObjectTypeDB::bind_method(_MD("get_randomness"),&Particles::get_randomness);
+ ObjectTypeDB::bind_method(_MD("set_color_phase_pos","phase","pos"),&Particles::set_color_phase_pos);
+ ObjectTypeDB::bind_method(_MD("get_color_phase_pos","phase"),&Particles::get_color_phase_pos);
+ ObjectTypeDB::bind_method(_MD("set_color_phase_color","phase","color"),&Particles::set_color_phase_color);
+ ObjectTypeDB::bind_method(_MD("get_color_phase_color","phase"),&Particles::get_color_phase_color);
+ ObjectTypeDB::bind_method(_MD("set_material","material:Material"),&Particles::set_material);
+ ObjectTypeDB::bind_method(_MD("get_material:Material"),&Particles::get_material);
+ ObjectTypeDB::bind_method(_MD("set_emit_timeout"),&Particles::set_emit_timeout);
+ ObjectTypeDB::bind_method(_MD("get_emit_timeout"),&Particles::get_emit_timeout);
+ ObjectTypeDB::bind_method(_MD("set_height_from_velocity","enable"),&Particles::set_height_from_velocity);
+ ObjectTypeDB::bind_method(_MD("has_height_from_velocity"),&Particles::has_height_from_velocity);
+ ObjectTypeDB::bind_method(_MD("set_use_local_coordinates","enable"),&Particles::set_use_local_coordinates);
+ ObjectTypeDB::bind_method(_MD("is_using_local_coordinates"),&Particles::is_using_local_coordinates);
+
+ ObjectTypeDB::bind_method(_MD("set_color_phases","count"),&Particles::set_color_phases);
+ ObjectTypeDB::bind_method(_MD("get_color_phases"),&Particles::get_color_phases);
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "material", PROPERTY_HINT_RESOURCE_TYPE, "Material" ), _SCS("set_material"), _SCS("get_material") );
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,4096,1" ), _SCS("set_amount"), _SCS("get_amount") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "emitting" ), _SCS("set_emitting"), _SCS("is_emitting") );
+ ADD_PROPERTY( PropertyInfo( Variant::_AABB, "visibility" ), _SCS("set_visibility_aabb"), _SCS("get_visibility_aabb") );
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "emission_extents" ), _SCS("set_emission_half_extents"), _SCS("get_emission_half_extents") );
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "emission_base_velocity" ), _SCS("set_emission_base_velocity"), _SCS("get_emission_base_velocity") );
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR3_ARRAY, "emission_points" ), _SCS("set_emission_points"), _SCS("get_emission_points") );
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "gravity_normal" ), _SCS("set_gravity_normal"), _SCS("get_gravity_normal") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "local_coords" ), _SCS("set_use_local_coordinates"), _SCS("is_using_local_coordinates") );
+ ADD_PROPERTY( PropertyInfo( Variant::REAL, "emit_timeout",PROPERTY_HINT_RANGE,"0,256,0.01"), _SCS("set_emit_timeout"), _SCS("get_emit_timeout") );
+
+
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/lifetime", PROPERTY_HINT_RANGE,"0.1,60,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LIFETIME );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/spread", PROPERTY_HINT_RANGE,"0,1,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_SPREAD );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/gravity", PROPERTY_HINT_RANGE,"-48,48,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_GRAVITY );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/linear_vel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LINEAR_VELOCITY );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/angular_vel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_ANGULAR_VELOCITY );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/linear_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_LINEAR_ACCELERATION );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/radial_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_DRAG );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/tan_accel", PROPERTY_HINT_RANGE,"-100,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_TANGENTIAL_ACCELERATION );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/damping", PROPERTY_HINT_RANGE,"0,128,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_DAMPING );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/initial_size", PROPERTY_HINT_RANGE,"0,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_INITIAL_SIZE );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/final_size", PROPERTY_HINT_RANGE,"0,100,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_FINAL_SIZE );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/initial_angle",PROPERTY_HINT_RANGE,"0,1,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_INITIAL_ANGLE );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "vars/height_from_velocity"), _SCS("set_height_from_velocity"), _SCS("has_height_from_velocity") );
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/height",PROPERTY_HINT_RANGE,"0,4096,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_HEIGHT);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "vars/height_speed_scale",PROPERTY_HINT_RANGE,"0,4096,0.01"), _SCS("set_variable"), _SCS("get_variable"), VAR_HEIGHT_SPEED_SCALE );
+
+ for(int i=0;i<VAR_MAX;i++)
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, _rand_names[i], PROPERTY_HINT_RANGE,"-16.0,16.0,0.01"),_SCS("set_randomness"), _SCS("get_randomness"),_var_indices[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<VS::MAX_PARTICLE_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 );
+ }
+
+ BIND_CONSTANT( VAR_LIFETIME );
+ BIND_CONSTANT( VAR_SPREAD );
+ BIND_CONSTANT( VAR_GRAVITY );
+ BIND_CONSTANT( VAR_LINEAR_VELOCITY );
+ BIND_CONSTANT( VAR_ANGULAR_VELOCITY );
+ BIND_CONSTANT( VAR_LINEAR_ACCELERATION );
+ BIND_CONSTANT( VAR_DRAG );
+ BIND_CONSTANT( VAR_TANGENTIAL_ACCELERATION );
+ BIND_CONSTANT( VAR_INITIAL_SIZE );
+ BIND_CONSTANT( VAR_FINAL_SIZE );
+ BIND_CONSTANT( VAR_INITIAL_ANGLE );
+ BIND_CONSTANT( VAR_HEIGHT );
+ BIND_CONSTANT( VAR_HEIGHT_SPEED_SCALE );
+ BIND_CONSTANT( VAR_MAX );
+
+}
+
+Particles::Particles() {
+
+ particles = VisualServer::get_singleton()->particles_create();
+ timer = memnew(Timer);
+ add_child(timer);
+ emit_timeout = 0;
+
+ set_amount(64);
+ set_emitting(true);
+ set_visibility_aabb(AABB( Vector3(-4,-4,-4), Vector3(8,8,8) ) );
+
+ for (int i=0;i<VAR_MAX;i++) {
+ set_randomness((Variable)i,0.0);
+ }
+
+ set_variable( VAR_LIFETIME, 5.0);
+ set_variable( VAR_SPREAD, 0.2);
+ set_variable( VAR_GRAVITY, 9.8);
+ set_variable( VAR_LINEAR_VELOCITY, 0.2);
+ set_variable( VAR_ANGULAR_VELOCITY, 0.0);
+ set_variable( VAR_LINEAR_ACCELERATION, 0.0);
+ set_variable( VAR_DRAG, 0.0);
+ set_variable( VAR_TANGENTIAL_ACCELERATION, 0.0);
+ set_variable( VAR_DAMPING, 0.0);
+ set_variable( VAR_INITIAL_SIZE, 1.0);
+ set_variable( VAR_FINAL_SIZE, 1.0);
+ set_variable( VAR_INITIAL_ANGLE, 0.0);
+ set_variable( VAR_HEIGHT, 1.0);
+ set_variable( VAR_HEIGHT_SPEED_SCALE, 0.0);
+
+ color_phase_count=0;
+
+ 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));
+
+ set_gravity_normal(Vector3(0,-1.0,0));
+ set_emission_half_extents(Vector3(0.1,0.1,0.1));
+
+ height_from_velocity=false;
+
+ Vector<Variant> pars;
+ pars.push_back(false);
+ timer->connect("timeout", this, "set_emitting", pars);
+ set_base(particles);
+ local_coordinates=false;
+}
+
+
+Particles::~Particles() {
+
+ VisualServer::get_singleton()->free(particles);
+}
+
diff --git a/scene/3d/particles.h b/scene/3d/particles.h
new file mode 100644
index 0000000000..40d569d7b4
--- /dev/null
+++ b/scene/3d/particles.h
@@ -0,0 +1,165 @@
+/*************************************************************************/
+/* particles.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 VISUALINSTANCEPARTICLES_H
+#define VISUALINSTANCEPARTICLES_H
+
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/material.h"
+#include "scene/main/timer.h"
+#include "rid.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class Particles : public GeometryInstance {
+public:
+
+ enum Variable {
+ VAR_LIFETIME=VS::PARTICLE_LIFETIME,
+ VAR_SPREAD=VS::PARTICLE_SPREAD,
+ VAR_GRAVITY=VS::PARTICLE_GRAVITY,
+ VAR_LINEAR_VELOCITY=VS::PARTICLE_LINEAR_VELOCITY,
+ VAR_ANGULAR_VELOCITY=VS::PARTICLE_ANGULAR_VELOCITY,
+ VAR_LINEAR_ACCELERATION=VS::PARTICLE_LINEAR_ACCELERATION,
+ VAR_DRAG=VS::PARTICLE_RADIAL_ACCELERATION,
+ VAR_TANGENTIAL_ACCELERATION=VS::PARTICLE_TANGENTIAL_ACCELERATION,
+ VAR_DAMPING=VS::PARTICLE_DAMPING,
+ VAR_INITIAL_SIZE=VS::PARTICLE_INITIAL_SIZE,
+ VAR_FINAL_SIZE=VS::PARTICLE_FINAL_SIZE,
+ VAR_INITIAL_ANGLE=VS::PARTICLE_INITIAL_ANGLE,
+ VAR_HEIGHT=VS::PARTICLE_HEIGHT,
+ VAR_HEIGHT_SPEED_SCALE=VS::PARTICLE_HEIGHT_SPEED_SCALE,
+ VAR_MAX=VS::PARTICLE_VAR_MAX
+ };
+
+private:
+ OBJ_TYPE( Particles, GeometryInstance );
+
+ RID particles;
+
+ int amount;
+ bool emitting;
+ float emit_timeout;
+ AABB visibility_aabb;
+ Vector3 gravity_normal;
+ Vector3 emission_half_extents;
+ bool using_points;
+ float var[VAR_MAX];
+ float var_random[VAR_MAX];
+ bool height_from_velocity;
+ Vector3 emission_base_velocity;
+ bool local_coordinates;
+
+ struct ColorPhase {
+
+ Color color;
+ float pos;
+ };
+
+ virtual bool _can_gizmo_scale() const;
+ virtual RES _get_gizmo_geometry() const;
+
+ int color_phase_count;
+
+ ColorPhase color_phase[4];
+
+ Ref<Material> material;
+
+ Timer* timer;
+ void setup_timer();
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+
+ AABB get_aabb() const;
+ DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_amount(int p_amount);
+ int get_amount() const;
+
+ void set_emitting(bool p_emitting);
+ bool is_emitting() const;
+
+ void set_visibility_aabb(const AABB& p_aabb);
+ AABB get_visibility_aabb() const;
+
+ void set_emission_half_extents(const Vector3& p_half_extents);
+ Vector3 get_emission_half_extents() const;
+
+ void set_emission_base_velocity(const Vector3& p_base_velocity);
+ Vector3 get_emission_base_velocity() const;
+
+ void set_emission_points(const DVector<Vector3>& p_points);
+ DVector<Vector3> get_emission_points() const;
+
+ void set_gravity_normal(const Vector3& p_normal);
+ Vector3 get_gravity_normal() const;
+
+ void set_variable(Variable p_variable,float p_value);
+ float get_variable(Variable p_variable) const;
+
+ void set_randomness(Variable p_variable,float p_randomness);
+ float get_randomness(Variable p_variable) const;
+
+ void set_color_phases(int p_phases);
+ int get_color_phases() const;
+
+ void set_color_phase_pos(int p_phase, float p_pos);
+ float get_color_phase_pos(int p_phase) const;
+
+ void set_color_phase_color(int p_phase, const Color& p_color);
+ Color get_color_phase_color(int p_phase) const;
+
+ void set_height_from_velocity(bool p_enable);
+ bool has_height_from_velocity() const;
+
+ void set_material(const Ref<Material>& p_material);
+ Ref<Material> get_material() const;
+
+ void set_emit_timeout(float p_timeout);
+ float get_emit_timeout() const;
+
+ void set_use_local_coordinates(bool p_use);
+ bool is_using_local_coordinates() const;
+
+ void start_emitting(float p_time);
+
+
+ Particles();
+ ~Particles();
+
+};
+
+VARIANT_ENUM_CAST( Particles::Variable );
+#endif
diff --git a/scene/3d/path.cpp b/scene/3d/path.cpp
new file mode 100644
index 0000000000..bc5cb1c4a2
--- /dev/null
+++ b/scene/3d/path.cpp
@@ -0,0 +1,388 @@
+/*************************************************************************/
+/* path.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.h"
+#include "scene/scene_string_names.h"
+
+void Path::_notification(int p_what) {
+#if 0
+ 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;
+ }
+ }
+ }
+#endif
+}
+
+void Path::_curve_changed() {
+
+
+ if (is_inside_scene() && get_scene()->is_editor_hint())
+ update_gizmo();
+}
+
+
+void Path::set_curve(const Ref<Curve3D>& 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");
+ }
+ _curve_changed();
+
+}
+
+Ref<Curve3D> Path::get_curve() const{
+
+ return curve;
+}
+
+void Path::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_curve","curve:Curve3D"),&Path::set_curve);
+ ObjectTypeDB::bind_method(_MD("get_curve:Curve3D","curve"),&Path::get_curve);
+ ObjectTypeDB::bind_method(_MD("_curve_changed"),&Path::_curve_changed);
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve3D"), _SCS("set_curve"),_SCS("get_curve"));
+}
+
+Path::Path() {
+
+ set_curve(Ref<Curve3D>( memnew( Curve3D ))); //create one by default
+}
+
+
+//////////////
+
+
+void PathFollow::_update_transform() {
+
+
+ if (!path)
+ return;
+
+ Ref<Curve3D> c =path->get_curve();
+ if (!c.is_valid())
+ return;
+
+
+ float o = offset;
+ if (loop)
+ o=Math::fposmod(o,c->get_baked_length());
+
+ Vector3 pos = c->interpolate_baked(o,cubic);
+ Transform t=get_transform();
+
+
+ if (rotation_mode!=ROTATION_NONE) {
+
+ Vector3 n = (c->interpolate_baked(o+lookahead,cubic)-pos).normalized();
+
+ if (rotation_mode==ROTATION_Y) {
+
+ n.y=0;
+ n.normalize();
+ }
+
+ if (n.length()<CMP_EPSILON) {//nothing, use previous
+ n=-t.get_basis().get_axis(2).normalized();
+ }
+
+
+ Vector3 up = Vector3(0,1,0);
+
+ if (rotation_mode==ROTATION_XYZ) {
+
+ float tilt = c->interpolate_baked_tilt(o);
+ if (tilt!=0) {
+
+ Matrix3 rot(-n,tilt); //remember.. lookat will be znegative.. znegative!! we abide by opengl clan.
+ up=rot.xform(up);
+ }
+ }
+
+ t.set_look_at(pos,pos+n,up);
+
+ } else {
+
+ t.origin=pos;
+ }
+
+ t.origin+=t.basis.get_axis(0)*h_offset + t.basis.get_axis(1)*v_offset;
+ set_transform(t);
+
+}
+
+void PathFollow::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ Node *parent=get_parent();
+ if (parent) {
+
+ path=parent->cast_to<Path>();
+ if (path) {
+ _update_transform();
+ }
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+
+ path=NULL;
+ } break;
+ }
+
+}
+
+void PathFollow::set_cubic_interpolation(bool p_enable) {
+
+ cubic=p_enable;
+}
+
+bool PathFollow::get_cubic_interpolation() const {
+
+ return cubic;
+}
+
+
+bool PathFollow::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name==SceneStringNames::get_singleton()->offset) {
+ set_offset(p_value);
+ } else if (p_name==SceneStringNames::get_singleton()->unit_offset) {
+ set_unit_offset(p_value);
+ } else if (p_name==SceneStringNames::get_singleton()->rotation_mode) {
+ set_rotation_mode(RotationMode(p_value.operator int()));
+ } else if (p_name==SceneStringNames::get_singleton()->v_offset) {
+ set_v_offset(p_value);
+ } else if (p_name==SceneStringNames::get_singleton()->h_offset) {
+ set_h_offset(p_value);
+ } else if (String(p_name)=="cubic_interp") {
+ set_cubic_interpolation(p_value);
+ } else if (String(p_name)=="loop") {
+ set_loop(p_value);
+ } else if (String(p_name)=="lookahead") {
+ set_lookahead(p_value);
+ } else
+ return false;
+
+ return true;
+}
+
+bool PathFollow::_get(const StringName& p_name,Variant &r_ret) const{
+
+ if (p_name==SceneStringNames::get_singleton()->offset) {
+ r_ret=get_offset();
+ } else if (p_name==SceneStringNames::get_singleton()->unit_offset) {
+ r_ret=get_unit_offset();
+ } else if (p_name==SceneStringNames::get_singleton()->rotation_mode) {
+ r_ret=get_rotation_mode();
+ } else if (p_name==SceneStringNames::get_singleton()->v_offset) {
+ r_ret=get_v_offset();
+ } else if (p_name==SceneStringNames::get_singleton()->h_offset) {
+ r_ret=get_h_offset();
+ } else if (String(p_name)=="cubic_interp") {
+ r_ret=cubic;
+ } else if (String(p_name)=="loop") {
+ r_ret=loop;
+ } else if (String(p_name)=="lookahead") {
+ r_ret=lookahead;
+ } else
+ return false;
+
+ return true;
+
+}
+void PathFollow::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ float max=10000;
+ if (path && path->get_curve().is_valid())
+ max=path->get_curve()->get_baked_length();
+ p_list->push_back( PropertyInfo( Variant::REAL, "offset", PROPERTY_HINT_RANGE,"0,"+rtos(max)+",0.01"));
+ p_list->push_back( PropertyInfo( Variant::REAL, "unit_offset", PROPERTY_HINT_RANGE,"0,1,0.0001",PROPERTY_USAGE_EDITOR));
+ p_list->push_back( PropertyInfo( Variant::REAL, "h_offset") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "v_offset") );
+ p_list->push_back( PropertyInfo( Variant::INT, "rotation_mode", PROPERTY_HINT_ENUM,"None,Y,XY,XYZ"));
+ p_list->push_back( PropertyInfo( Variant::BOOL, "cubic_interp"));
+ p_list->push_back( PropertyInfo( Variant::BOOL, "loop"));
+ p_list->push_back( PropertyInfo( Variant::REAL, "lookahead",PROPERTY_HINT_RANGE,"0.001,1024.0,0.001"));
+}
+
+
+void PathFollow::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&PathFollow::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&PathFollow::get_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_h_offset","h_offset"),&PathFollow::set_h_offset);
+ ObjectTypeDB::bind_method(_MD("get_h_offset"),&PathFollow::get_h_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_v_offset","v_offset"),&PathFollow::set_v_offset);
+ ObjectTypeDB::bind_method(_MD("get_v_offset"),&PathFollow::get_v_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_unit_offset","unit_offset"),&PathFollow::set_unit_offset);
+ ObjectTypeDB::bind_method(_MD("get_unit_offset"),&PathFollow::get_unit_offset);
+
+ ObjectTypeDB::bind_method(_MD("set_rotation_mode","rotation_mode"),&PathFollow::set_rotation_mode);
+ ObjectTypeDB::bind_method(_MD("get_rotation_mode"),&PathFollow::get_rotation_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_cubic_interpolation","enable"),&PathFollow::set_cubic_interpolation);
+ ObjectTypeDB::bind_method(_MD("get_cubic_interpolation"),&PathFollow::get_cubic_interpolation);
+
+ ObjectTypeDB::bind_method(_MD("set_loop","loop"),&PathFollow::set_loop);
+ ObjectTypeDB::bind_method(_MD("has_loop"),&PathFollow::has_loop);
+
+ BIND_CONSTANT( ROTATION_NONE );
+ BIND_CONSTANT( ROTATION_Y );
+ BIND_CONSTANT( ROTATION_XY );
+ BIND_CONSTANT( ROTATION_XYZ );
+
+}
+
+void PathFollow::set_offset(float p_offset) {
+
+ offset=p_offset;
+ if (path)
+ _update_transform();
+ _change_notify("offset");
+ _change_notify("unit_offset");
+
+}
+
+void PathFollow::set_h_offset(float p_h_offset) {
+
+ h_offset=p_h_offset;
+ if (path)
+ _update_transform();
+
+}
+
+float PathFollow::get_h_offset() const {
+
+ return h_offset;
+}
+
+void PathFollow::set_v_offset(float p_v_offset) {
+
+ v_offset=p_v_offset;
+ if (path)
+ _update_transform();
+
+}
+
+float PathFollow::get_v_offset() const {
+
+ return v_offset;
+}
+
+
+float PathFollow::get_offset() const{
+
+ return offset;
+}
+
+void PathFollow::set_unit_offset(float p_unit_offset) {
+
+ if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
+ set_offset(p_unit_offset*path->get_curve()->get_baked_length());
+
+}
+
+float PathFollow::get_unit_offset() const{
+
+ if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length())
+ return get_offset()/path->get_curve()->get_baked_length();
+ else
+ return 0;
+}
+
+void PathFollow::set_lookahead(float p_lookahead) {
+
+ lookahead=p_lookahead;
+
+}
+
+float PathFollow::get_lookahead() const{
+
+ return lookahead;
+}
+
+void PathFollow::set_rotation_mode(RotationMode p_rotation_mode) {
+
+ rotation_mode=p_rotation_mode;
+ _update_transform();
+}
+
+PathFollow::RotationMode PathFollow::get_rotation_mode() const {
+
+ return rotation_mode;
+}
+
+void PathFollow::set_loop(bool p_loop) {
+
+ loop=p_loop;
+}
+
+bool PathFollow::has_loop() const{
+
+ return loop;
+}
+
+
+PathFollow::PathFollow() {
+
+ offset=0;
+ h_offset=0;
+ v_offset=0;
+ path=NULL;
+ rotation_mode=ROTATION_XYZ;
+ cubic=true;
+ loop=true;
+ lookahead=0.1;
+}
diff --git a/scene/3d/path.h b/scene/3d/path.h
new file mode 100644
index 0000000000..6f907265ba
--- /dev/null
+++ b/scene/3d/path.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* path.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_H
+#define PATH_H
+
+#include "scene/resources/curve.h"
+#include "scene/3d/spatial.h"
+
+class Path : public Spatial {
+
+ OBJ_TYPE( Path, Spatial );
+
+ Ref<Curve3D> curve;
+
+ void _curve_changed();
+
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_curve(const Ref<Curve3D>& p_curve);
+ Ref<Curve3D> get_curve() const;
+
+
+ Path();
+};
+
+class PathFollow : public Spatial {
+
+ OBJ_TYPE(PathFollow,Spatial);
+public:
+
+ enum RotationMode {
+
+ ROTATION_NONE,
+ ROTATION_Y,
+ ROTATION_XY,
+ ROTATION_XYZ
+ };
+
+private:
+ Path *path;
+ real_t offset;
+ real_t h_offset;
+ real_t v_offset;
+ real_t lookahead;
+ bool cubic;
+ bool loop;
+ RotationMode rotation_mode;
+
+ void _update_transform();
+
+
+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_offset(float p_offset);
+ float get_offset() const;
+
+ void set_h_offset(float p_h_offset);
+ float get_h_offset() const;
+
+ void set_v_offset(float p_v_offset);
+ float get_v_offset() const;
+
+ void set_unit_offset(float p_unit_offset);
+ float get_unit_offset() const;
+
+ void set_lookahead(float p_lookahead);
+ float get_lookahead() const;
+
+ void set_loop(bool p_loop);
+ bool has_loop() const;
+
+ void set_rotation_mode(RotationMode p_rotation_mode);
+ RotationMode get_rotation_mode() const;
+
+ void set_cubic_interpolation(bool p_enable);
+ bool get_cubic_interpolation() const;
+
+ PathFollow();
+};
+
+VARIANT_ENUM_CAST(PathFollow::RotationMode);
+
+#endif // PATH_H
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
new file mode 100644
index 0000000000..e42a96cc4e
--- /dev/null
+++ b/scene/3d/physics_body.cpp
@@ -0,0 +1,741 @@
+/*************************************************************************/
+/* physics_body.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.h"
+#include "scene/scene_string_names.h"
+
+void PhysicsBody::_notification(int p_what) {
+
+/*
+ switch(p_what) {
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_TRANSFORM,get_global_transform());
+
+ } break;
+ }
+ */
+}
+
+PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) {
+
+
+
+}
+
+void StaticBody::set_constant_linear_velocity(const Vector3& p_vel) {
+
+ constant_linear_velocity=p_vel;
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_LINEAR_VELOCITY,constant_linear_velocity);
+
+}
+
+void StaticBody::set_constant_angular_velocity(const Vector3& p_vel) {
+
+ constant_angular_velocity=p_vel;
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity);
+}
+
+Vector3 StaticBody::get_constant_linear_velocity() const {
+
+ return constant_linear_velocity;
+}
+Vector3 StaticBody::get_constant_angular_velocity() const {
+
+ return constant_angular_velocity;
+}
+
+
+void StaticBody::_state_notify(Object *p_object) {
+
+ if (!pre_xform)
+ return;
+
+ PhysicsDirectBodyState *p2d = (PhysicsDirectBodyState*)p_object;
+ setting=true;
+
+ Transform new_xform = p2d->get_transform();
+ *pre_xform=new_xform;
+ set_ignore_transform_notification(true);
+ set_global_transform(new_xform);
+ set_ignore_transform_notification(false);
+
+ setting=false;
+
+
+}
+
+void StaticBody::_update_xform() {
+
+ if (!pre_xform || !pending)
+ return;
+
+ setting=true;
+
+
+ Transform new_xform = get_global_transform(); //obtain the new one
+
+ //set_block_transform_notify(true);
+ set_ignore_transform_notification(true);
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::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_ignore_transform_notification(false);
+
+ PhysicsServer::get_singleton()->body_static_simulate_motion(get_rid(),new_xform); //then simulate motion!
+
+ setting=false;
+ pending=false;
+
+}
+
+void StaticBody::_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 StaticBody::set_simulate_motion(bool p_enable) {
+
+ if (p_enable==simulating_motion)
+ return;
+ simulating_motion=p_enable;
+
+ if (p_enable) {
+ pre_xform = memnew( Transform );
+ if (is_inside_scene())
+ *pre_xform=get_transform();
+// query = PhysicsServer::get_singleton()->query_create(this,"_state_notify",Variant());
+ // PhysicsServer::get_singleton()->query_body_direct_state(query,get_rid());
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_state_notify");
+
+ } else {
+ memdelete( pre_xform );
+ pre_xform=NULL;
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),NULL,StringName());
+ pending=false;
+ }
+}
+
+bool StaticBody::is_simulating_motion() const {
+
+ return simulating_motion;
+}
+
+void StaticBody::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_simulate_motion","enabled"),&StaticBody::set_simulate_motion);
+ ObjectTypeDB::bind_method(_MD("is_simulating_motion"),&StaticBody::is_simulating_motion);
+ ObjectTypeDB::bind_method(_MD("_update_xform"),&StaticBody::_update_xform);
+ ObjectTypeDB::bind_method(_MD("_state_notify"),&StaticBody::_state_notify);
+ ObjectTypeDB::bind_method(_MD("set_constant_linear_velocity","vel"),&StaticBody::set_constant_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("set_constant_angular_velocity","vel"),&StaticBody::set_constant_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_constant_linear_velocity"),&StaticBody::get_constant_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_constant_angular_velocity"),&StaticBody::get_constant_angular_velocity);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"simulate_motion"),_SCS("set_simulate_motion"),_SCS("is_simulating_motion"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"constant_linear_velocity"),_SCS("set_constant_linear_velocity"),_SCS("get_constant_linear_velocity"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"constant_angular_velocity"),_SCS("set_constant_angular_velocity"),_SCS("get_constant_angular_velocity"));
+}
+
+StaticBody::StaticBody() : PhysicsBody(PhysicsServer::BODY_MODE_STATIC) {
+
+ simulating_motion=false;
+ pre_xform=NULL;
+ setting=false;
+ pending=false;
+ //constant_angular_velocity=0;
+
+}
+
+StaticBody::~StaticBody() {
+
+ if (pre_xform)
+ memdelete(pre_xform);
+ //if (query.is_valid())
+ // PhysicsServer::get_singleton()->free(query);
+}
+
+
+
+
+void RigidBody::_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 RigidBody::_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 RigidBody::_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 _RigidBodyInOut {
+
+ ObjectID id;
+ int shape;
+ int local_shape;
+};
+
+void RigidBody::_direct_state_changed(Object *p_state) {
+
+ //eh.. fuck
+#ifdef DEBUG_ENABLED
+
+ state=p_state->cast_to<PhysicsDirectBodyState>();
+#else
+ state=(PhysicsDirectBodyState*)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++;
+ }
+ }
+
+ _RigidBodyInOut *toadd=(_RigidBodyInOut*)alloca(state->get_contact_count()*sizeof(_RigidBodyInOut));
+ int toadd_count=0;//state->get_contact_count();
+ RigidBody_RemoveAction *toremove=(RigidBody_RemoveAction*)alloca(rc*sizeof(RigidBody_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_ignore_transform_notification(true);
+ 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_ignore_transform_notification(false);
+
+ state=NULL;
+}
+
+void RigidBody::_notification(int p_what) {
+
+
+}
+
+void RigidBody::set_mode(Mode p_mode) {
+
+ mode=p_mode;
+ switch(p_mode) {
+
+ case MODE_RIGID: {
+
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_RIGID);
+ } break;
+ case MODE_STATIC: {
+
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_STATIC);
+
+ } break;
+ case MODE_CHARACTER: {
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_CHARACTER);
+
+ } break;
+ case MODE_STATIC_ACTIVE: {
+
+ PhysicsServer::get_singleton()->body_set_mode(get_rid(),PhysicsServer::BODY_MODE_STATIC_ACTIVE);
+ } break;
+
+ }
+}
+
+RigidBody::Mode RigidBody::get_mode() const{
+
+ return mode;
+}
+
+void RigidBody::set_mass(real_t p_mass){
+
+ ERR_FAIL_COND(p_mass<=0);
+ mass=p_mass;
+ _change_notify("mass");
+ _change_notify("weight");
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass);
+
+}
+real_t RigidBody::get_mass() const{
+
+ return mass;
+}
+
+void RigidBody::set_weight(real_t p_weight){
+
+ set_mass(p_weight/9.8);
+}
+real_t RigidBody::get_weight() const{
+
+ return mass*9.8;
+}
+
+
+void RigidBody::set_friction(real_t p_friction){
+
+ ERR_FAIL_COND(p_friction<0 || p_friction>1);
+
+ friction=p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction);
+
+}
+real_t RigidBody::get_friction() const{
+
+ return friction;
+}
+
+void RigidBody::set_bounce(real_t p_bounce){
+
+ ERR_FAIL_COND(p_bounce<0 || p_bounce>1);
+
+ bounce=p_bounce;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_BOUNCE,bounce);
+
+}
+real_t RigidBody::get_bounce() const{
+
+ return bounce;
+}
+
+void RigidBody::set_axis_velocity(const Vector3& p_axis) {
+
+ Vector3 v = state? state->get_linear_velocity() : linear_velocity;
+ Vector3 axis = p_axis.normalized();
+ v-=axis*axis.dot(v);
+ v+=p_axis;
+ if (state) {
+ set_linear_velocity(v);
+ } else {
+ PhysicsServer::get_singleton()->body_set_axis_velocity(get_rid(),p_axis);
+ linear_velocity=v;
+ }
+}
+
+void RigidBody::set_linear_velocity(const Vector3& p_velocity){
+
+ linear_velocity=p_velocity;
+ if (state)
+ state->set_linear_velocity(linear_velocity);
+ else
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_LINEAR_VELOCITY,linear_velocity);
+
+}
+
+Vector3 RigidBody::get_linear_velocity() const{
+
+ return linear_velocity;
+}
+
+void RigidBody::set_angular_velocity(const Vector3& p_velocity){
+
+ angular_velocity=p_velocity;
+ if (state)
+ state->set_angular_velocity(angular_velocity);
+ else
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,angular_velocity);
+}
+Vector3 RigidBody::get_angular_velocity() const{
+
+ return angular_velocity;
+}
+
+void RigidBody::set_use_custom_integrator(bool p_enable){
+
+ if (custom_integrator==p_enable)
+ return;
+
+ custom_integrator=p_enable;
+ PhysicsServer::get_singleton()->body_set_omit_force_integration(get_rid(),p_enable);
+
+
+}
+bool RigidBody::is_using_custom_integrator(){
+
+ return custom_integrator;
+}
+
+void RigidBody::set_active(bool p_active) {
+
+ active=p_active;
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_SLEEPING,!active);
+
+}
+
+void RigidBody::set_can_sleep(bool p_active) {
+
+ can_sleep=p_active;
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_CAN_SLEEP,p_active);
+}
+
+bool RigidBody::is_able_to_sleep() const {
+
+ return can_sleep;
+}
+
+bool RigidBody::is_active() const {
+
+ return active;
+}
+
+void RigidBody::set_max_contacts_reported(int p_amount) {
+
+ max_contacts_reported=p_amount;
+ PhysicsServer::get_singleton()->body_set_max_contacts_reported(get_rid(),p_amount);
+}
+
+int RigidBody::get_max_contacts_reported() const{
+
+ return max_contacts_reported;
+}
+
+void RigidBody::apply_impulse(const Vector3& p_pos, const Vector3& p_impulse) {
+
+ PhysicsServer::get_singleton()->body_apply_impulse(get_rid(),p_pos,p_impulse);
+}
+
+void RigidBody::set_use_continuous_collision_detection(bool p_enable) {
+
+ ccd=p_enable;
+ PhysicsServer::get_singleton()->body_set_enable_continuous_collision_detection(get_rid(),p_enable);
+}
+
+bool RigidBody::is_using_continuous_collision_detection() const {
+
+
+ return ccd;
+}
+
+
+void RigidBody::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 RigidBody::is_contact_monitor_enabled() const {
+
+ return contact_monitor!=NULL;
+}
+
+
+
+void RigidBody::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_mode","mode"),&RigidBody::set_mode);
+ ObjectTypeDB::bind_method(_MD("get_mode"),&RigidBody::get_mode);
+
+ ObjectTypeDB::bind_method(_MD("set_mass","mass"),&RigidBody::set_mass);
+ ObjectTypeDB::bind_method(_MD("get_mass"),&RigidBody::get_mass);
+
+ ObjectTypeDB::bind_method(_MD("set_weight","weight"),&RigidBody::set_weight);
+ ObjectTypeDB::bind_method(_MD("get_weight"),&RigidBody::get_weight);
+
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&RigidBody::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&RigidBody::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&RigidBody::set_bounce);
+ ObjectTypeDB::bind_method(_MD("get_bounce"),&RigidBody::get_bounce);
+
+ ObjectTypeDB::bind_method(_MD("set_linear_velocity","linear_velocity"),&RigidBody::set_linear_velocity);
+ ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&RigidBody::get_linear_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_angular_velocity","angular_velocity"),&RigidBody::set_angular_velocity);
+ ObjectTypeDB::bind_method(_MD("get_angular_velocity"),&RigidBody::get_angular_velocity);
+
+ ObjectTypeDB::bind_method(_MD("set_max_contacts_reported","amount"),&RigidBody::set_max_contacts_reported);
+ ObjectTypeDB::bind_method(_MD("get_max_contacts_reported"),&RigidBody::get_max_contacts_reported);
+
+ ObjectTypeDB::bind_method(_MD("set_use_custom_integrator","enable"),&RigidBody::set_use_custom_integrator);
+ ObjectTypeDB::bind_method(_MD("is_using_custom_integrator"),&RigidBody::is_using_custom_integrator);
+
+ ObjectTypeDB::bind_method(_MD("set_contact_monitor","enabled"),&RigidBody::set_contact_monitor);
+ ObjectTypeDB::bind_method(_MD("is_contact_monitor_enabled"),&RigidBody::is_contact_monitor_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_use_continuous_collision_detection","enable"),&RigidBody::set_use_continuous_collision_detection);
+ ObjectTypeDB::bind_method(_MD("is_using_continuous_collision_detection"),&RigidBody::is_using_continuous_collision_detection);
+
+ ObjectTypeDB::bind_method(_MD("set_axis_velocity","axis_velocity"),&RigidBody::set_axis_velocity);
+ ObjectTypeDB::bind_method(_MD("apply_impulse","pos","impulse"),&RigidBody::apply_impulse);
+
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&RigidBody::set_active);
+ ObjectTypeDB::bind_method(_MD("is_active"),&RigidBody::is_active);
+
+ ObjectTypeDB::bind_method(_MD("set_can_sleep","able_to_sleep"),&RigidBody::set_can_sleep);
+ ObjectTypeDB::bind_method(_MD("is_able_to_sleep"),&RigidBody::is_able_to_sleep);
+
+ ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&RigidBody::_direct_state_changed);
+ ObjectTypeDB::bind_method(_MD("_body_enter_scene"),&RigidBody::_body_enter_scene);
+ ObjectTypeDB::bind_method(_MD("_body_exit_scene"),&RigidBody::_body_exit_scene);
+
+ BIND_VMETHOD(MethodInfo("_integrate_forces",PropertyInfo(Variant::OBJECT,"state:PhysicsDirectBodyState")));
+
+ 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::VECTOR3,"velocity/linear"),_SCS("set_linear_velocity"),_SCS("get_linear_velocity"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"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 );
+}
+
+RigidBody::RigidBody() : PhysicsBody(PhysicsServer::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;
+
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed");
+}
+
+RigidBody::~RigidBody() {
+
+ if (contact_monitor)
+ memdelete( contact_monitor );
+
+
+
+}
+
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
new file mode 100644
index 0000000000..0cb24075bd
--- /dev/null
+++ b/scene/3d/physics_body.h
@@ -0,0 +1,219 @@
+/*************************************************************************/
+/* physics_body.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__H
+#define PHYSICS_BODY__H
+
+#include "scene/3d/collision_object.h"
+#include "servers/physics_server.h"
+#include "vset.h"
+
+
+class PhysicsBody : public CollisionObject {
+
+ OBJ_TYPE(PhysicsBody,CollisionObject);
+
+protected:
+
+ void _notification(int p_what);
+ PhysicsBody(PhysicsServer::BodyMode p_mode);
+public:
+
+ PhysicsBody();
+
+};
+
+class StaticBody : public PhysicsBody {
+
+ OBJ_TYPE(StaticBody,PhysicsBody);
+
+ Transform *pre_xform;
+ //RID query;
+ bool setting;
+ bool pending;
+ bool simulating_motion;
+ Vector3 constant_linear_velocity;
+ Vector3 constant_angular_velocity;
+ void _update_xform();
+ void _state_notify(Object *p_object);
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_simulate_motion(bool p_enable);
+ bool is_simulating_motion() const;
+
+ void set_constant_linear_velocity(const Vector3& p_vel);
+ void set_constant_angular_velocity(const Vector3& p_vel);
+
+ Vector3 get_constant_linear_velocity() const;
+ Vector3 get_constant_angular_velocity() const;
+
+ StaticBody();
+ ~StaticBody();
+
+};
+
+class RigidBody : public PhysicsBody {
+
+ OBJ_TYPE(RigidBody,PhysicsBody);
+public:
+
+ enum Mode {
+ MODE_RIGID,
+ MODE_STATIC,
+ MODE_CHARACTER,
+ MODE_STATIC_ACTIVE,
+ };
+private:
+
+ bool can_sleep;
+ PhysicsDirectBodyState *state;
+ Mode mode;
+
+ real_t bounce;
+ real_t mass;
+ real_t friction;
+
+ Vector3 linear_velocity;
+ Vector3 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 RigidBody_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 Vector3& p_velocity);
+ Vector3 get_linear_velocity() const;
+
+ void set_axis_velocity(const Vector3& p_axis);
+
+ void set_angular_velocity(const Vector3&p_velocity);
+ Vector3 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 Vector3& p_pos, const Vector3& p_impulse);
+
+ RigidBody();
+ ~RigidBody();
+
+};
+
+VARIANT_ENUM_CAST(RigidBody::Mode);
+#endif // PHYSICS_BODY__H
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
new file mode 100644
index 0000000000..961198c8d4
--- /dev/null
+++ b/scene/3d/physics_joint.cpp
@@ -0,0 +1,327 @@
+/*************************************************************************/
+/* physics_joint.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_joint.h"
+
+#if 0
+
+void PhysicsJoint::_set(const String& p_name, const Variant& p_value) {
+
+ if (p_name=="body_A")
+ set_body_A(p_value);
+ else if (p_name=="body_B")
+ set_body_B(p_value);
+ else if (p_name=="active")
+ set_active(p_value);
+ else if (p_name=="no_collision")
+ set_disable_collision(p_value);
+}
+Variant PhysicsJoint::_get(const String& p_name) const {
+
+ if (p_name=="body_A")
+ return get_body_A();
+ else if (p_name=="body_B")
+ return get_body_B();
+ else if (p_name=="active")
+ return is_active();
+ else if (p_name=="no_collision")
+ return has_disable_collision();
+
+ return Variant();
+}
+void PhysicsJoint::_get_property_list( List<PropertyInfo> *p_list) const {
+
+
+ p_list->push_back( PropertyInfo( Variant::NODE_PATH, "body_A" ) );
+ p_list->push_back( PropertyInfo( Variant::NODE_PATH, "body_B" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "active" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "no_collision" ) );
+}
+void PhysicsJoint::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_PARENT_CONFIGURED: {
+
+ _connect();
+ if (get_root_node()->get_editor() && !indicator.is_valid()) {
+
+ indicator=VisualServer::get_singleton()->poly_create();
+ RID mat=VisualServer::get_singleton()->fixed_material_create();
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_UNSHADED, true );
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_ONTOP, true );
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_WIREFRAME, true );
+ VisualServer::get_singleton()->material_set_flag( mat, VisualServer::MATERIAL_FLAG_DOUBLE_SIDED, true );
+ VisualServer::get_singleton()->material_set_line_width( mat, 3 );
+
+ VisualServer::get_singleton()->poly_set_material(indicator,mat,true);
+ _update_indicator();
+
+ }
+
+ if (indicator.is_valid()) {
+
+ indicator_instance=VisualServer::get_singleton()->instance_create(indicator,get_world()->get_scenario());
+ VisualServer::get_singleton()->instance_attach_object_instance_ID( indicator_instance,get_instance_ID() );
+ }
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (indicator_instance.is_valid()) {
+
+ VisualServer::get_singleton()->instance_set_transform(indicator_instance,get_global_transform());
+ }
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (indicator_instance.is_valid()) {
+
+ VisualServer::get_singleton()->free(indicator_instance);
+ }
+ _disconnect();
+
+ } break;
+
+ }
+}
+
+
+RID PhysicsJoint::_get_visual_instance_rid() const {
+
+ return indicator_instance;
+
+}
+
+void PhysicsJoint::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_get_visual_instance_rid"),&PhysicsJoint::_get_visual_instance_rid);
+ ObjectTypeDB::bind_method(_MD("set_body_A","path"),&PhysicsJoint::set_body_A);
+ ObjectTypeDB::bind_method(_MD("set_body_B"),&PhysicsJoint::set_body_B);
+ ObjectTypeDB::bind_method(_MD("get_body_A","path"),&PhysicsJoint::get_body_A);
+ ObjectTypeDB::bind_method(_MD("get_body_B"),&PhysicsJoint::get_body_B);
+
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsJoint::set_active);
+ ObjectTypeDB::bind_method(_MD("is_active"),&PhysicsJoint::is_active);
+
+ ObjectTypeDB::bind_method(_MD("set_disable_collision","disable"),&PhysicsJoint::set_disable_collision);
+ ObjectTypeDB::bind_method(_MD("has_disable_collision"),&PhysicsJoint::has_disable_collision);
+
+
+ ObjectTypeDB::bind_method("reconnect",&PhysicsJoint::reconnect);
+
+ ObjectTypeDB::bind_method(_MD("get_rid"),&PhysicsJoint::get_rid);
+
+}
+
+void PhysicsJoint::set_body_A(const NodePath& p_path) {
+
+ _disconnect();
+ body_A=p_path;
+ _connect();
+ _change_notify("body_A");
+}
+void PhysicsJoint::set_body_B(const NodePath& p_path) {
+
+ _disconnect();
+ body_B=p_path;
+ _connect();
+ _change_notify("body_B");
+
+}
+NodePath PhysicsJoint::get_body_A() const {
+
+ return body_A;
+}
+NodePath PhysicsJoint::get_body_B() const {
+
+ return body_B;
+}
+
+void PhysicsJoint::set_active(bool p_active) {
+
+ active=p_active;
+ if (is_inside_scene()) {
+ PhysicsServer::get_singleton()->joint_set_active(joint,active);
+ }
+ _change_notify("active");
+}
+
+void PhysicsJoint::set_disable_collision(bool p_active) {
+
+ if (no_collision==p_active)
+ return;
+ _disconnect();
+ no_collision=p_active;
+ _connect();
+
+ _change_notify("no_collision");
+}
+bool PhysicsJoint::has_disable_collision() const {
+
+ return no_collision;
+}
+
+
+
+bool PhysicsJoint::is_active() const {
+
+ return active;
+}
+
+void PhysicsJoint::_disconnect() {
+
+ if (!is_inside_scene())
+ return;
+
+ if (joint.is_valid())
+ PhysicsServer::get_singleton()->free(joint);
+
+ joint=RID();
+
+ Node *nA = get_node(body_A);
+ Node *nB = get_node(body_B);
+
+ PhysicsBody *A = nA?nA->cast_to<PhysicsBody>():NULL;
+ PhysicsBody *B = nA?nB->cast_to<PhysicsBody>():NULL;
+
+ if (!A ||!B)
+ return;
+
+ if (no_collision)
+ PhysicsServer::get_singleton()->body_remove_collision_exception(A->get_body(),B->get_body());
+
+}
+void PhysicsJoint::_connect() {
+
+ if (!is_inside_scene())
+ return;
+
+ ERR_FAIL_COND(joint.is_valid());
+
+ Node *nA = get_node(body_A);
+ Node *nB = get_node(body_B);
+
+ PhysicsBody *A = nA?nA->cast_to<PhysicsBody>():NULL;
+ PhysicsBody *B = nA?nB->cast_to<PhysicsBody>():NULL;
+
+ if (!A && !B)
+ return;
+
+ if (B && !A)
+ SWAP(B,A);
+
+ joint = create(A,B);
+
+ if (A<B)
+ SWAP(A,B);
+
+ if (no_collision)
+ PhysicsServer::get_singleton()->body_add_collision_exception(A->get_body(),B->get_body());
+
+
+
+}
+
+void PhysicsJoint::reconnect() {
+
+ _disconnect();
+ _connect();
+
+}
+
+
+RID PhysicsJoint::get_rid() {
+
+ return joint;
+}
+
+
+PhysicsJoint::PhysicsJoint() {
+
+ active=true;
+ no_collision=true;
+}
+
+
+PhysicsJoint::~PhysicsJoint() {
+
+ if (indicator.is_valid()) {
+
+ VisualServer::get_singleton()->free(indicator);
+ }
+
+}
+
+/* PIN */
+
+void PhysicsJointPin::_update_indicator() {
+
+
+ VisualServer::get_singleton()->poly_clear(indicator);
+
+ Vector<Color> colors;
+ colors.push_back( Color(0.3,0.9,0.2,0.7) );
+ colors.push_back( Color(0.3,0.9,0.2,0.7) );
+
+ Vector<Vector3> points;
+ points.resize(2);
+ points[0]=Vector3(Vector3(-0.2,0,0));
+ points[1]=Vector3(Vector3(0.2,0,0));
+ VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector<Vector3>(),colors,Vector<Vector3>());
+
+ points[0]=Vector3(Vector3(0,-0.2,0));
+ points[1]=Vector3(Vector3(0,0.2,0));
+ VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector<Vector3>(),colors,Vector<Vector3>());
+
+ points[0]=Vector3(Vector3(0,0,-0.2));
+ points[1]=Vector3(Vector3(0,0,0.2));
+ VisualServer::get_singleton()->poly_add_primitive(indicator,points,Vector<Vector3>(),colors,Vector<Vector3>());
+
+}
+
+RID PhysicsJointPin::create(PhysicsBody*A,PhysicsBody*B) {
+
+ RID body_A = A->get_body();
+ RID body_B = B?B->get_body():RID();
+
+ ERR_FAIL_COND_V( !body_A.is_valid(), RID() );
+
+ Vector3 pin_pos = get_global_transform().origin;
+
+ if (body_B.is_valid())
+ return PhysicsServer::get_singleton()->joint_create_double_pin_global(body_A,pin_pos,body_B,pin_pos);
+ else
+ return PhysicsServer::get_singleton()->joint_create_pin(body_A,A->get_global_transform().xform_inv(pin_pos),pin_pos);
+}
+
+PhysicsJointPin::PhysicsJointPin() {
+
+
+}
+#endif
diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h
new file mode 100644
index 0000000000..4a0c609e69
--- /dev/null
+++ b/scene/3d/physics_joint.h
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* physics_joint.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_JOINT_H
+#define PHYSICS_JOINT_H
+
+#include "scene/3d/spatial.h"
+#include "scene/3d/physics_body.h"
+
+#if 0
+class PhysicsJoint : public Spatial {
+
+ OBJ_TYPE(PhysicsJoint,Spatial);
+ OBJ_CATEGORY("3D Physics Nodes");
+
+ NodePath body_A;
+ NodePath body_B;
+ bool active;
+ bool no_collision;
+
+
+ RID indicator_instance;
+
+ RID _get_visual_instance_rid() const;
+protected:
+
+ RID joint;
+ RID indicator;
+
+ 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();
+
+ virtual RID create(PhysicsBody*A,PhysicsBody*B)=0;
+ virtual void _update_indicator()=0;
+
+ void _disconnect();
+ void _connect();
+public:
+
+ void set_body_A(const NodePath& p_path);
+ void set_body_B(const NodePath& p_path);
+ NodePath get_body_A() const;
+ NodePath get_body_B() const;
+
+ void set_active(bool p_active);
+ bool is_active() const;
+
+ void set_disable_collision(bool p_active);
+ bool has_disable_collision() const;
+
+ void reconnect();
+
+ RID get_rid();
+
+ PhysicsJoint();
+ ~PhysicsJoint();
+};
+
+
+
+class PhysicsJointPin : public PhysicsJoint {
+
+ OBJ_TYPE( PhysicsJointPin, PhysicsJoint );
+
+protected:
+
+ virtual void _update_indicator();
+ virtual RID create(PhysicsBody*A,PhysicsBody*B);
+public:
+
+
+ PhysicsJointPin();
+};
+
+#endif // PHYSICS_JOINT_H
+#endif
diff --git a/scene/3d/portal.cpp b/scene/3d/portal.cpp
new file mode 100644
index 0000000000..12b9dc4b7a
--- /dev/null
+++ b/scene/3d/portal.cpp
@@ -0,0 +1,281 @@
+/*************************************************************************/
+/* portal.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 "portal.h"
+#include "servers/visual_server.h"
+#include "scene/resources/surface_tool.h"
+#include "globals.h"
+
+bool Portal::_set(const StringName& p_name, const Variant& p_value) {
+
+ if (p_name=="shape") {
+ DVector<float> src_coords=p_value;
+ Vector<Point2> points;
+ int src_coords_size = src_coords.size();
+ ERR_FAIL_COND_V(src_coords_size%2,false);
+ points.resize(src_coords_size/2);
+ for (int i=0;i<points.size();i++) {
+
+ points[i].x=src_coords[i*2+0];
+ points[i].y=src_coords[i*2+1];
+ set_shape(points);
+ }
+ } else if (p_name=="enabled") {
+ set_enabled(p_value);
+ } else if (p_name=="disable_distance") {
+ set_disable_distance(p_value);
+ } else if (p_name=="disabled_color") {
+ set_disabled_color(p_value);
+ } else if (p_name=="connect_range") {
+ set_connect_range(p_value);
+ } else
+ return false;
+
+ return true;
+}
+
+bool Portal::_get(const StringName& p_name,Variant &r_ret) const {
+
+ if (p_name=="shape") {
+ Vector<Point2> points=get_shape();
+ DVector<float> dst_coords;
+ dst_coords.resize(points.size()*2);
+
+ for (int i=0;i<points.size();i++) {
+
+ dst_coords.set(i*2+0,points[i].x);
+ dst_coords.set(i*2+1,points[i].y);
+ }
+
+ r_ret= dst_coords;
+ } else if (p_name=="enabled") {
+ r_ret= is_enabled();
+ } else if (p_name=="disable_distance") {
+ r_ret= get_disable_distance();
+ } else if (p_name=="disabled_color") {
+ r_ret= get_disabled_color();
+ } else if (p_name=="connect_range") {
+ r_ret= get_connect_range();
+ } else
+ return false;
+ return true;
+}
+
+void Portal::_get_property_list( List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::REAL_ARRAY, "shape" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "enabled" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "disable_distance",PROPERTY_HINT_RANGE,"0,4096,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::COLOR, "disabled_color") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "connect_range",PROPERTY_HINT_RANGE,"0.1,4096,0.01" ) );
+}
+
+
+RES Portal::_get_gizmo_geometry() const {
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(1.0,0.8,0.8,0.7) );
+ mat->set_line_width(4);
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat);
+
+ Vector<Point2> shape = get_shape();
+
+ Vector2 center;
+ for (int i=0;i<shape.size();i++) {
+
+ int n=(i+1)%shape.size();
+ Vector<Vector3> points;
+ surface_tool->add_vertex( Vector3( shape[i].x, shape[i].y,0 ));
+ surface_tool->add_vertex( Vector3( shape[n].x, shape[n].y,0 ));
+ center+=shape[i];
+
+ }
+
+ if (shape.size()>0) {
+
+ center/=shape.size();
+ Vector<Vector3> points;
+ surface_tool->add_vertex( Vector3( center.x, center.y,0 ));
+ surface_tool->add_vertex( Vector3( center.x, center.y,1.0 ));
+ }
+
+ return surface_tool->commit();
+}
+
+
+
+AABB Portal::get_aabb() const {
+
+ return aabb;
+}
+DVector<Face3> Portal::get_faces(uint32_t p_usage_flags) const {
+
+ if (!(p_usage_flags&FACES_ENCLOSING))
+ return DVector<Face3>();
+
+ Vector<Point2> shape = get_shape();
+ if (shape.size()==0)
+ return DVector<Face3>();
+
+ Vector2 center;
+ for (int i=0;i<shape.size();i++) {
+
+ center+=shape[i];
+
+ }
+
+ DVector<Face3> ret;
+ center/=shape.size();
+
+ for (int i=0;i<shape.size();i++) {
+
+ int n=(i+1)%shape.size();
+
+ Face3 f;
+ f.vertex[0]=Vector3( center.x, center.y, 0 );
+ f.vertex[1]=Vector3( shape[i].x, shape[i].y, 0 );
+ f.vertex[2]=Vector3( shape[n].x, shape[n].y, 0 );
+ ret.push_back(f);
+ }
+
+ return ret;
+}
+
+void Portal::set_shape(const Vector<Point2>& p_shape) {
+
+
+ VisualServer::get_singleton()->portal_set_shape(portal, p_shape);
+ update_gizmo();
+}
+
+Vector<Point2> Portal::get_shape() const {
+
+ return VisualServer::get_singleton()->portal_get_shape(portal);
+}
+
+void Portal::set_connect_range(float p_range) {
+
+ connect_range=p_range;
+ VisualServer::get_singleton()->portal_set_connect_range(portal,p_range);
+}
+
+float Portal::get_connect_range() const {
+
+ return connect_range;
+}
+
+
+void Portal::set_enabled(bool p_enabled) {
+
+ enabled=p_enabled;
+ VisualServer::get_singleton()->portal_set_enabled(portal,enabled);
+}
+
+bool Portal::is_enabled() const {
+
+
+ return enabled;
+}
+
+void Portal::set_disable_distance(float p_distance) {
+
+ disable_distance=p_distance;
+ VisualServer::get_singleton()->portal_set_disable_distance(portal,disable_distance);
+
+}
+float Portal::get_disable_distance() const {
+
+
+ return disable_distance;
+}
+
+void Portal::set_disabled_color(const Color& p_disabled_color) {
+
+ disabled_color=p_disabled_color;
+ VisualServer::get_singleton()->portal_set_disabled_color(portal,disabled_color);
+}
+
+Color Portal::get_disabled_color() const {
+
+ return disabled_color;
+}
+
+void Portal::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_shape","points"),&Portal::set_shape);
+ ObjectTypeDB::bind_method(_MD("get_shape"),&Portal::get_shape);
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enable"),&Portal::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&Portal::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_disable_distance","distance"),&Portal::set_disable_distance);
+ ObjectTypeDB::bind_method(_MD("get_disable_distance"),&Portal::get_disable_distance);
+
+ ObjectTypeDB::bind_method(_MD("set_disabled_color","color"),&Portal::set_disabled_color);
+ ObjectTypeDB::bind_method(_MD("get_disabled_color"),&Portal::get_disabled_color);
+
+ ObjectTypeDB::bind_method(_MD("set_connect_range","range"),&Portal::set_connect_range);
+ ObjectTypeDB::bind_method(_MD("get_connect_range"),&Portal::get_connect_range);
+
+}
+
+Portal::Portal() {
+
+ portal = VisualServer::get_singleton()->portal_create();
+ Vector< Point2 > points;
+ points.push_back( Point2( -1, 1 ) );
+ points.push_back( Point2( 1, 1 ) );
+ points.push_back( Point2( 1, -1 ) );
+ points.push_back( Point2( -1, -1 ) );
+ set_shape(points); // default shape
+
+
+ set_connect_range(0.8);
+ set_disable_distance(50);
+ set_enabled(true);
+
+ set_base(portal);
+
+
+}
+
+
+Portal::~Portal() {
+
+ VisualServer::get_singleton()->free(portal);
+}
+
+
diff --git a/scene/3d/portal.h b/scene/3d/portal.h
new file mode 100644
index 0000000000..6905724499
--- /dev/null
+++ b/scene/3d/portal.h
@@ -0,0 +1,93 @@
+/*************************************************************************/
+/* portal.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 PORTAL_H
+#define PORTAL_H
+
+#include "scene/3d/visual_instance.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+/* Portal Logic:
+ If a portal is placed next (very close to) a similar, opposing portal, they automatically connect,
+ otherwise, a portal connects to the parent room
+*/
+
+
+
+class Portal : public VisualInstance {
+
+ OBJ_TYPE(Portal, VisualInstance);
+
+ RID portal;
+
+ bool enabled;
+ float disable_distance;
+ Color disabled_color;
+ float connect_range;
+
+ AABB aabb;
+
+ virtual RES _get_gizmo_geometry() const;
+
+protected:
+
+ bool _set(const StringName& p_name, const Variant& p_value);
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ void _get_property_list( List<PropertyInfo> *p_list) const;
+
+ static void _bind_methods();
+
+public:
+
+ virtual AABB get_aabb() const;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_disable_distance(float p_distance);
+ float get_disable_distance() const;
+
+ void set_disabled_color(const Color& p_disabled_color);
+ Color get_disabled_color() const;
+
+ void set_shape(const Vector<Point2>& p_shape);
+ Vector<Point2> get_shape() const;
+
+ void set_connect_range(float p_range);
+ float get_connect_range() const;
+
+ Portal();
+ ~Portal();
+
+};
+
+#endif
diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp
new file mode 100644
index 0000000000..19bf1c4e1d
--- /dev/null
+++ b/scene/3d/position_3d.cpp
@@ -0,0 +1,66 @@
+/*************************************************************************/
+/* position_3d.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_3d.h"
+#include "scene/resources/mesh.h"
+
+RES Position3D::_get_gizmo_geometry() const {
+
+
+ Ref<Mesh> mesh = memnew( Mesh );
+
+ DVector<Vector3> cursor_points;
+ DVector<Color> cursor_colors;
+ float cs = 0.25;
+ cursor_points.push_back(Vector3(+cs,0,0));
+ cursor_points.push_back(Vector3(-cs,0,0));
+ cursor_points.push_back(Vector3(0,+cs,0));
+ cursor_points.push_back(Vector3(0,-cs,0));
+ cursor_points.push_back(Vector3(0,0,+cs));
+ cursor_points.push_back(Vector3(0,0,-cs));
+ cursor_colors.push_back(Color(1,0.5,0.5,1));
+ cursor_colors.push_back(Color(1,0.5,0.5,1));
+ cursor_colors.push_back(Color(0.5,1,0.5,1));
+ cursor_colors.push_back(Color(0.5,1,0.5,1));
+ cursor_colors.push_back(Color(0.5,0.5,1,1));
+ cursor_colors.push_back(Color(0.5,0.5,1,1));
+
+ Ref<FixedMaterial> mat = memnew( FixedMaterial );
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_line_width(3);
+ Array d;
+ d[Mesh::ARRAY_VERTEX]=cursor_points;
+ d[Mesh::ARRAY_COLOR]=cursor_colors;
+ mesh->add_surface(Mesh::PRIMITIVE_LINES,d);
+ mesh->surface_set_material(0,mat);
+ return mesh;
+}
+
+Position3D::Position3D()
+{
+}
diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h
new file mode 100644
index 0000000000..468b9aafeb
--- /dev/null
+++ b/scene/3d/position_3d.h
@@ -0,0 +1,45 @@
+/*************************************************************************/
+/* position_3d.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_3D_H
+#define POSITION_3D_H
+
+#include "scene/3d/spatial.h"
+
+class Position3D : public Spatial {
+
+ OBJ_TYPE(Position3D,Spatial);
+
+ virtual RES _get_gizmo_geometry() const;
+
+public:
+
+ Position3D();
+};
+
+#endif // POSITION_3D_H
diff --git a/scene/3d/proximity_group.cpp b/scene/3d/proximity_group.cpp
new file mode 100644
index 0000000000..b3378c7698
--- /dev/null
+++ b/scene/3d/proximity_group.cpp
@@ -0,0 +1,198 @@
+/*************************************************************************/
+/* proximity_group.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 "proximity_group.h"
+
+#include "math_funcs.h"
+
+void ProximityGroup::clear_groups() {
+
+ Map<StringName, uint32_t>::Element *E;
+
+ {
+ const int size = 16;
+ StringName remove_list[size];
+ E = groups.front();
+ int num = 0;
+ while (E && num < size) {
+
+ if (E->get() != group_version) {
+ remove_list[num++] = E->key();
+ };
+
+ E = E->next();
+ };
+ for (int i=0; i<num; i++) {
+
+ groups.erase(remove_list[i]);
+ };
+ };
+
+ if (E) {
+ clear_groups(); // call until we go through the whole list
+ };
+};
+
+void ProximityGroup::update_groups() {
+
+ if (grid_radius == Vector3(0, 0, 0))
+ return;
+
+ ++group_version;
+
+ Vector3 pos = get_global_transform().get_origin();
+ Vector3 vcell = pos / cell_size;
+ int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) };
+
+ add_groups(cell, group_name, 0);
+
+ clear_groups();
+};
+
+void ProximityGroup::add_groups(int* p_cell, String p_base, int p_depth) {
+
+ p_base = p_base + "|";
+ if (grid_radius[p_depth] == 0) {
+
+ if (p_depth == 2) {
+ _new_group(p_base);
+ } else {
+ add_groups(p_cell, p_base, p_depth + 1);
+ };
+ };
+
+ int start = p_cell[p_depth] - grid_radius[p_depth];
+ int end = p_cell[p_depth] + grid_radius[p_depth];
+
+ for (int i=start; i<=end; i++) {
+
+ String gname = p_base + itos(i);
+ if (p_depth == 2) {
+ _new_group(gname);
+ } else {
+ add_groups(p_cell, gname, p_depth + 1);
+ };
+ };
+};
+
+void ProximityGroup::_new_group(StringName p_name) {
+
+ const Map<StringName, uint32_t>::Element* E = groups.find(p_name);
+ if (!E) {
+ add_to_group(p_name);
+ };
+
+ groups[p_name] = group_version;
+};
+
+void ProximityGroup::set_group_name(String p_group_name) {
+
+ group_name = p_group_name;
+};
+
+void ProximityGroup::_notification(int what) {
+
+ switch (what) {
+
+ case NOTIFICATION_EXIT_SCENE:
+ ++group_version;
+ clear_groups();
+ break;
+ case NOTIFICATION_TRANSFORM_CHANGED:
+ update_groups();
+ break;
+ };
+};
+
+void ProximityGroup::broadcast(String p_name, Variant p_params) {
+
+ Map<StringName, uint32_t>::Element *E;
+ E = groups.front();
+ while (E) {
+
+ get_scene()->call_group(SceneMainLoop::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params);
+ E = E->next();
+ };
+
+};
+
+void ProximityGroup::_proximity_group_broadcast(String p_name, Variant p_params) {
+
+ if (dispatch_mode == MODE_PROXY) {
+
+ get_parent()->call(p_name, p_params);
+ } else {
+
+ emit_signal("broadcast", p_name, p_params);
+ };
+};
+
+
+void ProximityGroup::set_dispatch_mode(int p_mode) {
+
+ dispatch_mode = (DispatchMode)p_mode;
+};
+
+void ProximityGroup::set_grid_radius(const Vector3& p_radius) {
+
+ grid_radius = p_radius;
+};
+
+Vector3 ProximityGroup::get_grid_radius() const {
+
+ return grid_radius;
+};
+
+
+void ProximityGroup::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_group_name","name"), &ProximityGroup::set_group_name);
+ ObjectTypeDB::bind_method(_MD("broadcast","name", "parameters"), &ProximityGroup::broadcast);
+ ObjectTypeDB::bind_method(_MD("set_dispatch_mode","mode"), &ProximityGroup::set_dispatch_mode);
+ ObjectTypeDB::bind_method(_MD("_proximity_group_broadcast","name","params"), &ProximityGroup::_proximity_group_broadcast);
+ ObjectTypeDB::bind_method(_MD("set_grid_radius","radius"), &ProximityGroup::set_grid_radius);
+ ObjectTypeDB::bind_method(_MD("get_grid_radius"), &ProximityGroup::get_grid_radius);
+
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR3, "grid_radius"), _SCS("set_grid_radius"), _SCS("get_grid_radius"));
+
+ ADD_SIGNAL( MethodInfo("broadcast", PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::ARRAY, "parameters")) );
+};
+
+
+ProximityGroup::ProximityGroup() {
+
+ group_version = 0;
+ dispatch_mode = MODE_PROXY;
+
+ grid_radius = Vector3(1, 1, 1);
+
+};
+
+ProximityGroup::~ProximityGroup() {
+
+};
diff --git a/scene/3d/proximity_group.h b/scene/3d/proximity_group.h
new file mode 100644
index 0000000000..c8660c17dd
--- /dev/null
+++ b/scene/3d/proximity_group.h
@@ -0,0 +1,81 @@
+/*************************************************************************/
+/* proximity_group.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 PROXIMITY_GROUP_H
+#define PROXIMITY_GROUP_H
+
+#include "spatial.h"
+
+class ProximityGroup : public Spatial {
+
+ OBJ_TYPE( ProximityGroup, Spatial );
+ OBJ_CATEGORY("3D");
+
+public:
+ enum DispatchMode {
+ MODE_PROXY,
+ MODE_SIGNAL,
+ };
+
+public:
+ void clear_groups();
+ void update_groups();
+
+ void _notification(int p_what);
+
+ DispatchMode dispatch_mode;
+
+ Map<StringName, uint32_t> groups;
+ String group_name;
+
+ float cell_size;
+ Vector3 grid_radius;
+ uint32_t group_version;
+
+ void add_groups(int* p_cell, String p_base, int p_depth);
+ void _new_group(StringName p_name);
+
+ void _proximity_group_broadcast(String p_name, Variant p_params);
+
+ static void _bind_methods();
+
+public:
+
+ void set_group_name(String p_group_name);
+ void broadcast(String p_name, Variant p_params);
+ void set_dispatch_mode(int p_mode);
+
+ void set_grid_radius(const Vector3& p_radius);
+ Vector3 get_grid_radius() const;
+
+ ProximityGroup();
+ ~ProximityGroup();
+};
+
+#endif
+
diff --git a/scene/3d/quad.cpp b/scene/3d/quad.cpp
new file mode 100644
index 0000000000..78dd56141c
--- /dev/null
+++ b/scene/3d/quad.cpp
@@ -0,0 +1,232 @@
+/*************************************************************************/
+/* quad.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 "quad.h"
+#include "servers/visual_server.h"
+
+void Quad::_update() {
+
+ if (!is_inside_scene())
+ return;
+
+ Vector3 normal;
+ normal[axis]=1.0;
+
+ const int axis_order_1[3]={1,2,0};
+ const int axis_order_2[3]={2,0,1};
+ const int a1=axis_order_1[axis];
+ const int a2=axis_order_2[axis];
+
+
+
+ DVector<Vector3> points;
+ points.resize(4);
+ DVector<Vector3>::Write pointsw = points.write();
+
+ Vector2 s2 = size*0.5;
+ Vector2 o = offset;
+ if (!centered)
+ o+=s2;
+
+ pointsw[0][a1]=-s2.x+offset.x;
+ pointsw[0][a2]=s2.y+offset.y;
+
+ pointsw[1][a1]=s2.x+offset.x;
+ pointsw[1][a2]=s2.y+offset.y;
+
+ pointsw[2][a1]=s2.x+offset.x;
+ pointsw[2][a2]=-s2.y+offset.y;
+
+ pointsw[3][a1]=-s2.x+offset.x;
+ pointsw[3][a2]=-s2.y+offset.y;
+
+
+ aabb=AABB(pointsw[0],Vector3());
+ for(int i=1;i<4;i++)
+ aabb.expand_to(pointsw[i]);
+
+ pointsw = DVector<Vector3>::Write();
+
+ DVector<Vector3> normals;
+ normals.resize(4);
+ DVector<Vector3>::Write normalsw = normals.write();
+
+ for(int i=0;i<4;i++)
+ normalsw[i]=normal;
+
+ normalsw=DVector<Vector3>::Write();
+
+
+
+ DVector<Vector2> uvs;
+ uvs.resize(4);
+ DVector<Vector2>::Write uvsw = uvs.write();
+
+ uvsw[0]=Vector2(0,0);
+ uvsw[1]=Vector2(1,0);
+ uvsw[2]=Vector2(1,1);
+ uvsw[3]=Vector2(0,1);
+
+ uvsw = DVector<Vector2>::Write();
+
+ DVector<int> indices;
+ indices.resize(6);
+
+ DVector<int>::Write indicesw = indices.write();
+ indicesw[0]=0;
+ indicesw[1]=1;
+ indicesw[2]=2;
+ indicesw[3]=2;
+ indicesw[4]=3;
+ indicesw[5]=0;
+
+ indicesw=DVector<int>::Write();
+
+ Array arr;
+ arr.resize(VS::ARRAY_MAX);
+ arr[VS::ARRAY_VERTEX]=points;
+ arr[VS::ARRAY_NORMAL]=normals;
+ arr[VS::ARRAY_TEX_UV]=uvs;
+ arr[VS::ARRAY_INDEX]=indices;
+
+
+ if (configured) {
+ VS::get_singleton()->mesh_remove_surface(mesh,0);
+ } else {
+ configured=true;
+ }
+ VS::get_singleton()->mesh_add_surface(mesh,VS::PRIMITIVE_TRIANGLES,arr);
+
+ pending_update=false;
+}
+
+
+void Quad::set_axis(Vector3::Axis p_axis) {
+
+ axis=p_axis;
+ _update();
+}
+
+Vector3::Axis Quad::get_axis() const{
+
+ return axis;
+}
+
+void Quad::set_size(const Vector2& p_size){
+
+ size=p_size;
+ _update();
+}
+Vector2 Quad::get_size() const{
+
+ return size;
+}
+
+void Quad::set_offset(const Vector2& p_offset){
+
+ offset=p_offset;
+ _update();
+}
+Vector2 Quad::get_offset() const{
+
+ return offset;
+}
+
+void Quad::set_centered(bool p_enabled){
+
+ centered=p_enabled;
+ _update();
+}
+bool Quad::is_centered() const{
+
+ return centered;
+}
+
+void Quad::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (pending_update)
+ _update();
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ pending_update=true;
+
+
+ } break;
+ }
+}
+
+DVector<Face3> Quad::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+}
+
+AABB Quad::get_aabb() const {
+
+ return aabb;
+}
+
+void Quad::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_axis","axis"),&Quad::set_axis);
+ ObjectTypeDB::bind_method(_MD("get_axis"),&Quad::get_axis);
+
+ ObjectTypeDB::bind_method(_MD("set_size","size"),&Quad::set_size);
+ ObjectTypeDB::bind_method(_MD("get_size"),&Quad::get_size);
+
+ ObjectTypeDB::bind_method(_MD("set_centered","centered"),&Quad::set_centered);
+ ObjectTypeDB::bind_method(_MD("is_centered"),&Quad::is_centered);
+
+ ObjectTypeDB::bind_method(_MD("set_offset","offset"),&Quad::set_offset);
+ ObjectTypeDB::bind_method(_MD("get_offset"),&Quad::get_offset);
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "quad/axis", PROPERTY_HINT_ENUM,"X,Y,Z" ), _SCS("set_axis"), _SCS("get_axis"));
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "quad/size" ), _SCS("set_size"), _SCS("get_size"));
+ ADD_PROPERTY( PropertyInfo( Variant::VECTOR2, "quad/offset" ), _SCS("set_offset"), _SCS("get_offset"));
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "quad/centered" ), _SCS("set_centered"), _SCS("is_centered"));
+
+}
+
+Quad::Quad() {
+
+ pending_update=true;
+ centered=true;
+ //offset=0;
+ size=Vector2(1,1);
+ axis=Vector3::AXIS_Z;
+ mesh=VisualServer::get_singleton()->mesh_create();
+ set_base(mesh);
+ configured=false;
+
+}
diff --git a/scene/3d/quad.h b/scene/3d/quad.h
new file mode 100644
index 0000000000..7d1b4f5dc4
--- /dev/null
+++ b/scene/3d/quad.h
@@ -0,0 +1,76 @@
+/*************************************************************************/
+/* quad.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 QUAD_H
+#define QUAD_H
+
+
+#include "scene/3d/visual_instance.h"
+#include "rid.h"
+
+class Quad : public GeometryInstance {
+
+ OBJ_TYPE(Quad,GeometryInstance);
+
+ Vector3::Axis axis;
+ bool centered;
+ Vector2 offset;
+ Vector2 size;
+
+ AABB aabb;
+ bool configured;
+ bool pending_update;
+ RID mesh;
+
+ void _update();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_axis(Vector3::Axis p_axis);
+ Vector3::Axis get_axis() const;
+
+ void set_size(const Vector2& p_sizze);
+ Vector2 get_size() const;
+
+ void set_offset(const Vector2& p_offset);
+ Vector2 get_offset() const;
+
+ void set_centered(bool p_enabled);
+ bool is_centered() const;
+
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+ virtual AABB get_aabb() const;
+
+ Quad();
+};
+
+#endif // QUAD_H
diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
new file mode 100644
index 0000000000..e36e2da14f
--- /dev/null
+++ b/scene/3d/ray_cast.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* ray_cast.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.h"
+
+#include "servers/physics_server.h"
+#include "collision_object.h"
+void RayCast::set_cast_to(const Vector3& p_point) {
+
+ cast_to=p_point;
+ if (is_inside_scene() && get_scene()->is_editor_hint())
+ update_gizmo();
+
+}
+
+Vector3 RayCast::get_cast_to() const{
+
+ return cast_to;
+}
+
+bool RayCast::is_colliding() const{
+
+ return collided;
+}
+Object *RayCast::get_collider() const{
+
+ if (against==0)
+ return NULL;
+
+ return ObjectDB::get_instance(against);
+}
+
+int RayCast::get_collider_shape() const {
+
+ return against_shape;
+}
+Vector3 RayCast::get_collision_point() const{
+
+ return collision_point;
+}
+Vector3 RayCast::get_collision_normal() const{
+
+ return collision_normal;
+}
+
+
+void RayCast::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 RayCast::is_enabled() const {
+
+
+ return enabled;
+}
+
+
+void RayCast::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (enabled && !get_scene()->is_editor_hint()) {
+ set_fixed_process(true);
+ Node *p = get_parent();
+ while( p && p->cast_to<Spatial>() ) {
+
+ CollisionObject *co = p->cast_to<CollisionObject>();
+ if (co) {
+
+ exception=co->get_rid();
+ exceptions.insert(exception);
+ }
+
+ p=p->get_parent();
+ }
+ } else
+ set_fixed_process(false);
+
+
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (enabled) {
+ set_fixed_process(false);
+ }
+
+ exceptions.erase(exception);
+
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ if (!enabled)
+ break;
+
+
+
+ Ref<World> w3d = get_world();
+ ERR_BREAK( w3d.is_null() );
+
+ PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(w3d->get_space());
+ ERR_BREAK( !dss );
+
+ Transform gt = get_global_transform();
+
+ Vector3 to = cast_to;
+ if (to==Vector3())
+ to=Vector3(0,0.01,0);
+
+ PhysicsDirectSpaceState::RayResult rr;
+
+ if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exceptions)) {
+
+ collided=true;
+ against=rr.collider_id;
+ collision_point=rr.position;
+ collision_normal=rr.normal;
+ against_shape=rr.shape;
+ } else {
+ collided=false;
+ }
+
+
+
+ } break;
+ }
+}
+
+void RayCast::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&RayCast::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&RayCast::is_enabled);
+
+ ObjectTypeDB::bind_method(_MD("set_cast_to","local_point"),&RayCast::set_cast_to);
+ ObjectTypeDB::bind_method(_MD("get_cast_to"),&RayCast::get_cast_to);
+
+ ObjectTypeDB::bind_method(_MD("is_colliding"),&RayCast::is_colliding);
+
+ ObjectTypeDB::bind_method(_MD("get_collider"),&RayCast::get_collider);
+ ObjectTypeDB::bind_method(_MD("get_collider_shape"),&RayCast::get_collider_shape);
+ ObjectTypeDB::bind_method(_MD("get_collision_point"),&RayCast::get_collision_point);
+ ObjectTypeDB::bind_method(_MD("get_collision_normal"),&RayCast::get_collision_normal);
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to"));
+}
+
+RayCast::RayCast() {
+
+ enabled=false;
+ against=0;
+ collided=false;
+ against_shape=0;
+ cast_to=Vector3(0,-1,0);
+}
diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h
new file mode 100644
index 0000000000..96606b1628
--- /dev/null
+++ b/scene/3d/ray_cast.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* ray_cast.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_H
+#define RAY_CAST_H
+
+#include "scene/3d/spatial.h"
+
+class RayCast : public Spatial {
+
+ OBJ_TYPE(RayCast,Spatial);
+
+
+ bool enabled;
+ bool collided;
+ ObjectID against;
+ int against_shape;
+ Vector3 collision_point;
+ Vector3 collision_normal;
+
+ Vector3 cast_to;
+
+ RID exception;
+ Set<RID> exceptions;
+
+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 Vector3& p_point);
+ Vector3 get_cast_to() const;
+
+ bool is_colliding() const;
+ Object *get_collider() const;
+ int get_collider_shape() const;
+ Vector3 get_collision_point() const;
+ Vector3 get_collision_normal() const;
+
+ RayCast();
+};
+
+#endif // RAY_CAST_H
diff --git a/scene/3d/room_instance.cpp b/scene/3d/room_instance.cpp
new file mode 100644
index 0000000000..0f390c15af
--- /dev/null
+++ b/scene/3d/room_instance.cpp
@@ -0,0 +1,299 @@
+/*************************************************************************/
+/* room_instance.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 "room_instance.h"
+
+#include "servers/visual_server.h"
+
+#include "geometry.h"
+#include "globals.h"
+#include "scene/resources/surface_tool.h"
+
+
+void Room::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_WORLD: {
+ // go find parent level
+ Node *parent_room=get_parent();
+ level=0;
+
+ while(parent_room) {
+
+ Room *r = parent_room->cast_to<Room>();
+ if (r) {
+
+ level=r->level+1;
+ break;
+ }
+
+ parent_room=parent_room->get_parent();
+ }
+
+
+ if (sound_enabled)
+ SpatialSoundServer::get_singleton()->room_set_space(sound_room,get_world()->get_sound_space());
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+ SpatialSoundServer::get_singleton()->room_set_transform(sound_room,get_global_transform());
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ if (sound_enabled)
+ SpatialSoundServer::get_singleton()->room_set_space(sound_room,RID());
+
+
+ } break;
+ }
+
+}
+
+
+RES Room::_get_gizmo_geometry() const {
+
+ DVector<Face3> faces;
+ if (!room.is_null())
+ faces=room->get_geometry_hint();
+
+ int count=faces.size();
+ if (count==0)
+ return RES();
+
+ DVector<Face3>::Read facesr=faces.read();
+
+ const Face3* facesptr=facesr.ptr();
+
+ DVector<Vector3> points;
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.2,0.8,0.9,0.3) );
+ mat->set_line_width(4);
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat);
+
+ for (int i=0;i<count;i++) {
+
+ surface_tool->add_vertex(facesptr[i].vertex[0]);
+ surface_tool->add_vertex(facesptr[i].vertex[1]);
+
+ surface_tool->add_vertex(facesptr[i].vertex[1]);
+ surface_tool->add_vertex(facesptr[i].vertex[2]);
+
+ surface_tool->add_vertex(facesptr[i].vertex[2]);
+ surface_tool->add_vertex(facesptr[i].vertex[0]);
+
+ }
+
+ return surface_tool->commit();
+}
+
+
+
+AABB Room::get_aabb() const {
+
+ if (room.is_null())
+ return AABB();
+
+ return room->get_bounds().get_aabb();
+}
+DVector<Face3> Room::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+
+}
+
+void Room::set_room( const Ref<RoomBounds>& p_room ) {
+
+ room=p_room;
+ update_gizmo();
+
+ if (room.is_valid()) {
+
+ set_base(room->get_rid());
+ } else {
+ set_base(RID());
+ }
+
+ if (!is_inside_scene())
+ return;
+
+
+ propagate_notification(NOTIFICATION_AREA_CHANGED);
+ update_gizmo();
+
+ if (room.is_valid())
+ SpatialSoundServer::get_singleton()->room_set_bounds(sound_room,room->get_bounds());
+
+
+}
+
+Ref<RoomBounds> Room::get_room() const {
+
+ return room;
+}
+
+void Room::_parse_node_faces(DVector<Face3> &all_faces,const Node *p_node) const {
+
+ const VisualInstance *vi=p_node->cast_to<VisualInstance>();
+
+ if (vi) {
+ DVector<Face3> faces=vi->get_faces(FACES_ENCLOSING);
+
+ if (faces.size()) {
+ int old_len=all_faces.size();
+ all_faces.resize( all_faces.size() + faces.size() );
+ int new_len=all_faces.size();
+ DVector<Face3>::Write all_facesw=all_faces.write();
+ Face3 * all_facesptr=all_facesw.ptr();
+
+ DVector<Face3>::Read facesr=faces.read();
+ const Face3 * facesptr=facesr.ptr();
+
+ Transform tr=vi->get_relative_transform(this);
+
+ for(int i=old_len;i<new_len;i++) {
+
+ Face3 f=facesptr[i-old_len];
+ for (int j=0;j<3;j++)
+ f.vertex[j]=tr.xform(f.vertex[j]);
+ all_facesptr[i]=f;
+ }
+ }
+ }
+
+
+ for (int i=0;i<p_node->get_child_count();i++) {
+
+ _parse_node_faces(all_faces,p_node->get_child(i));
+ }
+
+}
+
+void Room::compute_room_from_subtree() {
+
+
+ DVector<Face3> all_faces;
+ _parse_node_faces(all_faces,this);
+
+
+ if (all_faces.size()==0)
+ return;
+ float error;
+ DVector<Face3> wrapped_faces = Geometry::wrap_geometry(all_faces,&error);
+
+
+ if (wrapped_faces.size()==0)
+ return;
+
+ BSP_Tree tree(wrapped_faces,error);
+
+ Ref<RoomBounds> room( memnew( RoomBounds ) );
+ room->set_bounds(tree);
+ room->set_geometry_hint(wrapped_faces);
+
+ set_room(room);
+
+}
+
+
+
+void Room::set_simulate_acoustics(bool p_enable) {
+
+ if (sound_enabled==p_enable)
+ return;
+
+ sound_enabled=p_enable;
+ if (!is_inside_world())
+ return; //nothing to do
+
+ if (sound_enabled)
+ SpatialSoundServer::get_singleton()->room_set_space(sound_room,get_world()->get_sound_space());
+ else
+ SpatialSoundServer::get_singleton()->room_set_space(sound_room,RID());
+
+
+}
+
+void Room::_bounds_changed() {
+
+ update_gizmo();
+}
+
+bool Room::is_simulating_acoustics() const {
+
+ return sound_enabled;
+}
+
+
+
+RID Room::get_sound_room() const {
+
+ return RID();
+}
+
+void Room::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_room","room:Room"),&Room::set_room );
+ ObjectTypeDB::bind_method(_MD("get_room:Room"),&Room::get_room );
+ ObjectTypeDB::bind_method(_MD("compute_room_from_subtree"),&Room::compute_room_from_subtree);
+
+
+
+ ObjectTypeDB::bind_method(_MD("set_simulate_acoustics","enable"),&Room::set_simulate_acoustics );
+ ObjectTypeDB::bind_method(_MD("is_simulating_acoustics"),&Room::is_simulating_acoustics );
+
+
+
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "room/room", PROPERTY_HINT_RESOURCE_TYPE, "Area" ), _SCS("set_room"), _SCS("get_room") );
+ ADD_PROPERTY( PropertyInfo( Variant::BOOL, "room/simulate_acoustics"), _SCS("set_simulate_acoustics"), _SCS("is_simulating_acoustics") );
+}
+
+
+Room::Room() {
+
+ sound_enabled=false;
+ sound_room=SpatialSoundServer::get_singleton()->room_create();
+
+ level=0;
+
+}
+
+
+Room::~Room() {
+
+ SpatialSoundServer::get_singleton()->free(sound_room);
+}
+
diff --git a/scene/3d/room_instance.h b/scene/3d/room_instance.h
new file mode 100644
index 0000000000..1d11630cef
--- /dev/null
+++ b/scene/3d/room_instance.h
@@ -0,0 +1,102 @@
+/*************************************************************************/
+/* room_instance.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 ROOM_INSTANCE_H
+#define ROOM_INSTANCE_H
+
+#include "scene/3d/visual_instance.h"
+#include "scene/resources/room.h"
+#include "servers/spatial_sound_server.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+/* RoomInstance Logic:
+ a) Instances that belong to the room are drawn only if the room is visible (seen through portal, or player inside)
+ b) Instances that don't belong to any room are considered to belong to the root room (RID empty)
+ c) "dynamic" Instances are assigned to the rooms their AABB touch
+
+*/
+
+
+class Room : public VisualInstance {
+
+ OBJ_TYPE( Room, VisualInstance );
+public:
+
+
+
+
+private:
+ Ref<RoomBounds> room;
+
+ RID sound_room;
+
+ bool sound_enabled;
+
+ int level;
+ void _parse_node_faces(DVector<Face3> &all_faces,const Node *p_node) const;
+
+
+ void _bounds_changed();
+ virtual RES _get_gizmo_geometry() const;
+
+protected:
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+
+public:
+
+ enum {
+ // used to notify portals that the room in which they are has changed.
+ NOTIFICATION_AREA_CHANGED=60
+ };
+
+ virtual AABB get_aabb() const;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ void set_room( const Ref<RoomBounds>& p_room );
+ Ref<RoomBounds> get_room() const;
+
+ void set_simulate_acoustics(bool p_enable);
+ bool is_simulating_acoustics() const;
+
+ void compute_room_from_subtree();
+
+ RID get_sound_room() const;
+
+ Room();
+ ~Room();
+
+};
+
+
+#endif // ROOM_INSTANCE_H
diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp
new file mode 100644
index 0000000000..05546ee4db
--- /dev/null
+++ b/scene/3d/scenario_fx.cpp
@@ -0,0 +1,74 @@
+/*************************************************************************/
+/* scenario_fx.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 "scenario_fx.h"
+
+
+
+void WorldEnvironment::_notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_WORLD) {
+
+ get_world()->set_environment(environment);
+ } else if (p_what==NOTIFICATION_EXIT_WORLD) {
+
+ get_world()->set_environment(Ref<Environment>());
+ }
+}
+
+void WorldEnvironment::set_environment(const Ref<Environment>& p_environment) {
+
+ environment=p_environment;
+ if (is_inside_world()) {
+ get_world()->set_environment(environment);
+ }
+
+}
+
+Ref<Environment> WorldEnvironment::get_environment() const {
+
+ return environment;
+}
+
+
+void WorldEnvironment::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_environment","env:Environment"),&WorldEnvironment::set_environment);
+ ObjectTypeDB::bind_method(_MD("get_environment:Environment"),&WorldEnvironment::get_environment);
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"environment",PROPERTY_HINT_RESOURCE_TYPE,"Environment"),_SCS("set_environment"),_SCS("get_environment"));
+
+}
+
+
+WorldEnvironment::WorldEnvironment() {
+
+
+}
+
+
diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h
new file mode 100644
index 0000000000..b8b06ea983
--- /dev/null
+++ b/scene/3d/scenario_fx.h
@@ -0,0 +1,58 @@
+/*************************************************************************/
+/* scenario_fx.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 SCENARIO_FX_H
+#define SCENARIO_FX_H
+
+#include "scene/3d/spatial.h"
+
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+
+class WorldEnvironment : public Spatial {
+
+ OBJ_TYPE(WorldEnvironment,Spatial );
+
+ Ref<Environment> environment;
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+ void set_environment(const Ref<Environment>& p_environment);
+ Ref<Environment> get_environment() const;
+
+ WorldEnvironment();
+
+};
+
+#endif
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
new file mode 100644
index 0000000000..6ec23f96fb
--- /dev/null
+++ b/scene/3d/skeleton.cpp
@@ -0,0 +1,536 @@
+/*************************************************************************/
+/* skeleton.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 "skeleton.h"
+
+#include "message_queue.h"
+
+#include "scene/resources/surface_tool.h"
+#include "core/globals.h"
+
+
+bool Skeleton::_set(const StringName& p_path, const Variant& p_value) {
+
+ String path = p_path;
+
+ if (!path.begins_with("bones/"))
+ return false;
+
+ int which=path.get_slice("/",1).to_int();
+ String what=path.get_slice("/",2);
+
+ if (which==bones.size() && what=="name") {
+
+ add_bone(p_value);
+ return true;
+ }
+
+ ERR_FAIL_INDEX_V( which, bones.size(), false );
+
+ if (what=="parent")
+ set_bone_parent(which, p_value );
+ else if (what=="rest")
+ set_bone_rest(which, p_value);
+ else if (what=="enabled")
+ set_bone_enabled(which, p_value);
+ else if (what=="pose")
+ set_bone_pose(which, p_value);
+ else if (what=="bound_childs") {
+ Array children=p_value;
+
+ bones[which].nodes_bound.clear();
+
+ for (int i=0;i<children.size();i++) {
+
+ NodePath path=children[i];
+ ERR_CONTINUE( path.operator String()=="" );
+ Node *node = get_node(path);
+ ERR_CONTINUE(!node);
+ bind_child_node_to_bone(which,node);
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+bool Skeleton::_get(const StringName& p_name,Variant &r_ret) const {
+
+ String path=p_name;
+
+ if (!path.begins_with("bones/"))
+ return false;
+
+ int which=path.get_slice("/",1).to_int();
+ String what=path.get_slice("/",2);
+
+ ERR_FAIL_INDEX_V( which, bones.size(), false );
+
+ if (what=="name")
+ r_ret=get_bone_name(which);
+ if (what=="parent")
+ r_ret=get_bone_parent(which);
+ else if (what=="rest")
+ r_ret=get_bone_rest(which);
+ else if (what=="enabled")
+ r_ret=is_bone_enabled(which);
+ else if (what=="pose")
+ r_ret=get_bone_pose(which);
+ else if (what=="bound_childs") {
+ Array children;
+
+ for (const List<uint32_t>::Element *E=bones[which].nodes_bound.front();E;E=E->next()) {
+
+ Object *obj=ObjectDB::get_instance(E->get());
+ ERR_CONTINUE(!obj);
+ Node *node=obj->cast_to<Node>();
+ ERR_CONTINUE(!node);
+ NodePath path=get_path_to(node);
+ children.push_back(path);
+
+ }
+
+ r_ret=children;
+ } else
+ return false;
+
+ return true;
+
+}
+void Skeleton::_get_property_list( List<PropertyInfo>* p_list ) const {
+
+ for (int i=0;i<bones.size();i++) {
+
+ String prep="bones/"+itos(i)+"/";
+ p_list->push_back( PropertyInfo( Variant::STRING, prep+"name" ) );
+ p_list->push_back( PropertyInfo( Variant::INT, prep+"parent" , PROPERTY_HINT_RANGE,"-1,"+itos(i-1)+",1") );
+ p_list->push_back( PropertyInfo( Variant::TRANSFORM, prep+"rest" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, prep+"enabled" ) );
+ p_list->push_back( PropertyInfo( Variant::TRANSFORM, prep+"pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ) );
+ p_list->push_back( PropertyInfo( Variant::ARRAY, prep+"bound_childs" ) );
+ }
+}
+
+void Skeleton::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ if (dirty) {
+
+ dirty=false;
+ _make_dirty(); // property make it dirty
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ } break;
+ case NOTIFICATION_UPDATE_SKELETON: {
+
+
+ VisualServer *vs=VisualServer::get_singleton();
+ Bone *bonesptr=&bones[0];
+ int len=bones.size();
+
+ vs->skeleton_resize( skeleton, len ); // if same size, nothin really happens
+
+ // pose changed, rebuild cache of inverses
+ if (rest_global_inverse_dirty) {
+
+ // calculate global rests and invert them
+ for (int i=0;i<len;i++) {
+ Bone &b=bonesptr[i];
+ if (b.parent>=0)
+ b.rest_global_inverse=bonesptr[b.parent].rest_global_inverse * b.rest;
+ else
+ b.rest_global_inverse=b.rest;
+ }
+ for (int i=0;i<len;i++) {
+ Bone &b=bonesptr[i];
+ b.rest_global_inverse.affine_invert();
+ }
+
+ rest_global_inverse_dirty=false;
+
+ }
+
+ for (int i=0;i<len;i++) {
+
+ Bone &b=bonesptr[i];
+
+ if (b.enabled) {
+
+ Transform pose=b.pose;
+ if (b.custom_pose_enable) {
+
+ pose = b.custom_pose * pose;
+ }
+
+ if (b.parent>=0) {
+
+ b.pose_global=bonesptr[b.parent].pose_global * (b.rest * pose);
+ } else {
+
+ b.pose_global=b.rest * pose;
+ }
+ } else {
+
+ if (b.parent>=0) {
+
+ b.pose_global=bonesptr[b.parent].pose_global * b.rest;
+ } else {
+
+ b.pose_global=b.rest;
+ }
+ }
+
+ vs->skeleton_bone_set_transform( skeleton, i, b.pose_global * b.rest_global_inverse );
+
+ for(List<uint32_t>::Element *E=b.nodes_bound.front();E;E=E->next()) {
+
+ Object *obj=ObjectDB::get_instance(E->get());
+ ERR_CONTINUE(!obj);
+ Spatial *sp = obj->cast_to<Spatial>();
+ ERR_CONTINUE(!sp);
+ sp->set_transform(b.pose_global * b.rest_global_inverse);
+ }
+ }
+
+ dirty=false;
+ } break;
+ }
+}
+
+Transform Skeleton::get_bone_transform(int p_bone) const {
+ ERR_FAIL_INDEX_V(p_bone,bones.size(),Transform());
+ if (dirty)
+ const_cast<Skeleton*>(this)->notification(NOTIFICATION_UPDATE_SKELETON);
+ return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse;
+}
+
+RID Skeleton::get_skeleton() const {
+
+ return skeleton;
+}
+
+// skeleton creation api
+void Skeleton::add_bone(const String& p_name) {
+
+ ERR_FAIL_COND( p_name=="" || p_name.find(":")!=-1 || p_name.find("/")!=-1 );
+
+ for (int i=0;i<bones.size();i++) {
+
+ ERR_FAIL_COND( bones[i].name=="p_name");
+ }
+
+ Bone b;
+ b.name=p_name;
+ bones.push_back(b);
+
+ rest_global_inverse_dirty=true;
+ _make_dirty();
+ update_gizmo();
+}
+int Skeleton::find_bone(String p_name) const {
+
+ for (int i=0;i<bones.size();i++) {
+
+ if (bones[i].name==p_name)
+ return i;
+ }
+
+ return -1;
+}
+String Skeleton::get_bone_name(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), "" );
+
+ return bones[p_bone].name;
+}
+
+int Skeleton::get_bone_count() const {
+
+ return bones.size();
+}
+
+void Skeleton::set_bone_parent(int p_bone, int p_parent) {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+ ERR_FAIL_COND( p_parent!=-1 && (p_parent<0 || p_parent>=p_bone));
+
+ bones[p_bone].parent=p_parent;
+ rest_global_inverse_dirty=true;
+ _make_dirty();
+}
+
+int Skeleton::get_bone_parent(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), -1 );
+
+ return bones[p_bone].parent;
+}
+
+void Skeleton::set_bone_rest(int p_bone, const Transform& p_rest) {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+
+ bones[p_bone].rest=p_rest;
+ rest_global_inverse_dirty=true;
+ _make_dirty();
+
+}
+Transform Skeleton::get_bone_rest(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() );
+
+ return bones[p_bone].rest;
+
+}
+
+void Skeleton::set_bone_enabled(int p_bone, bool p_enabled) {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+
+ bones[p_bone].enabled=p_enabled;
+ rest_global_inverse_dirty=true;
+ _make_dirty();
+}
+bool Skeleton::is_bone_enabled(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), false );
+ return bones[p_bone].enabled;
+
+}
+
+void Skeleton::bind_child_node_to_bone(int p_bone,Node *p_node) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+
+ uint32_t id=p_node->get_instance_ID();
+
+ for (List<uint32_t>::Element *E=bones[p_bone].nodes_bound.front();E;E=E->next()) {
+
+ if (E->get()==id)
+ return; // already here
+ }
+
+ bones[p_bone].nodes_bound.push_back(id);
+
+}
+void Skeleton::unbind_child_node_from_bone(int p_bone,Node *p_node) {
+
+ ERR_FAIL_NULL(p_node);
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+
+ uint32_t id=p_node->get_instance_ID();
+ bones[p_bone].nodes_bound.erase(id);
+
+}
+void Skeleton::get_bound_child_nodes_to_bone(int p_bone,List<Node*> *p_bound) const {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+
+ for (const List<uint32_t>::Element *E=bones[p_bone].nodes_bound.front();E;E=E->next()) {
+
+ Object *obj=ObjectDB::get_instance(E->get());
+ ERR_CONTINUE(!obj);
+ p_bound->push_back(obj->cast_to<Node>());
+ }
+
+}
+
+void Skeleton::clear_bones() {
+
+ bones.clear();
+ rest_global_inverse_dirty=true;
+ _make_dirty();
+}
+
+// posing api
+
+void Skeleton::set_bone_pose(int p_bone, const Transform& p_pose) {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+ ERR_FAIL_COND( !is_inside_scene() );
+
+
+ bones[p_bone].pose=p_pose;
+ _make_dirty();
+}
+Transform Skeleton::get_bone_pose(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() );
+ return bones[p_bone].pose;
+
+}
+
+void Skeleton::set_bone_custom_pose(int p_bone, const Transform& p_custom_pose) {
+
+ ERR_FAIL_INDEX( p_bone, bones.size() );
+// ERR_FAIL_COND( !is_inside_scene() );
+
+
+ bones[p_bone].custom_pose_enable=(p_custom_pose!=Transform());
+ bones[p_bone].custom_pose=p_custom_pose;
+
+ _make_dirty();
+}
+
+Transform Skeleton::get_bone_custom_pose(int p_bone) const {
+
+ ERR_FAIL_INDEX_V( p_bone, bones.size(), Transform() );
+ return bones[p_bone].custom_pose;
+
+}
+
+
+void Skeleton::_make_dirty() {
+
+ if (dirty)
+ return;
+
+ if (!is_inside_scene()) {
+ dirty=true;
+ return;
+ }
+ MessageQueue::get_singleton()->push_notification( this, NOTIFICATION_UPDATE_SKELETON );
+ dirty=true;
+}
+
+
+RES Skeleton::_get_gizmo_geometry() const {
+
+ if (!GLOBAL_DEF("debug/draw_skeleton", true))
+ return RES();
+
+ if (bones.size()==0)
+ return RES();
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.6,1.0,0.3,0.1) );
+ mat->set_line_width(4);
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_flag(Material::FLAG_UNSHADED,true);
+ mat->set_flag(Material::FLAG_ONTOP,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_LINES);
+ surface_tool->set_material(mat);
+
+
+ const Bone *bonesptr=&bones[0];
+ int len=bones.size();
+
+ for (int i=0;i<len;i++) {
+
+ const Bone &b=bonesptr[i];
+
+ Transform t;
+ if (b.parent<0)
+ continue;
+
+ Vector3 v1=(bonesptr[b.parent].pose_global * bonesptr[b.parent].rest_global_inverse).xform(bonesptr[b.parent].rest_global_inverse.affine_inverse().origin);
+ Vector3 v2=(b.pose_global * b.rest_global_inverse).xform(b.rest_global_inverse.affine_inverse().origin);
+
+ surface_tool->add_vertex(v1);
+ surface_tool->add_vertex(v2);
+
+ }
+
+ return surface_tool->commit();
+
+}
+
+void Skeleton::localize_rests() {
+
+ for(int i=bones.size()-1;i>=0;i--) {
+
+ if (bones[i].parent>=0)
+ set_bone_rest(i,bones[bones[i].parent].rest.affine_inverse() * bones[i].rest);
+ }
+}
+
+
+
+void Skeleton::_bind_methods() {
+
+
+
+
+ ObjectTypeDB::bind_method(_MD("add_bone","name"),&Skeleton::add_bone);
+ ObjectTypeDB::bind_method(_MD("find_bone","name"),&Skeleton::find_bone);
+ ObjectTypeDB::bind_method(_MD("get_bone_name","bone_idx"),&Skeleton::get_bone_name);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_parent","bone_idx"),&Skeleton::get_bone_parent);
+ ObjectTypeDB::bind_method(_MD("set_bone_parent","bone_idx","parent_idx"),&Skeleton::set_bone_parent);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_count"),&Skeleton::get_bone_count);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_rest","bone_idx"),&Skeleton::get_bone_rest);
+ ObjectTypeDB::bind_method(_MD("set_bone_rest","bone_idx","rest"),&Skeleton::set_bone_rest);
+
+ ObjectTypeDB::bind_method(_MD("bind_child_node_to_bone","bone_idx","node:Node"),&Skeleton::bind_child_node_to_bone);
+ ObjectTypeDB::bind_method(_MD("unbind_child_node_from_bone","bone_idx","node:Node"),&Skeleton::unbind_child_node_from_bone);
+ ObjectTypeDB::bind_method(_MD("get_bound_child_nodes_to_bone","bone_idx"),&Skeleton::_get_bound_child_nodes_to_bone);
+
+ ObjectTypeDB::bind_method(_MD("clear_bones"),&Skeleton::clear_bones);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_pose","bone_idx"),&Skeleton::get_bone_pose);
+ ObjectTypeDB::bind_method(_MD("set_bone_pose","bone_idx","pose"),&Skeleton::set_bone_pose);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_custom_pose","bone_idx"),&Skeleton::get_bone_custom_pose);
+ ObjectTypeDB::bind_method(_MD("set_bone_custom_pose","bone_idx","custom_pose"),&Skeleton::set_bone_custom_pose);
+
+ ObjectTypeDB::bind_method(_MD("get_bone_transform","bone_idx"),&Skeleton::get_bone_transform);
+
+ BIND_CONSTANT( NOTIFICATION_UPDATE_SKELETON );
+}
+
+
+
+Skeleton::Skeleton() {
+
+ rest_global_inverse_dirty=true;
+ dirty=false;
+ skeleton=VisualServer::get_singleton()->skeleton_create();
+}
+
+
+Skeleton::~Skeleton() {
+
+ VisualServer::get_singleton()->free( skeleton );
+}
+
+
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
new file mode 100644
index 0000000000..c95734fbf1
--- /dev/null
+++ b/scene/3d/skeleton.h
@@ -0,0 +1,144 @@
+/*************************************************************************/
+/* skeleton.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 SKELETON_H
+#define SKELETON_H
+
+#include "scene/3d/spatial.h"
+#include "rid.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class Skeleton : public Spatial {
+
+ OBJ_TYPE( Skeleton, Spatial );
+
+ struct Bone {
+
+ String name;
+
+ bool enabled;
+ int parent;
+
+ Transform rest;
+ Transform rest_global_inverse;
+
+ Transform pose;
+ Transform pose_global;
+
+ bool custom_pose_enable;
+ Transform custom_pose;
+
+ List<uint32_t> nodes_bound;
+
+ Bone() { parent=-1; enabled=true; custom_pose_enable=false; }
+ };
+
+ bool rest_global_inverse_dirty;
+
+ Vector<Bone> bones;
+
+ RID skeleton;
+
+ void _make_dirty();
+ bool dirty;
+
+//bind helpers
+ Array _get_bound_child_nodes_to_bone(int p_bone) const {
+
+ Array bound;
+ List<Node*> childs;
+ get_bound_child_nodes_to_bone(p_bone,&childs);
+
+ for (int i=0;i<childs.size();i++) {
+
+ bound.push_back( childs[i] );
+ }
+ return bound;
+ }
+
+ virtual RES _get_gizmo_geometry() const;
+
+protected:
+
+ bool _get(const StringName& p_name,Variant &r_ret) const;
+ bool _set(const StringName& p_name, const Variant& p_value);
+ void _get_property_list( List<PropertyInfo>* p_list ) const;
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ enum {
+
+ NOTIFICATION_UPDATE_SKELETON=50
+ };
+
+
+ RID get_skeleton() const;
+
+ // skeleton creation api
+ void add_bone(const String&p_name);
+ int find_bone(String p_name) const;
+ String get_bone_name(int p_bone) const;
+
+ void set_bone_parent(int p_bone, int p_parent);
+ int get_bone_parent(int p_bone) const;
+
+ int get_bone_count() const;
+
+ void set_bone_rest(int p_bone, const Transform& p_rest);
+ Transform get_bone_rest(int p_bone) const;
+ Transform get_bone_transform(int p_bone) const;
+
+ void set_bone_enabled(int p_bone, bool p_enabled);
+ bool is_bone_enabled(int p_bone) const;
+
+ void bind_child_node_to_bone(int p_bone,Node *p_node);
+ void unbind_child_node_from_bone(int p_bone,Node *p_node);
+ void get_bound_child_nodes_to_bone(int p_bone,List<Node*> *p_bound) const;
+
+ void clear_bones();
+
+ // posing api
+
+ void set_bone_pose(int p_bone, const Transform& p_pose);
+ Transform get_bone_pose(int p_bone) const;
+
+ void set_bone_custom_pose(int p_bone, const Transform& p_custom_pose);
+ Transform get_bone_custom_pose(int p_bone) const;
+
+ void localize_rests(); // used for loaders and tools
+
+ Skeleton();
+ ~Skeleton();
+
+};
+
+#endif
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
new file mode 100644
index 0000000000..de1fd7e8c0
--- /dev/null
+++ b/scene/3d/spatial.cpp
@@ -0,0 +1,567 @@
+/*************************************************************************/
+/* spatial.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 "spatial.h"
+
+#include "scene/main/viewport.h"
+#include "message_queue.h"
+#include "scene/scene_string_names.h"
+
+/*
+
+ possible algorithms:
+
+ Algorithm 1: (current)
+
+ definition of invalidation: global is invalid
+
+ 1) If a node sets a LOCAL, it produces an invalidation of everything above
+ a) If above is invalid, don't keep invalidating upwards
+ 2) If a node sets a GLOBAL, it is converted to LOCAL (and forces validation of everything pending below)
+
+ drawback: setting/reading globals is useful and used very very often, and using affine inverses is slow
+
+---
+
+ Algorithm 2: (no longer current)
+
+ definition of invalidation: NONE dirty, LOCAL dirty, GLOBAL dirty
+
+ 1) If a node sets a LOCAL, it must climb the tree and set it as GLOBAL dirty
+ a) marking GLOBALs as dirty up all the tree must be done always
+ 2) If a node sets a GLOBAL, it marks local as dirty, and that's all?
+
+ //is clearing the dirty state correct in this case?
+
+ drawback: setting a local down the tree forces many tree walks often
+
+--
+
+future: no idea
+
+ */
+
+
+
+SpatialGizmo::SpatialGizmo() {
+
+}
+
+void Spatial::_notify_dirty() {
+
+ if (!data.ignore_notification && !xform_change.in_list()) {
+
+ get_scene()->xform_change_list.add(&xform_change);
+ }
+}
+
+
+
+void Spatial::_update_local_transform() const {
+
+ data.local_transform.basis.set_euler(data.rotation);
+ data.local_transform.basis.scale(data.scale);
+
+ data.dirty&=~DIRTY_LOCAL;
+}
+void Spatial::_propagate_transform_changed(Spatial *p_origin) {
+
+ if (!is_inside_scene()) {
+ return;
+ }
+
+// if (data.dirty&DIRTY_GLOBAL)
+// return; //already dirty
+
+ data.children_lock++;
+
+ for (List<Spatial*>::Element *E=data.children.front();E;E=E->next()) {
+
+ if (E->get()->data.toplevel_active)
+ continue; //don't propagate to a toplevel
+ E->get()->_propagate_transform_changed(p_origin);
+ }
+
+
+ if (!data.ignore_notification && !xform_change.in_list()) {
+
+ get_scene()->xform_change_list.add(&xform_change);
+
+ }
+ data.dirty|=DIRTY_GLOBAL;
+
+ data.children_lock--;
+}
+
+void Spatial::_notification(int p_what) {
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+ Node *p = get_parent();
+ if (p)
+ data.parent=p->cast_to<Spatial>();
+
+ if (data.parent)
+ data.C=data.parent->data.children.push_back(this);
+ else
+ data.C=NULL;
+
+ if (data.toplevel && !get_scene()->is_editor_hint()) {
+
+ if (data.parent) {
+ data.local_transform = data.parent->get_global_transform() * get_transform();
+ data.dirty=DIRTY_VECTORS; //global is always dirty upon entering a scene
+ }
+ data.toplevel_active=true;
+ }
+
+ data.dirty|=DIRTY_GLOBAL; //global is always dirty upon entering a scene
+ _notify_dirty();
+
+ notification(NOTIFICATION_ENTER_WORLD);
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ notification(NOTIFICATION_EXIT_WORLD,true);
+ if (xform_change.in_list())
+ get_scene()->xform_change_list.remove(&xform_change);
+ if (data.C)
+ data.parent->data.children.erase(data.C);
+ data.parent=NULL;
+ data.C=NULL;
+ data.toplevel_active=false;
+ } break;
+ case NOTIFICATION_ENTER_WORLD: {
+
+ data.inside_world=true;
+ data.viewport=NULL;
+ Node *parent = get_parent();
+ while(parent && !data.viewport) {
+ data.viewport=parent->cast_to<Viewport>();
+ parent=parent->get_parent();
+ }
+
+ ERR_FAIL_COND(!data.viewport);
+
+
+ if (get_script_instance()) {
+
+ Variant::CallError err;
+ get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_enter_world,NULL,0);
+ }
+#ifdef TOOLS_ENABLED
+ if (get_scene()->is_editor_hint()) {
+
+// get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this);
+ get_scene()->call_group(0,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this);
+ if (!data.gizmo_disabled) {
+
+ if (data.gizmo.is_valid())
+ data.gizmo->create();
+ }
+ }
+#endif
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+#ifdef TOOLS_ENABLED
+ if (data.gizmo.is_valid()) {
+ data.gizmo->free();
+ }
+#endif
+
+ if (get_script_instance()) {
+
+ Variant::CallError err;
+ get_script_instance()->call_multilevel(SceneStringNames::get_singleton()->_exit_world,NULL,0);
+ }
+
+ data.viewport=NULL;
+ data.inside_world=false;
+
+ } break;
+
+
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+#ifdef TOOLS_ENABLED
+ if (data.gizmo.is_valid()) {
+ data.gizmo->transform();
+ }
+#endif
+ } break;
+
+ default: {}
+ }
+}
+
+void Spatial::set_transform(const Transform& p_transform) {
+
+ data.local_transform=p_transform;
+ data.dirty|=DIRTY_VECTORS;
+ _change_notify("transform/translation");
+ _change_notify("transform/rotation");
+ _change_notify("transform/scale");
+ _propagate_transform_changed(this);
+
+}
+
+void Spatial::set_global_transform(const Transform& p_transform) {
+
+ Transform xform =
+ (data.parent && !data.toplevel_active) ?
+ data.parent->get_global_transform().affine_inverse() * p_transform :
+ p_transform;
+
+ set_transform(xform);
+
+}
+
+
+Transform Spatial::get_transform() const {
+
+ if (data.dirty & DIRTY_LOCAL) {
+
+ _update_local_transform();
+ }
+
+ return data.local_transform;
+}
+Transform Spatial::get_global_transform() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(), Transform());
+
+ if (data.dirty & DIRTY_GLOBAL) {
+
+ if (data.dirty & DIRTY_LOCAL) {
+
+ _update_local_transform();
+ }
+
+ if (data.parent && !data.toplevel_active) {
+
+ data.global_transform=data.parent->get_global_transform() * data.local_transform;
+ } else {
+
+ data.global_transform=data.local_transform;
+ }
+
+ data.dirty&=~DIRTY_GLOBAL;
+ }
+
+ return data.global_transform;
+}
+#if 0
+void Spatial::add_child_notify(Node *p_child) {
+/*
+ Spatial *s=p_child->cast_to<Spatial>();
+ if (!s)
+ return;
+
+ ERR_FAIL_COND(data.children_lock>0);
+
+ s->data.dirty=DIRTY_GLOBAL; // don't allow global transform to be valid
+ s->data.parent=this;
+ data.children.push_back(s);
+ s->data.C=data.children.back();
+*/
+}
+
+void Spatial::remove_child_notify(Node *p_child) {
+/*
+ Spatial *s=p_child->cast_to<Spatial>();
+ if (!s)
+ return;
+
+ ERR_FAIL_COND(data.children_lock>0);
+
+ if (s->data.C)
+ data.children.erase(s->data.C);
+ s->data.parent=NULL;
+ s->data.C=NULL;
+*/
+}
+#endif
+
+Spatial *Spatial::get_parent_spatial() const {
+
+ return data.parent;
+
+}
+
+Transform Spatial::get_relative_transform(const Node *p_parent) const {
+
+ if (p_parent==this)
+ return Transform();
+
+ ERR_FAIL_COND_V(!data.parent,Transform());
+
+ if (p_parent==data.parent)
+ return get_transform();
+ else
+ return data.parent->get_relative_transform(p_parent) * get_transform();
+
+}
+
+void Spatial::set_translation(const Vector3& p_translation) {
+
+ data.local_transform.origin=p_translation;
+ _propagate_transform_changed(this);
+
+}
+
+void Spatial::set_rotation(const Vector3& p_euler){
+
+ if (data.dirty&DIRTY_VECTORS) {
+ data.scale=data.local_transform.basis.get_scale();
+ data.dirty&=~DIRTY_VECTORS;
+ }
+
+ data.rotation=p_euler;
+ data.dirty|=DIRTY_LOCAL;
+ _propagate_transform_changed(this);
+
+}
+void Spatial::set_scale(const Vector3& p_scale){
+
+ if (data.dirty&DIRTY_VECTORS) {
+ data.rotation=data.local_transform.basis.get_euler();
+ data.dirty&=~DIRTY_VECTORS;
+ }
+
+ data.scale=p_scale;
+ data.dirty|=DIRTY_LOCAL;
+ _propagate_transform_changed(this);
+
+}
+
+Vector3 Spatial::get_translation() const{
+
+ return data.local_transform.origin;
+}
+Vector3 Spatial::get_rotation() const{
+
+ if (data.dirty&DIRTY_VECTORS) {
+ data.scale=data.local_transform.basis.get_scale();
+ data.rotation=data.local_transform.basis.get_euler();
+ data.dirty&=~DIRTY_VECTORS;
+ }
+
+ return data.rotation;
+}
+Vector3 Spatial::get_scale() const{
+
+ if (data.dirty&DIRTY_VECTORS) {
+ data.scale=data.local_transform.basis.get_scale();
+ data.rotation=data.local_transform.basis.get_euler();
+ data.dirty&=~DIRTY_VECTORS;
+ }
+
+ return data.scale;
+}
+
+
+void Spatial::update_gizmo() {
+
+#ifdef TOOLS_ENABLED
+ if (!is_inside_world())
+ return;
+ if (!data.gizmo.is_valid())
+ return;
+ if (data.gizmo_dirty)
+ return;
+ data.gizmo_dirty=true;
+ MessageQueue::get_singleton()->push_call(this,"_update_gizmo");
+#endif
+}
+
+void Spatial::set_gizmo(const Ref<SpatialGizmo>& p_gizmo) {
+
+#ifdef TOOLS_ENABLED
+
+ if (data.gizmo_disabled)
+ return;
+ if (data.gizmo.is_valid() && is_inside_world())
+ data.gizmo->free();
+ data.gizmo=p_gizmo;
+ if (data.gizmo.is_valid() && is_inside_world()) {
+
+ data.gizmo->create();
+ data.gizmo->redraw();
+ data.gizmo->transform();
+ }
+
+#endif
+}
+
+Ref<SpatialGizmo> Spatial::get_gizmo() const {
+
+#ifdef TOOLS_ENABLED
+
+ return data.gizmo;
+#else
+
+ return Ref<SpatialGizmo>();
+#endif
+}
+
+#ifdef TOOLS_ENABLED
+
+void Spatial::_update_gizmo() {
+
+ data.gizmo_dirty=false;
+ if (data.gizmo.is_valid())
+ data.gizmo->redraw();
+}
+
+
+void Spatial::set_disable_gizmo(bool p_enabled) {
+
+ data.gizmo_disabled=p_enabled;
+ if (!p_enabled && data.gizmo.is_valid())
+ data.gizmo=Ref<SpatialGizmo>();
+}
+
+#endif
+
+void Spatial::set_as_toplevel(bool p_enabled) {
+
+ if (data.toplevel==p_enabled)
+ return;
+ if (is_inside_scene() && !get_scene()->is_editor_hint()) {
+
+ if (p_enabled)
+ set_transform(get_global_transform());
+ else if (data.parent)
+ set_transform(data.parent->get_global_transform().affine_inverse() * get_global_transform());
+
+ data.toplevel=p_enabled;
+ data.toplevel_active=p_enabled;
+
+ } else {
+ data.toplevel=p_enabled;
+ }
+
+}
+
+bool Spatial::is_set_as_toplevel() const{
+
+ return data.toplevel;
+}
+
+void Spatial::_set_rotation_deg(const Vector3& p_deg) {
+
+ set_rotation(p_deg * Math_PI / 180.0);
+}
+
+Vector3 Spatial::_get_rotation_deg() const {
+
+ return get_rotation() * 180.0 / Math_PI;
+}
+
+Ref<World> Spatial::get_world() const {
+
+ ERR_FAIL_COND_V(!is_inside_world(),Ref<World>());
+ return data.viewport->find_world();
+
+}
+
+
+void Spatial::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_transform","local"), &Spatial::set_transform);
+ ObjectTypeDB::bind_method(_MD("get_transform"), &Spatial::get_transform);
+ ObjectTypeDB::bind_method(_MD("set_translation","translation"), &Spatial::set_translation);
+ ObjectTypeDB::bind_method(_MD("get_translation"), &Spatial::get_translation);
+ ObjectTypeDB::bind_method(_MD("set_rotation","rotation"), &Spatial::set_rotation);
+ ObjectTypeDB::bind_method(_MD("get_rotation"), &Spatial::get_rotation);
+ ObjectTypeDB::bind_method(_MD("set_scale","scale"), &Spatial::set_scale);
+ ObjectTypeDB::bind_method(_MD("get_scale"), &Spatial::get_scale);
+ ObjectTypeDB::bind_method(_MD("set_global_transform","global"), &Spatial::set_global_transform);
+ ObjectTypeDB::bind_method(_MD("get_global_transform"), &Spatial::get_global_transform);
+ ObjectTypeDB::bind_method(_MD("get_parent_spatial"), &Spatial::get_parent_spatial);
+ ObjectTypeDB::bind_method(_MD("set_ignore_transform_notification","enabled"), &Spatial::set_ignore_transform_notification);
+ ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"), &Spatial::set_as_toplevel);
+ ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel);
+ ObjectTypeDB::bind_method(_MD("_set_rotation_deg","rotation_deg"), &Spatial::_set_rotation_deg);
+ ObjectTypeDB::bind_method(_MD("_get_rotation_deg"), &Spatial::_get_rotation_deg);
+ ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world);
+
+#ifdef TOOLS_ENABLED
+ ObjectTypeDB::bind_method(_MD("_update_gizmo"), &Spatial::_update_gizmo);
+#endif
+
+ ObjectTypeDB::bind_method(_MD("update_gizmo"), &Spatial::update_gizmo);
+ ObjectTypeDB::bind_method(_MD("set_gizmo","gizmo:SpatialGizmo"), &Spatial::set_gizmo);
+ ObjectTypeDB::bind_method(_MD("get_gizmo:SpatialGizmo"), &Spatial::get_gizmo);
+
+ BIND_CONSTANT( NOTIFICATION_TRANSFORM_CHANGED );
+ BIND_CONSTANT( NOTIFICATION_ENTER_WORLD );
+ BIND_CONSTANT( NOTIFICATION_EXIT_WORLD );
+
+ //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), _SCS("set_global_transform"), _SCS("get_global_transform") );
+ ADD_PROPERTYNZ( PropertyInfo(Variant::TRANSFORM,"transform/local",PROPERTY_HINT_NONE,""), _SCS("set_transform"), _SCS("get_transform") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/translation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_translation"), _SCS("get_translation") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("_set_rotation_deg"), _SCS("_get_rotation_deg") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation_rad",PROPERTY_HINT_NONE,"",0), _SCS("set_rotation"), _SCS("get_rotation") );
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/scale",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_scale"), _SCS("get_scale") );
+ //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/local"), _SCS("set_transform"), _SCS("get_transform") );
+
+}
+
+
+Spatial::Spatial() : xform_change(this)
+{
+
+ data.dirty=DIRTY_NONE;
+ data.children_lock=0;
+
+ data.ignore_notification=false;
+ data.toplevel=false;
+ data.toplevel_active=false;
+ data.scale=Vector3(1,1,1);
+ data.viewport=NULL;
+ data.inside_world=false;
+#ifdef TOOLS_ENABLED
+ data.gizmo_disabled=false;
+ data.gizmo_dirty=false;
+#endif
+ data.parent=NULL;
+ data.C=NULL;
+
+}
+
+
+Spatial::~Spatial() {
+
+}
+
+
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
new file mode 100644
index 0000000000..e10629f7de
--- /dev/null
+++ b/scene/3d/spatial.h
@@ -0,0 +1,166 @@
+/*************************************************************************/
+/* spatial.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 SPATIAL_H
+#define SPATIAL_H
+
+#include "scene/main/node.h"
+#include "scene/main/scene_main_loop.h"
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+
+class SpatialGizmo : public Reference {
+
+ OBJ_TYPE(SpatialGizmo,Reference);
+
+
+public:
+
+ virtual void create()=0;
+ virtual void transform()=0;
+ virtual void redraw()=0;
+ virtual void free()=0;
+
+ SpatialGizmo();
+};
+
+
+class Spatial : public Node {
+
+ OBJ_TYPE( Spatial, Node );
+ OBJ_CATEGORY("3D");
+
+ enum TransformDirty {
+ DIRTY_NONE=0,
+ DIRTY_VECTORS=1,
+ DIRTY_LOCAL=2,
+ DIRTY_GLOBAL=4
+ };
+
+ mutable SelfList<Node> xform_change;
+
+ struct Data {
+
+
+
+ mutable Transform global_transform;
+ mutable Transform local_transform;
+ mutable Vector3 rotation;
+ mutable Vector3 scale;
+
+ mutable int dirty;
+
+ Viewport *viewport;
+
+
+ bool toplevel_active;
+ bool toplevel;
+ bool inside_world;
+
+ int children_lock;
+ Spatial *parent;
+ List<Spatial*> children;
+ List<Spatial*>::Element *C;
+
+ bool ignore_notification;
+
+#ifdef TOOLS_ENABLED
+ Ref<SpatialGizmo> gizmo;
+ bool gizmo_disabled;
+ bool gizmo_dirty;
+#endif
+
+ } data;
+#ifdef TOOLS_ENABLED
+
+ void _update_gizmo();
+#endif
+ void _notify_dirty();
+ void _propagate_transform_changed(Spatial *p_origin);
+
+ void _set_rotation_deg(const Vector3& p_deg);
+ Vector3 _get_rotation_deg() const;
+
+
+protected:
+
+ _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification=p_ignore; }
+
+ _FORCE_INLINE_ void _update_local_transform() const;
+
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ enum {
+
+ NOTIFICATION_TRANSFORM_CHANGED=SceneMainLoop::NOTIFICATION_TRANSFORM_CHANGED,
+ NOTIFICATION_ENTER_WORLD=41,
+ NOTIFICATION_EXIT_WORLD=42,
+ };
+
+ Spatial *get_parent_spatial() const;
+
+
+ Ref<World> get_world() const;
+
+ void set_translation(const Vector3& p_translation);
+ void set_rotation(const Vector3& p_euler);
+ void set_scale(const Vector3& p_scale);
+
+ Vector3 get_translation() const;
+ Vector3 get_rotation() const;
+ Vector3 get_scale() const;
+
+ void set_transform(const Transform& p_transform);
+ void set_global_transform(const Transform& p_transform);
+
+ Transform get_transform() const;
+ Transform get_global_transform() const;
+
+ void set_as_toplevel(bool p_enabled);
+ bool is_set_as_toplevel() const;
+
+ void set_disable_gizmo(bool p_enabled);
+ void update_gizmo();
+ void set_gizmo(const Ref<SpatialGizmo>& p_gizmo);
+ Ref<SpatialGizmo> get_gizmo() const;
+
+ _FORCE_INLINE_ bool is_inside_world() const { return data.inside_world; }
+
+ Transform get_relative_transform(const Node *p_parent) const;
+
+ Spatial();
+ ~Spatial();
+
+};
+
+#endif
diff --git a/scene/3d/spatial_indexer.cpp b/scene/3d/spatial_indexer.cpp
new file mode 100644
index 0000000000..261c62f51c
--- /dev/null
+++ b/scene/3d/spatial_indexer.cpp
@@ -0,0 +1,165 @@
+/*************************************************************************/
+/* spatial_indexer.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 "spatial_indexer.h"
+
+#if 0
+
+#include "proximity_area.h"
+#include "camera.h"
+#include "scene/scene_string_names.h"
+
+void SpatialIndexer::add_camera(Camera* p_camera) {
+
+ cameras.insert(p_camera);
+}
+
+void SpatialIndexer::remove_camera(Camera* p_camera){
+
+ for (Set<ProximityArea*>::Element *F=proximity_areas.front();F;F=F->next()) {
+
+ ProximityArea *prox = F->get();
+ TK<Camera> k;
+ k.against=p_camera;
+ k.area=prox;
+ if (camera_pairs.has(k)) {
+ camera_pairs.erase(k);
+ prox->area_exit(ProximityArea::TRACK_CAMERAS,p_camera);
+ }
+ }
+ cameras.erase(p_camera);
+
+}
+
+void SpatialIndexer::update_camera(Camera* p_camera) {
+
+
+ _request_update();
+}
+
+void SpatialIndexer::_update_pairs() {
+
+ // brute force interseciton code, no broadphase
+ // will implement broadphase in the future
+
+ for (Set<Camera*>::Element *E=cameras.front();E;E=E->next()) {
+
+ Camera *cam = E->get();
+ Vector<Plane> cplanes = cam->get_frustum();
+
+ for (Set<ProximityArea*>::Element *F=proximity_areas.front();F;F=F->next()) {
+
+ ProximityArea *prox = F->get();
+
+ bool inters=false;
+
+ if (prox->get_track_flag(ProximityArea::TRACK_CAMERAS)) {
+
+ AABB aabb = prox->get_global_transform().xform(prox->get_aabb());
+ if (aabb.intersects_convex_shape(cplanes.ptr(),cplanes.size()))
+ inters=true;
+ }
+
+ TK<Camera> k;
+ k.against=cam;
+ k.area=prox;
+
+ bool has = camera_pairs.has(k);
+
+ if (inters==has)
+ continue;
+
+ if (inters) {
+ camera_pairs.insert(k);
+ prox->area_enter(ProximityArea::TRACK_CAMERAS,cam);
+ } else {
+
+ camera_pairs.erase(k);
+ prox->area_exit(ProximityArea::TRACK_CAMERAS,cam);
+ }
+ }
+
+ }
+
+ pending_update=false;
+}
+
+void SpatialIndexer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("_update_pairs"),&SpatialIndexer::_update_pairs);
+}
+
+
+void SpatialIndexer::add_proximity_area(ProximityArea* p_area) {
+
+ proximity_areas.insert(p_area);
+
+}
+
+void SpatialIndexer::remove_proximity_area(ProximityArea* p_area) {
+
+ for (Set<Camera*>::Element *E=cameras.front();E;E=E->next()) {
+
+ Camera *cam = E->get();
+ TK<Camera> k;
+ k.against=cam;
+ k.area=p_area;
+ if (camera_pairs.has(k)) {
+ camera_pairs.erase(k);
+ p_area->area_exit(ProximityArea::TRACK_CAMERAS,cam);
+ }
+ }
+ proximity_areas.erase(p_area);
+
+}
+
+void SpatialIndexer::_request_update() {
+
+ if (pending_update)
+ return;
+ pending_update=true;
+ call_deferred(SceneStringNames::get_singleton()->_update_pairs);
+
+}
+
+void SpatialIndexer::update_proximity_area_transform(ProximityArea* p_area) {
+
+ _request_update();
+}
+
+void SpatialIndexer::update_proximity_area_flags(ProximityArea* p_area) {
+
+ _request_update();
+}
+
+SpatialIndexer::SpatialIndexer() {
+
+ pending_update=false;
+}
+#endif
diff --git a/scene/3d/spatial_indexer.h b/scene/3d/spatial_indexer.h
new file mode 100644
index 0000000000..d3038a1293
--- /dev/null
+++ b/scene/3d/spatial_indexer.h
@@ -0,0 +1,83 @@
+/*************************************************************************/
+/* spatial_indexer.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 SPATIAL_INDEXER_H
+#define SPATIAL_INDEXER_H
+
+#include "scene/3d/spatial.h"
+#if 0
+
+class Camera;
+class ProximityArea;
+
+class SpatialIndexer : public Object {
+
+ OBJ_TYPE( SpatialIndexer, Object );
+
+ template<class T>
+ struct TK {
+
+ T *against;
+ ProximityArea *area;
+ bool operator<(const TK<T>& p_k) const { return against==p_k.against ? area < p_k.area : against < p_k.against; }
+ };
+
+
+ Set<Camera*> cameras; //cameras
+ Set<ProximityArea*> proximity_areas;
+
+ Set<TK<Camera> > camera_pairs;
+
+ bool pending_update;
+ void _update_pairs();
+ void _request_update();
+
+protected:
+
+ static void _bind_methods();
+
+friend class ProximityArea;
+friend class Camera;
+
+ void add_proximity_area(ProximityArea* p_area);
+ void remove_proximity_area(ProximityArea* p_area);
+ void update_proximity_area_transform(ProximityArea* p_area);
+ void update_proximity_area_flags(ProximityArea* p_area);
+
+ void add_camera(Camera* p_camera);
+ void remove_camera(Camera* p_camera);
+ void update_camera(Camera* p_camera);
+
+public:
+
+
+ SpatialIndexer();
+
+};
+#endif
+#endif // SPATIAL_INDEXER_H
diff --git a/scene/3d/spatial_player.cpp b/scene/3d/spatial_player.cpp
new file mode 100644
index 0000000000..0847598356
--- /dev/null
+++ b/scene/3d/spatial_player.cpp
@@ -0,0 +1,272 @@
+/*************************************************************************/
+/* spatial_player.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 "spatial_player.h"
+
+#include "servers/audio_server.h"
+#include "camera.h"
+#include "servers/spatial_sound_server.h"
+#include "scene/resources/surface_tool.h"
+
+
+void SpatialPlayer::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+ //find the sound space
+
+ source_rid = SpatialSoundServer::get_singleton()->source_create(get_world()->get_sound_space());
+ for(int i=0;i<PARAM_MAX;i++)
+ set_param(Param(i),params[i]);
+
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ SpatialSoundServer::get_singleton()->source_set_transform(source_rid,get_global_transform());
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ if (source_rid.is_valid())
+ SpatialSoundServer::get_singleton()->free(source_rid);
+
+ } break;
+ }
+
+}
+
+
+void SpatialPlayer::set_param( Param p_param, float p_value) {
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params[p_param]=p_value;
+ if (p_param==PARAM_EMISSION_CONE_DEGREES) {
+ update_gizmo();
+ }
+ if (source_rid.is_valid())
+ SpatialSoundServer::get_singleton()->source_set_param(source_rid,(SpatialSoundServer::SourceParam)p_param,p_value);
+
+}
+
+float SpatialPlayer::get_param( Param p_param) const {
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params[p_param];
+
+}
+
+bool SpatialPlayer::_can_gizmo_scale() const {
+
+ return false;
+}
+
+RES SpatialPlayer::_get_gizmo_geometry() const {
+
+ Ref<SurfaceTool> surface_tool( memnew( SurfaceTool ));
+
+ Ref<FixedMaterial> mat( memnew( FixedMaterial ));
+
+ mat->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.0,0.6,0.7,0.05) );
+ mat->set_parameter( FixedMaterial::PARAM_EMISSION,Color(0.5,0.7,0.8) );
+ mat->set_blend_mode( Material::BLEND_MODE_ADD );
+ mat->set_flag(Material::FLAG_DOUBLE_SIDED,true);
+ mat->set_hint(Material::HINT_NO_DEPTH_DRAW,true);
+
+
+ surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ surface_tool->set_material(mat);
+
+ int sides=16;
+ int sections=24;
+
+// float len=1;
+ float deg=Math::deg2rad(params[PARAM_EMISSION_CONE_DEGREES]);
+ if (deg==180)
+ deg=179.5;
+
+ Vector3 to=Vector3(0,0,-1);
+
+ for(int j=0;j<sections;j++) {
+
+ Vector3 p1=Matrix3(Vector3(1,0,0),deg*j/sections).xform(to);
+ Vector3 p2=Matrix3(Vector3(1,0,0),deg*(j+1)/sections).xform(to);
+
+ for(int i=0;i<sides;i++) {
+
+ Vector3 p1r = Matrix3(Vector3(0,0,1),Math_PI*2*float(i)/sides).xform(p1);
+ Vector3 p1s = Matrix3(Vector3(0,0,1),Math_PI*2*float(i+1)/sides).xform(p1);
+ Vector3 p2s = Matrix3(Vector3(0,0,1),Math_PI*2*float(i+1)/sides).xform(p2);
+ Vector3 p2r = Matrix3(Vector3(0,0,1),Math_PI*2*float(i)/sides).xform(p2);
+
+ surface_tool->add_normal(p1r.normalized());
+ surface_tool->add_vertex(p1r);
+ surface_tool->add_normal(p1s.normalized());
+ surface_tool->add_vertex(p1s);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+
+ surface_tool->add_normal(p1r.normalized());
+ surface_tool->add_vertex(p1r);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+ surface_tool->add_normal(p2r.normalized());
+ surface_tool->add_vertex(p2r);
+
+ if (j==sections-1) {
+
+ surface_tool->add_normal(p2r.normalized());
+ surface_tool->add_vertex(p2r);
+ surface_tool->add_normal(p2s.normalized());
+ surface_tool->add_vertex(p2s);
+ surface_tool->add_normal(Vector3(0,0,1));
+ surface_tool->add_vertex(Vector3());
+ }
+ }
+ }
+
+
+ Ref<Mesh> mesh = surface_tool->commit();
+
+ Ref<FixedMaterial> mat_speaker( memnew( FixedMaterial ));
+
+ mat_speaker->set_parameter( FixedMaterial::PARAM_DIFFUSE,Color(0.3,0.3,0.6) );
+ mat_speaker->set_parameter( FixedMaterial::PARAM_SPECULAR,Color(0.5,0.5,0.6) );
+ //mat_speaker->set_blend_mode( Material::BLEND_MODE_MIX);
+ //mat_speaker->set_flag(Material::FLAG_DOUBLE_SIDED,false);
+ //mat_speaker->set_flag(Material::FLAG_UNSHADED,true);
+
+ surface_tool->begin(Mesh::PRIMITIVE_TRIANGLES);
+ surface_tool->set_material(mat_speaker);
+
+// float radius=1;
+
+
+ const int speaker_points=8;
+ Vector3 speaker[speaker_points]={
+ Vector3(0,0,1)*0.15,
+ Vector3(1,1,1)*0.15,
+ Vector3(1,1,0)*0.15,
+ Vector3(2,2,-1)*0.15,
+ Vector3(1,1,-1)*0.15,
+ Vector3(0.8,0.8,-1.2)*0.15,
+ Vector3(0.5,0.5,-1.4)*0.15,
+ Vector3(0.0,0.0,-1.6)*0.15
+ };
+
+ int speaker_sides=10;
+
+
+ for(int i = 0; i < speaker_sides ; i++) {
+
+
+ Matrix3 ma(Vector3(0,0,1),Math_PI*2*float(i)/speaker_sides);
+ Matrix3 mb(Vector3(0,0,1),Math_PI*2*float(i+1)/speaker_sides);
+
+
+ for(int j=0;j<speaker_points-1;j++) {
+
+ Vector3 points[4]={
+ ma.xform(speaker[j]),
+ mb.xform(speaker[j]),
+ mb.xform(speaker[j+1]),
+ ma.xform(speaker[j+1]),
+ };
+
+ Vector3 n = -Plane(points[0],points[1],points[2]).normal;
+
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[0]);
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[2]);
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[1]);
+
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[0]);
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[3]);
+ surface_tool->add_normal(n);
+ surface_tool->add_vertex(points[2]);
+
+
+ }
+
+
+ }
+
+
+ return surface_tool->commit(mesh);
+
+}
+
+
+void SpatialPlayer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SpatialPlayer::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&SpatialPlayer::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_EMISSION_CONE_DEGREES );
+ BIND_CONSTANT( PARAM_EMISSION_CONE_ATTENUATION_DB );
+ 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_RANGE, "0.01,4096,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_ATTENUATION_MIN_DISTANCE);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/attenuation/max_distance",PROPERTY_HINT_RANGE, "0.01,4096,0.01"),_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);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/emission_cone/degrees",PROPERTY_HINT_RANGE, "0,180,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_EMISSION_CONE_DEGREES);
+ ADD_PROPERTYI( PropertyInfo( Variant::REAL, "params/emission_cone/attenuation_db",PROPERTY_HINT_RANGE, "-80,24,0.01"),_SCS("set_param"),_SCS("get_param"),PARAM_EMISSION_CONE_ATTENUATION_DB);
+
+}
+
+
+SpatialPlayer::SpatialPlayer() {
+
+ params[PARAM_VOLUME_DB]=0.0;
+ params[PARAM_PITCH_SCALE]=1.0;
+ params[PARAM_ATTENUATION_MIN_DISTANCE]=1;
+ params[PARAM_ATTENUATION_MAX_DISTANCE]=100;
+ params[PARAM_ATTENUATION_DISTANCE_EXP]=1.0; //linear (and not really good)
+ params[PARAM_EMISSION_CONE_DEGREES]=180.0; //cone disabled
+ params[PARAM_EMISSION_CONE_ATTENUATION_DB]=-6.0; //minus 6 db attenuation
+
+}
+
+SpatialPlayer::~SpatialPlayer() {
+
+
+}
diff --git a/scene/3d/spatial_player.h b/scene/3d/spatial_player.h
new file mode 100644
index 0000000000..e11028d2c9
--- /dev/null
+++ b/scene/3d/spatial_player.h
@@ -0,0 +1,87 @@
+/*************************************************************************/
+/* spatial_player.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 SPATIAL_PLAYER_H
+#define SPATIAL_PLAYER_H
+
+
+#include "scene/3d/spatial.h"
+#include "scene/main/node.h"
+#include "scene/resources/sample_library.h"
+#include "servers/spatial_sound_server.h"
+#include "scene/main/viewport.h"
+
+class SpatialPlayer : public Spatial {
+
+ OBJ_TYPE(SpatialPlayer,Spatial);
+public:
+
+
+ enum Param {
+
+ PARAM_VOLUME_DB=SpatialSoundServer::SOURCE_PARAM_VOLUME_DB,
+ PARAM_PITCH_SCALE=SpatialSoundServer::SOURCE_PARAM_PITCH_SCALE,
+ PARAM_ATTENUATION_MIN_DISTANCE=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_MIN_DISTANCE,
+ PARAM_ATTENUATION_MAX_DISTANCE=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_MAX_DISTANCE,
+ PARAM_ATTENUATION_DISTANCE_EXP=SpatialSoundServer::SOURCE_PARAM_ATTENUATION_DISTANCE_EXP,
+ PARAM_EMISSION_CONE_DEGREES=SpatialSoundServer::SOURCE_PARAM_EMISSION_CONE_DEGREES,
+ PARAM_EMISSION_CONE_ATTENUATION_DB=SpatialSoundServer::SOURCE_PARAM_EMISSION_CONE_ATTENUATION_DB,
+ PARAM_MAX=SpatialSoundServer::SOURCE_PARAM_MAX
+ };
+
+private:
+
+ float params[PARAM_MAX];
+ RID source_rid;
+
+ virtual bool _can_gizmo_scale() const;
+ virtual RES _get_gizmo_geometry() const;
+
+
+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;
+
+
+ SpatialPlayer();
+ ~SpatialPlayer();
+
+
+};
+
+VARIANT_ENUM_CAST( SpatialPlayer::Param );
+#endif // SPATIAL_PLAYER_H
diff --git a/scene/3d/spatial_sample_player.cpp b/scene/3d/spatial_sample_player.cpp
new file mode 100644
index 0000000000..b4a5d3bc1b
--- /dev/null
+++ b/scene/3d/spatial_sample_player.cpp
@@ -0,0 +1,229 @@
+/*************************************************************************/
+/* spatial_sample_player.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 "spatial_sample_player.h"
+
+#include "servers/audio_server.h"
+#include "camera.h"
+#include "servers/spatial_sound_server.h"
+#include "scene/scene_string_names.h"
+
+bool SpatialSamplePlayer::_set(const StringName& p_name, const Variant& p_value) {
+
+ String name=p_name;
+
+ if (name==SceneStringNames::get_singleton()->play_play) {
+ if (library.is_valid()) {
+
+ String what=p_value;
+ if (what=="")
+ stop_all();
+ else
+ play(what);
+
+ played_back=what;
+ }
+ return true;
+
+ }
+
+ return false;
+}
+
+bool SpatialSamplePlayer::_get(const StringName& p_name,Variant &r_ret) const {
+
+
+ String name=p_name;
+
+ if (name==SceneStringNames::get_singleton()->play_play) {
+ r_ret=played_back;
+ return true;
+ }
+
+ return false;
+}
+
+void SpatialSamplePlayer::_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 SpatialSamplePlayer::_notification(int p_what) {
+
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ SpatialSoundServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony);
+
+
+ } break;
+ }
+
+}
+
+void SpatialSamplePlayer::set_sample_library(const Ref<SampleLibrary>& p_library) {
+
+ library=p_library;
+}
+
+Ref<SampleLibrary> SpatialSamplePlayer::get_sample_library() const {
+
+ return library;
+}
+
+void SpatialSamplePlayer::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())
+ SpatialSoundServer::get_singleton()->source_set_polyphony(get_source_rid(),polyphony);
+
+}
+
+int SpatialSamplePlayer::get_polyphony() const {
+
+ return polyphony;
+}
+
+SpatialSamplePlayer::VoiceID SpatialSamplePlayer::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 = SpatialSoundServer::get_singleton()->source_play_sample(get_source_rid(),sample->get_rid(),sample->get_mix_rate()*pitch_change,p_voice);
+ if (vol_change)
+ SpatialSoundServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),vid,vol_change);
+
+ return vid;
+
+
+}
+//voices
+void SpatialSamplePlayer::voice_set_pitch_scale(VoiceID p_voice, float p_pitch_scale) {
+
+ if (!get_source_rid().is_valid())
+ return;
+
+ SpatialSoundServer::get_singleton()->source_voice_set_pitch_scale(get_source_rid(),p_voice,p_pitch_scale);
+
+}
+
+void SpatialSamplePlayer::voice_set_volume_scale_db(VoiceID p_voice, float p_volume_db) {
+
+ if (!get_source_rid().is_valid())
+ return;
+ SpatialSoundServer::get_singleton()->source_voice_set_volume_scale_db(get_source_rid(),p_voice,p_volume_db);
+
+}
+
+bool SpatialSamplePlayer::is_voice_active(VoiceID p_voice) const {
+
+ if (!get_source_rid().is_valid())
+ return false;
+ return SpatialSoundServer::get_singleton()->source_is_voice_active(get_source_rid(),p_voice);
+
+}
+
+void SpatialSamplePlayer::stop_voice(VoiceID p_voice) {
+
+ if (!get_source_rid().is_valid())
+ return;
+ SpatialSoundServer::get_singleton()->source_stop_voice(get_source_rid(),p_voice);
+
+}
+
+void SpatialSamplePlayer::stop_all() {
+
+ if (!get_source_rid().is_valid())
+ return;
+
+ for(int i=0;i<polyphony;i++) {
+
+ SpatialSoundServer::get_singleton()->source_stop_voice(get_source_rid(),i);
+ }
+}
+
+void SpatialSamplePlayer::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_sample_library","library:SampleLibrary"),&SpatialSamplePlayer::set_sample_library);
+ ObjectTypeDB::bind_method(_MD("get_sample_library:SampleLibrary"),&SpatialSamplePlayer::get_sample_library);
+
+ ObjectTypeDB::bind_method(_MD("set_polyphony","voices"),&SpatialSamplePlayer::set_polyphony);
+ ObjectTypeDB::bind_method(_MD("get_polyphony"),&SpatialSamplePlayer::get_polyphony);
+
+ ObjectTypeDB::bind_method(_MD("play","sample","voice"),&SpatialSamplePlayer::play,DEFVAL(NEXT_VOICE));
+ //voices,DEV
+ ObjectTypeDB::bind_method(_MD("voice_set_pitch_scale","voice","ratio"),&SpatialSamplePlayer::voice_set_pitch_scale);
+ ObjectTypeDB::bind_method(_MD("voice_set_volume_scale_db","voice","db"),&SpatialSamplePlayer::voice_set_volume_scale_db);
+
+ ObjectTypeDB::bind_method(_MD("is_voice_active","voice"),&SpatialSamplePlayer::is_voice_active);
+ ObjectTypeDB::bind_method(_MD("stop_voice","voice"),&SpatialSamplePlayer::stop_voice);
+ ObjectTypeDB::bind_method(_MD("stop_all"),&SpatialSamplePlayer::stop_all);
+
+ 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"));
+
+
+}
+
+
+SpatialSamplePlayer::SpatialSamplePlayer() {
+
+ polyphony=1;
+
+}
+
+SpatialSamplePlayer::~SpatialSamplePlayer() {
+
+
+}
diff --git a/scene/3d/spatial_sample_player.h b/scene/3d/spatial_sample_player.h
new file mode 100644
index 0000000000..68a0326c49
--- /dev/null
+++ b/scene/3d/spatial_sample_player.h
@@ -0,0 +1,89 @@
+/*************************************************************************/
+/* spatial_sample_player.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 SPATIAL_SAMPLE_PLAYER_H
+#define SPATIAL_SAMPLE_PLAYER_H
+
+#include "scene/3d/spatial_player.h"
+#include "scene/resources/sample_library.h"
+#include "servers/spatial_sound_server.h"
+
+class SpatialSamplePlayer : public SpatialPlayer {
+
+ OBJ_TYPE(SpatialSamplePlayer,SpatialPlayer);
+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;
+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();
+
+
+ SpatialSamplePlayer();
+ ~SpatialSamplePlayer();
+
+
+};
+
+
+#endif // SPATIAL_SAMPLE_PLAYER_H
diff --git a/scene/3d/spatial_stream_player.cpp b/scene/3d/spatial_stream_player.cpp
new file mode 100644
index 0000000000..25448498cb
--- /dev/null
+++ b/scene/3d/spatial_stream_player.cpp
@@ -0,0 +1,190 @@
+/*************************************************************************/
+/* spatial_stream_player.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 "spatial_stream_player.h"
+
+
+
+void SpatialStreamPlayer::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+// set_idle_process(false); //don't annoy
+ } break;
+ case NOTIFICATION_PROCESS: {
+
+// if (!stream.is_null())
+// stream->update();
+
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ stop(); //wathever it may be doing, stop
+ } break;
+ }
+}
+
+
+
+void SpatialStreamPlayer::set_stream(const Ref<AudioStream> &p_stream) {
+
+ stop();
+
+ stream=p_stream;
+ if (!stream.is_null()) {
+
+ stream->set_loop(loops);
+ }
+
+
+}
+
+Ref<AudioStream> SpatialStreamPlayer::get_stream() const {
+
+ return stream;
+}
+
+
+void SpatialStreamPlayer::play() {
+
+ if (!is_inside_scene())
+ return;
+ if (stream.is_null())
+ return;
+ if (stream->is_playing())
+ stop();
+ stream->play();
+ SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),stream->get_audio_stream());
+ //if (stream->get_update_mode()!=AudioStream::UPDATE_NONE)
+ // set_idle_process(true);
+
+}
+
+void SpatialStreamPlayer::stop() {
+
+ if (!is_inside_scene())
+ return;
+ if (stream.is_null())
+ return;
+
+ SpatialSoundServer::get_singleton()->source_set_audio_stream(get_source_rid(),NULL);
+ stream->stop();
+ //set_idle_process(false);
+}
+
+bool SpatialStreamPlayer::is_playing() const {
+
+ if (stream.is_null())
+ return false;
+
+ return stream->is_playing();
+}
+
+void SpatialStreamPlayer::set_loop(bool p_enable) {
+
+ loops=p_enable;
+ if (stream.is_null())
+ return;
+ stream->set_loop(loops);
+
+}
+bool SpatialStreamPlayer::has_loop() const {
+
+ return loops;
+}
+
+
+
+String SpatialStreamPlayer::get_stream_name() const {
+
+ if (stream.is_null())
+ return "<No Stream>";
+ return stream->get_name();
+
+}
+
+int SpatialStreamPlayer::get_loop_count() const {
+
+ if (stream.is_null())
+ return 0;
+ return stream->get_loop_count();
+
+}
+
+float SpatialStreamPlayer::get_pos() const {
+
+ if (stream.is_null())
+ return 0;
+ return stream->get_pos();
+
+}
+void SpatialStreamPlayer::seek_pos(float p_time) {
+
+ if (stream.is_null())
+ return;
+ return stream->seek_pos(p_time);
+
+}
+
+void SpatialStreamPlayer::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream);
+ ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&SpatialStreamPlayer::get_stream);
+
+ ObjectTypeDB::bind_method(_MD("play"),&SpatialStreamPlayer::play);
+ ObjectTypeDB::bind_method(_MD("stop"),&SpatialStreamPlayer::stop);
+
+ ObjectTypeDB::bind_method(_MD("is_playing"),&SpatialStreamPlayer::is_playing);
+
+ ObjectTypeDB::bind_method(_MD("set_loop","enabled"),&SpatialStreamPlayer::set_loop);
+ ObjectTypeDB::bind_method(_MD("has_loop"),&SpatialStreamPlayer::has_loop);
+
+ ObjectTypeDB::bind_method(_MD("get_stream_name"),&SpatialStreamPlayer::get_stream_name);
+ ObjectTypeDB::bind_method(_MD("get_loop_count"),&SpatialStreamPlayer::get_loop_count);
+
+ ObjectTypeDB::bind_method(_MD("get_pos"),&SpatialStreamPlayer::get_pos);
+ ObjectTypeDB::bind_method(_MD("seek_pos","time"),&SpatialStreamPlayer::seek_pos);
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"),_SCS("get_stream") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"),_SCS("has_loop") );
+
+}
+
+
+SpatialStreamPlayer::SpatialStreamPlayer() {
+
+ loops=false;
+}
+
+SpatialStreamPlayer::~SpatialStreamPlayer() {
+
+}
+
+
diff --git a/scene/3d/spatial_stream_player.h b/scene/3d/spatial_stream_player.h
new file mode 100644
index 0000000000..6b73a8ad1b
--- /dev/null
+++ b/scene/3d/spatial_stream_player.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* spatial_stream_player.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 SPATIAL_STREAM_PLAYER_H
+#define SPATIAL_STREAM_PLAYER_H
+
+#include "scene/resources/audio_stream.h"
+#include "scene/3d/spatial_player.h"
+
+
+class SpatialStreamPlayer : public SpatialPlayer {
+
+ OBJ_TYPE(SpatialStreamPlayer,SpatialPlayer);
+
+ Ref<AudioStream> stream;
+ bool loops;
+protected:
+
+ void _notification(int p_what);
+
+ static void _bind_methods();
+public:
+
+ void set_stream(const Ref<AudioStream> &p_stream);
+ Ref<AudioStream> get_stream() const;
+
+ void play();
+ void stop();
+ bool is_playing() const;
+
+ void set_loop(bool p_enable);
+ bool has_loop() const;
+
+
+ String get_stream_name() const;
+ int get_loop_count() const;
+
+
+ float get_pos() const;
+ void seek_pos(float p_time);
+
+
+ SpatialStreamPlayer();
+ ~SpatialStreamPlayer();
+};
+
+#endif // SPATIAL_STREAM_PLAYER_H
diff --git a/scene/3d/test_cube.cpp b/scene/3d/test_cube.cpp
new file mode 100644
index 0000000000..265263a760
--- /dev/null
+++ b/scene/3d/test_cube.cpp
@@ -0,0 +1,53 @@
+/*************************************************************************/
+/* test_cube.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 "test_cube.h"
+#include "servers/visual_server.h"
+
+
+
+AABB TestCube::get_aabb() const {
+
+ return AABB( Vector3(-1,-1,-1), Vector3(2, 2, 2 ) );
+}
+DVector<Face3> TestCube::get_faces(uint32_t p_usage_flags) const {
+
+ return DVector<Face3>();
+}
+
+
+TestCube::TestCube() {
+
+ set_base(VisualServer::get_singleton()->get_test_cube());
+}
+
+
+TestCube::~TestCube() {
+}
+
+
diff --git a/scene/3d/test_cube.h b/scene/3d/test_cube.h
new file mode 100644
index 0000000000..8a5b566f1f
--- /dev/null
+++ b/scene/3d/test_cube.h
@@ -0,0 +1,57 @@
+/*************************************************************************/
+/* test_cube.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 TEST_CUBE_H
+#define TEST_CUBE_H
+
+
+#include "scene/3d/visual_instance.h"
+#include "rid.h"
+
+
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class TestCube : public GeometryInstance {
+
+ OBJ_TYPE( TestCube, GeometryInstance );
+
+ RID instance;
+
+
+public:
+
+ virtual AABB get_aabb() const;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const;
+
+ TestCube();
+ ~TestCube();
+
+};
+
+#endif
diff --git a/scene/3d/visibility_notifier.cpp b/scene/3d/visibility_notifier.cpp
new file mode 100644
index 0000000000..da01827c49
--- /dev/null
+++ b/scene/3d/visibility_notifier.cpp
@@ -0,0 +1,314 @@
+/*************************************************************************/
+/* visibility_notifier.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.h"
+
+#include "scene/scene_string_names.h"
+#include "scene/3d/physics_body.h"
+#include "scene/animation/animation_player.h"
+#include "scene/scene_string_names.h"
+
+void VisibilityNotifier::_enter_camera(Camera* p_camera) {
+
+ ERR_FAIL_COND(cameras.has(p_camera));
+ cameras.insert(p_camera);
+ if (cameras.size()==1) {
+ emit_signal(SceneStringNames::get_singleton()->enter_screen);
+ _screen_enter();
+ }
+ emit_signal(SceneStringNames::get_singleton()->enter_camera,p_camera);
+
+}
+
+void VisibilityNotifier::_exit_camera(Camera* p_camera){
+
+ ERR_FAIL_COND(!cameras.has(p_camera));
+ cameras.erase(p_camera);
+
+ emit_signal(SceneStringNames::get_singleton()->exit_camera,p_camera);
+ if (cameras.size()==0) {
+ emit_signal(SceneStringNames::get_singleton()->exit_screen);
+
+ _screen_exit();
+
+ }
+}
+
+
+void VisibilityNotifier::set_aabb(const AABB& p_aabb){
+
+ if (aabb==p_aabb)
+ return;
+ aabb=p_aabb;
+
+ if (is_inside_world()) {
+ get_world()->_update_notifier(this,get_global_transform().xform(aabb));
+ }
+
+ _change_notify("aabb");
+ update_gizmo();
+}
+
+AABB VisibilityNotifier::get_aabb() const{
+
+ return aabb;
+}
+
+
+void VisibilityNotifier::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_WORLD: {
+
+
+ get_world()->_register_notifier(this,get_global_transform().xform(aabb));
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+
+ get_world()->_update_notifier(this,get_global_transform().xform(aabb));
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ get_world()->_remove_notifier(this);
+ } break;
+ }
+}
+
+
+bool VisibilityNotifier::is_on_screen() const {
+
+ return cameras.size()!=0;
+}
+
+void VisibilityNotifier::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_aabb","rect"),&VisibilityNotifier::set_aabb);
+ ObjectTypeDB::bind_method(_MD("get_aabb"),&VisibilityNotifier::get_aabb);
+ ObjectTypeDB::bind_method(_MD("is_on_screen"),&VisibilityNotifier::is_on_screen);
+
+ ADD_PROPERTY( PropertyInfo(Variant::_AABB,"aabb"),_SCS("set_aabb"),_SCS("get_aabb"));
+
+ ADD_SIGNAL( MethodInfo("enter_camera",PropertyInfo(Variant::OBJECT,"camera",PROPERTY_HINT_RESOURCE_TYPE,"Camera")) );
+ ADD_SIGNAL( MethodInfo("exit_camera",PropertyInfo(Variant::OBJECT,"camera",PROPERTY_HINT_RESOURCE_TYPE,"Camera")) );
+ ADD_SIGNAL( MethodInfo("enter_screen"));
+ ADD_SIGNAL( MethodInfo("exit_screen"));
+}
+
+
+VisibilityNotifier::VisibilityNotifier() {
+
+ aabb=AABB(Vector3(-1,-1,-1),Vector3(2,2,2));
+
+}
+
+
+
+
+
+//////////////////////////////////////
+
+
+void VisibilityEnabler::_screen_enter() {
+
+
+ for(Map<Node*,Variant>::Element *E=nodes.front();E;E=E->next()) {
+
+ _change_node_state(E->key(),true);
+ }
+
+ visible=true;
+}
+
+void VisibilityEnabler::_screen_exit() {
+
+ for(Map<Node*,Variant>::Element *E=nodes.front();E;E=E->next()) {
+
+ _change_node_state(E->key(),false);
+ }
+
+ visible=false;
+}
+
+void VisibilityEnabler::_find_nodes(Node* p_node) {
+
+
+ bool add=false;
+ Variant meta;
+
+ if (enabler[ENABLER_FREEZE_BODIES]) {
+
+ RigidBody *rb = p_node->cast_to<RigidBody>();
+ if (rb && ((rb->get_mode()==RigidBody::MODE_CHARACTER || (rb->get_mode()==RigidBody::MODE_RIGID && !rb->is_able_to_sleep())))) {
+
+
+ add=true;
+ meta=rb->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 VisibilityEnabler::_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 VisibilityEnabler::_change_node_state(Node* p_node,bool p_enabled) {
+
+ ERR_FAIL_COND(!nodes.has(p_node));
+
+ {
+ RigidBody *rb = p_node->cast_to<RigidBody>();
+ if (rb) {
+
+ if (p_enabled) {
+ RigidBody::Mode mode = RigidBody::Mode(nodes[p_node].operator int());
+ //rb->set_mode(mode);
+ rb->set_active(true);
+ } else {
+ //rb->set_mode(RigidBody::MODE_STATIC);
+ rb->set_active(false);
+ }
+ }
+ }
+
+ {
+ AnimationPlayer *ap=p_node->cast_to<AnimationPlayer>();
+
+ if (ap) {
+
+ ap->set_active(p_enabled);
+ }
+ }
+
+}
+
+
+void VisibilityEnabler::_node_removed(Node* p_node) {
+
+ if (!visible)
+ _change_node_state(p_node,true);
+ p_node->disconnect(SceneStringNames::get_singleton()->exit_scene,this,"_node_removed");
+ nodes.erase(p_node);
+
+}
+
+void VisibilityEnabler::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_enabler","enabler","enabled"),&VisibilityEnabler::set_enabler);
+ ObjectTypeDB::bind_method(_MD("is_enabler_enabled","enabler"),&VisibilityEnabler::is_enabler_enabled);
+ ObjectTypeDB::bind_method(_MD("_node_removed"),&VisibilityEnabler::_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 VisibilityEnabler::set_enabler(Enabler p_enabler,bool p_enable){
+
+ ERR_FAIL_INDEX(p_enabler,ENABLER_MAX);
+ enabler[p_enabler]=p_enable;
+
+}
+bool VisibilityEnabler::is_enabler_enabled(Enabler p_enabler) const{
+
+ ERR_FAIL_INDEX_V(p_enabler,ENABLER_MAX,false);
+ return enabler[p_enabler];
+
+}
+
+VisibilityEnabler::VisibilityEnabler() {
+
+ for(int i=0;i<ENABLER_MAX;i++)
+ enabler[i]=true;
+
+ visible=false;
+
+}
+
diff --git a/scene/3d/visibility_notifier.h b/scene/3d/visibility_notifier.h
new file mode 100644
index 0000000000..52acd0ba2c
--- /dev/null
+++ b/scene/3d/visibility_notifier.h
@@ -0,0 +1,106 @@
+/*************************************************************************/
+/* visibility_notifier.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_H
+#define VISIBILITY_NOTIFIER_H
+
+#include "scene/3d/spatial.h"
+
+class Camera;
+class VisibilityNotifier : public Spatial {
+
+ OBJ_TYPE(VisibilityNotifier,Spatial);
+
+ Set<Camera*> cameras;
+
+ AABB aabb;
+
+protected:
+
+ virtual void _screen_enter() {}
+ virtual void _screen_exit() {}
+
+ void _notification(int p_what);
+ static void _bind_methods();
+friend class SpatialIndexer;
+
+ void _enter_camera(Camera* p_camera);
+ void _exit_camera(Camera* p_camera);
+
+public:
+
+ void set_aabb(const AABB& p_aabb);
+ AABB get_aabb() const;
+ bool is_on_screen() const;
+
+ VisibilityNotifier();
+};
+
+
+class VisibilityEnabler : public VisibilityNotifier {
+
+ OBJ_TYPE(VisibilityEnabler,VisibilityNotifier);
+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;
+
+ VisibilityEnabler();
+
+};
+
+VARIANT_ENUM_CAST(VisibilityEnabler::Enabler);
+
+
+#endif // VISIBILITY_NOTIFIER_H
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
new file mode 100644
index 0000000000..9419996187
--- /dev/null
+++ b/scene/3d/visual_instance.cpp
@@ -0,0 +1,260 @@
+/*************************************************************************/
+/* visual_instance.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 "visual_instance.h"
+
+#include "servers/visual_server.h"
+#include "room_instance.h"
+#include "scene/scene_string_names.h"
+
+#include "skeleton.h"
+
+AABB VisualInstance::get_transformed_aabb() const {
+
+ return get_global_transform().xform( get_aabb() );
+}
+
+
+
+void VisualInstance::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_WORLD: {
+
+ // CHECK ROOM
+ Spatial * parent = get_parent_spatial();
+ Room *room=NULL;
+
+ while(parent) {
+
+ room = parent->cast_to<Room>();
+ if (room)
+ break;
+ else
+ parent=parent->get_parent_spatial();
+ }
+
+
+ if (room) {
+
+ VisualServer::get_singleton()->instance_set_room(instance,room->get_instance());
+ }
+ // CHECK SKELETON
+ Skeleton *skeleton=get_parent()?get_parent()->cast_to<Skeleton>():NULL;
+ if (skeleton)
+ VisualServer::get_singleton()->instance_attach_skeleton( instance, skeleton->get_skeleton() );
+
+ VisualServer::get_singleton()->instance_set_scenario( instance, get_world()->get_scenario() );
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ Transform gt = get_global_transform();
+ VisualServer::get_singleton()->instance_set_transform(instance,gt);
+ } break;
+ case NOTIFICATION_EXIT_WORLD: {
+
+ VisualServer::get_singleton()->instance_set_scenario( instance, RID() );
+ VisualServer::get_singleton()->instance_set_room(instance,RID());
+ VisualServer::get_singleton()->instance_attach_skeleton( instance, RID() );
+
+
+ } break;
+ }
+}
+
+RID VisualInstance::get_instance() const {
+
+ return instance;
+}
+
+RID VisualInstance::_get_visual_instance_rid() const {
+
+ return instance;
+}
+
+void VisualInstance::set_layer_mask(uint32_t p_mask) {
+
+ layers=p_mask;
+ VisualServer::get_singleton()->instance_set_layer_mask(instance,p_mask);
+}
+
+uint32_t VisualInstance::get_layer_mask() const {
+
+ return layers;
+}
+
+
+void VisualInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_get_visual_instance_rid"),&VisualInstance::_get_visual_instance_rid);
+ ObjectTypeDB::bind_method(_MD("set_base","base"), &VisualInstance::set_base);
+ ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"), &VisualInstance::set_layer_mask);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask"), &VisualInstance::get_layer_mask);
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "layers",PROPERTY_HINT_ALL_FLAGS), _SCS("set_layer_mask"), _SCS("get_layer_mask"));
+
+
+}
+
+
+void VisualInstance::set_base(const RID& p_base) {
+
+ VisualServer::get_singleton()->instance_set_base(instance,p_base);
+}
+
+
+VisualInstance::VisualInstance()
+{
+
+ instance = VisualServer::get_singleton()->instance_create();
+ VisualServer::get_singleton()->instance_attach_object_instance_ID( instance, get_instance_ID() );
+ layers=1;
+}
+
+
+VisualInstance::~VisualInstance() {
+
+ VisualServer::get_singleton()->free(instance);
+}
+
+
+
+
+void GeometryInstance::set_material_override(const Ref<Material>& p_material) {
+
+ material_override=p_material;
+ VS::get_singleton()->instance_geometry_set_material_override(get_instance(),p_material.is_valid() ? p_material->get_rid() : RID());
+}
+
+Ref<Material> GeometryInstance::get_material_override() const{
+
+ return material_override;
+}
+
+
+
+void GeometryInstance::set_draw_range_begin(float p_dist){
+
+ draw_begin=p_dist;
+ VS::get_singleton()->instance_geometry_set_draw_range(get_instance(),draw_begin,draw_end);
+}
+
+float GeometryInstance::get_draw_range_begin() const{
+
+ return draw_begin;
+}
+
+
+void GeometryInstance::set_draw_range_end(float p_dist) {
+
+ draw_end=p_dist;
+ VS::get_singleton()->instance_geometry_set_draw_range(get_instance(),draw_begin,draw_end);
+
+}
+
+float GeometryInstance::get_draw_range_end() const {
+
+ return draw_end;
+}
+
+void GeometryInstance::set_flag(Flags p_flag,bool p_value) {
+
+ ERR_FAIL_INDEX(p_flag,FLAG_MAX);
+ if (flags[p_flag]==p_value)
+ return;
+
+ flags[p_flag]=p_value;
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(),(VS::InstanceFlags)p_flag,p_value);
+ if (p_flag==FLAG_VISIBLE) {
+ _change_notify("geometry/visible");
+ emit_signal(SceneStringNames::get_singleton()->visibility_changed);
+ }
+
+
+}
+
+bool GeometryInstance::get_flag(Flags p_flag) const{
+
+ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false);
+ return flags[p_flag];
+
+}
+
+
+void GeometryInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_material_override","material"), &GeometryInstance::set_material_override);
+ ObjectTypeDB::bind_method(_MD("get_material_override"), &GeometryInstance::get_material_override);
+
+ ObjectTypeDB::bind_method(_MD("set_flag","flag","value"), &GeometryInstance::set_flag);
+ ObjectTypeDB::bind_method(_MD("get_flag","flag"), &GeometryInstance::get_flag);
+
+ ObjectTypeDB::bind_method(_MD("set_draw_range_begin","mode"), &GeometryInstance::set_draw_range_begin);
+ ObjectTypeDB::bind_method(_MD("get_draw_range_begin"), &GeometryInstance::get_draw_range_begin);
+
+ ObjectTypeDB::bind_method(_MD("set_draw_range_end","mode"), &GeometryInstance::set_draw_range_end);
+ ObjectTypeDB::bind_method(_MD("get_draw_range_end"), &GeometryInstance::get_draw_range_end);
+
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE);
+ ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "geometry/material_override",PROPERTY_HINT_RESOURCE_TYPE,"Material"), _SCS("set_material_override"), _SCS("get_material_override"));
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/cast_shadow"), _SCS("set_flag"), _SCS("get_flag"),FLAG_CAST_SHADOW);
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/receive_shadows"), _SCS("set_flag"), _SCS("get_flag"),FLAG_RECEIVE_SHADOWS);
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/range_begin",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_draw_range_begin"), _SCS("get_draw_range_begin"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/range_end",PROPERTY_HINT_RANGE,"0,32768,0.01"), _SCS("set_draw_range_end"), _SCS("get_draw_range_end"));
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/billboard"), _SCS("set_flag"), _SCS("get_flag"),FLAG_BILLBOARD);
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/billboard_y"), _SCS("set_flag"), _SCS("get_flag"),FLAG_BILLBOARD_FIX_Y);
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/depth_scale"), _SCS("set_flag"), _SCS("get_flag"),FLAG_DEPH_SCALE);
+ ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible_in_all_rooms"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE_IN_ALL_ROOMS);
+
+ ADD_SIGNAL( MethodInfo("visibility_changed"));
+
+ BIND_CONSTANT(FLAG_VISIBLE );
+ BIND_CONSTANT(FLAG_CAST_SHADOW );
+ BIND_CONSTANT(FLAG_RECEIVE_SHADOWS );
+ BIND_CONSTANT(FLAG_BILLBOARD );
+ BIND_CONSTANT(FLAG_BILLBOARD_FIX_Y );
+ BIND_CONSTANT(FLAG_DEPH_SCALE );
+ BIND_CONSTANT(FLAG_VISIBLE_IN_ALL_ROOMS );
+ BIND_CONSTANT(FLAG_MAX );
+
+}
+
+GeometryInstance::GeometryInstance() {
+ draw_begin=0;
+ draw_end=0;
+ flags[FLAG_VISIBLE]=true;
+ flags[FLAG_CAST_SHADOW]=true;
+ flags[FLAG_RECEIVE_SHADOWS]=true;
+ flags[FLAG_BILLBOARD]=false;
+ flags[FLAG_BILLBOARD_FIX_Y]=false;
+ flags[FLAG_DEPH_SCALE]=false;
+ flags[FLAG_VISIBLE_IN_ALL_ROOMS]=false;
+
+}
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
new file mode 100644
index 0000000000..4e652912c6
--- /dev/null
+++ b/scene/3d/visual_instance.h
@@ -0,0 +1,126 @@
+/*************************************************************************/
+/* visual_instance.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 VISUAL_INSTANCE_H
+#define VISUAL_INSTANCE_H
+
+#include "scene/3d/spatial.h"
+#include "face3.h"
+#include "rid.h"
+#include "scene/resources/material.h"
+/**
+ @author Juan Linietsky <reduzio@gmail.com>
+*/
+class VisualInstance : public Spatial {
+
+ OBJ_TYPE( VisualInstance, Spatial );
+ OBJ_CATEGORY("3D Visual Nodes");
+
+ RID instance;
+ uint32_t layers;
+
+
+ RID _get_visual_instance_rid() const;
+
+protected:
+
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ enum GetFacesFlags {
+ FACES_SOLID=1, // solid geometry
+ FACES_ENCLOSING=2,
+ FACES_DYNAMIC=4 // dynamic object geometry
+
+ };
+
+ RID get_instance() const;
+ virtual AABB get_aabb() const=0;
+ virtual DVector<Face3> get_faces(uint32_t p_usage_flags) const=0;
+
+ virtual AABB get_transformed_aabb() const; // helper
+
+ void set_base(const RID& p_base);
+
+ void set_layer_mask(uint32_t p_mask);
+ uint32_t get_layer_mask() const;
+
+
+ VisualInstance();
+ ~VisualInstance();
+
+};
+
+class GeometryInstance : public VisualInstance {
+
+ OBJ_TYPE( GeometryInstance, VisualInstance );
+public:
+
+ enum Flags {
+ FLAG_VISIBLE=VS::INSTANCE_FLAG_VISIBLE,
+ FLAG_CAST_SHADOW=VS::INSTANCE_FLAG_CAST_SHADOW,
+ FLAG_RECEIVE_SHADOWS=VS::INSTANCE_FLAG_RECEIVE_SHADOWS,
+ FLAG_BILLBOARD=VS::INSTANCE_FLAG_BILLBOARD,
+ FLAG_BILLBOARD_FIX_Y=VS::INSTANCE_FLAG_BILLBOARD_FIX_Y,
+ FLAG_DEPH_SCALE=VS::INSTANCE_FLAG_DEPH_SCALE,
+ FLAG_VISIBLE_IN_ALL_ROOMS=VS::INSTANCE_FLAG_VISIBLE_IN_ALL_ROOMS,
+ FLAG_MAX=VS::INSTANCE_FLAG_MAX,
+ };
+
+
+private:
+
+ bool flags[FLAG_MAX];
+ Ref<Material> material_override;
+ float draw_begin;
+ float draw_end;
+protected:
+
+ static void _bind_methods();
+public:
+
+ void set_flag(Flags p_flag,bool p_value);
+ bool get_flag(Flags p_flag) const;
+
+ void set_draw_range_begin(float p_dist);
+ float get_draw_range_begin() const;
+
+ void set_draw_range_end(float p_dist);
+ float get_draw_range_end() const;
+
+ void set_material_override(const Ref<Material>& p_material);
+ Ref<Material> get_material_override() const;
+
+ GeometryInstance();
+};
+
+VARIANT_ENUM_CAST( GeometryInstance::Flags );
+
+#endif