summaryrefslogtreecommitdiff
path: root/scene
diff options
context:
space:
mode:
Diffstat (limited to 'scene')
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_polygon_2d.cpp3
-rw-r--r--scene/2d/physics_body_2d.cpp2
-rw-r--r--scene/2d/screen_button.cpp3
-rw-r--r--scene/3d/area.cpp17
-rw-r--r--scene/3d/area.h4
-rw-r--r--scene/3d/camera.cpp83
-rw-r--r--scene/3d/camera.h12
-rw-r--r--scene/3d/collision_object.cpp51
-rw-r--r--scene/3d/collision_object.h9
-rw-r--r--scene/3d/collision_polygon.cpp206
-rw-r--r--scene/3d/collision_polygon.h50
-rw-r--r--scene/3d/light.cpp10
-rw-r--r--scene/3d/light.h1
-rw-r--r--scene/3d/navigation.cpp603
-rw-r--r--scene/3d/navigation.h139
-rw-r--r--scene/3d/navigation_agent.cpp5
-rw-r--r--scene/3d/navigation_agent.h10
-rw-r--r--scene/3d/navigation_mesh.cpp237
-rw-r--r--scene/3d/navigation_mesh.h68
-rw-r--r--scene/3d/physics_body.cpp567
-rw-r--r--scene/3d/physics_body.h99
-rw-r--r--scene/3d/physics_joint.cpp1012
-rw-r--r--scene/3d/physics_joint.h307
-rw-r--r--scene/3d/skeleton.cpp10
-rw-r--r--scene/3d/skeleton.h1
-rw-r--r--scene/3d/spatial.cpp93
-rw-r--r--scene/3d/spatial.h14
-rw-r--r--scene/3d/vehicle_body.cpp1044
-rw-r--r--scene/3d/vehicle_body.h185
-rw-r--r--scene/3d/visual_instance.cpp38
-rw-r--r--scene/3d/visual_instance.h5
-rw-r--r--scene/animation/animation_player.cpp6
-rw-r--r--scene/animation/animation_tree_player.cpp5
-rw-r--r--scene/animation/tween.cpp1219
-rw-r--r--scene/animation/tween.h239
-rw-r--r--scene/animation/tween_interpolaters.cpp407
-rw-r--r--scene/gui/tab_container.cpp6
-rw-r--r--scene/gui/tabs.cpp3
-rw-r--r--scene/gui/text_edit.cpp119
-rw-r--r--scene/gui/text_edit.h7
-rw-r--r--scene/gui/tree.cpp2
-rw-r--r--scene/gui/video_player.cpp14
-rw-r--r--scene/main/scene_main_loop.cpp12
-rw-r--r--scene/main/viewport.cpp242
-rw-r--r--scene/main/viewport.h14
-rw-r--r--scene/register_scene_types.cpp23
-rw-r--r--scene/resources/animation.cpp16
-rw-r--r--scene/resources/animation.h4
-rw-r--r--scene/resources/baked_light.cpp184
-rw-r--r--scene/resources/baked_light.h30
-rw-r--r--scene/resources/video_stream.h22
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h4
54 files changed, 7221 insertions, 251 deletions
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index ab8c4551ee..e5d9872a28 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -47,6 +47,10 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_ENTER_SCENE: {
+ if (area)
+ Physics2DServer::get_singleton()->area_set_transform(rid,get_global_transform());
+ else
+ Physics2DServer::get_singleton()->body_set_state(rid,Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform());
RID space = get_world_2d()->get_space();
if (area) {
diff --git a/scene/2d/collision_polygon_2d.cpp b/scene/2d/collision_polygon_2d.cpp
index 5ab223a1b8..ef63286697 100644
--- a/scene/2d/collision_polygon_2d.cpp
+++ b/scene/2d/collision_polygon_2d.cpp
@@ -153,6 +153,7 @@ void CollisionPolygon2D::set_build_mode(BuildMode p_mode) {
ERR_FAIL_INDEX(p_mode,2);
build_mode=p_mode;
+ _update_parent();
}
CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const{
@@ -174,7 +175,7 @@ void CollisionPolygon2D::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_build_mode"),&CollisionPolygon2D::set_build_mode);
ObjectTypeDB::bind_method(_MD("get_build_mode"),&CollisionPolygon2D::get_build_mode);
- ADD_PROPERTY( PropertyInfo(Variant::INT,"build_mode",PROPERTY_HINT_ENUM,"Automatic,Segments,Solids"),_SCS("set_build_mode"),_SCS("get_build_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"build_mode",PROPERTY_HINT_ENUM,"Solids,Segments"),_SCS("set_build_mode"),_SCS("get_build_mode"));
ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon"));
}
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index ecd147afde..47d78399b6 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -803,7 +803,7 @@ Vector2 KinematicBody2D::move(const Vector2& p_motion) {
//print_line("margin: "+rtos(margin));
do {
- //fill exclude list..
+ //motion recover
for(int i=0;i<get_shape_count();i++) {
diff --git a/scene/2d/screen_button.cpp b/scene/2d/screen_button.cpp
index 848f5afeb9..b606634819 100644
--- a/scene/2d/screen_button.cpp
+++ b/scene/2d/screen_button.cpp
@@ -132,6 +132,9 @@ void TouchScreenButton::_input(const InputEvent& p_event) {
if (!get_scene())
return;
+ if (p_event.device != 0)
+ return;
+
if (passby_press) {
if (p_event.type==InputEvent::SCREEN_TOUCH && !p_event.screen_touch.pressed && finger_pressed==p_event.screen_touch.index) {
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp
index 964a086cf6..f5895453cc 100644
--- a/scene/3d/area.cpp
+++ b/scene/3d/area.cpp
@@ -255,6 +255,17 @@ bool Area::is_monitoring_enabled() const {
}
+void Area::set_ray_pickable(bool p_ray_pickable) {
+
+ ray_pickable=p_ray_pickable;
+ PhysicsServer::get_singleton()->area_set_ray_pickable(get_rid(),p_ray_pickable);
+}
+
+bool Area::is_ray_pickable() const {
+
+ return ray_pickable;
+}
+
void Area::_bind_methods() {
ObjectTypeDB::bind_method(_MD("_body_enter_scene","id"),&Area::_body_enter_scene);
@@ -278,6 +289,9 @@ void Area::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_priority","priority"),&Area::set_priority);
ObjectTypeDB::bind_method(_MD("get_priority"),&Area::get_priority);
+ ObjectTypeDB::bind_method(_MD("set_ray_pickable","ray_pickable"),&Area::set_ray_pickable);
+ ObjectTypeDB::bind_method(_MD("is_ray_pickable"),&Area::is_ray_pickable);
+
ObjectTypeDB::bind_method(_MD("set_enable_monitoring","enable"),&Area::set_enable_monitoring);
ObjectTypeDB::bind_method(_MD("is_monitoring_enabled"),&Area::is_monitoring_enabled);
@@ -296,6 +310,7 @@ void Area::_bind_methods() {
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"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"ray_pickable"),_SCS("set_ray_pickable"),_SCS("is_ray_pickable"));
}
@@ -308,6 +323,8 @@ Area::Area() : CollisionObject(PhysicsServer::get_singleton()->area_create(),tru
density=0.1;
priority=0;
monitoring=false;
+ ray_pickable=false;
+ set_enable_monitoring(true);
}
diff --git a/scene/3d/area.h b/scene/3d/area.h
index 79e98f9dab..92b5d39f59 100644
--- a/scene/3d/area.h
+++ b/scene/3d/area.h
@@ -52,6 +52,7 @@ private:
real_t density;
int priority;
bool monitoring;
+ bool ray_pickable;
void _body_inout(int p_status,const RID& p_body, int p_instance, int p_body_shape,int p_area_shape);
@@ -108,6 +109,9 @@ public:
void set_priority(real_t p_priority);
real_t get_priority() const;
+ void set_ray_pickable(bool p_ray_pickable);
+ bool is_ray_pickable() const;
+
void set_enable_monitoring(bool p_enable);
bool is_monitoring_enabled() const;
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 56fbf358bc..4245bfa2c9 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -74,16 +74,18 @@ bool Camera::_set(const StringName& p_name, const Variant& p_value) {
mode=PROJECTION_ORTHOGONAL;
changed_all=true;
- } else if (p_name=="fov")
+ } else if (p_name=="fov" || p_name=="fovy" || p_name=="fovx")
fov=p_value;
- else if (p_name=="size")
+ else if (p_name=="size" || p_name=="sizex" || p_name=="sizey")
size=p_value;
else if (p_name=="near")
near=p_value;
else if (p_name=="far")
far=p_value;
+ else if (p_name=="keep_aspect")
+ set_keep_aspect_mode(KeepAspect(int(p_value)));
else if (p_name=="vaspect")
- set_use_vertical_aspect(p_value);
+ set_keep_aspect_mode(p_value?KEEP_WIDTH:KEEP_HEIGHT);
else if (p_name=="current") {
if (p_value.operator bool()) {
make_current();
@@ -107,16 +109,16 @@ bool Camera::_get(const StringName& p_name,Variant &r_ret) const {
if (p_name=="projection") {
r_ret= mode;
- } else if (p_name=="fov")
+ } else if (p_name=="fov" || p_name=="fovy" || p_name=="fovx")
r_ret= fov;
- else if (p_name=="size")
+ else if (p_name=="size" || p_name=="sizex" || p_name=="sizey")
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=="keep_aspect")
+ r_ret= int(keep_aspect);
else if (p_name=="current") {
if (is_inside_scene() && get_scene()->is_editor_hint()) {
@@ -142,19 +144,29 @@ void Camera::_get_property_list( List<PropertyInfo> *p_list) const {
case PROJECTION_PERSPECTIVE: {
- p_list->push_back( PropertyInfo( Variant::REAL, "fov" , PROPERTY_HINT_RANGE, "1,89,0.1") );
+ p_list->push_back( PropertyInfo( Variant::REAL, "fov" , PROPERTY_HINT_RANGE, "1,89,0.1",PROPERTY_USAGE_NOEDITOR) );
+ if (keep_aspect==KEEP_WIDTH)
+ p_list->push_back( PropertyInfo( Variant::REAL, "fovx" , PROPERTY_HINT_RANGE, "1,89,0.1",PROPERTY_USAGE_EDITOR) );
+ else
+ p_list->push_back( PropertyInfo( Variant::REAL, "fovy" , PROPERTY_HINT_RANGE, "1,89,0.1",PROPERTY_USAGE_EDITOR) );
+
} break;
case PROJECTION_ORTHOGONAL: {
- p_list->push_back( PropertyInfo( Variant::REAL, "size" , PROPERTY_HINT_RANGE, "1,16384,0.01" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "size" , PROPERTY_HINT_RANGE, "1,16384,0.01",PROPERTY_USAGE_NOEDITOR ) );
+ if (keep_aspect==KEEP_WIDTH)
+ p_list->push_back( PropertyInfo( Variant::REAL, "sizex" , PROPERTY_HINT_RANGE, "0.1,16384,0.01",PROPERTY_USAGE_EDITOR) );
+ else
+ p_list->push_back( PropertyInfo( Variant::REAL, "sizey" , PROPERTY_HINT_RANGE, "0.1,16384,0.01",PROPERTY_USAGE_EDITOR) );
+
} 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::INT, "keep_aspect",PROPERTY_HINT_ENUM,"Keep Width,Keep Height") );
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" ) );
@@ -441,7 +453,7 @@ Vector3 Camera::project_local_ray_normal(const Point2& p_pos) const {
ray=Vector3(0,0,-1);
} else {
CameraMatrix cm;
- cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
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();
@@ -472,7 +484,7 @@ Vector3 Camera::project_ray_origin(const Point2& p_pos) const {
Vector2 pos = p_pos / viewport_size;
float vsize,hsize;
- if (vaspect) {
+ if (keep_aspect==KEEP_WIDTH) {
vsize = size/viewport_size.get_aspect();
hsize = size;
} else {
@@ -503,9 +515,9 @@ Point2 Camera::unproject_position(const Vector3& p_pos) const {
if (mode==PROJECTION_ORTHOGONAL)
- cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
else
- cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
Plane p(get_camera_transform().xform_inv(p_pos),1.0);
@@ -533,9 +545,9 @@ Vector3 Camera::project_position(const Point2& p_point) const {
CameraMatrix cm;
if (mode==PROJECTION_ORTHOGONAL)
- cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
else
- cm.set_perspective(fov,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
Size2 vp_size;
cm.get_viewport_size(vp_size.x,vp_size.y);
@@ -583,6 +595,20 @@ Ref<Environment> Camera::get_environment() const {
}
+void Camera::set_keep_aspect_mode(KeepAspect p_aspect) {
+
+ keep_aspect=p_aspect;
+ VisualServer::get_singleton()->camera_set_use_vertical_aspect(camera,p_aspect==KEEP_WIDTH);
+
+ _change_notify();
+}
+
+Camera::KeepAspect Camera::get_keep_aspect_mode() const{
+
+ return keep_aspect;
+}
+
+
void Camera::_bind_methods() {
@@ -608,12 +634,16 @@ void Camera::_bind_methods() {
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("set_keep_aspect_mode","mode"),&Camera::set_keep_aspect_mode);
+ ObjectTypeDB::bind_method(_MD("get_keep_aspect_mode"),&Camera::get_keep_aspect_mode);
//ObjectTypeDB::bind_method( _MD("_camera_make_current"),&Camera::_camera_make_current );
BIND_CONSTANT( PROJECTION_PERSPECTIVE );
BIND_CONSTANT( PROJECTION_ORTHOGONAL );
+
+ BIND_CONSTANT( KEEP_WIDTH );
+ BIND_CONSTANT( KEEP_HEIGHT );
+
}
float Camera::get_fov() const {
@@ -661,26 +691,15 @@ Vector<Plane> Camera::get_frustum() const {
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);
+ cm.set_perspective(fov,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
else
- cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,vaspect);
+ cm.set_orthogonal(size,viewport_size.get_aspect(),near,far,keep_aspect==KEEP_WIDTH);
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) {
@@ -712,7 +731,7 @@ Camera::Camera() {
force_change=false;
mode=PROJECTION_PERSPECTIVE;
set_perspective(60.0,0.1,100.0);
- vaspect=false;
+ keep_aspect=KEEP_HEIGHT;
layers=0xFFFFFFFF;
//active=false;
}
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index a8599497ac..014c7cb520 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -46,6 +46,11 @@ public:
PROJECTION_ORTHOGONAL
};
+ enum KeepAspect {
+ KEEP_WIDTH,
+ KEEP_HEIGHT
+ };
+
private:
bool force_change;
@@ -56,7 +61,7 @@ private:
float fov;
float size;
float near,far;
- bool vaspect;
+ KeepAspect keep_aspect;
RID camera;
RID scenario_id;
@@ -126,8 +131,8 @@ public:
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 set_keep_aspect_mode(KeepAspect p_aspect);
+ KeepAspect get_keep_aspect_mode() 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);
@@ -140,5 +145,6 @@ public:
VARIANT_ENUM_CAST( Camera::Projection );
+VARIANT_ENUM_CAST( Camera::KeepAspect );
#endif
diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp
index 7ad10d3222..06564f5c49 100644
--- a/scene/3d/collision_object.cpp
+++ b/scene/3d/collision_object.cpp
@@ -28,7 +28,7 @@
/*************************************************************************/
#include "collision_object.h"
#include "servers/physics_server.h"
-
+#include "scene/scene_string_names.h"
void CollisionObject::_update_shapes_from_children() {
shapes.resize(0);
@@ -165,6 +165,34 @@ void CollisionObject::_get_property_list( List<PropertyInfo> *p_list) const {
}
}
+
+void CollisionObject::_input_event(const InputEvent& p_input_event,const Vector3& p_pos, const Vector3& p_normal, int p_shape) {
+
+ if (get_script_instance()) {
+ get_script_instance()->call(SceneStringNames::get_singleton()->_input_event,p_input_event,p_pos,p_normal,p_shape);
+ }
+ emit_signal(SceneStringNames::get_singleton()->input_event,p_input_event,p_pos,p_normal,p_shape);
+}
+
+void CollisionObject::_mouse_enter() {
+
+ if (get_script_instance()) {
+ get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_enter);
+ }
+ emit_signal(SceneStringNames::get_singleton()->mouse_enter);
+}
+
+
+void CollisionObject::_mouse_exit() {
+
+ if (get_script_instance()) {
+ get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_exit);
+ }
+ emit_signal(SceneStringNames::get_singleton()->mouse_exit);
+
+}
+
+
void CollisionObject::_bind_methods() {
ObjectTypeDB::bind_method(_MD("add_shape","shape:Shape","transform"),&CollisionObject::add_shape,DEFVAL(Transform()));
@@ -178,8 +206,16 @@ void CollisionObject::_bind_methods() {
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("set_capture_input_on_drag","enable"),&CollisionObject::set_capture_input_on_drag);
+ ObjectTypeDB::bind_method(_MD("get_capture_input_on_drag"),&CollisionObject::get_capture_input_on_drag);
ObjectTypeDB::bind_method(_MD("get_rid"),&CollisionObject::get_rid);
+ BIND_VMETHOD( MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"event"),PropertyInfo(Variant::VECTOR3,"click_pos"),PropertyInfo(Variant::VECTOR3,"click_normal"),PropertyInfo(Variant::INT,"shape_idx")));
+ ADD_SIGNAL( MethodInfo("input_event",PropertyInfo(Variant::INPUT_EVENT,"event"),PropertyInfo(Variant::VECTOR3,"click_pos"),PropertyInfo(Variant::VECTOR3,"click_normal"),PropertyInfo(Variant::INT,"shape_idx")));
+ ADD_SIGNAL( MethodInfo("mouse_enter"));
+ ADD_SIGNAL( MethodInfo("mouse_exit"));
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"input/capture_on_drag"),_SCS("set_capture_input_on_drag"),_SCS("get_capture_input_on_drag"));
}
@@ -269,10 +305,23 @@ CollisionObject::CollisionObject(RID p_rid, bool p_area) {
}
+void CollisionObject::set_capture_input_on_drag(bool p_capture) {
+
+ capture_input_on_drag=p_capture;
+
+}
+
+bool CollisionObject::get_capture_input_on_drag() const {
+
+ return capture_input_on_drag;
+}
+
CollisionObject::CollisionObject() {
+ capture_input_on_drag=false;
+
//owner=
//set_transform_notify(true);
diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h
index 54dc6508ab..afd73aa9cc 100644
--- a/scene/3d/collision_object.h
+++ b/scene/3d/collision_object.h
@@ -50,6 +50,7 @@ class CollisionObject : public Spatial {
};
+ bool capture_input_on_drag;
Vector<ShapeData> shapes;
@@ -67,6 +68,11 @@ protected:
bool _get(const StringName& p_name,Variant &r_ret) const;
void _get_property_list( List<PropertyInfo> *p_list) const;
static void _bind_methods();
+friend class Viewport;
+ virtual void _input_event(const InputEvent& p_input_event,const Vector3& p_pos, const Vector3& p_normal, int p_shape);
+ virtual void _mouse_enter();
+ virtual void _mouse_exit();
+
public:
@@ -81,6 +87,9 @@ public:
void set_shape_as_trigger(int p_shape_idx, bool p_trigger);
bool is_shape_set_as_trigger(int p_shape_idx) const;
+ void set_capture_input_on_drag(bool p_capture);
+ bool get_capture_input_on_drag() const;
+
_FORCE_INLINE_ RID get_rid() const { return rid; }
CollisionObject();
diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp
new file mode 100644
index 0000000000..5a613f360a
--- /dev/null
+++ b/scene/3d/collision_polygon.cpp
@@ -0,0 +1,206 @@
+#include "collision_polygon.h"
+
+#include "collision_object.h"
+#include "scene/resources/concave_polygon_shape.h"
+#include "scene/resources/convex_polygon_shape.h"
+
+void CollisionPolygon::_add_to_collision_object(Object *p_obj) {
+
+
+ CollisionObject *co = p_obj->cast_to<CollisionObject>();
+ ERR_FAIL_COND(!co);
+
+ if (polygon.size()==0)
+ return;
+
+ bool solids=build_mode==BUILD_SOLIDS;
+
+ Vector< Vector<Vector2> > decomp = Geometry::decompose_polygon(polygon);
+ if (decomp.size()==0)
+ return;
+
+ if (true || solids) {
+
+ //here comes the sun, lalalala
+ //decompose concave into multiple convex polygons and add them
+ for(int i=0;i<decomp.size();i++) {
+ Ref<ConvexPolygonShape> convex = memnew( ConvexPolygonShape );
+ DVector<Vector3> cp;
+ int cs = decomp[i].size();
+ cp.resize(cs*2);
+ {
+ DVector<Vector3>::Write w = cp.write();
+ int idx=0;
+ for(int j=0;j<cs;j++) {
+
+ Vector2 d = decomp[i][j];
+ w[idx++]=Vector3(d.x,d.y,depth*0.5);
+ w[idx++]=Vector3(d.x,d.y,-depth*0.5);
+ }
+ }
+
+ convex->set_points(cp);
+ co->add_shape(convex,get_transform());
+
+ }
+
+ } else {
+#if 0
+ Ref<ConcavePolygonShape> concave = memnew( ConcavePolygonShape );
+
+ DVector<Vector2> segments;
+ segments.resize(polygon.size()*2);
+ DVector<Vector2>::Write w=segments.write();
+
+ for(int i=0;i<polygon.size();i++) {
+ w[(i<<1)+0]=polygon[i];
+ w[(i<<1)+1]=polygon[(i+1)%polygon.size()];
+ }
+
+ w=DVector<Vector2>::Write();
+ concave->set_segments(segments);
+
+ co->add_shape(concave,get_transform());
+#endif
+ }
+
+
+ //co->add_shape(shape,get_transform());
+
+}
+
+void CollisionPolygon::_update_parent() {
+
+ Node *parent = get_parent();
+ if (!parent)
+ return;
+ CollisionObject *co = parent->cast_to<CollisionObject>();
+ if (!co)
+ return;
+ co->_update_shapes_from_children();
+}
+
+void CollisionPolygon::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (!is_inside_scene())
+ break;
+ _update_parent();
+
+ } break;
+#if 0
+ case NOTIFICATION_DRAW: {
+ for(int i=0;i<polygon.size();i++) {
+
+ Vector2 p = polygon[i];
+ Vector2 n = polygon[(i+1)%polygon.size()];
+ draw_line(p,n,Color(0,0.6,0.7,0.5),3);
+ }
+
+ Vector< Vector<Vector2> > decomp = Geometry::decompose_polygon(polygon);
+#define DEBUG_DECOMPOSE
+#ifdef DEBUG_DECOMPOSE
+ Color c(0.4,0.9,0.1);
+ for(int i=0;i<decomp.size();i++) {
+
+ c.set_hsv( Math::fmod(c.get_h() + 0.738,1),c.get_s(),c.get_v(),0.5);
+ draw_colored_polygon(decomp[i],c);
+ }
+#endif
+
+ } break;
+#endif
+ }
+}
+
+void CollisionPolygon::set_polygon(const Vector<Point2>& p_polygon) {
+
+ polygon=p_polygon;
+
+ for(int i=0;i<polygon.size();i++) {
+
+ Vector3 p1(polygon[i].x,polygon[i].y,depth*0.5);
+
+ if (i==0)
+ aabb=AABB(p1,Vector3());
+ else
+ aabb.expand_to(p1);
+
+ Vector3 p2(polygon[i].x,polygon[i].y,-depth*0.5);
+ aabb.expand_to(p2);
+
+
+ }
+ if (aabb==AABB()) {
+
+ aabb=AABB(Vector3(-1,-1,-1),Vector3(2,2,2));
+ } else {
+ aabb.pos-=aabb.size*0.3;
+ aabb.size+=aabb.size*0.6;
+ }
+ _update_parent();
+ update_gizmo();
+}
+
+Vector<Point2> CollisionPolygon::get_polygon() const {
+
+ return polygon;
+}
+
+void CollisionPolygon::set_build_mode(BuildMode p_mode) {
+
+ ERR_FAIL_INDEX(p_mode,2);
+ build_mode=p_mode;
+ _update_parent();
+}
+
+CollisionPolygon::BuildMode CollisionPolygon::get_build_mode() const{
+
+ return build_mode;
+}
+
+AABB CollisionPolygon::get_item_rect() const {
+
+ return aabb;
+}
+
+void CollisionPolygon::set_depth(float p_depth) {
+
+ depth=p_depth;
+ _update_parent();
+ update_gizmo();
+}
+
+float CollisionPolygon::get_depth() const {
+
+ return depth;
+}
+
+
+void CollisionPolygon::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("_add_to_collision_object"),&CollisionPolygon::_add_to_collision_object);
+ ObjectTypeDB::bind_method(_MD("set_polygon","polygon"),&CollisionPolygon::set_polygon);
+ ObjectTypeDB::bind_method(_MD("get_polygon"),&CollisionPolygon::get_polygon);
+
+ ObjectTypeDB::bind_method(_MD("set_depth","depth"),&CollisionPolygon::set_depth);
+ ObjectTypeDB::bind_method(_MD("get_depth"),&CollisionPolygon::get_depth);
+
+ ObjectTypeDB::bind_method(_MD("set_build_mode"),&CollisionPolygon::set_build_mode);
+ ObjectTypeDB::bind_method(_MD("get_build_mode"),&CollisionPolygon::get_build_mode);
+
+ ADD_PROPERTY( PropertyInfo(Variant::INT,"build_mode",PROPERTY_HINT_ENUM,"Solids,Triangles"),_SCS("set_build_mode"),_SCS("get_build_mode"));
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR2_ARRAY,"polygon"),_SCS("set_polygon"),_SCS("get_polygon"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"depth"),_SCS("set_depth"),_SCS("get_depth"));
+}
+
+CollisionPolygon::CollisionPolygon() {
+
+ aabb=AABB(Vector3(-1,-1,-1),Vector3(2,2,2));
+ build_mode=BUILD_SOLIDS;
+ depth=1.0;
+
+}
diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h
new file mode 100644
index 0000000000..efb3666778
--- /dev/null
+++ b/scene/3d/collision_polygon.h
@@ -0,0 +1,50 @@
+#ifndef COLLISION_POLYGON_H
+#define COLLISION_POLYGON_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/shape.h"
+
+
+
+class CollisionPolygon : public Spatial {
+
+ OBJ_TYPE(CollisionPolygon,Spatial);
+public:
+
+ enum BuildMode {
+ BUILD_SOLIDS,
+ BUILD_TRIANGLES,
+ };
+
+protected:
+
+
+ float depth;
+ AABB aabb;
+ BuildMode build_mode;
+ Vector<Point2> polygon;
+
+ void _add_to_collision_object(Object *p_obj);
+ void _update_parent();
+
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+ void set_build_mode(BuildMode p_mode);
+ BuildMode get_build_mode() const;
+
+ void set_depth(float p_depth);
+ float get_depth() const;
+
+ void set_polygon(const Vector<Point2>& p_polygon);
+ Vector<Point2> get_polygon() const;
+
+ virtual AABB get_item_rect() const;
+ CollisionPolygon();
+};
+
+VARIANT_ENUM_CAST( CollisionPolygon::BuildMode );
+#endif // COLLISION_POLYGON_H
diff --git a/scene/3d/light.cpp b/scene/3d/light.cpp
index 25f3b3d3a5..1efc74e672 100644
--- a/scene/3d/light.cpp
+++ b/scene/3d/light.cpp
@@ -460,7 +460,7 @@ void Light::_bind_methods() {
ADD_PROPERTY( PropertyInfo( Variant::BOOL, "params/enabled"), _SCS("set_enabled"), _SCS("is_enabled"));
- ADD_PROPERTY( PropertyInfo( Variant::INT, "params/bake_mode",PROPERTY_HINT_ENUM,"Disabled,Indirect,Full"), _SCS("set_bake_mode"), _SCS("get_bake_mode"));
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "params/bake_mode",PROPERTY_HINT_ENUM,"Disabled,Indirect,Indirect+Shadows,Full"), _SCS("set_bake_mode"), _SCS("get_bake_mode"));
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) {
@@ -477,7 +477,7 @@ void Light::_bind_methods() {
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/darkening", PROPERTY_HINT_RANGE, "0,1,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_PROPERTYI( PropertyInfo( Variant::REAL, "shadow/esm_multiplier", PROPERTY_HINT_RANGE, "1.0,512.0,0.1"), _SCS("set_parameter"), _SCS("get_parameter"), PARAM_SHADOW_ESM_MULTIPLIER);
@@ -498,6 +498,12 @@ void Light::_bind_methods() {
BIND_CONSTANT( COLOR_DIFFUSE );
BIND_CONSTANT( COLOR_SPECULAR );
+ BIND_CONSTANT( BAKE_MODE_DISABLED );
+ BIND_CONSTANT( BAKE_MODE_INDIRECT );
+ BIND_CONSTANT( BAKE_MODE_INDIRECT_AND_SHADOWS );
+ BIND_CONSTANT( BAKE_MODE_FULL );
+
+
}
diff --git a/scene/3d/light.h b/scene/3d/light.h
index 6b1ea2d455..9fdd7295dc 100644
--- a/scene/3d/light.h
+++ b/scene/3d/light.h
@@ -69,6 +69,7 @@ public:
BAKE_MODE_DISABLED,
BAKE_MODE_INDIRECT,
+ BAKE_MODE_INDIRECT_AND_SHADOWS,
BAKE_MODE_FULL
};
diff --git a/scene/3d/navigation.cpp b/scene/3d/navigation.cpp
new file mode 100644
index 0000000000..d2abdad079
--- /dev/null
+++ b/scene/3d/navigation.cpp
@@ -0,0 +1,603 @@
+#include "navigation.h"
+
+void Navigation::_navmesh_link(int p_id) {
+
+ ERR_FAIL_COND(!navmesh_map.has(p_id));
+ NavMesh &nm=navmesh_map[p_id];
+ ERR_FAIL_COND(nm.linked);
+
+ print_line("LINK");
+
+ DVector<Vector3> vertices=nm.navmesh->get_vertices();
+ int len = vertices.size();
+ if (len==0)
+ return;
+
+ DVector<Vector3>::Read r=vertices.read();
+
+ for(int i=0;i<nm.navmesh->get_polygon_count();i++) {
+
+ //build
+
+ List<Polygon>::Element *P=nm.polygons.push_back(Polygon());
+ Polygon &p=P->get();
+
+ Vector<int> poly = nm.navmesh->get_polygon(i);
+ int plen=poly.size();
+ const int *indices=poly.ptr();
+ bool valid=true;
+ p.edges.resize(plen);
+
+ Vector3 center;
+
+ for(int j=0;j<plen;j++) {
+
+ int idx = indices[j];
+ if (idx<0 || idx>=len) {
+ valid=false;
+ break;
+ }
+
+ Polygon::Edge e;
+ Vector3 ep=nm.xform.xform(r[idx]);
+ center+=ep;
+ e.point=_get_point(ep);
+ p.edges[j]=e;
+ }
+
+ if (!valid) {
+ nm.polygons.pop_back();
+ ERR_CONTINUE(!valid);
+ continue;
+ }
+
+ p.center=center/plen;
+
+ //connect
+
+ for(int j=0;j<plen;j++) {
+
+ int next = (j+1)%plen;
+ EdgeKey ek(p.edges[j].point,p.edges[next].point);
+
+ Map<EdgeKey,Connection>::Element *C=connections.find(ek);
+ if (!C) {
+
+ Connection c;
+ c.A=&p;
+ c.A_edge=j;
+ c.B=NULL;
+ c.B_edge=-1;
+ connections[ek]=c;
+ } else {
+
+ if (C->get().B!=NULL) {
+ print_line(String()+_get_vertex(ek.a)+" -> "+_get_vertex(ek.b));
+ }
+ ERR_CONTINUE(C->get().B!=NULL); //wut
+
+ C->get().B=&p;
+ C->get().B_edge=j;
+ C->get().A->edges[C->get().A_edge].C=&p;
+ C->get().A->edges[C->get().A_edge].C_edge=j;;
+ p.edges[j].C=C->get().A;
+ p.edges[j].C_edge=C->get().A_edge;
+ //connection successful.
+ }
+ }
+ }
+
+ nm.linked=true;
+
+}
+
+
+void Navigation::_navmesh_unlink(int p_id) {
+
+ ERR_FAIL_COND(!navmesh_map.has(p_id));
+ NavMesh &nm=navmesh_map[p_id];
+ ERR_FAIL_COND(!nm.linked);
+
+ print_line("UNLINK");
+
+ for (List<Polygon>::Element *E=nm.polygons.front();E;E=E->next()) {
+
+
+ Polygon &p=E->get();
+
+ int ec = p.edges.size();
+ Polygon::Edge *edges=p.edges.ptr();
+
+ for(int i=0;i<ec;i++) {
+ int next = (i+1)%ec;
+
+ EdgeKey ek(edges[i].point,edges[next].point);
+ Map<EdgeKey,Connection>::Element *C=connections.find(ek);
+ ERR_CONTINUE(!C);
+ if (C->get().B) {
+ //disconnect
+
+ C->get().B->edges[C->get().B_edge].C=NULL;
+ C->get().B->edges[C->get().B_edge].C_edge=-1;
+ C->get().A->edges[C->get().A_edge].C=NULL;
+ C->get().A->edges[C->get().A_edge].C_edge=-1;
+
+ if (C->get().A==&E->get()) {
+
+ C->get().A=C->get().B;
+ C->get().A_edge=C->get().B_edge;
+ }
+ C->get().B=NULL;
+ C->get().B_edge=-1;
+
+ } else {
+ connections.erase(C);
+ //erase
+ }
+ }
+ }
+
+ nm.polygons.clear();
+
+ nm.linked=false;
+
+
+}
+
+
+int Navigation::navmesh_create(const Ref<NavigationMesh>& p_mesh,const Transform& p_xform) {
+
+ int id = last_id++;
+ NavMesh nm;
+ nm.linked=false;
+ nm.navmesh=p_mesh;
+ nm.xform=p_xform;
+ navmesh_map[id]=nm;
+
+ _navmesh_link(id);
+
+ return id;
+}
+
+void Navigation::navmesh_set_transform(int p_id, const Transform& p_xform){
+
+ ERR_FAIL_COND(!navmesh_map.has(p_id));
+ NavMesh &nm=navmesh_map[p_id];
+ if (nm.xform==p_xform)
+ return; //bleh
+ _navmesh_unlink(p_id);
+ nm.xform=p_xform;
+ _navmesh_link(p_id);
+
+
+
+}
+void Navigation::navmesh_remove(int p_id){
+
+ ERR_FAIL_COND(!navmesh_map.has(p_id));
+ _navmesh_unlink(p_id);
+ navmesh_map.erase(p_id);
+
+}
+
+Vector<Vector3> Navigation::get_simple_path(const Vector3& p_start, const Vector3& p_end, bool p_optimize) {
+
+
+ Polygon *begin_poly=NULL;
+ Polygon *end_poly=NULL;
+ Vector3 begin_point;
+ Vector3 end_point;
+ float begin_d=1e20;
+ float end_d=1e20;
+
+
+ for (Map<int,NavMesh>::Element*E=navmesh_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ for(int i=2;i<p.edges.size();i++) {
+
+ Face3 f(_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point));
+ Vector3 spoint = f.get_closest_point_to(p_start);
+ float dpoint = spoint.distance_to(p_start);
+ if (dpoint<begin_d) {
+ begin_d=dpoint;
+ begin_poly=&p;
+ begin_point=spoint;
+ }
+
+ spoint = f.get_closest_point_to(p_end);
+ dpoint = spoint.distance_to(p_end);
+ if (dpoint<end_d) {
+ end_d=dpoint;
+ end_poly=&p;
+ end_point=spoint;
+ }
+ }
+
+ p.prev_edge=-1;
+ }
+ }
+
+ if (!begin_poly || !end_poly) {
+
+ //print_line("No Path Path");
+ return Vector<Vector3>(); //no path
+ }
+
+ if (begin_poly==end_poly) {
+
+ Vector<Vector3> path;
+ path.resize(2);
+ path[0]=begin_point;
+ path[1]=end_point;
+ //print_line("Direct Path");
+ return path;
+ }
+
+
+ bool found_route=false;
+
+ List<Polygon*> open_list;
+
+ for(int i=0;i<begin_poly->edges.size();i++) {
+
+ if (begin_poly->edges[i].C) {
+
+ begin_poly->edges[i].C->prev_edge=begin_poly->edges[i].C_edge;
+ begin_poly->edges[i].C->distance=begin_poly->center.distance_to(begin_poly->edges[i].C->center);
+ open_list.push_back(begin_poly->edges[i].C);
+
+ if (begin_poly->edges[i].C==end_poly) {
+ found_route=true;
+ }
+ }
+ }
+
+
+ while(!found_route) {
+
+ if (open_list.size()==0) {
+ // print_line("NOU OPEN LIST");
+ break;
+ }
+ //check open list
+
+ List<Polygon*>::Element *least_cost_poly=NULL;
+ float least_cost=1e30;
+
+ //this could be faster (cache previous results)
+ for (List<Polygon*>::Element *E=open_list.front();E;E=E->next()) {
+
+ Polygon *p=E->get();
+
+
+ float cost=p->distance;
+ cost+=p->center.distance_to(end_point);
+
+ if (cost<least_cost) {
+
+ least_cost_poly=E;
+ least_cost=cost;
+ }
+ }
+
+
+ Polygon *p=least_cost_poly->get();
+ //open the neighbours for search
+
+ for(int i=0;i<p->edges.size();i++) {
+
+
+ Polygon::Edge &e=p->edges[i];
+
+ if (!e.C)
+ continue;
+
+ float distance = p->center.distance_to(e.C->center) + p->distance;
+
+ if (e.C->prev_edge!=-1) {
+ //oh this was visited already, can we win the cost?
+
+ if (e.C->distance>distance) {
+
+ e.C->prev_edge=e.C_edge;
+ e.C->distance=distance;
+ }
+ } else {
+ //add to open neighbours
+
+ e.C->prev_edge=e.C_edge;
+ e.C->distance=distance;
+ open_list.push_back(e.C);
+
+ if (e.C==end_poly) {
+ //oh my reached end! stop algorithm
+ found_route=true;
+ break;
+
+ }
+
+ }
+ }
+
+ if (found_route)
+ break;
+
+ open_list.erase(least_cost_poly);
+ }
+
+ if (found_route) {
+
+ Vector<Vector3> path;
+
+ if (p_optimize) {
+ //string pulling
+
+ Polygon *apex_poly=end_poly;
+ Vector3 apex_point=end_point;
+ Vector3 portal_left=apex_point;
+ Vector3 portal_right=apex_point;
+ Polygon *left_poly=end_poly;
+ Polygon *right_poly=end_poly;
+ Polygon *p=end_poly;
+ path.push_back(end_point);
+
+ while(p) {
+
+ Vector3 left;
+ Vector3 right;
+
+#define CLOCK_TANGENT(m_a,m_b,m_c) ( ((m_a)-(m_c)).cross((m_a)-(m_b)) )
+
+ if (p==begin_poly) {
+ left=begin_point;
+ right=begin_point;
+ } else {
+ int prev = p->prev_edge;
+ int prev_n = (p->prev_edge+1)%p->edges.size();
+ left = _get_vertex(p->edges[prev].point);
+ right = _get_vertex(p->edges[prev_n].point);
+
+ if (CLOCK_TANGENT(apex_point,left,(left+right)*0.5).dot(up) < 0){
+ SWAP(left,right);
+ }
+ }
+
+ bool skip=false;
+
+
+ if (CLOCK_TANGENT(apex_point,portal_left,left).dot(up) >= 0){
+ //process
+ if (portal_left==apex_point || CLOCK_TANGENT(apex_point,left,portal_right).dot(up) > 0) {
+ left_poly=p;
+ portal_left=left;
+ } else {
+
+ apex_point=portal_right;
+ p=right_poly;
+ left_poly=p;
+ portal_left=apex_point;
+ portal_right=apex_point;
+ path.push_back(apex_point);
+ skip=true;
+ }
+ }
+
+ if (!skip && CLOCK_TANGENT(apex_point,portal_right,right).dot(up) <= 0){
+ //process
+ if (portal_right==apex_point || CLOCK_TANGENT(apex_point,right,portal_left).dot(up) < 0) {
+ right_poly=p;
+ portal_right=right;
+ } else {
+
+ apex_point=portal_left;
+ p=left_poly;
+ right_poly=p;
+ portal_right=apex_point;
+ portal_left=apex_point;
+ path.push_back(apex_point);
+ }
+ }
+
+ if (p!=begin_poly)
+ p=p->edges[p->prev_edge].C;
+ else
+ p=NULL;
+
+ }
+
+ if (path[path.size()-1]!=begin_point)
+ path.push_back(begin_point);
+
+ path.invert();
+
+
+
+
+ } else {
+ //midpoints
+ Polygon *p=end_poly;
+
+ path.push_back(end_point);
+ while(true) {
+ int prev = p->prev_edge;
+ int prev_n = (p->prev_edge+1)%p->edges.size();
+ Vector3 point = (_get_vertex(p->edges[prev].point) + _get_vertex(p->edges[prev_n].point))*0.5;
+ path.push_back(point);
+ p = p->edges[prev].C;
+ if (p==begin_poly)
+ break;
+ }
+
+ path.push_back(begin_point);
+
+
+ path.invert();;
+ }
+
+ return path;
+ }
+
+
+ return Vector<Vector3>();
+
+}
+
+Vector3 Navigation::get_closest_point_to_segment(const Vector3& p_from,const Vector3& p_to) {
+
+
+ bool use_collision=false;
+ Vector3 closest_point;
+ float closest_point_d=1e20;
+
+ for (Map<int,NavMesh>::Element*E=navmesh_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ for(int i=2;i<p.edges.size();i++) {
+
+ Face3 f(_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point));
+ Vector3 inters;
+ if (f.intersects_segment(p_from,p_to,&inters)) {
+
+ if (!use_collision) {
+ closest_point=inters;
+ use_collision=true;
+ closest_point_d=p_from.distance_to(inters);
+ } else if (closest_point_d > inters.distance_to(p_from)){
+
+ closest_point=inters;
+ closest_point_d=p_from.distance_to(inters);
+ }
+ }
+ }
+
+ if (!use_collision) {
+
+ for(int i=0;i<p.edges.size();i++) {
+
+ Vector3 a,b;
+
+ Geometry::get_closest_points_between_segments(p_from,p_to,_get_vertex(p.edges[i].point),_get_vertex(p.edges[(i+1)%p.edges.size()].point),a,b);
+
+ float d = a.distance_to(b);
+ if (d<closest_point_d) {
+
+ closest_point_d=d;
+ closest_point=b;
+ }
+
+ }
+ }
+ }
+ }
+
+ return closest_point;
+}
+
+Vector3 Navigation::get_closest_point(const Vector3& p_point) {
+
+ Vector3 closest_point;
+ float closest_point_d=1e20;
+
+ for (Map<int,NavMesh>::Element*E=navmesh_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ for(int i=2;i<p.edges.size();i++) {
+
+ Face3 f(_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point));
+ Vector3 inters = f.get_closest_point_to(p_point);
+ float d = inters.distance_to(p_point);
+ if (d<closest_point_d) {
+ closest_point=inters;
+ closest_point_d=d;
+ }
+ }
+ }
+ }
+
+ return closest_point;
+
+}
+
+Vector3 Navigation::get_closest_point_normal(const Vector3& p_point){
+
+ Vector3 closest_point;
+ Vector3 closest_normal;
+ float closest_point_d=1e20;
+
+ for (Map<int,NavMesh>::Element*E=navmesh_map.front();E;E=E->next()) {
+
+ if (!E->get().linked)
+ continue;
+ for(List<Polygon>::Element *F=E->get().polygons.front();F;F=F->next()) {
+
+ Polygon &p=F->get();
+ for(int i=2;i<p.edges.size();i++) {
+
+ Face3 f(_get_vertex(p.edges[0].point),_get_vertex(p.edges[i-1].point),_get_vertex(p.edges[i].point));
+ Vector3 inters = f.get_closest_point_to(p_point);
+ float d = inters.distance_to(p_point);
+ if (d<closest_point_d) {
+ closest_point=inters;
+ closest_point_d=d;
+ closest_normal=f.get_plane().normal;
+ }
+ }
+ }
+ }
+
+ return closest_normal;
+
+}
+
+
+void Navigation::set_up_vector(const Vector3& p_up) {
+
+
+ up=p_up;
+}
+
+Vector3 Navigation::get_up_vector() const{
+
+ return up;
+}
+
+
+void Navigation::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("navmesh_create","mesh:NavigationMesh","xform"),&Navigation::navmesh_create);
+ ObjectTypeDB::bind_method(_MD("navmesh_set_transform","id","xform"),&Navigation::navmesh_set_transform);
+ ObjectTypeDB::bind_method(_MD("navmesh_remove","id"),&Navigation::navmesh_remove);
+
+ ObjectTypeDB::bind_method(_MD("get_simple_path","start","end","optimize"),&Navigation::get_simple_path,DEFVAL(true));
+ ObjectTypeDB::bind_method(_MD("get_closest_point_to_segment","start","end"),&Navigation::get_closest_point_to_segment);
+ ObjectTypeDB::bind_method(_MD("get_closest_point","to_point"),&Navigation::get_closest_point);
+ ObjectTypeDB::bind_method(_MD("get_closest_point_normal","to_point"),&Navigation::get_closest_point_normal);
+
+ ObjectTypeDB::bind_method(_MD("set_up_vector","up"),&Navigation::set_up_vector);
+ ObjectTypeDB::bind_method(_MD("get_up_vector"),&Navigation::get_up_vector);
+
+ ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"up_vector"),_SCS("set_up_vector"),_SCS("get_up_vector"));
+}
+
+Navigation::Navigation() {
+
+ ERR_FAIL_COND( sizeof(Point)!=8 );
+ cell_size=0.01; //one centimeter
+ last_id=1;
+ up=Vector3(0,1,0);
+}
+
+
diff --git a/scene/3d/navigation.h b/scene/3d/navigation.h
new file mode 100644
index 0000000000..e8a97a6591
--- /dev/null
+++ b/scene/3d/navigation.h
@@ -0,0 +1,139 @@
+#ifndef NAVIGATION_H
+#define NAVIGATION_H
+
+#include "scene/3d/spatial.h"
+#include "scene/3d/navigation_mesh.h"
+
+class Navigation : public Spatial {
+
+ OBJ_TYPE( Navigation, Spatial);
+
+
+ union Point {
+
+ struct {
+ int64_t x:21;
+ int64_t y:22;
+ int64_t z:21;
+ };
+
+ uint64_t key;
+ bool operator<(const Point& p_key) const { return key < p_key.key; }
+ };
+
+
+ struct EdgeKey {
+
+ Point a;
+ Point b;
+
+ bool operator<(const EdgeKey& p_key) const {
+ return (a.key==p_key.a.key)?(b.key<p_key.b.key):(a.key<p_key.a.key);
+ };
+
+ EdgeKey(const Point& p_a=Point(),const Point& p_b=Point()) {
+ a=p_a;
+ b=p_b;
+ if (a.key > b.key) {
+ SWAP(a,b);
+ }
+ }
+ };
+
+
+
+ struct Polygon {
+
+ struct Edge {
+ Point point;
+ Polygon *C; //connection
+ int C_edge;
+ Edge() { C=NULL; C_edge=-1; }
+ };
+
+ Vector<Edge> edges;
+
+ Vector3 center;
+
+ float distance;
+ int prev_edge;
+ };
+
+
+ struct Connection {
+
+ Polygon *A;
+ int A_edge;
+ Polygon *B;
+ int B_edge;
+ Connection() { A=NULL; B=NULL; A_edge=-1; B_edge=-1;}
+ };
+
+ Map<EdgeKey,Connection> connections;
+
+
+ struct NavMesh {
+
+ Transform xform;
+ bool linked;
+ Ref<NavigationMesh> navmesh;
+ List<Polygon> polygons;
+
+ };
+
+
+
+ _FORCE_INLINE_ Point _get_point(const Vector3& p_pos) const {
+
+ int x = int(Math::floor(p_pos.x/cell_size));
+ int y = int(Math::floor(p_pos.y/cell_size));
+ int z = int(Math::floor(p_pos.z/cell_size));
+
+ Point p;
+ p.key=0;
+ p.x=x;
+ p.y=y;
+ p.z=z;
+ return p;
+
+ }
+
+ _FORCE_INLINE_ Vector3 _get_vertex(const Point& p_point) const {
+
+ return Vector3(p_point.x,p_point.y,p_point.z)*cell_size;
+ }
+
+
+
+ void _navmesh_link(int p_id);
+ void _navmesh_unlink(int p_id);
+
+ float cell_size;
+ Map<int,NavMesh> navmesh_map;
+ int last_id;
+
+ Vector3 up;
+
+protected:
+
+ static void _bind_methods();
+
+public:
+
+ void set_up_vector(const Vector3& p_up);
+ Vector3 get_up_vector() const;
+
+ //API should be as dynamic as possible
+ int navmesh_create(const Ref<NavigationMesh>& p_mesh,const Transform& p_xform);
+ void navmesh_set_transform(int p_id, const Transform& p_xform);
+ void navmesh_remove(int p_id);
+
+ Vector<Vector3> get_simple_path(const Vector3& p_start, const Vector3& p_end,bool p_optimize=true);
+ Vector3 get_closest_point_to_segment(const Vector3& p_from,const Vector3& p_to);
+ Vector3 get_closest_point(const Vector3& p_point);
+ Vector3 get_closest_point_normal(const Vector3& p_point);
+
+ Navigation();
+};
+
+#endif // NAVIGATION_H
diff --git a/scene/3d/navigation_agent.cpp b/scene/3d/navigation_agent.cpp
new file mode 100644
index 0000000000..9b304e45ec
--- /dev/null
+++ b/scene/3d/navigation_agent.cpp
@@ -0,0 +1,5 @@
+#include "navigation_agent.h"
+
+NavigationAgent::NavigationAgent()
+{
+}
diff --git a/scene/3d/navigation_agent.h b/scene/3d/navigation_agent.h
new file mode 100644
index 0000000000..baceb693a5
--- /dev/null
+++ b/scene/3d/navigation_agent.h
@@ -0,0 +1,10 @@
+#ifndef NAVIGATION_AGENT_H
+#define NAVIGATION_AGENT_H
+
+class NavigationAgent
+{
+public:
+ NavigationAgent();
+};
+
+#endif // NAVIGATION_AGENT_H
diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp
new file mode 100644
index 0000000000..cf2e22a573
--- /dev/null
+++ b/scene/3d/navigation_mesh.cpp
@@ -0,0 +1,237 @@
+#include "navigation_mesh.h"
+#include "navigation.h"
+
+
+void NavigationMesh::create_from_mesh(const Ref<Mesh>& p_mesh) {
+
+
+ vertices=DVector<Vector3>();
+ clear_polygons();
+
+ for(int i=0;i<p_mesh->get_surface_count();i++) {
+
+ if (p_mesh->surface_get_primitive_type(i)!=Mesh::PRIMITIVE_TRIANGLES)
+ continue;
+ Array arr = p_mesh->surface_get_arrays(i);
+ DVector<Vector3> varr = arr[Mesh::ARRAY_VERTEX];
+ DVector<int> iarr = arr[Mesh::ARRAY_INDEX];
+ if (varr.size()==0 || iarr.size()==0)
+ continue;
+
+ int from = vertices.size();
+ vertices.append_array(varr);
+ int rlen = iarr.size();
+ DVector<int>::Read r = iarr.read();
+
+ for(int j=0;j<rlen;j+=3) {
+ Vector<int> vi;
+ vi.resize(3);
+ vi[0]=r[j+0]+from;
+ vi[1]=r[j+1]+from;
+ vi[2]=r[j+2]+from;
+
+ add_polygon(vi);
+ }
+ }
+}
+
+void NavigationMesh::set_vertices(const DVector<Vector3>& p_vertices) {
+
+ vertices=p_vertices;
+}
+
+DVector<Vector3> NavigationMesh::get_vertices() const{
+
+ return vertices;
+}
+
+
+void NavigationMesh::_set_polygons(const Array& p_array) {
+
+ polygons.resize(p_array.size());
+ for(int i=0;i<p_array.size();i++) {
+ polygons[i].indices=p_array[i];
+ }
+}
+
+Array NavigationMesh::_get_polygons() const {
+
+ Array ret;
+ ret.resize(polygons.size());
+ for(int i=0;i<ret.size();i++) {
+ ret[i]=polygons[i].indices;
+ }
+
+ return ret;
+}
+
+
+void NavigationMesh::add_polygon(const Vector<int>& p_polygon){
+
+ Polygon polygon;
+ polygon.indices=p_polygon;
+ polygons.push_back(polygon);
+
+}
+int NavigationMesh::get_polygon_count() const{
+
+ return polygons.size();
+}
+Vector<int> NavigationMesh::get_polygon(int p_idx){
+
+ ERR_FAIL_INDEX_V(p_idx,polygons.size(),Vector<int>());
+ return polygons[p_idx].indices;
+}
+void NavigationMesh::clear_polygons(){
+
+ polygons.clear();
+}
+
+void NavigationMesh::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_vertices","vertices"),&NavigationMesh::set_vertices);
+ ObjectTypeDB::bind_method(_MD("get_vertices"),&NavigationMesh::get_vertices);
+
+ ObjectTypeDB::bind_method(_MD("add_polygon","polygon"),&NavigationMesh::add_polygon);
+ ObjectTypeDB::bind_method(_MD("get_polygon_count"),&NavigationMesh::get_polygon_count);
+ ObjectTypeDB::bind_method(_MD("get_polygon","idx"),&NavigationMesh::get_polygon);
+ ObjectTypeDB::bind_method(_MD("clear_polygons"),&NavigationMesh::clear_polygons);
+
+ ObjectTypeDB::bind_method(_MD("_set_polygons","polygons"),&NavigationMesh::_set_polygons);
+ ObjectTypeDB::bind_method(_MD("_get_polygons"),&NavigationMesh::_get_polygons);
+
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR3_ARRAY,"vertices",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_vertices"),_SCS("get_vertices"));
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY,"polygons",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_polygons"),_SCS("_get_polygons"));
+}
+
+NavigationMesh::NavigationMesh() {
+
+}
+
+void NavigationMeshInstance::set_enabled(bool p_enabled) {
+
+ if (enabled==p_enabled)
+ return;
+ enabled=p_enabled;
+
+ if (!is_inside_scene())
+ return;
+
+ if (!enabled) {
+
+ if (nav_id!=-1) {
+ navigation->navmesh_remove(nav_id);
+ nav_id=-1;
+ }
+ } else {
+
+ if (navigation) {
+
+ if (navmesh.is_valid()) {
+
+ nav_id = navigation->navmesh_create(navmesh,get_relative_transform(navigation));
+ }
+ }
+
+ }
+
+ update_gizmo();
+}
+
+bool NavigationMeshInstance::is_enabled() const {
+
+
+ return enabled;
+}
+
+
+/////////////////////////////
+
+
+void NavigationMeshInstance::_notification(int p_what) {
+
+
+ switch(p_what) {
+ case NOTIFICATION_ENTER_SCENE: {
+
+ Spatial *c=this;
+ while(c) {
+
+ navigation=c->cast_to<Navigation>();
+ if (navigation) {
+
+ if (enabled && navmesh.is_valid()) {
+
+ nav_id = navigation->navmesh_create(navmesh,get_relative_transform(navigation));
+ }
+ break;
+ }
+
+ c=c->get_parent_spatial();
+ }
+
+ } break;
+ case NOTIFICATION_TRANSFORM_CHANGED: {
+
+ if (navigation && nav_id!=-1) {
+ navigation->navmesh_set_transform(nav_id,get_relative_transform(navigation));
+ }
+
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ if (navigation) {
+
+ if (nav_id!=-1) {
+ navigation->navmesh_remove(nav_id);
+ nav_id=-1;
+ }
+ }
+ navigation=NULL;
+ } break;
+ }
+}
+
+
+void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh>& p_navmesh) {
+
+ if (p_navmesh==navmesh)
+ return;
+
+ if (navigation && nav_id!=-1) {
+ navigation->navmesh_remove(nav_id);
+ nav_id=-1;
+ }
+ navmesh=p_navmesh;
+
+ if (navigation && navmesh.is_valid() && enabled) {
+ nav_id = navigation->navmesh_create(navmesh,get_relative_transform(navigation));
+ }
+ update_gizmo();
+
+}
+
+Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const{
+
+ return navmesh;
+}
+
+void NavigationMeshInstance::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_navigation_mesh","navmesh"),&NavigationMeshInstance::set_navigation_mesh);
+ ObjectTypeDB::bind_method(_MD("get_navigation_mesh"),&NavigationMeshInstance::get_navigation_mesh);
+
+ ObjectTypeDB::bind_method(_MD("set_enabled","enabled"),&NavigationMeshInstance::set_enabled);
+ ObjectTypeDB::bind_method(_MD("is_enabled"),&NavigationMeshInstance::is_enabled);
+
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"navmesh",PROPERTY_HINT_RESOURCE_TYPE,"NavigationMesh"),_SCS("set_navigation_mesh"),_SCS("get_navigation_mesh"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled"));
+}
+
+NavigationMeshInstance::NavigationMeshInstance() {
+
+ navigation=NULL;
+ nav_id=-1;
+ enabled=true;
+
+}
diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h
new file mode 100644
index 0000000000..fccf405f9d
--- /dev/null
+++ b/scene/3d/navigation_mesh.h
@@ -0,0 +1,68 @@
+#ifndef NAVIGATION_MESH_H
+#define NAVIGATION_MESH_H
+
+#include "scene/3d/spatial.h"
+#include "scene/resources/mesh.h"
+
+
+class NavigationMesh : public Resource {
+
+ OBJ_TYPE( NavigationMesh, Resource );
+
+ DVector<Vector3> vertices;
+ struct Polygon {
+ Vector<int> indices;
+ };
+ Vector<Polygon> polygons;
+
+protected:
+
+ static void _bind_methods();
+
+ void _set_polygons(const Array& p_array);
+ Array _get_polygons() const;
+public:
+
+ void create_from_mesh(const Ref<Mesh>& p_mesh);
+
+ void set_vertices(const DVector<Vector3>& p_vertices);
+ DVector<Vector3> get_vertices() const;
+
+ void add_polygon(const Vector<int>& p_polygon);
+ int get_polygon_count() const;
+ Vector<int> get_polygon(int p_idx);
+ void clear_polygons();
+
+ NavigationMesh();
+};
+
+
+class Navigation;
+
+class NavigationMeshInstance : public Spatial {
+
+ OBJ_TYPE(NavigationMeshInstance,Spatial);
+
+ bool enabled;
+ int nav_id;
+ Navigation *navigation;
+ Ref<NavigationMesh> navmesh;
+protected:
+
+ void _notification(int p_what);
+ static void _bind_methods();
+public:
+
+
+
+ void set_enabled(bool p_enabled);
+ bool is_enabled() const;
+
+ void set_navigation_mesh(const Ref<NavigationMesh>& p_navmesh);
+ Ref<NavigationMesh> get_navigation_mesh() const;
+
+ NavigationMeshInstance();
+};
+
+
+#endif // NAVIGATION_MESH_H
diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp
index 2a1a5972a9..f5e3ad66ee 100644
--- a/scene/3d/physics_body.cpp
+++ b/scene/3d/physics_body.cpp
@@ -43,160 +43,125 @@ void PhysicsBody::_notification(int p_what) {
*/
}
-PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) {
-
-
+Vector3 PhysicsBody::get_linear_velocity() const {
+ return Vector3();
}
+Vector3 PhysicsBody::get_angular_velocity() const {
-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);
-
+ return Vector3();
}
-void StaticBody::set_constant_angular_velocity(const Vector3& p_vel) {
+float PhysicsBody::get_inverse_mass() const {
- constant_angular_velocity=p_vel;
- PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity);
+ return 0;
}
-Vector3 StaticBody::get_constant_linear_velocity() const {
- return constant_linear_velocity;
-}
-Vector3 StaticBody::get_constant_angular_velocity() const {
+void PhysicsBody::set_layer_mask(uint32_t p_mask) {
- return constant_angular_velocity;
+ layer_mask=p_mask;
+ PhysicsServer::get_singleton()->body_set_layer_mask(get_rid(),p_mask);
}
+uint32_t PhysicsBody::get_layer_mask() const {
-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;
-
+ return layer_mask;
+}
+void PhysicsBody::_bind_methods() {
+ ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&PhysicsBody::set_layer_mask);
+ ObjectTypeDB::bind_method(_MD("get_layer_mask"),&PhysicsBody::get_layer_mask);
+ ADD_PROPERTY(PropertyInfo(Variant::INT,"layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask"));
}
-void StaticBody::_update_xform() {
- if (!pre_xform || !pending)
- return;
+PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) {
- setting=true;
+ layer_mask=1;
+}
- 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);
+void StaticBody::set_friction(real_t p_friction){
- PhysicsServer::get_singleton()->body_static_simulate_motion(get_rid(),new_xform); //then simulate motion!
+ ERR_FAIL_COND(p_friction<0 || p_friction>1);
- setting=false;
- pending=false;
+ friction=p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction);
}
+real_t StaticBody::get_friction() const{
-void StaticBody::_notification(int p_what) {
+ return friction;
+}
- switch(p_what) {
+void StaticBody::set_bounce(real_t p_bounce){
- case NOTIFICATION_ENTER_SCENE: {
+ ERR_FAIL_COND(p_bounce<0 || p_bounce>1);
- if (pre_xform)
- *pre_xform = get_global_transform();
- pending=false;
- } break;
- case NOTIFICATION_TRANSFORM_CHANGED: {
+ bounce=p_bounce;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_BOUNCE,bounce);
- if (simulating_motion && !pending && is_inside_scene() && !setting && !get_scene()->is_editor_hint()) {
+}
+real_t StaticBody::get_bounce() const{
- call_deferred(SceneStringNames::get_singleton()->_update_xform);
- pending=true;
- }
+ return bounce;
+}
- } break;
- }
+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_simulate_motion(bool p_enable) {
+void StaticBody::set_constant_angular_velocity(const Vector3& p_vel) {
- if (p_enable==simulating_motion)
- return;
- simulating_motion=p_enable;
+ constant_angular_velocity=p_vel;
+ PhysicsServer::get_singleton()->body_set_state(get_rid(),PhysicsServer::BODY_STATE_ANGULAR_VELOCITY,constant_angular_velocity);
+}
- 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");
+Vector3 StaticBody::get_constant_linear_velocity() const {
- } else {
- memdelete( pre_xform );
- pre_xform=NULL;
- PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),NULL,StringName());
- pending=false;
- }
+ return constant_linear_velocity;
}
+Vector3 StaticBody::get_constant_angular_velocity() const {
-bool StaticBody::is_simulating_motion() const {
-
- return simulating_motion;
+ return constant_angular_velocity;
}
+
+
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"));
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&StaticBody::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&StaticBody::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("set_bounce","bounce"),&StaticBody::set_bounce);
+ ObjectTypeDB::bind_method(_MD("get_bounce"),&StaticBody::get_bounce);
+
+ 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::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;
-
+ bounce=0;
+ friction=1;
}
StaticBody::~StaticBody() {
- if (pre_xform)
- memdelete(pre_xform);
- //if (query.is_valid())
- // PhysicsServer::get_singleton()->free(query);
+
}
@@ -754,4 +719,418 @@ RigidBody::~RigidBody() {
}
+//////////////////////////////////////////////////////
+//////////////////////////
+
+
+Variant KinematicBody::_get_collider() const {
+
+ ObjectID oid=get_collider();
+ if (oid==0)
+ return Variant();
+ Object *obj = ObjectDB::get_instance(oid);
+ if (!obj)
+ return Variant();
+
+ Reference *ref = obj->cast_to<Reference>();
+ if (ref) {
+ return Ref<Reference>(ref);
+ }
+
+ return obj;
+}
+
+
+bool KinematicBody::_ignores_mode(PhysicsServer::BodyMode p_mode) const {
+
+ switch(p_mode) {
+ case PhysicsServer::BODY_MODE_STATIC: return !collide_static;
+ case PhysicsServer::BODY_MODE_KINEMATIC: return !collide_kinematic;
+ case PhysicsServer::BODY_MODE_RIGID: return !collide_rigid;
+ case PhysicsServer::BODY_MODE_CHARACTER: return !collide_character;
+ }
+
+ return true;
+}
+
+Vector3 KinematicBody::move(const Vector3& p_motion) {
+
+ //give me back regular physics engine logic
+ //this is madness
+ //and most people using this function will think
+ //what it does is simpler than using physics
+ //this took about a week to get right..
+ //but is it right? who knows at this point..
+
+
+ colliding=false;
+ ERR_FAIL_COND_V(!is_inside_scene(),Vector3());
+ PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(get_world()->get_space());
+ ERR_FAIL_COND_V(!dss,Vector3());
+ const int max_shapes=32;
+ Vector3 sr[max_shapes*2];
+ int res_shapes;
+
+ Set<RID> exclude;
+ exclude.insert(get_rid());
+
+
+ //recover first
+ int recover_attempts=4;
+
+ bool collided=false;
+ uint32_t mask=0;
+ if (collide_static)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_STATIC_BODY;
+ if (collide_kinematic)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
+ if (collide_rigid)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_RIGID_BODY;
+ if (collide_character)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
+
+// print_line("motion: "+p_motion+" margin: "+rtos(margin));
+
+ //print_line("margin: "+rtos(margin));
+
+ float m = margin;
+ //m=0.001;
+
+ do {
+
+ //motion recover
+ for(int i=0;i<get_shape_count();i++) {
+
+
+ if (dss->collide_shape(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i),m,sr,max_shapes,res_shapes,exclude,get_layer_mask(),mask)) {
+ collided=true;
+ }
+
+ }
+
+
+
+ if (!collided)
+ break;
+
+ //print_line("have to recover");
+ Vector3 recover_motion;
+ bool all_outside=true;
+ for(int j=0;j<8;j++) {
+ for(int i=0;i<res_shapes;i++) {
+
+ Vector3 a = sr[i*2+0];
+ Vector3 b = sr[i*2+1];
+ //print_line(String()+a+" -> "+b);
+#if 0
+ float d = a.distance_to(b);
+
+ //if (d<margin)
+ /// continue;
+ ///
+ ///
+ recover_motion+=(b-a)*0.2;
+#else
+ float dist = a.distance_to(b);
+ if (dist>CMP_EPSILON) {
+ Vector3 norm = (b-a).normalized();
+ if (dist>margin*0.5)
+ all_outside=false;
+ float adv = norm.dot(recover_motion);
+ //print_line(itos(i)+" dist: "+rtos(dist)+" adv: "+rtos(adv));
+ recover_motion+=norm*MAX(dist-adv,0)*0.4;
+ }
+#endif
+
+ }
+ }
+
+
+ if (recover_motion==Vector3()) {
+ collided=false;
+ break;
+ }
+
+ //print_line("**** RECOVER: "+recover_motion);
+
+ Transform gt = get_global_transform();
+ gt.origin+=recover_motion;
+ set_global_transform(gt);
+
+ recover_attempts--;
+
+ if (all_outside)
+ break;
+
+ } while (recover_attempts);
+
+
+ //move second
+ float safe = 1.0;
+ float unsafe = 1.0;
+ int best_shape=-1;
+
+ PhysicsDirectSpaceState::ShapeRestInfo rest;
+
+ //print_line("pos: "+get_global_transform().origin);
+ //print_line("motion: "+p_motion);
+
+
+ for(int i=0;i<get_shape_count();i++) {
+
+
+
+ float lsafe,lunsafe;
+ PhysicsDirectSpaceState::ShapeRestInfo lrest;
+ bool valid = dss->cast_motion(get_shape(i)->get_rid(), get_global_transform() * get_shape_transform(i), p_motion,0, lsafe,lunsafe,exclude,get_layer_mask(),mask,&lrest);
+ //print_line("shape: "+itos(i)+" travel:"+rtos(ltravel));
+ if (!valid) {
+ safe=0;
+ unsafe=0;
+ best_shape=i; //sadly it's the best
+ //print_line("initial stuck");
+
+ break;
+ }
+ if (lsafe==1.0) {
+ //print_line("initial free");
+ continue;
+ }
+ if (lsafe < safe) {
+
+ //print_line("initial at "+rtos(lsafe));
+ safe=lsafe;
+ safe=MAX(0,lsafe-0.01);
+ unsafe=lunsafe;
+ best_shape=i;
+ rest=lrest;
+ }
+ }
+
+
+ //print_line("best shape: "+itos(best_shape)+" motion "+p_motion);
+
+ if (safe>=1) {
+ //not collided
+ colliding=false;
+ } else {
+
+ colliding=true;
+
+ if (true || (safe==0 && unsafe==0)) { //use it always because it's more precise than GJK
+ //no advance, use rest info from collision
+ Transform ugt = get_global_transform();
+ ugt.origin+=p_motion*unsafe;
+
+ PhysicsDirectSpaceState::ShapeRestInfo rest_info;
+ bool c2 = dss->rest_info(get_shape(best_shape)->get_rid(), ugt*get_shape_transform(best_shape), m,&rest,exclude,get_layer_mask(),mask);
+ if (!c2) {
+ //should not happen, but floating point precision is so weird..
+ colliding=false;
+ }
+
+ // print_line("Rest Travel: "+rest.normal);
+
+ }
+
+ if (colliding) {
+
+ collision=rest.point;
+ normal=rest.normal;
+ collider=rest.collider_id;
+ collider_vel=rest.linear_velocity;
+ }
+ }
+
+ Vector3 motion=p_motion*safe;
+ //if (colliding)
+ // motion+=normal*0.001;
+ Transform gt = get_global_transform();
+ gt.origin+=motion;
+ set_global_transform(gt);
+
+ return p_motion-motion;
+
+}
+
+Vector3 KinematicBody::move_to(const Vector3& p_position) {
+
+ return move(p_position-get_global_transform().origin);
+}
+
+bool KinematicBody::can_move_to(const Vector3& p_position, bool p_discrete) {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),false);
+ PhysicsDirectSpaceState *dss = PhysicsServer::get_singleton()->space_get_direct_state(get_world()->get_space());
+ ERR_FAIL_COND_V(!dss,false);
+
+ uint32_t mask=0;
+ if (collide_static)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_STATIC_BODY;
+ if (collide_kinematic)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_KINEMATIC_BODY;
+ if (collide_rigid)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_RIGID_BODY;
+ if (collide_character)
+ mask|=PhysicsDirectSpaceState::TYPE_MASK_CHARACTER_BODY;
+
+ Vector3 motion = p_position-get_global_transform().origin;
+ Transform xform=get_global_transform();
+
+ if (true || p_discrete) {
+
+ xform.origin+=motion;
+ motion=Vector3();
+ }
+
+ Set<RID> exclude;
+ exclude.insert(get_rid());
+
+ //fill exclude list..
+ for(int i=0;i<get_shape_count();i++) {
+
+
+ bool col = dss->intersect_shape(get_shape(i)->get_rid(), xform * get_shape_transform(i),0,NULL,0,exclude,get_layer_mask(),mask);
+ if (col)
+ return false;
+ }
+
+ return true;
+}
+
+bool KinematicBody::is_colliding() const {
+
+ ERR_FAIL_COND_V(!is_inside_scene(),false);
+
+ return colliding;
+}
+Vector3 KinematicBody::get_collision_pos() const {
+
+ ERR_FAIL_COND_V(!colliding,Vector3());
+ return collision;
+
+}
+Vector3 KinematicBody::get_collision_normal() const {
+
+ ERR_FAIL_COND_V(!colliding,Vector3());
+ return normal;
+
+}
+
+Vector3 KinematicBody::get_collider_velocity() const {
+
+ return collider_vel;
+}
+
+ObjectID KinematicBody::get_collider() const {
+
+ ERR_FAIL_COND_V(!colliding,0);
+ return collider;
+}
+
+void KinematicBody::set_collide_with_static_bodies(bool p_enable) {
+
+ collide_static=p_enable;
+}
+bool KinematicBody::can_collide_with_static_bodies() const {
+
+ return collide_static;
+}
+
+void KinematicBody::set_collide_with_rigid_bodies(bool p_enable) {
+
+ collide_rigid=p_enable;
+
+}
+bool KinematicBody::can_collide_with_rigid_bodies() const {
+
+
+ return collide_rigid;
+}
+
+void KinematicBody::set_collide_with_kinematic_bodies(bool p_enable) {
+
+ collide_kinematic=p_enable;
+
+}
+bool KinematicBody::can_collide_with_kinematic_bodies() const {
+
+ return collide_kinematic;
+}
+
+void KinematicBody::set_collide_with_character_bodies(bool p_enable) {
+
+ collide_character=p_enable;
+}
+bool KinematicBody::can_collide_with_character_bodies() const {
+
+ return collide_character;
+}
+
+void KinematicBody::set_collision_margin(float p_margin) {
+
+ margin=p_margin;
+}
+
+float KinematicBody::get_collision_margin() const{
+
+ return margin;
+}
+
+void KinematicBody::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("move","rel_vec"),&KinematicBody::move);
+ ObjectTypeDB::bind_method(_MD("move_to","position"),&KinematicBody::move_to);
+
+ ObjectTypeDB::bind_method(_MD("can_move_to","position"),&KinematicBody::can_move_to);
+
+ ObjectTypeDB::bind_method(_MD("is_colliding"),&KinematicBody::is_colliding);
+
+ ObjectTypeDB::bind_method(_MD("get_collision_pos"),&KinematicBody::get_collision_pos);
+ ObjectTypeDB::bind_method(_MD("get_collision_normal"),&KinematicBody::get_collision_normal);
+ ObjectTypeDB::bind_method(_MD("get_collider_velocity"),&KinematicBody::get_collider_velocity);
+ ObjectTypeDB::bind_method(_MD("get_collider:Object"),&KinematicBody::_get_collider);
+
+
+ ObjectTypeDB::bind_method(_MD("set_collide_with_static_bodies","enable"),&KinematicBody::set_collide_with_static_bodies);
+ ObjectTypeDB::bind_method(_MD("can_collide_with_static_bodies"),&KinematicBody::can_collide_with_static_bodies);
+
+ ObjectTypeDB::bind_method(_MD("set_collide_with_kinematic_bodies","enable"),&KinematicBody::set_collide_with_kinematic_bodies);
+ ObjectTypeDB::bind_method(_MD("can_collide_with_kinematic_bodies"),&KinematicBody::can_collide_with_kinematic_bodies);
+
+ ObjectTypeDB::bind_method(_MD("set_collide_with_rigid_bodies","enable"),&KinematicBody::set_collide_with_rigid_bodies);
+ ObjectTypeDB::bind_method(_MD("can_collide_with_rigid_bodies"),&KinematicBody::can_collide_with_rigid_bodies);
+
+ ObjectTypeDB::bind_method(_MD("set_collide_with_character_bodies","enable"),&KinematicBody::set_collide_with_character_bodies);
+ ObjectTypeDB::bind_method(_MD("can_collide_with_character_bodies"),&KinematicBody::can_collide_with_character_bodies);
+
+ ObjectTypeDB::bind_method(_MD("set_collision_margin","pixels"),&KinematicBody::set_collision_margin);
+ ObjectTypeDB::bind_method(_MD("get_collision_margin","pixels"),&KinematicBody::get_collision_margin);
+
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/static"),_SCS("set_collide_with_static_bodies"),_SCS("can_collide_with_static_bodies"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/kinematic"),_SCS("set_collide_with_kinematic_bodies"),_SCS("can_collide_with_kinematic_bodies"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/rigid"),_SCS("set_collide_with_rigid_bodies"),_SCS("can_collide_with_rigid_bodies"));
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"collide_with/character"),_SCS("set_collide_with_character_bodies"),_SCS("can_collide_with_character_bodies"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"collision/margin",PROPERTY_HINT_RANGE,"0.001,256,0.001"),_SCS("set_collision_margin"),_SCS("get_collision_margin"));
+
+
+}
+
+KinematicBody::KinematicBody() : PhysicsBody(PhysicsServer::BODY_MODE_KINEMATIC){
+
+ collide_static=true;
+ collide_rigid=true;
+ collide_kinematic=true;
+ collide_character=true;
+
+ colliding=false;
+ collider=0;
+ margin=0.001;
+}
+KinematicBody::~KinematicBody() {
+
+
+}
+
diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h
index 6695ee719a..0b7a389449 100644
--- a/scene/3d/physics_body.h
+++ b/scene/3d/physics_body.h
@@ -38,12 +38,22 @@ class PhysicsBody : public CollisionObject {
OBJ_TYPE(PhysicsBody,CollisionObject);
+ uint32_t layer_mask;
protected:
+ static void _bind_methods();
void _notification(int p_what);
PhysicsBody(PhysicsServer::BodyMode p_mode);
public:
+ virtual Vector3 get_linear_velocity() const;
+ virtual Vector3 get_angular_velocity() const;
+ virtual float get_inverse_mass() const;
+
+ void set_layer_mask(uint32_t p_mask);
+ uint32_t get_layer_mask() const;
+
+
PhysicsBody();
};
@@ -52,25 +62,26 @@ 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);
+
+ real_t bounce;
+ real_t friction;
+
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_friction(real_t p_friction);
+ real_t get_friction() const;
+
+ void set_bounce(real_t p_bounce);
+ real_t get_bounce() const;
+
void set_constant_linear_velocity(const Vector3& p_vel);
void set_constant_angular_velocity(const Vector3& p_vel);
@@ -183,6 +194,8 @@ public:
void set_mass(real_t p_mass);
real_t get_mass() const;
+ virtual float get_inverse_mass() const { return 1.0/mass; }
+
void set_weight(real_t p_weight);
real_t get_weight() const;
@@ -231,4 +244,70 @@ public:
VARIANT_ENUM_CAST(RigidBody::Mode);
VARIANT_ENUM_CAST(RigidBody::AxisLock);
+
+
+
+
+
+class KinematicBody : public PhysicsBody {
+
+ OBJ_TYPE(KinematicBody,PhysicsBody);
+
+ float margin;
+ bool collide_static;
+ bool collide_rigid;
+ bool collide_kinematic;
+ bool collide_character;
+
+ bool colliding;
+ Vector3 collision;
+ Vector3 normal;
+ Vector3 collider_vel;
+ ObjectID collider;
+
+
+ Variant _get_collider() const;
+
+ _FORCE_INLINE_ bool _ignores_mode(PhysicsServer::BodyMode) const;
+protected:
+
+ static void _bind_methods();
+public:
+
+ enum {
+ SLIDE_FLAG_FLOOR,
+ SLIDE_FLAG_WALL,
+ SLIDE_FLAG_ROOF
+ };
+
+ Vector3 move(const Vector3& p_motion);
+ Vector3 move_to(const Vector3& p_position);
+
+ bool can_move_to(const Vector3& p_position,bool p_discrete=false);
+ bool is_colliding() const;
+ Vector3 get_collision_pos() const;
+ Vector3 get_collision_normal() const;
+ Vector3 get_collider_velocity() const;
+ ObjectID get_collider() const;
+
+ void set_collide_with_static_bodies(bool p_enable);
+ bool can_collide_with_static_bodies() const;
+
+ void set_collide_with_rigid_bodies(bool p_enable);
+ bool can_collide_with_rigid_bodies() const;
+
+ void set_collide_with_kinematic_bodies(bool p_enable);
+ bool can_collide_with_kinematic_bodies() const;
+
+ void set_collide_with_character_bodies(bool p_enable);
+ bool can_collide_with_character_bodies() const;
+
+ void set_collision_margin(float p_margin);
+ float get_collision_margin() const;
+
+ KinematicBody();
+ ~KinematicBody();
+
+};
+
#endif // PHYSICS_BODY__H
diff --git a/scene/3d/physics_joint.cpp b/scene/3d/physics_joint.cpp
index 961198c8d4..341b02314d 100644
--- a/scene/3d/physics_joint.cpp
+++ b/scene/3d/physics_joint.cpp
@@ -27,7 +27,1017 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "physics_joint.h"
-
+
+
+
+void Joint::_update_joint(bool p_only_free) {
+
+
+ if (joint.is_valid()) {
+ if (ba.is_valid() && bb.is_valid())
+ PhysicsServer::get_singleton()->body_remove_collision_exception(ba,bb);
+ PhysicsServer::get_singleton()->free(joint);
+ joint=RID();
+ ba=RID();
+ bb=RID();
+ }
+
+ if (p_only_free || !is_inside_scene())
+ return;
+
+ Node *node_a = has_node( get_node_a() ) ? get_node( get_node_a() ) : (Node*)NULL;
+ Node *node_b = has_node( get_node_b() ) ? get_node( get_node_b() ) : (Node*)NULL;
+
+ if (!node_a && !node_b)
+ return;
+
+ PhysicsBody *body_a=node_a ? node_a->cast_to<PhysicsBody>() : (PhysicsBody*)NULL;
+ PhysicsBody *body_b=node_b ? node_b->cast_to<PhysicsBody>() : (PhysicsBody*)NULL;
+
+ if (!body_a && !body_b)
+ return;
+
+ if (!body_a) {
+ SWAP(body_a,body_b);
+ } else if (body_b) {
+ //add a collision exception between both
+ PhysicsServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
+ }
+
+ joint = _configure_joint(body_a,body_b);
+
+ if (body_b && joint.is_valid()) {
+
+ ba=body_a->get_rid();
+ bb=body_b->get_rid();
+ PhysicsServer::get_singleton()->body_add_collision_exception(body_a->get_rid(),body_b->get_rid());
+
+ }
+
+}
+
+
+void Joint::set_node_a(const NodePath& p_node_a) {
+
+
+ if (a==p_node_a)
+ return;
+
+ a=p_node_a;
+ _update_joint();
+}
+
+NodePath Joint::get_node_a() const{
+
+ return a;
+}
+
+void Joint::set_node_b(const NodePath& p_node_b){
+
+ if (b==p_node_b)
+ return;
+ b=p_node_b;
+ _update_joint();
+
+}
+NodePath Joint::get_node_b() const{
+
+
+ return b;
+}
+
+
+void Joint::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_READY: {
+ _update_joint();
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+ if (joint.is_valid()) {
+ _update_joint(true);
+
+
+ PhysicsServer::get_singleton()->free(joint);
+ joint=RID();
+ }
+ } break;
+
+ }
+
+}
+
+
+void Joint::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method( _MD("set_node_a","node"), &Joint::set_node_a );
+ ObjectTypeDB::bind_method( _MD("get_node_a"), &Joint::get_node_a );
+
+ ObjectTypeDB::bind_method( _MD("set_node_b","node"), &Joint::set_node_b );
+ ObjectTypeDB::bind_method( _MD("get_node_b"), &Joint::get_node_b );
+
+
+ ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "nodes/node_a"), _SCS("set_node_a"),_SCS("get_node_a") );
+ ADD_PROPERTY( PropertyInfo( Variant::NODE_PATH, "nodes/node_b"), _SCS("set_node_b"),_SCS("get_node_b") );
+
+}
+
+
+
+Joint::Joint() {
+
+
+}
+
+
+///////////////////////////////////
+
+void PinJoint::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&PinJoint::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&PinJoint::get_param);
+
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"params/bias",PROPERTY_HINT_RANGE,"0.01,0.99,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_BIAS );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"params/damping",PROPERTY_HINT_RANGE,"0.01,8.0,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_DAMPING );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"params/impulse_clamp",PROPERTY_HINT_RANGE,"0.0,64.0,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_IMPULSE_CLAMP );
+
+ BIND_CONSTANT( PARAM_BIAS );
+ BIND_CONSTANT( PARAM_DAMPING );
+ BIND_CONSTANT( PARAM_IMPULSE_CLAMP );
+}
+
+void PinJoint::set_param(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,3);
+ params[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->pin_joint_set_param(get_joint(),PhysicsServer::PinJointParam(p_param),p_value);
+}
+float PinJoint::get_param(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,3,0);
+ return params[p_param];
+}
+
+
+RID PinJoint::_configure_joint(PhysicsBody *body_a,PhysicsBody *body_b) {
+
+
+ Vector3 pinpos = get_global_transform().origin;
+ Vector3 local_a = body_a->get_global_transform().affine_inverse().xform(pinpos);
+ Vector3 local_b;
+
+ if (body_b)
+ local_b = body_b->get_global_transform().affine_inverse().xform(pinpos);
+ else
+ local_b=pinpos;
+
+ RID j = PhysicsServer::get_singleton()->joint_create_pin(body_a->get_rid(),local_a,body_b?body_b->get_rid():RID(),local_b);
+ for(int i=0;i<3;i++) {
+ PhysicsServer::get_singleton()->pin_joint_set_param(j,PhysicsServer::PinJointParam(i),params[i]);
+ }
+ return j;
+}
+
+
+PinJoint::PinJoint() {
+
+ params[PARAM_BIAS]=0.3;
+ params[PARAM_DAMPING]=1;
+ params[PARAM_IMPULSE_CLAMP]=0;
+
+}
+
+
+
+
+/////////////////////////////////////////////////
+
+
+
+///////////////////////////////////
+
+
+void HingeJoint::_set_upper_limit(float p_limit) {
+
+ set_param(PARAM_LIMIT_UPPER,Math::deg2rad(p_limit));
+}
+
+float HingeJoint::_get_upper_limit() const {
+
+ return Math::rad2deg(get_param(PARAM_LIMIT_UPPER));
+}
+
+void HingeJoint::_set_lower_limit(float p_limit) {
+
+ set_param(PARAM_LIMIT_LOWER,Math::deg2rad(p_limit));
+
+}
+
+float HingeJoint::_get_lower_limit() const {
+
+ return Math::rad2deg(get_param(PARAM_LIMIT_LOWER));
+
+}
+
+
+void HingeJoint::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&HingeJoint::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&HingeJoint::get_param);
+
+ ObjectTypeDB::bind_method(_MD("set_flag","flag","enabled"),&HingeJoint::set_flag);
+ ObjectTypeDB::bind_method(_MD("get_flag","flag"),&HingeJoint::get_flag);
+
+ ObjectTypeDB::bind_method(_MD("_set_upper_limit","upper_limit"),&HingeJoint::_set_upper_limit);
+ ObjectTypeDB::bind_method(_MD("_get_upper_limit"),&HingeJoint::_get_upper_limit);
+
+ ObjectTypeDB::bind_method(_MD("_set_lower_limit","lower_limit"),&HingeJoint::_set_lower_limit);
+ ObjectTypeDB::bind_method(_MD("_get_lower_limit"),&HingeJoint::_get_lower_limit);
+
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"params/bias",PROPERTY_HINT_RANGE,"0.01,0.99,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_BIAS );
+
+ ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"angular_limit/enable"),_SCS("set_flag"),_SCS("get_flag"), FLAG_USE_LIMIT );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_limit/upper",PROPERTY_HINT_RANGE,"-180,180,0.1"),_SCS("_set_upper_limit"),_SCS("_get_upper_limit") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_limit/lower",PROPERTY_HINT_RANGE,"-180,180,0.1"),_SCS("_set_lower_limit"),_SCS("_get_lower_limit") );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/bias",PROPERTY_HINT_RANGE,"0.01,0.99,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_LIMIT_BIAS );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_LIMIT_SOFTNESS );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/relaxation",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_LIMIT_RELAXATION );
+
+ ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"motor/enable"),_SCS("set_flag"),_SCS("get_flag"), FLAG_ENABLE_MOTOR );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"motor/target_velocity",PROPERTY_HINT_RANGE,"0.01,4096,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_MOTOR_TARGET_VELOCITY );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"motor/max_impulse",PROPERTY_HINT_RANGE,"0.01,1024,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_MOTOR_MAX_IMPULSE);
+
+
+ BIND_CONSTANT( PARAM_BIAS );
+ BIND_CONSTANT( PARAM_LIMIT_UPPER );
+ BIND_CONSTANT( PARAM_LIMIT_LOWER );
+ BIND_CONSTANT( PARAM_LIMIT_BIAS );
+ BIND_CONSTANT( PARAM_LIMIT_SOFTNESS );
+ BIND_CONSTANT( PARAM_LIMIT_RELAXATION );
+ BIND_CONSTANT( PARAM_MOTOR_TARGET_VELOCITY );
+ BIND_CONSTANT( PARAM_MOTOR_MAX_IMPULSE );
+ BIND_CONSTANT( PARAM_MAX );
+
+ BIND_CONSTANT( FLAG_USE_LIMIT );
+ BIND_CONSTANT( FLAG_ENABLE_MOTOR );
+ BIND_CONSTANT( FLAG_MAX );
+
+}
+
+void HingeJoint::set_param(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_param(get_joint(),PhysicsServer::HingeJointParam(p_param),p_value);
+
+ update_gizmo();
+
+}
+float HingeJoint::get_param(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params[p_param];
+}
+
+
+void HingeJoint::set_flag(Flag p_flag,bool p_value){
+
+ ERR_FAIL_INDEX(p_flag,FLAG_MAX);
+ flags[p_flag]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(get_joint(),PhysicsServer::HingeJointFlag(p_flag),p_value);
+
+ update_gizmo();
+}
+bool HingeJoint::get_flag(Flag p_flag) const{
+
+ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false);
+ return flags[p_flag];
+}
+
+RID HingeJoint::_configure_joint(PhysicsBody *body_a,PhysicsBody *body_b) {
+
+
+ Transform gt = get_global_transform();
+ Vector3 hingepos = gt.origin;
+ Vector3 hingedir = gt.basis.get_axis(2);
+
+ Transform ainv = body_a->get_global_transform().affine_inverse();
+
+ Transform local_a = ainv * gt;
+ local_a.orthonormalize();
+ Transform local_b = gt;
+
+ if (body_b) {
+ Transform binv = body_b->get_global_transform().affine_inverse();
+ local_b = binv * gt;
+ }
+
+ local_b.orthonormalize();
+
+ RID j = PhysicsServer::get_singleton()->joint_create_hinge(body_a->get_rid(),local_a,body_b?body_b->get_rid():RID(),local_b);
+ for(int i=0;i<PARAM_MAX;i++) {
+ PhysicsServer::get_singleton()->hinge_joint_set_param(j,PhysicsServer::HingeJointParam(i),params[i]);
+ }
+ for(int i=0;i<FLAG_MAX;i++) {
+ set_flag(Flag(i),flags[i]);
+ PhysicsServer::get_singleton()->hinge_joint_set_flag(j,PhysicsServer::HingeJointFlag(i),flags[i]);
+ }
+ return j;
+}
+
+
+HingeJoint::HingeJoint() {
+
+ params[PARAM_BIAS]=0.3;
+ params[PARAM_LIMIT_UPPER]=Math_PI*0.5;
+ params[PARAM_LIMIT_LOWER]=-Math_PI*0.5;
+ params[PARAM_LIMIT_BIAS]=0.3;
+ params[PARAM_LIMIT_SOFTNESS]=0.9;
+ params[PARAM_LIMIT_RELAXATION]=1.0;
+ params[PARAM_MOTOR_TARGET_VELOCITY]=1;
+ params[PARAM_MOTOR_MAX_IMPULSE]=1;
+
+
+ flags[FLAG_USE_LIMIT]=false;
+ flags[FLAG_ENABLE_MOTOR]=false;
+
+}
+
+
+
+
+/////////////////////////////////////////////////
+
+
+//////////////////////////////////
+
+
+
+void SliderJoint::_set_upper_limit_angular(float p_limit_angular) {
+
+ set_param(PARAM_ANGULAR_LIMIT_UPPER,Math::deg2rad(p_limit_angular));
+}
+
+float SliderJoint::_get_upper_limit_angular() const {
+
+ return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_UPPER));
+}
+
+void SliderJoint::_set_lower_limit_angular(float p_limit_angular) {
+
+ set_param(PARAM_ANGULAR_LIMIT_LOWER,Math::deg2rad(p_limit_angular));
+
+}
+
+float SliderJoint::_get_lower_limit_angular() const {
+
+ return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_LOWER));
+
+}
+
+
+void SliderJoint::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&SliderJoint::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&SliderJoint::get_param);
+
+
+ ObjectTypeDB::bind_method(_MD("_set_upper_limit_angular","upper_limit_angular"),&SliderJoint::_set_upper_limit_angular);
+ ObjectTypeDB::bind_method(_MD("_get_upper_limit_angular"),&SliderJoint::_get_upper_limit_angular);
+
+ ObjectTypeDB::bind_method(_MD("_set_lower_limit_angular","lower_limit_angular"),&SliderJoint::_set_lower_limit_angular);
+ ObjectTypeDB::bind_method(_MD("_get_lower_limit_angular"),&SliderJoint::_get_lower_limit_angular);
+
+
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_limit/upper_distance",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_LIMIT_UPPER);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_limit/lower_distance",PROPERTY_HINT_RANGE,"-1024,1024,0.01"),_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_LIMIT_LOWER);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_limit/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_limit/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_LIMIT_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_limit/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_LIMIT_DAMPING);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_motion/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_MOTION_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_motion/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_MOTION_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_motion/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_MOTION_DAMPING);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_ortho/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_ORTHOGONAL_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_ortho/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_ORTHOGONAL_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"linear_ortho/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_LINEAR_ORTHOGONAL_DAMPING);
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_limit/upper_angle",PROPERTY_HINT_RANGE,"-180,180,0.1"),_SCS("_set_upper_limit_angular"),_SCS("_get_upper_limit_angular") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"angular_limit/lower_angle",PROPERTY_HINT_RANGE,"-180,180,0.1"),_SCS("_set_lower_limit_angular"),_SCS("_get_lower_limit_angular") );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_LIMIT_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_limit/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_LIMIT_DAMPING);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_motion/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_MOTION_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_motion/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_MOTION_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_motion/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_MOTION_DAMPING);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_ortho/softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_ORTHOGONAL_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_ortho/restitution",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_ORTHOGONAL_RESTITUTION);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"angular_ortho/damping",PROPERTY_HINT_RANGE,"0,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_ANGULAR_ORTHOGONAL_DAMPING);
+
+
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_UPPER);
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_LOWER);
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_SOFTNESS);
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_RESTITUTION);
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_DAMPING);
+ BIND_CONSTANT( PARAM_LINEAR_MOTION_SOFTNESS);
+ BIND_CONSTANT( PARAM_LINEAR_MOTION_RESTITUTION);
+ BIND_CONSTANT( PARAM_LINEAR_MOTION_DAMPING);
+ BIND_CONSTANT( PARAM_LINEAR_ORTHOGONAL_SOFTNESS);
+ BIND_CONSTANT( PARAM_LINEAR_ORTHOGONAL_RESTITUTION);
+ BIND_CONSTANT( PARAM_LINEAR_ORTHOGONAL_DAMPING);
+
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_UPPER);
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_LOWER);
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_SOFTNESS);
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_RESTITUTION);
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_DAMPING);
+ BIND_CONSTANT( PARAM_ANGULAR_MOTION_SOFTNESS);
+ BIND_CONSTANT( PARAM_ANGULAR_MOTION_RESTITUTION);
+ BIND_CONSTANT( PARAM_ANGULAR_MOTION_DAMPING);
+ BIND_CONSTANT( PARAM_ANGULAR_ORTHOGONAL_SOFTNESS);
+ BIND_CONSTANT( PARAM_ANGULAR_ORTHOGONAL_RESTITUTION);
+ BIND_CONSTANT( PARAM_ANGULAR_ORTHOGONAL_DAMPING);
+
+ BIND_CONSTANT( PARAM_MAX);
+}
+
+void SliderJoint::set_param(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->slider_joint_set_param(get_joint(),PhysicsServer::SliderJointParam(p_param),p_value);
+ update_gizmo();
+
+}
+float SliderJoint::get_param(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params[p_param];
+}
+
+
+RID SliderJoint::_configure_joint(PhysicsBody *body_a,PhysicsBody *body_b) {
+
+
+ Transform gt = get_global_transform();
+ Vector3 sliderpos = gt.origin;
+ Vector3 sliderdir = gt.basis.get_axis(2);
+
+ Transform ainv = body_a->get_global_transform().affine_inverse();
+
+ Transform local_a = ainv * gt;
+ local_a.orthonormalize();
+ Transform local_b = gt;
+
+ if (body_b) {
+ Transform binv = body_b->get_global_transform().affine_inverse();
+ local_b = binv * gt;
+ }
+
+ local_b.orthonormalize();
+
+ RID j = PhysicsServer::get_singleton()->joint_create_slider(body_a->get_rid(),local_a,body_b?body_b->get_rid():RID(),local_b);
+ for(int i=0;i<PARAM_MAX;i++) {
+ PhysicsServer::get_singleton()->slider_joint_set_param(j,PhysicsServer::SliderJointParam(i),params[i]);
+ }
+
+ return j;
+}
+
+
+SliderJoint::SliderJoint() {
+
+
+
+ params[ PARAM_LINEAR_LIMIT_UPPER ]=1.0;
+ params[ PARAM_LINEAR_LIMIT_LOWER ]=-1.0;
+ params[ PARAM_LINEAR_LIMIT_SOFTNESS ]=1.0;
+ params[ PARAM_LINEAR_LIMIT_RESTITUTION]=0.7;
+ params[ PARAM_LINEAR_LIMIT_DAMPING]=1.0;
+ params[ PARAM_LINEAR_MOTION_SOFTNESS ]=1.0;
+ params[ PARAM_LINEAR_MOTION_RESTITUTION]=0.7;
+ params[ PARAM_LINEAR_MOTION_DAMPING]=0;//1.0;
+ params[ PARAM_LINEAR_ORTHOGONAL_SOFTNESS ]=1.0;
+ params[ PARAM_LINEAR_ORTHOGONAL_RESTITUTION]=0.7;
+ params[ PARAM_LINEAR_ORTHOGONAL_DAMPING]=1.0;
+
+ params[ PARAM_ANGULAR_LIMIT_UPPER ]=0 ;
+ params[ PARAM_ANGULAR_LIMIT_LOWER ]=0 ;
+ params[ PARAM_ANGULAR_LIMIT_SOFTNESS ]=1.0;
+ params[ PARAM_ANGULAR_LIMIT_RESTITUTION]=0.7;
+ params[ PARAM_ANGULAR_LIMIT_DAMPING]=0;//1.0;
+ params[ PARAM_ANGULAR_MOTION_SOFTNESS ]=1.0;
+ params[ PARAM_ANGULAR_MOTION_RESTITUTION]=0.7;
+ params[ PARAM_ANGULAR_MOTION_DAMPING]=1.0;
+ params[ PARAM_ANGULAR_ORTHOGONAL_SOFTNESS ]=1.0;
+ params[ PARAM_ANGULAR_ORTHOGONAL_RESTITUTION]=0.7;
+ params[ PARAM_ANGULAR_ORTHOGONAL_DAMPING]=1.0;
+}
+
+
+
+//////////////////////////////////
+
+
+
+void ConeTwistJoint::_set_swing_span(float p_limit_angular) {
+
+ set_param(PARAM_SWING_SPAN,Math::deg2rad(p_limit_angular));
+}
+
+float ConeTwistJoint::_get_swing_span() const {
+
+ return Math::rad2deg(get_param(PARAM_SWING_SPAN));
+}
+
+void ConeTwistJoint::_set_twist_span(float p_limit_angular) {
+
+ set_param(PARAM_TWIST_SPAN,Math::deg2rad(p_limit_angular));
+
+}
+
+float ConeTwistJoint::_get_twist_span() const {
+
+ return Math::rad2deg(get_param(PARAM_TWIST_SPAN));
+
+}
+
+
+void ConeTwistJoint::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("set_param","param","value"),&ConeTwistJoint::set_param);
+ ObjectTypeDB::bind_method(_MD("get_param","param"),&ConeTwistJoint::get_param);
+
+
+ ObjectTypeDB::bind_method(_MD("_set_swing_span","swing_span"),&ConeTwistJoint::_set_swing_span);
+ ObjectTypeDB::bind_method(_MD("_get_swing_span"),&ConeTwistJoint::_get_swing_span);
+
+ ObjectTypeDB::bind_method(_MD("_set_twist_span","twist_span"),&ConeTwistJoint::_set_twist_span);
+ ObjectTypeDB::bind_method(_MD("_get_twist_span"),&ConeTwistJoint::_get_twist_span);
+
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"swing_span",PROPERTY_HINT_RANGE,"-180,180,0.1"),_SCS("_set_swing_span"),_SCS("_get_swing_span") );
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"twist_span",PROPERTY_HINT_RANGE,"-40000,40000,0.1"),_SCS("_set_twist_span"),_SCS("_get_twist_span") );
+
+
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"bias",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_BIAS );
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"softness",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_SOFTNESS);
+ ADD_PROPERTYI( PropertyInfo(Variant::REAL,"relaxation",PROPERTY_HINT_RANGE,"0.01,16.0,0.01") ,_SCS("set_param"),_SCS("get_param"), PARAM_RELAXATION);
+
+ BIND_CONSTANT( PARAM_SWING_SPAN );
+ BIND_CONSTANT( PARAM_TWIST_SPAN );
+ BIND_CONSTANT( PARAM_BIAS );
+ BIND_CONSTANT( PARAM_SOFTNESS );
+ BIND_CONSTANT( PARAM_RELAXATION );
+ BIND_CONSTANT( PARAM_MAX );
+}
+
+void ConeTwistJoint::set_param(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(get_joint(),PhysicsServer::ConeTwistJointParam(p_param),p_value);
+
+ update_gizmo();
+}
+float ConeTwistJoint::get_param(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params[p_param];
+}
+
+
+RID ConeTwistJoint::_configure_joint(PhysicsBody *body_a,PhysicsBody *body_b) {
+
+
+ Transform gt = get_global_transform();
+ //Vector3 cone_twistpos = gt.origin;
+ //Vector3 cone_twistdir = gt.basis.get_axis(2);
+
+ Transform ainv = body_a->get_global_transform().affine_inverse();
+
+ Transform local_a = ainv * gt;
+ local_a.orthonormalize();
+ Transform local_b = gt;
+
+ if (body_b) {
+ Transform binv = body_b->get_global_transform().affine_inverse();
+ local_b = binv * gt;
+ }
+
+ local_b.orthonormalize();
+
+ RID j = PhysicsServer::get_singleton()->joint_create_cone_twist(body_a->get_rid(),local_a,body_b?body_b->get_rid():RID(),local_b);
+ for(int i=0;i<PARAM_MAX;i++) {
+ PhysicsServer::get_singleton()->cone_twist_joint_set_param(j,PhysicsServer::ConeTwistJointParam(i),params[i]);
+ }
+
+ return j;
+}
+
+
+ConeTwistJoint::ConeTwistJoint() {
+
+
+ params[ PARAM_SWING_SPAN ]=Math_PI*0.25;
+ params[ PARAM_TWIST_SPAN ]=Math_PI;
+ params[ PARAM_BIAS ]=0.3;
+ params[ PARAM_SOFTNESS ]=0.8;
+ params[ PARAM_RELAXATION ]=1.0;
+
+}
+
+/////////////////////////////////////////////////////////////////////
+
+
+void Generic6DOFJoint::_set_angular_hi_limit_x(float p_limit_angular) {
+
+ set_param_x(PARAM_ANGULAR_UPPER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_hi_limit_x() const{
+
+ return Math::rad2deg(get_param_x(PARAM_ANGULAR_UPPER_LIMIT));
+
+}
+
+void Generic6DOFJoint::_set_angular_lo_limit_x(float p_limit_angular) {
+
+ set_param_x(PARAM_ANGULAR_LOWER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_lo_limit_x() const{
+
+ return Math::rad2deg(get_param_x(PARAM_ANGULAR_LOWER_LIMIT));
+
+}
+
+
+void Generic6DOFJoint::_set_angular_hi_limit_y(float p_limit_angular) {
+
+ set_param_y(PARAM_ANGULAR_UPPER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_hi_limit_y() const{
+
+ return Math::rad2deg(get_param_y(PARAM_ANGULAR_UPPER_LIMIT));
+
+}
+
+void Generic6DOFJoint::_set_angular_lo_limit_y(float p_limit_angular) {
+
+ set_param_y(PARAM_ANGULAR_LOWER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_lo_limit_y() const{
+
+ return Math::rad2deg(get_param_y(PARAM_ANGULAR_LOWER_LIMIT));
+
+}
+
+
+void Generic6DOFJoint::_set_angular_hi_limit_z(float p_limit_angular) {
+
+ set_param_z(PARAM_ANGULAR_UPPER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_hi_limit_z() const{
+
+ return Math::rad2deg(get_param_z(PARAM_ANGULAR_UPPER_LIMIT));
+
+}
+
+void Generic6DOFJoint::_set_angular_lo_limit_z(float p_limit_angular) {
+
+ set_param_z(PARAM_ANGULAR_LOWER_LIMIT,Math::deg2rad(p_limit_angular));
+}
+
+float Generic6DOFJoint::_get_angular_lo_limit_z() const{
+
+ return Math::rad2deg(get_param_z(PARAM_ANGULAR_LOWER_LIMIT));
+
+}
+
+
+
+void Generic6DOFJoint::_bind_methods(){
+
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_hi_limit_x","angle"),&Generic6DOFJoint::_set_angular_hi_limit_x);
+ ObjectTypeDB::bind_method(_MD("_get_angular_hi_limit_x"),&Generic6DOFJoint::_get_angular_hi_limit_x);
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_lo_limit_x","angle"),&Generic6DOFJoint::_set_angular_lo_limit_x);
+ ObjectTypeDB::bind_method(_MD("_get_angular_lo_limit_x"),&Generic6DOFJoint::_get_angular_lo_limit_x);
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_hi_limit_y","angle"),&Generic6DOFJoint::_set_angular_hi_limit_y);
+ ObjectTypeDB::bind_method(_MD("_get_angular_hi_limit_y"),&Generic6DOFJoint::_get_angular_hi_limit_y);
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_lo_limit_y","angle"),&Generic6DOFJoint::_set_angular_lo_limit_y);
+ ObjectTypeDB::bind_method(_MD("_get_angular_lo_limit_y"),&Generic6DOFJoint::_get_angular_lo_limit_y);
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_hi_limit_z","angle"),&Generic6DOFJoint::_set_angular_hi_limit_z);
+ ObjectTypeDB::bind_method(_MD("_get_angular_hi_limit_z"),&Generic6DOFJoint::_get_angular_hi_limit_z);
+
+ ObjectTypeDB::bind_method(_MD("_set_angular_lo_limit_z","angle"),&Generic6DOFJoint::_set_angular_lo_limit_z);
+ ObjectTypeDB::bind_method(_MD("_get_angular_lo_limit_z"),&Generic6DOFJoint::_get_angular_lo_limit_z);
+
+ ObjectTypeDB::bind_method(_MD("set_param_x","param","value"),&Generic6DOFJoint::set_param_x);
+ ObjectTypeDB::bind_method(_MD("get_param_x","param"),&Generic6DOFJoint::get_param_x);
+
+ ObjectTypeDB::bind_method(_MD("set_param_y","param","value"),&Generic6DOFJoint::set_param_y);
+ ObjectTypeDB::bind_method(_MD("get_param_y","param"),&Generic6DOFJoint::get_param_y);
+
+ ObjectTypeDB::bind_method(_MD("set_param_z","param","value"),&Generic6DOFJoint::set_param_z);
+ ObjectTypeDB::bind_method(_MD("get_param_z","param"),&Generic6DOFJoint::get_param_z);
+
+ ObjectTypeDB::bind_method(_MD("set_flag_x","flag","value"),&Generic6DOFJoint::set_flag_x);
+ ObjectTypeDB::bind_method(_MD("get_flag_x","flag"),&Generic6DOFJoint::get_flag_x);
+
+ ObjectTypeDB::bind_method(_MD("set_flag_y","flag","value"),&Generic6DOFJoint::set_flag_y);
+ ObjectTypeDB::bind_method(_MD("get_flag_y","flag"),&Generic6DOFJoint::get_flag_y);
+
+ ObjectTypeDB::bind_method(_MD("set_flag_z","flag","value"),&Generic6DOFJoint::set_flag_z);
+ ObjectTypeDB::bind_method(_MD("get_flag_z","flag"),&Generic6DOFJoint::get_flag_z);
+
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"linear_limit_x/enabled"),_SCS("set_flag_x"),_SCS("get_flag_x"),FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_x/upper_distance"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_x/lower_distance"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_x/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_x/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_x/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_limit_x/enabled"),_SCS("set_flag_x"),_SCS("get_flag_x"),FLAG_ENABLE_ANGULAR_LIMIT);
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_x/upper_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_hi_limit_x"),_SCS("_get_angular_hi_limit_x"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_x/lower_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_lo_limit_x"),_SCS("_get_angular_lo_limit_x"));
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_x/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_x/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_x/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_x/force_limit"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_x/erp"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_ERP);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_motor_x/enabled"),_SCS("set_flag_x"),_SCS("get_flag_x"),FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_x/target_velocity"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_x/force_limit"),_SCS("set_param_x"),_SCS("get_param_x"),PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"linear_limit_y/enabled"),_SCS("set_flag_y"),_SCS("get_flag_y"),FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_y/upper_distance"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_y/lower_distance"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_y/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_y/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_y/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_limit_y/enabled"),_SCS("set_flag_y"),_SCS("get_flag_y"),FLAG_ENABLE_ANGULAR_LIMIT);
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_y/upper_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_hi_limit_y"),_SCS("_get_angular_hi_limit_y"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_y/lower_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_lo_limit_y"),_SCS("_get_angular_lo_limit_y"));
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_y/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_y/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_y/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_y/force_limit"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_y/erp"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_ERP);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_motor_y/enabled"),_SCS("set_flag_y"),_SCS("get_flag_y"),FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_y/target_velocity"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_y/force_limit"),_SCS("set_param_y"),_SCS("get_param_y"),PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"linear_limit_z/enabled"),_SCS("set_flag_z"),_SCS("get_flag_z"),FLAG_ENABLE_LINEAR_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_z/upper_distance"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_LINEAR_UPPER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_z/lower_distance"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_LINEAR_LOWER_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_z/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_LINEAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_z/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_LINEAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"linear_limit_z/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_LINEAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_limit_z/enabled"),_SCS("set_flag_z"),_SCS("get_flag_z"),FLAG_ENABLE_ANGULAR_LIMIT);
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_z/upper_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_hi_limit_z"),_SCS("_get_angular_hi_limit_z"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"angular_limit_z/lower_angle",PROPERTY_HINT_RANGE,"-180,180,0.01"),_SCS("_set_angular_lo_limit_z"),_SCS("_get_angular_lo_limit_z"));
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_z/softness",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_LIMIT_SOFTNESS);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_z/restitution",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_RESTITUTION);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_z/damping",PROPERTY_HINT_RANGE,"0.01,16,0.01"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_DAMPING);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_z/force_limit"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_FORCE_LIMIT);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_limit_z/erp"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_ERP);
+ ADD_PROPERTYI(PropertyInfo(Variant::BOOL,"angular_motor_z/enabled"),_SCS("set_flag_z"),_SCS("get_flag_z"),FLAG_ENABLE_MOTOR);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_z/target_velocity"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ ADD_PROPERTYI(PropertyInfo(Variant::REAL,"angular_motor_z/force_limit"),_SCS("set_param_z"),_SCS("get_param_z"),PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+
+
+ BIND_CONSTANT( PARAM_LINEAR_LOWER_LIMIT);
+ BIND_CONSTANT( PARAM_LINEAR_UPPER_LIMIT);
+ BIND_CONSTANT( PARAM_LINEAR_LIMIT_SOFTNESS);
+ BIND_CONSTANT( PARAM_LINEAR_RESTITUTION);
+ BIND_CONSTANT( PARAM_LINEAR_DAMPING);
+ BIND_CONSTANT( PARAM_ANGULAR_LOWER_LIMIT);
+ BIND_CONSTANT( PARAM_ANGULAR_UPPER_LIMIT);
+ BIND_CONSTANT( PARAM_ANGULAR_LIMIT_SOFTNESS);
+ BIND_CONSTANT( PARAM_ANGULAR_DAMPING);
+ BIND_CONSTANT( PARAM_ANGULAR_RESTITUTION);
+ BIND_CONSTANT( PARAM_ANGULAR_FORCE_LIMIT);
+ BIND_CONSTANT( PARAM_ANGULAR_ERP);
+ BIND_CONSTANT( PARAM_ANGULAR_MOTOR_TARGET_VELOCITY);
+ BIND_CONSTANT( PARAM_ANGULAR_MOTOR_FORCE_LIMIT);
+ BIND_CONSTANT( PARAM_MAX);
+
+ BIND_CONSTANT( FLAG_ENABLE_LINEAR_LIMIT);
+ BIND_CONSTANT( FLAG_ENABLE_ANGULAR_LIMIT);
+ BIND_CONSTANT( FLAG_ENABLE_MOTOR);
+ BIND_CONSTANT( FLAG_MAX );
+}
+
+
+void Generic6DOFJoint::set_param_x(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params_x[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(get_joint(),Vector3::AXIS_X,PhysicsServer::G6DOFJointAxisParam(p_param),p_value);
+
+ update_gizmo();
+}
+float Generic6DOFJoint::get_param_x(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params_x[p_param];
+}
+
+void Generic6DOFJoint::set_param_y(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params_y[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(get_joint(),Vector3::AXIS_Y,PhysicsServer::G6DOFJointAxisParam(p_param),p_value);
+ update_gizmo();
+
+}
+float Generic6DOFJoint::get_param_y(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params_y[p_param];
+}
+
+
+void Generic6DOFJoint::set_param_z(Param p_param,float p_value){
+
+ ERR_FAIL_INDEX(p_param,PARAM_MAX);
+ params_z[p_param]=p_value;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(get_joint(),Vector3::AXIS_Z,PhysicsServer::G6DOFJointAxisParam(p_param),p_value);
+ update_gizmo();
+}
+float Generic6DOFJoint::get_param_z(Param p_param) const{
+
+ ERR_FAIL_INDEX_V(p_param,PARAM_MAX,0);
+ return params_z[p_param];
+}
+
+
+void Generic6DOFJoint::set_flag_x(Flag p_flag,bool p_enabled){
+
+ ERR_FAIL_INDEX(p_flag,FLAG_MAX);
+ flags_x[p_flag]=p_enabled;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(get_joint(),Vector3::AXIS_X,PhysicsServer::G6DOFJointAxisFlag(p_flag),p_enabled);
+ update_gizmo();
+
+}
+bool Generic6DOFJoint::get_flag_x(Flag p_flag) const{
+
+ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false);
+ return flags_x[p_flag];
+
+}
+
+void Generic6DOFJoint::set_flag_y(Flag p_flag,bool p_enabled){
+
+ ERR_FAIL_INDEX(p_flag,FLAG_MAX);
+ flags_y[p_flag]=p_enabled;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(get_joint(),Vector3::AXIS_Y,PhysicsServer::G6DOFJointAxisFlag(p_flag),p_enabled);
+ update_gizmo();
+}
+bool Generic6DOFJoint::get_flag_y(Flag p_flag) const{
+
+ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false);
+ return flags_y[p_flag];
+
+}
+
+void Generic6DOFJoint::set_flag_z(Flag p_flag,bool p_enabled){
+
+ ERR_FAIL_INDEX(p_flag,FLAG_MAX);
+ flags_z[p_flag]=p_enabled;
+ if (get_joint().is_valid())
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(get_joint(),Vector3::AXIS_Z,PhysicsServer::G6DOFJointAxisFlag(p_flag),p_enabled);
+ update_gizmo();
+}
+bool Generic6DOFJoint::get_flag_z(Flag p_flag) const{
+
+ ERR_FAIL_INDEX_V(p_flag,FLAG_MAX,false);
+ return flags_z[p_flag];
+
+}
+
+RID Generic6DOFJoint::_configure_joint(PhysicsBody *body_a,PhysicsBody *body_b) {
+
+
+ Transform gt = get_global_transform();
+ //Vector3 cone_twistpos = gt.origin;
+ //Vector3 cone_twistdir = gt.basis.get_axis(2);
+
+ Transform ainv = body_a->get_global_transform().affine_inverse();
+
+ Transform local_a = ainv * gt;
+ local_a.orthonormalize();
+ Transform local_b = gt;
+
+ if (body_b) {
+ Transform binv = body_b->get_global_transform().affine_inverse();
+ local_b = binv * gt;
+ }
+
+ local_b.orthonormalize();
+
+ RID j = PhysicsServer::get_singleton()->joint_create_generic_6dof(body_a->get_rid(),local_a,body_b?body_b->get_rid():RID(),local_b);
+ for(int i=0;i<PARAM_MAX;i++) {
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j,Vector3::AXIS_X,PhysicsServer::G6DOFJointAxisParam(i),params_x[i]);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j,Vector3::AXIS_Y,PhysicsServer::G6DOFJointAxisParam(i),params_y[i]);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_param(j,Vector3::AXIS_Z,PhysicsServer::G6DOFJointAxisParam(i),params_z[i]);
+ }
+ for(int i=0;i<FLAG_MAX;i++) {
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j,Vector3::AXIS_X,PhysicsServer::G6DOFJointAxisFlag(i),flags_x[i]);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j,Vector3::AXIS_Y,PhysicsServer::G6DOFJointAxisFlag(i),flags_y[i]);
+ PhysicsServer::get_singleton()->generic_6dof_joint_set_flag(j,Vector3::AXIS_Z,PhysicsServer::G6DOFJointAxisFlag(i),flags_z[i]);
+ }
+
+ return j;
+}
+
+
+Generic6DOFJoint::Generic6DOFJoint() {
+
+ set_param_x( PARAM_LINEAR_LOWER_LIMIT,0);
+ set_param_x( PARAM_LINEAR_UPPER_LIMIT,0);
+ set_param_x( PARAM_LINEAR_LIMIT_SOFTNESS,0.7);
+ set_param_x( PARAM_LINEAR_RESTITUTION,0.5);
+ set_param_x( PARAM_LINEAR_DAMPING,1.0);
+ set_param_x( PARAM_ANGULAR_LOWER_LIMIT,0);
+ set_param_x( PARAM_ANGULAR_UPPER_LIMIT,0);
+ set_param_x( PARAM_ANGULAR_LIMIT_SOFTNESS,0.5f);
+ set_param_x( PARAM_ANGULAR_DAMPING,1.0f);
+ set_param_x( PARAM_ANGULAR_RESTITUTION,0);
+ set_param_x( PARAM_ANGULAR_FORCE_LIMIT,0);
+ set_param_x( PARAM_ANGULAR_ERP,0.5);
+ set_param_x( PARAM_ANGULAR_MOTOR_TARGET_VELOCITY,0);
+ set_param_x( PARAM_ANGULAR_MOTOR_FORCE_LIMIT,300);
+
+ set_flag_x( FLAG_ENABLE_ANGULAR_LIMIT,true);
+ set_flag_x( FLAG_ENABLE_LINEAR_LIMIT,true);
+ set_flag_x( FLAG_ENABLE_MOTOR,false);
+
+ set_param_y( PARAM_LINEAR_LOWER_LIMIT,0);
+ set_param_y( PARAM_LINEAR_UPPER_LIMIT,0);
+ set_param_y( PARAM_LINEAR_LIMIT_SOFTNESS,0.7);
+ set_param_y( PARAM_LINEAR_RESTITUTION,0.5);
+ set_param_y( PARAM_LINEAR_DAMPING,1.0);
+ set_param_y( PARAM_ANGULAR_LOWER_LIMIT,0);
+ set_param_y( PARAM_ANGULAR_UPPER_LIMIT,0);
+ set_param_y( PARAM_ANGULAR_LIMIT_SOFTNESS,0.5f);
+ set_param_y( PARAM_ANGULAR_DAMPING,1.0f);
+ set_param_y( PARAM_ANGULAR_RESTITUTION,0);
+ set_param_y( PARAM_ANGULAR_FORCE_LIMIT,0);
+ set_param_y( PARAM_ANGULAR_ERP,0.5);
+ set_param_y( PARAM_ANGULAR_MOTOR_TARGET_VELOCITY,0);
+ set_param_y( PARAM_ANGULAR_MOTOR_FORCE_LIMIT,300);
+
+ set_flag_y( FLAG_ENABLE_ANGULAR_LIMIT,true);
+ set_flag_y( FLAG_ENABLE_LINEAR_LIMIT,true);
+ set_flag_y( FLAG_ENABLE_MOTOR,false);
+
+
+ set_param_z( PARAM_LINEAR_LOWER_LIMIT,0);
+ set_param_z( PARAM_LINEAR_UPPER_LIMIT,0);
+ set_param_z( PARAM_LINEAR_LIMIT_SOFTNESS,0.7);
+ set_param_z( PARAM_LINEAR_RESTITUTION,0.5);
+ set_param_z( PARAM_LINEAR_DAMPING,1.0);
+ set_param_z( PARAM_ANGULAR_LOWER_LIMIT,0);
+ set_param_z( PARAM_ANGULAR_UPPER_LIMIT,0);
+ set_param_z( PARAM_ANGULAR_LIMIT_SOFTNESS,0.5f);
+ set_param_z( PARAM_ANGULAR_DAMPING,1.0f);
+ set_param_z( PARAM_ANGULAR_RESTITUTION,0);
+ set_param_z( PARAM_ANGULAR_FORCE_LIMIT,0);
+ set_param_z( PARAM_ANGULAR_ERP,0.5);
+ set_param_z( PARAM_ANGULAR_MOTOR_TARGET_VELOCITY,0);
+ set_param_z( PARAM_ANGULAR_MOTOR_FORCE_LIMIT,300);
+
+ set_flag_z( FLAG_ENABLE_ANGULAR_LIMIT,true);
+ set_flag_z( FLAG_ENABLE_LINEAR_LIMIT,true);
+ set_flag_z( FLAG_ENABLE_MOTOR,false);
+
+}
+
+
+
+
#if 0
void PhysicsJoint::_set(const String& p_name, const Variant& p_value) {
diff --git a/scene/3d/physics_joint.h b/scene/3d/physics_joint.h
index 4a0c609e69..6daa06da2b 100644
--- a/scene/3d/physics_joint.h
+++ b/scene/3d/physics_joint.h
@@ -32,6 +32,313 @@
#include "scene/3d/spatial.h"
#include "scene/3d/physics_body.h"
+
+class Joint : public Spatial {
+
+ OBJ_TYPE(Joint,Spatial);
+
+ RID ba,bb;
+
+ RID joint;
+
+ NodePath a;
+ NodePath b;
+
+
+protected:
+
+ void _update_joint(bool p_only_free=false);
+
+ void _notification(int p_what);
+
+ virtual RID _configure_joint(PhysicsBody *body_a,PhysicsBody *body_b)=0;
+
+ static void _bind_methods();
+public:
+
+ void set_node_a(const NodePath& p_node_a);
+ NodePath get_node_a() const;
+
+ void set_node_b(const NodePath& p_node_b);
+ NodePath get_node_b() const;
+
+ RID get_joint() const { return joint; }
+ Joint();
+
+};
+
+///////////////////////////////////////////
+
+
+class PinJoint : public Joint {
+
+ OBJ_TYPE(PinJoint,Joint);
+public:
+
+ enum Param {
+ PARAM_BIAS=PhysicsServer::PIN_JOINT_BIAS,
+ PARAM_DAMPING=PhysicsServer::PIN_JOINT_DAMPING,
+ PARAM_IMPULSE_CLAMP=PhysicsServer::PIN_JOINT_IMPULSE_CLAMP
+ };
+
+protected:
+
+ float params[3];
+ virtual RID _configure_joint(PhysicsBody *body_a, PhysicsBody *body_b);
+ static void _bind_methods();
+public:
+
+ void set_param(Param p_param,float p_value);
+ float get_param(Param p_param) const;
+
+ PinJoint();
+};
+
+VARIANT_ENUM_CAST(PinJoint::Param);
+
+
+class HingeJoint : public Joint {
+
+ OBJ_TYPE(HingeJoint,Joint);
+public:
+
+ enum Param {
+ PARAM_BIAS=PhysicsServer::HINGE_JOINT_BIAS,
+ PARAM_LIMIT_UPPER=PhysicsServer::HINGE_JOINT_LIMIT_UPPER,
+ PARAM_LIMIT_LOWER=PhysicsServer::HINGE_JOINT_LIMIT_LOWER,
+ PARAM_LIMIT_BIAS=PhysicsServer::HINGE_JOINT_LIMIT_BIAS,
+ PARAM_LIMIT_SOFTNESS=PhysicsServer::HINGE_JOINT_LIMIT_SOFTNESS,
+ PARAM_LIMIT_RELAXATION=PhysicsServer::HINGE_JOINT_LIMIT_RELAXATION,
+ PARAM_MOTOR_TARGET_VELOCITY=PhysicsServer::HINGE_JOINT_MOTOR_TARGET_VELOCITY,
+ PARAM_MOTOR_MAX_IMPULSE=PhysicsServer::HINGE_JOINT_MOTOR_MAX_IMPULSE,
+ PARAM_MAX=PhysicsServer::HINGE_JOINT_MAX
+ };
+
+ enum Flag {
+ FLAG_USE_LIMIT=PhysicsServer::HINGE_JOINT_FLAG_USE_LIMIT,
+ FLAG_ENABLE_MOTOR=PhysicsServer::HINGE_JOINT_FLAG_ENABLE_MOTOR,
+ FLAG_MAX=PhysicsServer::HINGE_JOINT_FLAG_MAX
+ };
+
+
+
+protected:
+
+ float params[PARAM_MAX];
+ bool flags[FLAG_MAX];
+ virtual RID _configure_joint(PhysicsBody *body_a, PhysicsBody *body_b);
+ static void _bind_methods();
+
+ void _set_upper_limit(float p_limit);
+ float _get_upper_limit() const;
+
+ void _set_lower_limit(float p_limit);
+ float _get_lower_limit() const;
+
+public:
+
+ void set_param(Param p_param,float p_value);
+ float get_param(Param p_param) const;
+
+ void set_flag(Flag p_flag,bool p_value);
+ bool get_flag(Flag p_flag) const;
+
+ HingeJoint();
+};
+
+VARIANT_ENUM_CAST(HingeJoint::Param);
+VARIANT_ENUM_CAST(HingeJoint::Flag);
+
+
+class SliderJoint : public Joint {
+
+ OBJ_TYPE(SliderJoint,Joint);
+public:
+
+ enum Param {
+ PARAM_LINEAR_LIMIT_UPPER=PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_UPPER,
+ PARAM_LINEAR_LIMIT_LOWER=PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_LOWER,
+ PARAM_LINEAR_LIMIT_SOFTNESS=PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_SOFTNESS,
+ PARAM_LINEAR_LIMIT_RESTITUTION=PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_RESTITUTION,
+ PARAM_LINEAR_LIMIT_DAMPING=PhysicsServer::SLIDER_JOINT_LINEAR_LIMIT_DAMPING,
+ PARAM_LINEAR_MOTION_SOFTNESS=PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_SOFTNESS,
+ PARAM_LINEAR_MOTION_RESTITUTION=PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_RESTITUTION,
+ PARAM_LINEAR_MOTION_DAMPING=PhysicsServer::SLIDER_JOINT_LINEAR_MOTION_DAMPING,
+ PARAM_LINEAR_ORTHOGONAL_SOFTNESS=PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_SOFTNESS,
+ PARAM_LINEAR_ORTHOGONAL_RESTITUTION=PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_RESTITUTION,
+ PARAM_LINEAR_ORTHOGONAL_DAMPING=PhysicsServer::SLIDER_JOINT_LINEAR_ORTHOGONAL_DAMPING,
+
+ PARAM_ANGULAR_LIMIT_UPPER=PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_UPPER,
+ PARAM_ANGULAR_LIMIT_LOWER=PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_LOWER,
+ PARAM_ANGULAR_LIMIT_SOFTNESS=PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_SOFTNESS,
+ PARAM_ANGULAR_LIMIT_RESTITUTION=PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_RESTITUTION,
+ PARAM_ANGULAR_LIMIT_DAMPING=PhysicsServer::SLIDER_JOINT_ANGULAR_LIMIT_DAMPING,
+ PARAM_ANGULAR_MOTION_SOFTNESS=PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_SOFTNESS,
+ PARAM_ANGULAR_MOTION_RESTITUTION=PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_RESTITUTION,
+ PARAM_ANGULAR_MOTION_DAMPING=PhysicsServer::SLIDER_JOINT_ANGULAR_MOTION_DAMPING,
+ PARAM_ANGULAR_ORTHOGONAL_SOFTNESS=PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_SOFTNESS,
+ PARAM_ANGULAR_ORTHOGONAL_RESTITUTION=PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_RESTITUTION,
+ PARAM_ANGULAR_ORTHOGONAL_DAMPING=PhysicsServer::SLIDER_JOINT_ANGULAR_ORTHOGONAL_DAMPING,
+ PARAM_MAX=PhysicsServer::SLIDER_JOINT_MAX
+
+ };
+
+protected:
+
+
+
+ void _set_upper_limit_angular(float p_limit_angular);
+ float _get_upper_limit_angular() const;
+
+ void _set_lower_limit_angular(float p_limit_angular);
+ float _get_lower_limit_angular() const;
+
+ float params[PARAM_MAX];
+ virtual RID _configure_joint(PhysicsBody *body_a, PhysicsBody *body_b);
+ static void _bind_methods();
+public:
+
+ void set_param(Param p_param,float p_value);
+ float get_param(Param p_param) const;
+
+ SliderJoint();
+};
+
+
+VARIANT_ENUM_CAST(SliderJoint::Param);
+
+
+
+
+class ConeTwistJoint : public Joint {
+
+ OBJ_TYPE(ConeTwistJoint,Joint);
+public:
+
+ enum Param {
+
+ PARAM_SWING_SPAN,
+ PARAM_TWIST_SPAN,
+ PARAM_BIAS,
+ PARAM_SOFTNESS,
+ PARAM_RELAXATION,
+ PARAM_MAX
+ };
+
+protected:
+
+
+ void _set_swing_span(float p_limit_angular);
+ float _get_swing_span() const;
+
+ void _set_twist_span(float p_limit_angular);
+ float _get_twist_span() const;
+
+ float params[PARAM_MAX];
+ virtual RID _configure_joint(PhysicsBody *body_a, PhysicsBody *body_b);
+ static void _bind_methods();
+public:
+
+ void set_param(Param p_param,float p_value);
+ float get_param(Param p_param) const;
+
+ ConeTwistJoint();
+};
+
+
+VARIANT_ENUM_CAST(ConeTwistJoint::Param);
+
+
+class Generic6DOFJoint : public Joint {
+
+ OBJ_TYPE(Generic6DOFJoint,Joint);
+public:
+
+ enum Param {
+
+ PARAM_LINEAR_LOWER_LIMIT=PhysicsServer::G6DOF_JOINT_LINEAR_LOWER_LIMIT,
+ PARAM_LINEAR_UPPER_LIMIT=PhysicsServer::G6DOF_JOINT_LINEAR_UPPER_LIMIT,
+ PARAM_LINEAR_LIMIT_SOFTNESS=PhysicsServer::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS,
+ PARAM_LINEAR_RESTITUTION=PhysicsServer::G6DOF_JOINT_LINEAR_RESTITUTION,
+ PARAM_LINEAR_DAMPING=PhysicsServer::G6DOF_JOINT_LINEAR_DAMPING,
+ PARAM_ANGULAR_LOWER_LIMIT=PhysicsServer::G6DOF_JOINT_ANGULAR_LOWER_LIMIT,
+ PARAM_ANGULAR_UPPER_LIMIT=PhysicsServer::G6DOF_JOINT_ANGULAR_UPPER_LIMIT,
+ PARAM_ANGULAR_LIMIT_SOFTNESS=PhysicsServer::G6DOF_JOINT_ANGULAR_LIMIT_SOFTNESS,
+ PARAM_ANGULAR_DAMPING=PhysicsServer::G6DOF_JOINT_ANGULAR_DAMPING,
+ PARAM_ANGULAR_RESTITUTION=PhysicsServer::G6DOF_JOINT_ANGULAR_RESTITUTION,
+ PARAM_ANGULAR_FORCE_LIMIT=PhysicsServer::G6DOF_JOINT_ANGULAR_FORCE_LIMIT,
+ PARAM_ANGULAR_ERP=PhysicsServer::G6DOF_JOINT_ANGULAR_ERP,
+ PARAM_ANGULAR_MOTOR_TARGET_VELOCITY=PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_TARGET_VELOCITY,
+ PARAM_ANGULAR_MOTOR_FORCE_LIMIT=PhysicsServer::G6DOF_JOINT_ANGULAR_MOTOR_FORCE_LIMIT,
+ PARAM_MAX=PhysicsServer::G6DOF_JOINT_MAX,
+ };
+
+ enum Flag {
+ FLAG_ENABLE_LINEAR_LIMIT=PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT,
+ FLAG_ENABLE_ANGULAR_LIMIT=PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_ANGULAR_LIMIT,
+ FLAG_ENABLE_MOTOR=PhysicsServer::G6DOF_JOINT_FLAG_ENABLE_MOTOR,
+ FLAG_MAX=PhysicsServer::G6DOF_JOINT_FLAG_MAX
+ };
+
+
+protected:
+
+
+ void _set_angular_hi_limit_x(float p_limit_angular);
+ float _get_angular_hi_limit_x() const;
+
+ void _set_angular_hi_limit_y(float p_limit_angular);
+ float _get_angular_hi_limit_y() const;
+
+ void _set_angular_hi_limit_z(float p_limit_angular);
+ float _get_angular_hi_limit_z() const;
+
+ void _set_angular_lo_limit_x(float p_limit_angular);
+ float _get_angular_lo_limit_x() const;
+
+ void _set_angular_lo_limit_y(float p_limit_angular);
+ float _get_angular_lo_limit_y() const;
+
+ void _set_angular_lo_limit_z(float p_limit_angular);
+ float _get_angular_lo_limit_z() const;
+
+ float params_x[PARAM_MAX];
+ bool flags_x[FLAG_MAX];
+ float params_y[PARAM_MAX];
+ bool flags_y[FLAG_MAX];
+ float params_z[PARAM_MAX];
+ bool flags_z[FLAG_MAX];
+
+ virtual RID _configure_joint(PhysicsBody *body_a, PhysicsBody *body_b);
+ static void _bind_methods();
+public:
+
+ void set_param_x(Param p_param,float p_value);
+ float get_param_x(Param p_param) const;
+
+ void set_param_y(Param p_param,float p_value);
+ float get_param_y(Param p_param) const;
+
+ void set_param_z(Param p_param,float p_value);
+ float get_param_z(Param p_param) const;
+
+ void set_flag_x(Flag p_flag,bool p_enabled);
+ bool get_flag_x(Flag p_flag) const;
+
+ void set_flag_y(Flag p_flag,bool p_enabled);
+ bool get_flag_y(Flag p_flag) const;
+
+ void set_flag_z(Flag p_flag,bool p_enabled);
+ bool get_flag_z(Flag p_flag) const;
+
+ Generic6DOFJoint();
+};
+
+
+VARIANT_ENUM_CAST(Generic6DOFJoint::Param);
+VARIANT_ENUM_CAST(Generic6DOFJoint::Flag);
+
+
#if 0
class PhysicsJoint : public Spatial {
diff --git a/scene/3d/skeleton.cpp b/scene/3d/skeleton.cpp
index 323bfa4dc4..858ee4e4ad 100644
--- a/scene/3d/skeleton.cpp
+++ b/scene/3d/skeleton.cpp
@@ -237,6 +237,14 @@ Transform Skeleton::get_bone_transform(int p_bone) const {
return bones[p_bone].pose_global * bones[p_bone].rest_global_inverse;
}
+Transform Skeleton::get_bone_global_pose(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;
+}
+
RID Skeleton::get_skeleton() const {
return skeleton;
@@ -511,6 +519,8 @@ void Skeleton::_bind_methods() {
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_global_pose","bone_idx"),&Skeleton::get_bone_global_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);
diff --git a/scene/3d/skeleton.h b/scene/3d/skeleton.h
index c95734fbf1..3e0ab0afd7 100644
--- a/scene/3d/skeleton.h
+++ b/scene/3d/skeleton.h
@@ -116,6 +116,7 @@ public:
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;
+ Transform get_bone_global_pose(int p_bone) const;
void set_bone_enabled(int p_bone, bool p_enabled);
bool is_bone_enabled(int p_bone) const;
diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp
index c52503870f..13094300d0 100644
--- a/scene/3d/spatial.cpp
+++ b/scene/3d/spatial.cpp
@@ -506,6 +506,86 @@ Transform Spatial::get_import_transform() const {
#endif
+void Spatial::_propagate_visibility_changed() {
+
+ notification(NOTIFICATION_VISIBILITY_CHANGED);
+ emit_signal(SceneStringNames::get_singleton()->visibility_changed);
+ _change_notify("visibility/visible");
+
+ for (List<Spatial*>::Element*E=data.children.front();E;E=E->next()) {
+
+ Spatial *c=E->get();
+ if (!c || !c->data.visible)
+ continue;
+ c->_propagate_visibility_changed();
+ }
+}
+
+
+void Spatial::show() {
+
+ if (data.visible)
+ return;
+
+ data.visible=true;
+
+ if (!is_inside_scene())
+ return;
+
+ if (!data.parent || is_visible()) {
+
+ _propagate_visibility_changed();
+ }
+}
+
+void Spatial::hide(){
+
+ if (!data.visible)
+ return;
+
+ bool was_visible = is_visible();
+ data.visible=false;
+
+ if (!data.parent || was_visible) {
+
+ _propagate_visibility_changed();
+ }
+
+}
+bool Spatial::is_visible() const{
+
+ const Spatial *s=this;
+
+ while(s) {
+ if (!s->data.visible) {
+ return false;
+ }
+ s=s->data.parent;
+ }
+
+ return true;
+}
+
+
+bool Spatial::is_hidden() const{
+
+ return !data.visible;
+}
+
+void Spatial::_set_visible_(bool p_visible) {
+
+ if (p_visible)
+ show();
+ else
+ hide();
+}
+
+bool Spatial::_is_visible_() const {
+
+ return !is_hidden();
+}
+
+
void Spatial::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_transform","local"), &Spatial::set_transform);
@@ -537,9 +617,18 @@ void Spatial::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_gizmo","gizmo:SpatialGizmo"), &Spatial::set_gizmo);
ObjectTypeDB::bind_method(_MD("get_gizmo:SpatialGizmo"), &Spatial::get_gizmo);
+ ObjectTypeDB::bind_method(_MD("show"), &Spatial::show);
+ ObjectTypeDB::bind_method(_MD("hide"), &Spatial::hide);
+ ObjectTypeDB::bind_method(_MD("is_visible"), &Spatial::is_visible);
+ ObjectTypeDB::bind_method(_MD("is_hidden"), &Spatial::is_hidden);
+
+ ObjectTypeDB::bind_method(_MD("_set_visible_"), &Spatial::_set_visible_);
+ ObjectTypeDB::bind_method(_MD("_is_visible_"), &Spatial::_is_visible_);
+
BIND_CONSTANT( NOTIFICATION_TRANSFORM_CHANGED );
BIND_CONSTANT( NOTIFICATION_ENTER_WORLD );
BIND_CONSTANT( NOTIFICATION_EXIT_WORLD );
+ BIND_CONSTANT( NOTIFICATION_VISIBILITY_CHANGED );
//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") );
@@ -547,8 +636,11 @@ void Spatial::_bind_methods() {
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::BOOL,"visibility/visible"), _SCS("_set_visible_"), _SCS("_is_visible_") );
//ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/local"), _SCS("set_transform"), _SCS("get_transform") );
+ ADD_SIGNAL( MethodInfo("visibility_changed" ) );
+
}
@@ -564,6 +656,7 @@ Spatial::Spatial() : xform_change(this)
data.scale=Vector3(1,1,1);
data.viewport=NULL;
data.inside_world=false;
+ data.visible=true;
#ifdef TOOLS_ENABLED
data.gizmo_disabled=false;
data.gizmo_dirty=false;
diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h
index e1119be515..f2ec26eb58 100644
--- a/scene/3d/spatial.h
+++ b/scene/3d/spatial.h
@@ -91,6 +91,8 @@ class Spatial : public Node {
bool ignore_notification;
+ bool visible;
+
#ifdef TOOLS_ENABLED
Ref<SpatialGizmo> gizmo;
bool gizmo_disabled;
@@ -109,6 +111,8 @@ class Spatial : public Node {
void _set_rotation_deg(const Vector3& p_deg);
Vector3 _get_rotation_deg() const;
+ void _propagate_visibility_changed();
+
protected:
@@ -118,7 +122,9 @@ protected:
void _notification(int p_what);
static void _bind_methods();
-
+
+ void _set_visible_(bool p_visible);
+ bool _is_visible_() const;
public:
enum {
@@ -126,6 +132,7 @@ public:
NOTIFICATION_TRANSFORM_CHANGED=SceneMainLoop::NOTIFICATION_TRANSFORM_CHANGED,
NOTIFICATION_ENTER_WORLD=41,
NOTIFICATION_EXIT_WORLD=42,
+ NOTIFICATION_VISIBILITY_CHANGED=43,
};
Spatial *get_parent_spatial() const;
@@ -159,6 +166,11 @@ public:
Transform get_relative_transform(const Node *p_parent) const;
+ void show();
+ void hide();
+ bool is_visible() const;
+ bool is_hidden() const;
+
#ifdef TOOLS_ENABLED
void set_import_transform(const Transform& p_transform) ;
Transform get_import_transform() const;
diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp
new file mode 100644
index 0000000000..07abd1dcd2
--- /dev/null
+++ b/scene/3d/vehicle_body.cpp
@@ -0,0 +1,1044 @@
+#include "vehicle_body.h"
+
+#define ROLLING_INFLUENCE_FIX
+
+class btVehicleJacobianEntry
+{
+public:
+
+ Vector3 m_linearJointAxis;
+ Vector3 m_aJ;
+ Vector3 m_bJ;
+ Vector3 m_0MinvJt;
+ Vector3 m_1MinvJt;
+ //Optimization: can be stored in the w/last component of one of the vectors
+ real_t m_Adiag;
+
+ real_t getDiagonal() const { return m_Adiag; }
+
+ btVehicleJacobianEntry() {};
+ //constraint between two different rigidbodies
+ btVehicleJacobianEntry(
+ const Matrix3& world2A,
+ const Matrix3& world2B,
+ const Vector3& rel_pos1,
+ const Vector3& rel_pos2,
+ const Vector3& jointAxis,
+ const Vector3& inertiaInvA,
+ const real_t massInvA,
+ const Vector3& inertiaInvB,
+ const real_t massInvB)
+ :m_linearJointAxis(jointAxis)
+ {
+ m_aJ = world2A.xform(rel_pos1.cross(m_linearJointAxis));
+ m_bJ = world2B.xform(rel_pos2.cross(-m_linearJointAxis));
+ m_0MinvJt = inertiaInvA * m_aJ;
+ m_1MinvJt = inertiaInvB * m_bJ;
+ m_Adiag = massInvA + m_0MinvJt.dot(m_aJ) + massInvB + m_1MinvJt.dot(m_bJ);
+
+ //btAssert(m_Adiag > real_t(0.0));
+ }
+
+ real_t getRelativeVelocity(const Vector3& linvelA,const Vector3& angvelA,const Vector3& linvelB,const Vector3& angvelB)
+ {
+ Vector3 linrel = linvelA - linvelB;
+ Vector3 angvela = angvelA * m_aJ;
+ Vector3 angvelb = angvelB * m_bJ;
+ linrel *= m_linearJointAxis;
+ angvela += angvelb;
+ angvela += linrel;
+ real_t rel_vel2 = angvela[0]+angvela[1]+angvela[2];
+ return rel_vel2 + CMP_EPSILON;
+ }
+
+
+};
+
+void VehicleWheel::_notification(int p_what) {
+
+
+ if (p_what==NOTIFICATION_ENTER_SCENE) {
+
+ if (!get_parent())
+ return;
+ VehicleBody *cb = get_parent()->cast_to<VehicleBody>();
+ if (!cb)
+ return;
+ body=cb;
+ local_xform=get_transform();
+ cb->wheels.push_back(this);
+
+ m_chassisConnectionPointCS = get_transform().origin;
+ m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
+ m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized();
+
+ }
+ if (p_what==NOTIFICATION_EXIT_SCENE) {
+
+ if (!get_parent())
+ return;
+ VehicleBody *cb = get_parent()->cast_to<VehicleBody>();
+ if (!cb)
+ return;
+ cb->wheels.erase(this);
+ body=NULL;
+ }
+
+}
+
+
+void VehicleWheel::_update(PhysicsDirectBodyState *s) {
+
+
+
+ if (m_raycastInfo.m_isInContact)
+
+ {
+ real_t project= m_raycastInfo.m_contactNormalWS.dot( m_raycastInfo.m_wheelDirectionWS );
+ Vector3 chassis_velocity_at_contactPoint;
+ Vector3 relpos = m_raycastInfo.m_contactPointWS - s->get_transform().origin;
+
+ chassis_velocity_at_contactPoint = s->get_linear_velocity() +
+ (s->get_angular_velocity()).cross(relpos);// * mPos);
+
+ real_t projVel = m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint );
+ if ( project >= real_t(-0.1))
+ {
+ m_suspensionRelativeVelocity = real_t(0.0);
+ m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
+ }
+ else
+ {
+ real_t inv = real_t(-1.) / project;
+ m_suspensionRelativeVelocity = projVel * inv;
+ m_clippedInvContactDotSuspension = inv;
+ }
+
+ }
+
+ else // Not in contact : position wheel in a nice (rest length) position
+ {
+ m_raycastInfo.m_suspensionLength = m_suspensionRestLength;
+ m_suspensionRelativeVelocity = real_t(0.0);
+ m_raycastInfo.m_contactNormalWS = -m_raycastInfo.m_wheelDirectionWS;
+ m_clippedInvContactDotSuspension = real_t(1.0);
+ }
+}
+
+void VehicleWheel::set_radius(float p_radius) {
+
+ m_wheelRadius=p_radius;
+ update_gizmo();
+}
+
+float VehicleWheel::get_radius() const{
+
+ return m_wheelRadius;
+}
+
+void VehicleWheel::set_suspension_rest_length(float p_length){
+
+ m_suspensionRestLength=p_length;
+ update_gizmo();
+}
+float VehicleWheel::get_suspension_rest_length() const{
+
+ return m_suspensionRestLength;
+}
+
+void VehicleWheel::set_suspension_travel(float p_length){
+
+ m_maxSuspensionTravelCm=p_length/0.01;
+}
+float VehicleWheel::get_suspension_travel() const{
+
+ return m_maxSuspensionTravelCm*0.01;
+}
+
+void VehicleWheel::set_suspension_stiffness(float p_value){
+
+ m_suspensionStiffness=p_value;
+}
+float VehicleWheel::get_suspension_stiffness() const{
+
+ return m_suspensionStiffness;
+}
+
+void VehicleWheel::set_suspension_max_force(float p_value){
+
+ m_maxSuspensionForce=p_value;
+}
+float VehicleWheel::get_suspension_max_force() const{
+
+ return m_maxSuspensionForce;
+}
+
+void VehicleWheel::set_damping_compression(float p_value){
+
+ m_wheelsDampingCompression=p_value;
+}
+float VehicleWheel::get_damping_compression() const{
+
+ return m_wheelsDampingRelaxation;
+}
+
+void VehicleWheel::set_damping_relaxation(float p_value){
+
+ m_wheelsDampingRelaxation=p_value;
+}
+float VehicleWheel::get_damping_relaxation() const{
+
+ return m_wheelsDampingRelaxation;
+}
+
+void VehicleWheel::set_friction_slip(float p_value) {
+
+ m_frictionSlip=p_value;
+}
+float VehicleWheel::get_friction_slip() const{
+
+ return m_frictionSlip;
+}
+
+
+void VehicleWheel::_bind_methods() {
+
+
+ ObjectTypeDB::bind_method(_MD("set_radius","length"),&VehicleWheel::set_radius);
+ ObjectTypeDB::bind_method(_MD("get_radius"),&VehicleWheel::get_radius);
+
+ ObjectTypeDB::bind_method(_MD("set_suspension_rest_length","length"),&VehicleWheel::set_suspension_rest_length);
+ ObjectTypeDB::bind_method(_MD("get_suspension_rest_length"),&VehicleWheel::get_suspension_rest_length);
+
+ ObjectTypeDB::bind_method(_MD("set_suspension_travel","length"),&VehicleWheel::set_suspension_travel);
+ ObjectTypeDB::bind_method(_MD("get_suspension_travel"),&VehicleWheel::get_suspension_travel);
+
+ ObjectTypeDB::bind_method(_MD("set_suspension_stiffness","length"),&VehicleWheel::set_suspension_stiffness);
+ ObjectTypeDB::bind_method(_MD("get_suspension_stiffness"),&VehicleWheel::get_suspension_stiffness);
+
+ ObjectTypeDB::bind_method(_MD("set_suspension_max_force","length"),&VehicleWheel::set_suspension_max_force);
+ ObjectTypeDB::bind_method(_MD("get_suspension_max_force"),&VehicleWheel::get_suspension_max_force);
+
+
+ ObjectTypeDB::bind_method(_MD("set_damping_compression","length"),&VehicleWheel::set_damping_compression);
+ ObjectTypeDB::bind_method(_MD("get_damping_compression"),&VehicleWheel::get_damping_compression);
+
+ ObjectTypeDB::bind_method(_MD("set_damping_relaxation","length"),&VehicleWheel::set_damping_relaxation);
+ ObjectTypeDB::bind_method(_MD("get_damping_relaxation"),&VehicleWheel::get_damping_relaxation);
+
+ ObjectTypeDB::bind_method(_MD("set_use_as_traction","enable"),&VehicleWheel::set_use_as_traction);
+ ObjectTypeDB::bind_method(_MD("is_used_as_traction"),&VehicleWheel::is_used_as_traction);
+
+ ObjectTypeDB::bind_method(_MD("set_use_as_steering","enable"),&VehicleWheel::set_use_as_steering);
+ ObjectTypeDB::bind_method(_MD("is_used_as_steering"),&VehicleWheel::is_used_as_steering);
+
+ ObjectTypeDB::bind_method(_MD("set_friction_slip","length"),&VehicleWheel::set_friction_slip);
+ ObjectTypeDB::bind_method(_MD("get_friction_slip"),&VehicleWheel::get_friction_slip);
+
+
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"type/traction"),_SCS("set_use_as_traction"),_SCS("is_used_as_traction"));
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL,"type/steering"),_SCS("set_use_as_steering"),_SCS("is_used_as_steering"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/radius"),_SCS("set_radius"),_SCS("get_radius"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/rest_length"),_SCS("set_suspension_rest_length"),_SCS("get_suspension_rest_length"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"wheel/friction_slip"),_SCS("set_friction_slip"),_SCS("get_friction_slip"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/travel"),_SCS("set_suspension_travel"),_SCS("get_suspension_travel"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/stiffness"),_SCS("set_suspension_stiffness"),_SCS("get_suspension_stiffness"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"suspension/max_force"),_SCS("set_suspension_max_force"),_SCS("get_suspension_max_force"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"damping/compression"),_SCS("set_damping_compression"),_SCS("get_damping_compression"));
+ ADD_PROPERTY(PropertyInfo(Variant::REAL,"damping/relaxation"),_SCS("set_damping_relaxation"),_SCS("get_damping_relaxation"));
+
+}
+
+
+void VehicleWheel::set_use_as_traction(bool p_enable) {
+
+ engine_traction=p_enable;
+}
+
+bool VehicleWheel::is_used_as_traction() const{
+
+ return engine_traction;
+}
+
+
+void VehicleWheel::set_use_as_steering(bool p_enabled){
+
+ steers=p_enabled;
+}
+
+bool VehicleWheel::is_used_as_steering() const{
+
+ return steers;
+}
+
+
+VehicleWheel::VehicleWheel() {
+
+
+ steers=false;
+ engine_traction=false;
+
+ m_steering = real_t(0.);
+ //m_engineForce = real_t(0.);
+ m_rotation = real_t(0.);
+ m_deltaRotation = real_t(0.);
+ m_brake = real_t(0.);
+ m_rollInfluence = real_t(0.1);
+
+ m_suspensionRestLength = 0.15;
+ m_wheelRadius = 0.5;//0.28;
+ m_suspensionStiffness = 5.88;
+ m_wheelsDampingCompression = 0.83;
+ m_wheelsDampingRelaxation = 0.88;
+ m_frictionSlip = 10.5;
+ m_bIsFrontWheel = false;
+ m_maxSuspensionTravelCm = 500;
+ m_maxSuspensionForce = 6000;
+
+ m_suspensionRelativeVelocity=0;
+ m_clippedInvContactDotSuspension=1.0;
+ m_raycastInfo.m_isInContact=false;
+
+ body=NULL;
+}
+
+
+void VehicleBody::_update_wheel_transform(VehicleWheel& wheel ,PhysicsDirectBodyState *s) {
+
+ wheel.m_raycastInfo.m_isInContact = false;
+
+ Transform chassisTrans = s->get_transform();
+ //if (interpolatedTransform && (getRigidBody()->getMotionState()))
+ //{
+ // getRigidBody()->getMotionState()->getWorldTransform(chassisTrans);
+ //}
+
+ wheel.m_raycastInfo.m_hardPointWS = chassisTrans.xform( wheel.m_chassisConnectionPointCS );
+ //wheel.m_raycastInfo.m_hardPointWS+=s->get_linear_velocity()*s->get_step();
+ wheel.m_raycastInfo.m_wheelDirectionWS = chassisTrans.get_basis().xform( wheel.m_wheelDirectionCS).normalized();
+ wheel.m_raycastInfo.m_wheelAxleWS = chassisTrans.get_basis().xform( wheel.m_wheelAxleCS ).normalized();
+}
+
+void VehicleBody::_update_wheel(int p_idx,PhysicsDirectBodyState *s) {
+
+ VehicleWheel& wheel = *wheels[p_idx];
+ _update_wheel_transform(wheel,s);
+
+ Vector3 up = -wheel.m_raycastInfo.m_wheelDirectionWS;
+ const Vector3& right = wheel.m_raycastInfo.m_wheelAxleWS;
+ Vector3 fwd = up.cross(right);
+ fwd = fwd.normalized();
+// up = right.cross(fwd);
+// up.normalize();
+
+ //rotate around steering over de wheelAxleWS
+ real_t steering = wheel.steers?m_steeringValue:0.0;
+ //print_line(itos(p_idx)+": "+rtos(steering));
+
+ Matrix3 steeringMat(up,steering);
+
+ Matrix3 rotatingMat(right,-wheel.m_rotation);
+
+// if (p_idx==1)
+// print_line("steeringMat " +steeringMat);
+
+ Matrix3 basis2(
+ right[0],up[0],fwd[0],
+ right[1],up[1],fwd[1],
+ right[2],up[2],fwd[2]
+ );
+
+ wheel.m_worldTransform.set_basis(steeringMat * rotatingMat * basis2);
+ //wheel.m_worldTransform.set_basis(basis2 * (steeringMat * rotatingMat));
+ wheel.m_worldTransform.set_origin(
+ wheel.m_raycastInfo.m_hardPointWS + wheel.m_raycastInfo.m_wheelDirectionWS * wheel.m_raycastInfo.m_suspensionLength
+ );
+
+}
+
+
+real_t VehicleBody::_ray_cast(int p_idx,PhysicsDirectBodyState *s) {
+
+
+ VehicleWheel& wheel = *wheels[p_idx];
+
+ _update_wheel_transform(wheel,s);
+
+
+ real_t depth = -1;
+
+ real_t raylen = wheel.m_suspensionRestLength+wheel.m_wheelRadius;
+
+ Vector3 rayvector = wheel.m_raycastInfo.m_wheelDirectionWS * (raylen);
+ Vector3 source = wheel.m_raycastInfo.m_hardPointWS;
+ wheel.m_raycastInfo.m_contactPointWS = source + rayvector;
+ const Vector3& target = wheel.m_raycastInfo.m_contactPointWS;
+ source-=wheel.m_wheelRadius * wheel.m_raycastInfo.m_wheelDirectionWS;
+
+ real_t param = real_t(0.);
+
+
+ PhysicsDirectSpaceState::RayResult rr;
+
+
+ PhysicsDirectSpaceState *ss=s->get_space_state();
+
+ bool col = ss->intersect_ray(source,target,rr,exclude);
+
+
+ wheel.m_raycastInfo.m_groundObject = 0;
+
+ if (col)
+ {
+ //print_line("WHEEL "+itos(p_idx)+" FROM "+source+" TO: "+target);
+ //print_line("WHEEL "+itos(p_idx)+" COLLIDE? "+itos(col));
+ param = source.distance_to(rr.position)/source.distance_to(target);
+ depth = raylen * param;
+ wheel.m_raycastInfo.m_contactNormalWS = rr.normal;
+
+ wheel.m_raycastInfo.m_isInContact = true;
+ if (rr.collider)
+ wheel.m_raycastInfo.m_groundObject=rr.collider->cast_to<PhysicsBody>();
+
+
+ real_t hitDistance = param*raylen;
+ wheel.m_raycastInfo.m_suspensionLength = hitDistance - wheel.m_wheelRadius;
+ //clamp on max suspension travel
+
+ real_t minSuspensionLength = wheel.m_suspensionRestLength - wheel.m_maxSuspensionTravelCm*real_t(0.01);
+ real_t maxSuspensionLength = wheel.m_suspensionRestLength+ wheel.m_maxSuspensionTravelCm*real_t(0.01);
+ if (wheel.m_raycastInfo.m_suspensionLength < minSuspensionLength)
+ {
+ wheel.m_raycastInfo.m_suspensionLength = minSuspensionLength;
+ }
+ if (wheel.m_raycastInfo.m_suspensionLength > maxSuspensionLength)
+ {
+ wheel.m_raycastInfo.m_suspensionLength = maxSuspensionLength;
+ }
+
+ wheel.m_raycastInfo.m_contactPointWS = rr.position;
+
+ real_t denominator= wheel.m_raycastInfo.m_contactNormalWS.dot( wheel.m_raycastInfo.m_wheelDirectionWS );
+
+ Vector3 chassis_velocity_at_contactPoint;
+ //Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS-getRigidBody()->getCenterOfMassPosition();
+
+ //chassis_velocity_at_contactPoint = getRigidBody()->getVelocityInLocalPoint(relpos);
+
+ chassis_velocity_at_contactPoint = s->get_linear_velocity() +
+ (s->get_angular_velocity()).cross(wheel.m_raycastInfo.m_contactPointWS-s->get_transform().origin);// * mPos);
+
+
+ real_t projVel = wheel.m_raycastInfo.m_contactNormalWS.dot( chassis_velocity_at_contactPoint );
+
+ if ( denominator >= real_t(-0.1))
+ {
+ wheel.m_suspensionRelativeVelocity = real_t(0.0);
+ wheel.m_clippedInvContactDotSuspension = real_t(1.0) / real_t(0.1);
+ }
+ else
+ {
+ real_t inv = real_t(-1.) / denominator;
+ wheel.m_suspensionRelativeVelocity = projVel * inv;
+ wheel.m_clippedInvContactDotSuspension = inv;
+ }
+
+ } else
+ {
+ wheel.m_raycastInfo.m_isInContact = false;
+ //put wheel info as in rest position
+ wheel.m_raycastInfo.m_suspensionLength = wheel.m_suspensionRestLength;
+ wheel.m_suspensionRelativeVelocity = real_t(0.0);
+ wheel.m_raycastInfo.m_contactNormalWS = - wheel.m_raycastInfo.m_wheelDirectionWS;
+ wheel.m_clippedInvContactDotSuspension = real_t(1.0);
+ }
+
+ return depth;
+}
+
+
+void VehicleBody::_update_suspension(PhysicsDirectBodyState *s)
+{
+
+ real_t deltaTime = s->get_step();
+ real_t chassisMass = mass;
+
+ for (int w_it=0; w_it<wheels.size(); w_it++)
+ {
+ VehicleWheel& wheel_info = *wheels[w_it];
+
+
+ if ( wheel_info.m_raycastInfo.m_isInContact )
+ {
+ real_t force;
+ // Spring
+ {
+ real_t susp_length = wheel_info.m_suspensionRestLength;
+ real_t current_length = wheel_info.m_raycastInfo.m_suspensionLength;
+
+ real_t length_diff = (susp_length - current_length);
+
+ force = wheel_info.m_suspensionStiffness
+ * length_diff * wheel_info.m_clippedInvContactDotSuspension;
+ }
+
+ // Damper
+ {
+ real_t projected_rel_vel = wheel_info.m_suspensionRelativeVelocity;
+ {
+ real_t susp_damping;
+ if ( projected_rel_vel < real_t(0.0) )
+ {
+ susp_damping = wheel_info.m_wheelsDampingCompression;
+ }
+ else
+ {
+ susp_damping = wheel_info.m_wheelsDampingRelaxation;
+ }
+ force -= susp_damping * projected_rel_vel;
+ }
+ }
+
+ // RESULT
+ wheel_info.m_wheelsSuspensionForce = force * chassisMass;
+ if (wheel_info.m_wheelsSuspensionForce < real_t(0.))
+ {
+ wheel_info.m_wheelsSuspensionForce = real_t(0.);
+ }
+ }
+ else
+ {
+ wheel_info.m_wheelsSuspensionForce = real_t(0.0);
+ }
+ }
+
+}
+
+
+//bilateral constraint between two dynamic objects
+void VehicleBody::_resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3& pos1,
+ PhysicsBody* body2, const Vector3& pos2, const Vector3& normal,real_t& impulse)
+{
+
+ real_t normalLenSqr = normal.length_squared();
+ //ERR_FAIL_COND( normalLenSqr < real_t(1.1));
+
+ if (normalLenSqr > real_t(1.1))
+ {
+ impulse = real_t(0.);
+ return;
+ }
+
+ Vector3 rel_pos1 = pos1 - s->get_transform().origin;
+ Vector3 rel_pos2;
+ if (body2)
+ rel_pos2 = pos2 - body2->get_global_transform().origin;
+ //this jacobian entry could be re-used for all iterations
+
+ Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1);// * mPos);
+ Vector3 vel2;
+
+ if (body2)
+ vel2=body2->get_linear_velocity() + body2->get_angular_velocity().cross(rel_pos2);
+
+ Vector3 vel = vel1 - vel2;
+
+ Matrix3 b2trans;
+ float b2invmass=0;
+ Vector3 b2lv;
+ Vector3 b2av;
+ Vector3 b2invinertia; //todo
+
+ if (body2) {
+ b2trans = body2->get_global_transform().basis.transposed();
+ b2invmass = body2->get_inverse_mass();
+ b2lv = body2->get_linear_velocity();
+ b2av = body2->get_angular_velocity();
+ }
+
+
+
+ btVehicleJacobianEntry jac(s->get_transform().basis.transposed(),
+ b2trans,
+ rel_pos1,
+ rel_pos2,
+ normal,
+ s->get_inverse_inertia(),
+ 1.0/mass,
+ b2invinertia,
+ b2invmass);
+
+ real_t jacDiagAB = jac.getDiagonal();
+ real_t jacDiagABInv = real_t(1.) / jacDiagAB;
+
+ real_t rel_vel = jac.getRelativeVelocity(
+ s->get_linear_velocity(),
+ s->get_transform().basis.transposed().xform(s->get_angular_velocity()),
+ b2lv,
+ b2trans.xform(b2av));
+ real_t a;
+ a=jacDiagABInv;
+
+
+ rel_vel = normal.dot(vel);
+
+ //todo: move this into proper structure
+ real_t contactDamping = real_t(0.4);
+#define ONLY_USE_LINEAR_MASS
+#ifdef ONLY_USE_LINEAR_MASS
+ real_t massTerm = real_t(1.) / ((1.0/mass) + b2invmass);
+ impulse = - contactDamping * rel_vel * massTerm;
+#else
+ real_t velocityImpulse = -contactDamping * rel_vel * jacDiagABInv;
+ impulse = velocityImpulse;
+#endif
+
+}
+
+
+
+VehicleBody::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDirectBodyState *s,PhysicsBody* body1,const Vector3& frictionPosWorld,const Vector3& frictionDirectionWorld, real_t maxImpulse)
+ :m_s(s),
+ m_body1(body1),
+ m_frictionPositionWorld(frictionPosWorld),
+ m_frictionDirectionWorld(frictionDirectionWorld),
+ m_maxImpulse(maxImpulse)
+{
+ float denom0=0;
+ float denom1=0;
+
+ {
+ Vector3 r0 = frictionPosWorld - s->get_transform().origin;
+ Vector3 c0 = (r0).cross(frictionDirectionWorld);
+ Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
+ denom0= s->get_inverse_mass() + frictionDirectionWorld.dot(vec);
+ }
+
+ if (body1) {
+
+ Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin;
+ Vector3 c0 = (r0).cross(frictionDirectionWorld);
+ Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0);
+ //denom1= body1->get_inverse_mass() + frictionDirectionWorld.dot(vec);
+ denom1=0;
+
+ }
+
+
+ real_t relaxation = 1.f;
+ m_jacDiagABInv = relaxation/(denom0+denom1);
+}
+
+
+real_t VehicleBody::_calc_rolling_friction(btVehicleWheelContactPoint& contactPoint) {
+
+ real_t j1=0.f;
+
+ const Vector3& contactPosWorld = contactPoint.m_frictionPositionWorld;
+
+ Vector3 rel_pos1 = contactPosWorld - contactPoint.m_s->get_transform().origin;
+ Vector3 rel_pos2;
+ if (contactPoint.m_body1)
+ rel_pos2 = contactPosWorld - contactPoint.m_body1->get_global_transform().origin;
+
+ real_t maxImpulse = contactPoint.m_maxImpulse;
+
+ Vector3 vel1 = contactPoint.m_s->get_linear_velocity() + (contactPoint.m_s->get_angular_velocity()).cross(rel_pos1);// * mPos);
+
+ Vector3 vel2;
+ if (contactPoint.m_body1) {
+ vel2=contactPoint.m_body1->get_linear_velocity() + contactPoint.m_body1->get_angular_velocity().cross(rel_pos2);
+
+ }
+
+ Vector3 vel = vel1 - vel2;
+
+ real_t vrel = contactPoint.m_frictionDirectionWorld.dot(vel);
+
+ // calculate j that moves us to zero relative velocity
+ j1 = -vrel * contactPoint.m_jacDiagABInv;
+
+ return CLAMP(j1,-maxImpulse,maxImpulse);
+}
+
+
+static const real_t sideFrictionStiffness2 = real_t(1.0);
+void VehicleBody::_update_friction(PhysicsDirectBodyState *s) {
+
+ //calculate the impulse, so that the wheels don't move sidewards
+ int numWheel = wheels.size();
+ if (!numWheel)
+ return;
+
+ m_forwardWS.resize(numWheel);
+ m_axle.resize(numWheel);
+ m_forwardImpulse.resize(numWheel);
+ m_sideImpulse.resize(numWheel);
+
+ int numWheelsOnGround = 0;
+
+
+ //collapse all those loops into one!
+ for (int i=0;i<wheels.size();i++)
+ {
+ VehicleWheel& wheelInfo = *wheels[i];
+ if (wheelInfo.m_raycastInfo.m_isInContact)
+ numWheelsOnGround++;
+ m_sideImpulse[i] = real_t(0.);
+ m_forwardImpulse[i] = real_t(0.);
+
+ }
+
+ {
+
+ for (int i=0;i<wheels.size();i++)
+ {
+
+ VehicleWheel& wheelInfo = *wheels[i];
+
+
+ if (wheelInfo.m_raycastInfo.m_isInContact)
+ {
+
+ //const btTransform& wheelTrans = getWheelTransformWS( i );
+
+ Matrix3 wheelBasis0 = wheelInfo.m_worldTransform.basis;//get_global_transform().basis;
+
+ m_axle[i] = wheelBasis0.get_axis(Vector3::AXIS_X);
+ //m_axle[i] = wheelInfo.m_raycastInfo.m_wheelAxleWS;
+
+ const Vector3& surfNormalWS = wheelInfo.m_raycastInfo.m_contactNormalWS;
+ real_t proj = m_axle[i].dot(surfNormalWS);
+ m_axle[i] -= surfNormalWS * proj;
+ m_axle[i] = m_axle[i].normalized();
+
+ m_forwardWS[i] = surfNormalWS.cross(m_axle[i]);
+ m_forwardWS[i].normalize();
+
+
+ _resolve_single_bilateral(s, wheelInfo.m_raycastInfo.m_contactPointWS,
+ wheelInfo.m_raycastInfo.m_groundObject, wheelInfo.m_raycastInfo.m_contactPointWS,
+ m_axle[i],m_sideImpulse[i]);
+
+ m_sideImpulse[i] *= sideFrictionStiffness2;
+
+
+ }
+ }
+ }
+
+ real_t sideFactor = real_t(1.);
+ real_t fwdFactor = 0.5;
+
+ bool sliding = false;
+ {
+ for (int wheel =0;wheel <wheels.size();wheel++)
+ {
+ VehicleWheel& wheelInfo = *wheels[wheel];
+
+
+ //class btRigidBody* groundObject = (class btRigidBody*) wheelInfo.m_raycastInfo.m_groundObject;
+
+ real_t rollingFriction = 0.f;
+
+ if (wheelInfo.m_raycastInfo.m_isInContact)
+ {
+ if (engine_force != 0.f)
+ {
+ rollingFriction = engine_force* s->get_step();
+ } else
+ {
+ real_t defaultRollingFrictionImpulse = 0.f;
+ real_t maxImpulse = wheelInfo.m_brake ? wheelInfo.m_brake : defaultRollingFrictionImpulse;
+ btVehicleWheelContactPoint contactPt(s,wheelInfo.m_raycastInfo.m_groundObject,wheelInfo.m_raycastInfo.m_contactPointWS,m_forwardWS[wheel],maxImpulse);
+ rollingFriction = _calc_rolling_friction(contactPt);
+ }
+ }
+
+ //switch between active rolling (throttle), braking and non-active rolling friction (no throttle/break)
+
+
+
+
+ m_forwardImpulse[wheel] = real_t(0.);
+ wheelInfo.m_skidInfo= real_t(1.);
+
+ if (wheelInfo.m_raycastInfo.m_isInContact)
+ {
+ wheelInfo.m_skidInfo= real_t(1.);
+
+ real_t maximp = wheelInfo.m_wheelsSuspensionForce * s->get_step() * wheelInfo.m_frictionSlip;
+ real_t maximpSide = maximp;
+
+ real_t maximpSquared = maximp * maximpSide;
+
+
+ m_forwardImpulse[wheel] = rollingFriction;//wheelInfo.m_engineForce* timeStep;
+
+ real_t x = (m_forwardImpulse[wheel] ) * fwdFactor;
+ real_t y = (m_sideImpulse[wheel] ) * sideFactor;
+
+ real_t impulseSquared = (x*x + y*y);
+
+ if (impulseSquared > maximpSquared)
+ {
+ sliding = true;
+
+ real_t factor = maximp / Math::sqrt(impulseSquared);
+
+ wheelInfo.m_skidInfo *= factor;
+ }
+ }
+
+ }
+ }
+
+
+
+
+ if (sliding)
+ {
+ for (int wheel = 0;wheel < wheels.size(); wheel++)
+ {
+ if (m_sideImpulse[wheel] != real_t(0.))
+ {
+ if (wheels[wheel]->m_skidInfo< real_t(1.))
+ {
+ m_forwardImpulse[wheel] *= wheels[wheel]->m_skidInfo;
+ m_sideImpulse[wheel] *= wheels[wheel]->m_skidInfo;
+ }
+ }
+ }
+ }
+
+ // apply the impulses
+ {
+ for (int wheel = 0;wheel<wheels.size(); wheel++)
+ {
+ VehicleWheel& wheelInfo = *wheels[wheel];
+
+ Vector3 rel_pos = wheelInfo.m_raycastInfo.m_contactPointWS -
+ s->get_transform().origin;
+
+ if (m_forwardImpulse[wheel] != real_t(0.))
+ {
+ s->apply_impulse(rel_pos,m_forwardWS[wheel]*(m_forwardImpulse[wheel]));
+ }
+ if (m_sideImpulse[wheel] != real_t(0.))
+ {
+ PhysicsBody* groundObject = wheelInfo.m_raycastInfo.m_groundObject;
+
+ Vector3 rel_pos2;
+ if (groundObject) {
+ rel_pos2=wheelInfo.m_raycastInfo.m_contactPointWS - groundObject->get_global_transform().origin;
+ }
+
+
+ Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel];
+
+#if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT.
+ Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1];//getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis);
+ rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f-wheelInfo.m_rollInfluence));
+#else
+ rel_pos[1] *= wheelInfo.m_rollInfluence; //?
+#endif
+ s->apply_impulse(rel_pos,sideImp);
+
+ //apply friction impulse on the ground
+ //todo
+ //groundObject->applyImpulse(-sideImp,rel_pos2);
+ }
+ }
+ }
+
+
+}
+
+
+void VehicleBody::_direct_state_changed(Object *p_state) {
+
+
+ PhysicsDirectBodyState *s = p_state->cast_to<PhysicsDirectBodyState>();
+
+ set_ignore_transform_notification(true);
+ set_global_transform(s->get_transform());
+ set_ignore_transform_notification(false);
+
+
+ float step = s->get_step();
+
+ for(int i=0;i<wheels.size();i++) {
+
+ _update_wheel(i,s);
+ }
+
+
+ for(int i=0;i<wheels.size();i++) {
+
+ _ray_cast(i,s);
+ wheels[i]->set_transform(s->get_transform().inverse() * wheels[i]->m_worldTransform);
+ }
+
+ _update_suspension(s);
+
+ for(int i=0;i<wheels.size();i++) {
+
+ //apply suspension force
+ VehicleWheel& wheel = *wheels[i];
+
+ real_t suspensionForce = wheel.m_wheelsSuspensionForce;
+
+ if (suspensionForce > wheel.m_maxSuspensionForce)
+ {
+ suspensionForce = wheel.m_maxSuspensionForce;
+ }
+ Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step;
+ Vector3 relpos = wheel.m_raycastInfo.m_contactPointWS - s->get_transform().origin;
+
+ s->apply_impulse(relpos,impulse);
+ //getRigidBody()->applyImpulse(impulse, relpos);
+
+ }
+
+
+ _update_friction(s);
+
+
+ for (int i=0;i<wheels.size();i++)
+ {
+ VehicleWheel& wheel = *wheels[i];
+ Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - s->get_transform().origin;
+ Vector3 vel = s->get_linear_velocity() + (s->get_angular_velocity()).cross(relpos);// * mPos);
+
+ if (wheel.m_raycastInfo.m_isInContact)
+ {
+ const Transform& chassisWorldTransform = s->get_transform();
+
+ Vector3 fwd (
+ chassisWorldTransform.basis[0][Vector3::AXIS_Z],
+ chassisWorldTransform.basis[1][Vector3::AXIS_Z],
+ chassisWorldTransform.basis[2][Vector3::AXIS_Z]);
+
+ real_t proj = fwd.dot(wheel.m_raycastInfo.m_contactNormalWS);
+ fwd -= wheel.m_raycastInfo.m_contactNormalWS * proj;
+
+ real_t proj2 = fwd.dot(vel);
+
+ wheel.m_deltaRotation = (proj2 * step) / (wheel.m_wheelRadius);
+ wheel.m_rotation += wheel.m_deltaRotation;
+
+ } else
+ {
+ wheel.m_rotation += wheel.m_deltaRotation;
+ }
+
+ wheel.m_deltaRotation *= real_t(0.99);//damping of rotation when not in contact
+
+ }
+
+}
+
+void VehicleBody::set_mass(real_t p_mass) {
+
+ mass=p_mass;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_MASS,mass);
+}
+
+real_t VehicleBody::get_mass() const{
+
+ return mass;
+}
+
+
+void VehicleBody::set_friction(real_t p_friction) {
+
+ friction=p_friction;
+ PhysicsServer::get_singleton()->body_set_param(get_rid(),PhysicsServer::BODY_PARAM_FRICTION,friction);
+}
+
+real_t VehicleBody::get_friction() const{
+
+ return friction;
+}
+
+void VehicleBody::set_engine_force(float p_force) {
+
+ engine_force=p_force;
+}
+
+float VehicleBody::get_engine_force() const{
+
+ return engine_force;
+}
+
+void VehicleBody::set_brake(float p_brake){
+
+ brake=p_brake;
+}
+float VehicleBody::get_brake() const{
+
+ return brake;
+}
+
+void VehicleBody::set_steering(float p_steering){
+
+ m_steeringValue=p_steering;
+}
+float VehicleBody::get_steering() const{
+
+ return m_steeringValue;
+}
+
+
+void VehicleBody::_bind_methods(){
+
+ ObjectTypeDB::bind_method(_MD("set_mass","mass"),&VehicleBody::set_mass);
+ ObjectTypeDB::bind_method(_MD("get_mass"),&VehicleBody::get_mass);
+
+ ObjectTypeDB::bind_method(_MD("set_friction","friction"),&VehicleBody::set_friction);
+ ObjectTypeDB::bind_method(_MD("get_friction"),&VehicleBody::get_friction);
+
+ ObjectTypeDB::bind_method(_MD("set_engine_force","engine_force"),&VehicleBody::set_engine_force);
+ ObjectTypeDB::bind_method(_MD("get_engine_force"),&VehicleBody::get_engine_force);
+
+ ObjectTypeDB::bind_method(_MD("set_brake","brake"),&VehicleBody::set_brake);
+ ObjectTypeDB::bind_method(_MD("get_brake"),&VehicleBody::get_brake);
+
+ ObjectTypeDB::bind_method(_MD("set_steering","steering"),&VehicleBody::set_steering);
+ ObjectTypeDB::bind_method(_MD("get_steering"),&VehicleBody::get_steering);
+
+ ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&VehicleBody::_direct_state_changed);
+
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/engine_force",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_engine_force"),_SCS("get_engine_force"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/brake",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_brake"),_SCS("get_brake"));
+ ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/steering",PROPERTY_HINT_RANGE,"0.01,1024.0,0.01"),_SCS("set_steering"),_SCS("get_steering"));
+ 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"));
+
+
+}
+
+
+
+VehicleBody::VehicleBody() : PhysicsBody(PhysicsServer::BODY_MODE_RIGID) {
+
+
+ m_pitchControl=0;
+ m_currentVehicleSpeedKmHour = real_t(0.);
+ m_steeringValue = real_t(0.);
+
+ engine_force=0;
+ brake=0;
+
+
+
+ friction=1;
+
+ ccd=false;
+
+ exclude.insert(get_rid());
+ PhysicsServer::get_singleton()->body_set_force_integration_callback(get_rid(),this,"_direct_state_changed");
+
+ set_mass(40);
+}
+
diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h
new file mode 100644
index 0000000000..285cca142d
--- /dev/null
+++ b/scene/3d/vehicle_body.h
@@ -0,0 +1,185 @@
+#ifndef VEHICLE_BODY_H
+#define VEHICLE_BODY_H
+
+#include "scene/3d/physics_body.h"
+
+class VehicleBody;
+
+class VehicleWheel : public Spatial {
+
+ OBJ_TYPE(VehicleWheel,Spatial);
+
+friend class VehicleBody;
+
+
+ Transform m_worldTransform;
+ Transform local_xform;
+ bool engine_traction;
+ bool steers;
+
+
+ Vector3 m_chassisConnectionPointCS; //const
+ Vector3 m_wheelDirectionCS;//const
+ Vector3 m_wheelAxleCS; // const or modified by steering
+
+ real_t m_suspensionRestLength;
+ real_t m_maxSuspensionTravelCm;
+ real_t m_wheelRadius;
+
+ real_t m_suspensionStiffness;
+ real_t m_wheelsDampingCompression;
+ real_t m_wheelsDampingRelaxation;
+ real_t m_frictionSlip;
+ real_t m_maxSuspensionForce;
+ bool m_bIsFrontWheel;
+
+ VehicleBody *body;
+
+// btVector3 m_wheelAxleCS; // const or modified by steering ?
+
+ real_t m_steering;
+ real_t m_rotation;
+ real_t m_deltaRotation;
+ real_t m_rollInfluence;
+ //real_t m_engineForce;
+ real_t m_brake;
+
+ real_t m_clippedInvContactDotSuspension;
+ real_t m_suspensionRelativeVelocity;
+ //calculated by suspension
+ real_t m_wheelsSuspensionForce;
+ real_t m_skidInfo;
+
+
+ struct RaycastInfo {
+ //set by raycaster
+ Vector3 m_contactNormalWS;//contactnormal
+ Vector3 m_contactPointWS;//raycast hitpoint
+ real_t m_suspensionLength;
+ Vector3 m_hardPointWS;//raycast starting point
+ Vector3 m_wheelDirectionWS; //direction in worldspace
+ Vector3 m_wheelAxleWS; // axle in worldspace
+ bool m_isInContact;
+ PhysicsBody* m_groundObject; //could be general void* ptr
+ } m_raycastInfo;
+
+ void _update(PhysicsDirectBodyState *s);
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+
+ void set_radius(float p_radius);
+ float get_radius() const;
+
+ void set_suspension_rest_length(float p_length);
+ float get_suspension_rest_length() const;
+
+ void set_suspension_travel(float p_length);
+ float get_suspension_travel() const;
+
+ void set_suspension_stiffness(float p_value);
+ float get_suspension_stiffness() const;
+
+ void set_suspension_max_force(float p_value);
+ float get_suspension_max_force() const;
+
+ void set_damping_compression(float p_value);
+ float get_damping_compression() const;
+
+ void set_damping_relaxation(float p_value);
+ float get_damping_relaxation() const;
+
+ void set_friction_slip(float p_value);
+ float get_friction_slip() const;
+
+ void set_use_as_traction(bool p_enable);
+ bool is_used_as_traction() const;
+
+ void set_use_as_steering(bool p_enabled);
+ bool is_used_as_steering() const;
+
+ VehicleWheel();
+
+};
+
+
+class VehicleBody : public PhysicsBody {
+
+ OBJ_TYPE(VehicleBody,PhysicsBody);
+
+ real_t mass;
+ real_t friction;
+
+ float engine_force;
+ float brake;
+
+ Vector3 linear_velocity;
+ Vector3 angular_velocity;
+ bool ccd;
+
+ real_t m_pitchControl;
+ real_t m_steeringValue;
+ real_t m_currentVehicleSpeedKmHour;
+
+ Set<RID> exclude;
+
+ Vector<Vector3> m_forwardWS;
+ Vector<Vector3> m_axle;
+ Vector<real_t> m_forwardImpulse;
+ Vector<real_t> m_sideImpulse;
+
+ struct btVehicleWheelContactPoint {
+ PhysicsDirectBodyState *m_s;
+ PhysicsBody* m_body1;
+ Vector3 m_frictionPositionWorld;
+ Vector3 m_frictionDirectionWorld;
+ real_t m_jacDiagABInv;
+ real_t m_maxImpulse;
+
+
+ btVehicleWheelContactPoint(PhysicsDirectBodyState *s,PhysicsBody* body1,const Vector3& frictionPosWorld,const Vector3& frictionDirectionWorld, real_t maxImpulse);
+ };
+
+ void _resolve_single_bilateral(PhysicsDirectBodyState *s, const Vector3& pos1, PhysicsBody* body2, const Vector3& pos2, const Vector3& normal, real_t& impulse);
+ real_t _calc_rolling_friction(btVehicleWheelContactPoint& contactPoint);
+
+ void _update_friction(PhysicsDirectBodyState *s);
+ void _update_suspension(PhysicsDirectBodyState *s);
+ real_t _ray_cast(int p_idx,PhysicsDirectBodyState *s);
+ void _update_wheel_transform(VehicleWheel& wheel ,PhysicsDirectBodyState *s);
+ void _update_wheel(int p_idx,PhysicsDirectBodyState *s);
+
+
+
+friend class VehicleWheel;
+ Vector<VehicleWheel*> 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_engine_force(float p_engine_force);
+ float get_engine_force() const;
+
+ void set_brake(float p_force);
+ float get_brake() const;
+
+ void set_steering(float p_steering);
+ float get_steering() const;
+
+
+ VehicleBody();
+};
+
+#endif // VEHICLE_BODY_H
diff --git a/scene/3d/visual_instance.cpp b/scene/3d/visual_instance.cpp
index 625da9b093..af535e139f 100644
--- a/scene/3d/visual_instance.cpp
+++ b/scene/3d/visual_instance.cpp
@@ -195,6 +195,7 @@ void GeometryInstance::_notification(int p_what) {
_find_baked_light();
}
+ _update_visibility();
} else if (p_what==NOTIFICATION_EXIT_WORLD) {
@@ -207,8 +208,13 @@ void GeometryInstance::_notification(int p_what) {
_baked_light_changed();
}
+
+ } if (p_what==NOTIFICATION_VISIBILITY_CHANGED) {
+
+ _update_visibility();
}
+
}
void GeometryInstance::_baked_light_changed() {
@@ -241,6 +247,15 @@ void GeometryInstance::_find_baked_light() {
_baked_light_changed();
}
+void GeometryInstance::_update_visibility() {
+
+ if (!is_inside_scene())
+ return;
+
+ _change_notify("geometry/visible");
+ VS::get_singleton()->instance_geometry_set_flag(get_instance(),VS::INSTANCE_FLAG_VISIBLE,is_visible() && flags[FLAG_VISIBLE]);
+}
+
void GeometryInstance::set_flag(Flags p_flag,bool p_value) {
ERR_FAIL_INDEX(p_flag,FLAG_MAX);
@@ -250,8 +265,7 @@ void GeometryInstance::set_flag(Flags p_flag,bool p_value) {
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);
+ _update_visibility();
}
if (p_flag==FLAG_USE_BAKED_LIGHT) {
@@ -276,6 +290,18 @@ bool GeometryInstance::get_flag(Flags p_flag) const{
}
+void GeometryInstance::set_baked_light_texture_id(int p_id) {
+
+ baked_light_texture_id=p_id;
+ VS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),baked_light_texture_id);
+
+}
+
+int GeometryInstance::get_baked_light_texture_id() const{
+
+ return baked_light_texture_id;
+}
+
void GeometryInstance::_bind_methods() {
@@ -291,6 +317,9 @@ void GeometryInstance::_bind_methods() {
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);
+ ObjectTypeDB::bind_method(_MD("set_baked_light_texture_id","id"), &GeometryInstance::set_baked_light_texture_id);
+ ObjectTypeDB::bind_method(_MD("get_baked_light_texture_id"), &GeometryInstance::get_baked_light_texture_id);
+
ObjectTypeDB::bind_method(_MD("_baked_light_changed"), &GeometryInstance::_baked_light_changed);
ADD_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/visible"), _SCS("set_flag"), _SCS("get_flag"),FLAG_VISIBLE);
@@ -304,8 +333,9 @@ void GeometryInstance::_bind_methods() {
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_PROPERTYI( PropertyInfo( Variant::BOOL, "geometry/use_baked_light"), _SCS("set_flag"), _SCS("get_flag"),FLAG_USE_BAKED_LIGHT);
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "geometry/baked_light_tex_id"), _SCS("set_baked_light_texture_id"), _SCS("get_baked_light_texture_id"));
- ADD_SIGNAL( MethodInfo("visibility_changed"));
+// ADD_SIGNAL( MethodInfo("visibility_changed"));
BIND_CONSTANT(FLAG_VISIBLE );
BIND_CONSTANT(FLAG_CAST_SHADOW );
@@ -329,6 +359,8 @@ GeometryInstance::GeometryInstance() {
flags[FLAG_DEPH_SCALE]=false;
flags[FLAG_VISIBLE_IN_ALL_ROOMS]=false;
baked_light_instance=NULL;
+ baked_light_texture_id=0;
+ VS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),0);
}
diff --git a/scene/3d/visual_instance.h b/scene/3d/visual_instance.h
index 1cf96d5d9e..bbb49a2e78 100644
--- a/scene/3d/visual_instance.h
+++ b/scene/3d/visual_instance.h
@@ -106,8 +106,10 @@ private:
float draw_end;
void _find_baked_light();
BakedLightInstance *baked_light_instance;
+ int baked_light_texture_id;
void _baked_light_changed();
+ void _update_visibility();
protected:
void _notification(int p_what);
@@ -126,6 +128,9 @@ public:
void set_material_override(const Ref<Material>& p_material);
Ref<Material> get_material_override() const;
+ void set_baked_light_texture_id(int p_id);
+ int get_baked_light_texture_id() const;
+
GeometryInstance();
};
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index 15d3dccb71..030f3f27e0 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -637,14 +637,15 @@ void AnimationPlayer::_animation_process(float p_delta) {
play(queued.front()->get());
String new_name = playback.assigned;
queued.pop_front();
+ end_notify=false;
emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
} else {
//stop();
playing = false;
_set_process(false);
+ end_notify=false;
emit_signal(SceneStringNames::get_singleton()->finished);
}
-
}
} else {
@@ -912,7 +913,8 @@ void AnimationPlayer::play(const StringName& p_name, float p_custom_blend, float
c.current.speed_scale=p_custom_scale;
c.assigned=p_name;
- queued.clear();
+ if (!end_notify)
+ queued.clear();
_set_process(true); // always process when starting an animation
playing = true;
diff --git a/scene/animation/animation_tree_player.cpp b/scene/animation/animation_tree_player.cpp
index bd124746ba..2d1821bc5c 100644
--- a/scene/animation/animation_tree_player.cpp
+++ b/scene/animation/animation_tree_player.cpp
@@ -1046,8 +1046,9 @@ void AnimationTreePlayer::timescale_node_set_scale(const StringName& p_node,floa
void AnimationTreePlayer::timeseek_node_seek(const StringName& p_node,float p_pos) {
-// GET_NODE( NODE_TIMESEEK, TimeSeekNode );
-//hmm
+ GET_NODE( NODE_TIMESEEK, TimeSeekNode );
+ n->seek_pos=p_pos;
+
}
void AnimationTreePlayer::transition_node_set_input_count(const StringName& p_node, int p_inputs) {
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
new file mode 100644
index 0000000000..f2df6d47c9
--- /dev/null
+++ b/scene/animation/tween.cpp
@@ -0,0 +1,1219 @@
+/*************************************************************************/
+/* tween.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 "tween.h"
+
+bool Tween::_set(const StringName& p_name, const Variant& p_value) {
+
+ String name=p_name;
+
+ if (name=="playback/speed" || name=="speed") { //bw compatibility
+ set_speed(p_value);
+
+ } else if (name=="playback/active") {
+ set_active(p_value);
+
+ } else if (name=="playback/repeat") {
+ set_repeat(p_value);
+
+ }
+ return true;
+}
+
+bool Tween::_get(const StringName& p_name,Variant &r_ret) const {
+
+ String name=p_name;
+
+ if (name=="playback/speed") { //bw compatibility
+
+ r_ret=speed_scale;
+ } else if (name=="playback/active") {
+
+ r_ret=is_active();
+ } else if(name=="playback/repeat") {
+
+ r_ret=is_repeat();
+ }
+
+ return true;
+}
+
+void Tween::_get_property_list(List<PropertyInfo> *p_list) const {
+
+ p_list->push_back( PropertyInfo( Variant::BOOL, "playback/active", PROPERTY_HINT_NONE,"" ) );
+ p_list->push_back( PropertyInfo( Variant::BOOL, "playback/repeat", PROPERTY_HINT_NONE,"" ) );
+ p_list->push_back( PropertyInfo( Variant::REAL, "playback/speed", PROPERTY_HINT_RANGE, "-64,64,0.01") );
+}
+
+void Tween::_notification(int p_what) {
+
+ switch(p_what) {
+
+ case NOTIFICATION_ENTER_SCENE: {
+
+ if (!processing) {
+ //make sure that a previous process state was not saved
+ //only process if "processing" is set
+ set_fixed_process(false);
+ set_process(false);
+ }
+ } break;
+ case NOTIFICATION_READY: {
+
+ } break;
+ case NOTIFICATION_PROCESS: {
+ if (tween_process_mode==TWEEN_PROCESS_FIXED)
+ break;
+
+ if (processing)
+ _tween_process( get_process_delta_time() );
+ } break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ if (tween_process_mode==TWEEN_PROCESS_IDLE)
+ break;
+
+ if (processing)
+ _tween_process( get_fixed_process_delta_time() );
+ } break;
+ case NOTIFICATION_EXIT_SCENE: {
+
+ stop_all();
+ } break;
+ }
+}
+
+void Tween::_bind_methods() {
+
+ ObjectTypeDB::bind_method(_MD("is_active"),&Tween::is_active );
+ ObjectTypeDB::bind_method(_MD("set_active","active"),&Tween::set_active );
+
+ ObjectTypeDB::bind_method(_MD("is_repeat"),&Tween::is_repeat );
+ ObjectTypeDB::bind_method(_MD("set_repeat","repeat"),&Tween::set_repeat );
+
+ ObjectTypeDB::bind_method(_MD("set_speed","speed"),&Tween::set_speed);
+ ObjectTypeDB::bind_method(_MD("get_speed"),&Tween::get_speed);
+
+ ObjectTypeDB::bind_method(_MD("set_tween_process_mode","mode"),&Tween::set_tween_process_mode);
+ ObjectTypeDB::bind_method(_MD("get_tween_process_mode"),&Tween::get_tween_process_mode);
+
+ ObjectTypeDB::bind_method(_MD("start"),&Tween::start );
+ ObjectTypeDB::bind_method(_MD("reset","node","key"),&Tween::reset );
+ ObjectTypeDB::bind_method(_MD("reset_all"),&Tween::reset_all );
+ ObjectTypeDB::bind_method(_MD("stop","node","key"),&Tween::stop );
+ ObjectTypeDB::bind_method(_MD("stop_all"),&Tween::stop_all );
+ ObjectTypeDB::bind_method(_MD("resume","node","key"),&Tween::resume );
+ ObjectTypeDB::bind_method(_MD("resume_all"),&Tween::resume_all );
+ ObjectTypeDB::bind_method(_MD("remove","node","key"),&Tween::remove );
+ ObjectTypeDB::bind_method(_MD("remove_all"),&Tween::remove_all );
+ ObjectTypeDB::bind_method(_MD("seek","time"),&Tween::seek );
+ ObjectTypeDB::bind_method(_MD("tell"),&Tween::tell );
+ ObjectTypeDB::bind_method(_MD("get_runtime"),&Tween::get_runtime );
+
+ ObjectTypeDB::bind_method(_MD("interpolate_property","node","property","initial_val","final_val","times_in_sec","trans_type","ease_type","delay"),&Tween::interpolate_property, DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("interpolate_method","node","method","initial_val","final_val","times_in_sec","trans_type","ease_type","delay"),&Tween::interpolate_method, DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("interpolate_callback","node","callback","times_in_sec","args"),&Tween::interpolate_callback, DEFVAL(Variant()) );
+ ObjectTypeDB::bind_method(_MD("follow_property","node","property","initial_val","target","target_property","times_in_sec","trans_type","ease_type","delay"),&Tween::follow_property, DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("follow_method","node","method","initial_val","target","target_method","times_in_sec","trans_type","ease_type","delay"),&Tween::follow_method, DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("targeting_property","node","property","initial","initial_val","final_val","times_in_sec","trans_type","ease_type","delay"),&Tween::targeting_property, DEFVAL(0) );
+ ObjectTypeDB::bind_method(_MD("targeting_method","node","method","initial","initial_method","final_val","times_in_sec","trans_type","ease_type","delay"),&Tween::targeting_method, DEFVAL(0) );
+
+ ADD_SIGNAL( MethodInfo("tween_start", PropertyInfo( Variant::OBJECT,"node"), PropertyInfo( Variant::STRING,"key")) );
+ ADD_SIGNAL( MethodInfo("tween_step", PropertyInfo( Variant::OBJECT,"node"), PropertyInfo( Variant::STRING,"key"), PropertyInfo( Variant::REAL,"elapsed"), PropertyInfo( Variant::OBJECT,"value")) );
+ ADD_SIGNAL( MethodInfo("tween_complete", PropertyInfo( Variant::OBJECT,"node"), PropertyInfo( Variant::STRING,"key")) );
+
+ ADD_PROPERTY( PropertyInfo( Variant::INT, "playback/process_mode", PROPERTY_HINT_ENUM, "Fixed,Idle"), _SCS("set_tween_process_mode"), _SCS("get_tween_process_mode"));
+ //ADD_PROPERTY( PropertyInfo( Variant::BOOL, "activate"), _SCS("set_active"), _SCS("is_active"));
+
+ BIND_CONSTANT(TRANS_LINEAR);
+ BIND_CONSTANT(TRANS_SINE);
+ BIND_CONSTANT(TRANS_QUINT);
+ BIND_CONSTANT(TRANS_QUART);
+ BIND_CONSTANT(TRANS_QUAD);
+ BIND_CONSTANT(TRANS_EXPO);
+ BIND_CONSTANT(TRANS_ELASTIC);
+ BIND_CONSTANT(TRANS_CUBIC);
+ BIND_CONSTANT(TRANS_CIRC);
+ BIND_CONSTANT(TRANS_BOUNCE);
+ BIND_CONSTANT(TRANS_BACK);
+
+ BIND_CONSTANT(EASE_IN);
+ BIND_CONSTANT(EASE_OUT);
+ BIND_CONSTANT(EASE_IN_OUT);
+ BIND_CONSTANT(EASE_OUT_IN);
+}
+
+Variant& Tween::_get_initial_val(InterpolateData& p_data) {
+
+ switch(p_data.type) {
+ case INTER_PROPERTY:
+ case INTER_METHOD:
+ case FOLLOW_PROPERTY:
+ case FOLLOW_METHOD:
+ return p_data.initial_val;
+
+ case TARGETING_PROPERTY:
+ case TARGETING_METHOD: {
+
+ Node *node = get_node(p_data.target);
+ ERR_FAIL_COND_V(node == NULL,p_data.initial_val);
+
+ static Variant initial_val;
+ if(p_data.type == TARGETING_PROPERTY) {
+
+ bool valid = false;
+ initial_val = node->get(p_data.target_key, &valid);
+ ERR_FAIL_COND_V(!valid,p_data.initial_val);
+ } else {
+
+ Variant::CallError error;
+ initial_val = node->call(p_data.target_key, NULL, 0, error);
+ ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK,p_data.initial_val);
+ }
+ return initial_val;
+ }
+ break;
+ }
+ return p_data.delta_val;
+}
+
+Variant& Tween::_get_delta_val(InterpolateData& p_data) {
+
+ switch(p_data.type) {
+ case INTER_PROPERTY:
+ case INTER_METHOD:
+ return p_data.delta_val;
+
+ case FOLLOW_PROPERTY:
+ case FOLLOW_METHOD: {
+
+ Node *target = get_node(p_data.target);
+ ERR_FAIL_COND_V(target == NULL,p_data.initial_val);
+
+ Variant final_val;
+
+ if(p_data.type == FOLLOW_PROPERTY) {
+
+ bool valid = false;
+ final_val = target->get(p_data.target_key, &valid);
+ ERR_FAIL_COND_V(!valid,p_data.initial_val);
+ } else {
+
+ Variant::CallError error;
+ final_val = target->call(p_data.target_key, NULL, 0, error);
+ ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK,p_data.initial_val);
+ }
+
+ // convert INT to REAL is better for interpolaters
+ if(final_val.get_type() == Variant::INT) final_val = final_val.operator real_t();
+ _calc_delta_val(p_data.initial_val, final_val, p_data.delta_val);
+ return p_data.delta_val;
+ }
+ break;
+
+ case TARGETING_PROPERTY:
+ case TARGETING_METHOD: {
+
+ Variant initial_val = _get_initial_val(p_data);
+ // convert INT to REAL is better for interpolaters
+ if(initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
+
+ //_calc_delta_val(p_data.initial_val, p_data.final_val, p_data.delta_val);
+ _calc_delta_val(initial_val, p_data.final_val, p_data.delta_val);
+ return p_data.delta_val;
+ }
+ break;
+ }
+ return p_data.initial_val;
+}
+
+Variant Tween::_run_equation(InterpolateData& p_data) {
+
+ Variant& initial_val = _get_initial_val(p_data);
+ Variant& delta_val = _get_delta_val(p_data);
+ Variant result;
+
+#define APPLY_EQUATION(element)\
+ r.element = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, i.element, d.element, p_data.times_in_sec);
+
+ switch(initial_val.get_type())
+ {
+ case Variant::INT:
+ result = (int) _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (int) initial_val, (int) delta_val, p_data.times_in_sec);
+ break;
+
+ case Variant::REAL:
+ result = _run_equation(p_data.trans_type, p_data.ease_type, p_data.elapsed - p_data.delay, (real_t) initial_val, (real_t) delta_val, p_data.times_in_sec);
+ break;
+
+ case Variant::VECTOR2:
+ {
+ Vector2 i = initial_val;
+ Vector2 d = delta_val;
+ Vector2 r;
+
+ APPLY_EQUATION(x);
+ APPLY_EQUATION(y);
+
+ result = r;
+ }
+ break;
+
+ case Variant::VECTOR3:
+ {
+ Vector3 i = initial_val;
+ Vector3 d = delta_val;
+ Vector3 r;
+
+ APPLY_EQUATION(x);
+ APPLY_EQUATION(y);
+ APPLY_EQUATION(z);
+
+ result = r;
+ }
+ break;
+
+ case Variant::MATRIX3:
+ {
+ Matrix3 i = initial_val;
+ Matrix3 d = delta_val;
+ Matrix3 r;
+
+ APPLY_EQUATION(elements[0][0]);
+ APPLY_EQUATION(elements[0][1]);
+ APPLY_EQUATION(elements[0][2]);
+ APPLY_EQUATION(elements[1][0]);
+ APPLY_EQUATION(elements[1][1]);
+ APPLY_EQUATION(elements[1][2]);
+ APPLY_EQUATION(elements[2][0]);
+ APPLY_EQUATION(elements[2][1]);
+ APPLY_EQUATION(elements[2][2]);
+
+ result = r;
+ }
+ break;
+
+ case Variant::MATRIX32:
+ {
+ Matrix3 i = initial_val;
+ Matrix3 d = delta_val;
+ Matrix3 r;
+
+ APPLY_EQUATION(elements[0][0]);
+ APPLY_EQUATION(elements[0][1]);
+ APPLY_EQUATION(elements[1][0]);
+ APPLY_EQUATION(elements[1][1]);
+ APPLY_EQUATION(elements[2][0]);
+ APPLY_EQUATION(elements[2][1]);
+
+ result = r;
+ }
+ break;
+ case Variant::QUAT:
+ {
+ Quat i = initial_val;
+ Quat d = delta_val;
+ Quat r;
+
+ APPLY_EQUATION(x);
+ APPLY_EQUATION(y);
+ APPLY_EQUATION(z);
+ APPLY_EQUATION(w);
+
+ result = r;
+ }
+ break;
+ case Variant::_AABB:
+ {
+ AABB i = initial_val;
+ AABB d = delta_val;
+ AABB r;
+
+ APPLY_EQUATION(pos.x);
+ APPLY_EQUATION(pos.y);
+ APPLY_EQUATION(pos.z);
+ APPLY_EQUATION(size.x);
+ APPLY_EQUATION(size.y);
+ APPLY_EQUATION(size.z);
+
+ result = r;
+ }
+ break;
+ case Variant::TRANSFORM:
+ {
+ Transform i = initial_val;
+ Transform d = delta_val;
+ Transform r;
+
+ APPLY_EQUATION(basis.elements[0][0]);
+ APPLY_EQUATION(basis.elements[0][1]);
+ APPLY_EQUATION(basis.elements[0][2]);
+ APPLY_EQUATION(basis.elements[1][0]);
+ APPLY_EQUATION(basis.elements[1][1]);
+ APPLY_EQUATION(basis.elements[1][2]);
+ APPLY_EQUATION(basis.elements[2][0]);
+ APPLY_EQUATION(basis.elements[2][1]);
+ APPLY_EQUATION(basis.elements[2][2]);
+ APPLY_EQUATION(origin.x);
+ APPLY_EQUATION(origin.y);
+ APPLY_EQUATION(origin.z);
+
+ result = r;
+ }
+ break;
+ case Variant::COLOR:
+ {
+ Color i = initial_val;
+ Color d = delta_val;
+ Color r;
+
+ APPLY_EQUATION(r);
+ APPLY_EQUATION(g);
+ APPLY_EQUATION(b);
+ APPLY_EQUATION(a);
+
+ result = r;
+ }
+ break;
+ };
+#undef APPLY_EQUATION
+
+ return result;
+}
+
+bool Tween::_apply_tween_value(InterpolateData& p_data, Variant& value) {
+
+ Object *object = get_node(p_data.path);
+ ERR_FAIL_COND_V(object == NULL, false);
+
+ switch(p_data.type) {
+
+ case INTER_PROPERTY:
+ case FOLLOW_PROPERTY:
+ case TARGETING_PROPERTY:
+ {
+ bool valid = false;
+ object->set(p_data.key,value, &valid);
+ return valid;
+ }
+
+ case INTER_METHOD:
+ case FOLLOW_METHOD:
+ case TARGETING_METHOD:
+ {
+ Variant::CallError error;
+ if (value.get_type() != Variant::NIL) {
+ Variant *arg[1] = { &value };
+ object->call(p_data.key, (const Variant **) arg, 1, error);
+ } else {
+ object->call(p_data.key, NULL, 0, error);
+ }
+
+ if(error.error == Variant::CallError::CALL_OK)
+ return true;
+ return false;
+ }
+
+ case INTER_CALLBACK:
+ break;
+ };
+ return true;
+}
+
+void Tween::_tween_process(float p_delta) {
+
+ if (speed_scale == 0)
+ return;
+ p_delta *= speed_scale;
+
+ // if repeat and all interpolates was finished then reset all interpolates
+ if(repeat) {
+ bool all_finished = true;
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+
+ if(!data.finish) {
+ all_finished = false;
+ break;
+ }
+ }
+
+ if(all_finished)
+ reset_all();
+ }
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ if(!data.active || data.finish)
+ continue;
+
+ Object *object = get_node(data.path);
+ if(object == NULL)
+ continue;
+
+ bool prev_delaying = data.elapsed <= data.delay;
+ data.elapsed += p_delta;
+ if(data.elapsed < data.delay)
+ continue;
+ else if(prev_delaying) {
+
+ emit_signal("tween_start",object,data.key);
+ _apply_tween_value(data, data.initial_val);
+ }
+
+ if(data.elapsed > (data.delay + data.times_in_sec)) {
+
+ data.elapsed = data.delay + data.times_in_sec;
+ data.finish = true;
+ }
+
+ switch(data.type)
+ {
+ case INTER_PROPERTY:
+ case INTER_METHOD:
+ break;
+ case INTER_CALLBACK:
+ if(data.finish) {
+
+ Variant::CallError error;
+ if (data.arg.get_type() != Variant::NIL) {
+ Variant *arg[1] = { &data.arg };
+ object->call(data.key, (const Variant **) arg, 1, error);
+ } else {
+ object->call(data.key, NULL, 0, error);
+ }
+ }
+ continue;
+ }
+
+ Variant result = _run_equation(data);
+ emit_signal("tween_step",object,data.key,data.elapsed,result);
+
+ _apply_tween_value(data, result);
+
+ if(data.finish)
+ emit_signal("tween_complete",object,data.key);
+ }
+}
+
+void Tween::set_tween_process_mode(TweenProcessMode p_mode) {
+
+ if (tween_process_mode==p_mode)
+ return;
+
+ bool pr = processing;
+ if (pr)
+ _set_process(false);
+ tween_process_mode=p_mode;
+ if (pr)
+ _set_process(true);
+}
+
+Tween::TweenProcessMode Tween::get_tween_process_mode() const {
+
+ return tween_process_mode;
+}
+
+void Tween::_set_process(bool p_process,bool p_force) {
+
+ if (processing==p_process && !p_force)
+ return;
+
+ switch(tween_process_mode) {
+
+ case TWEEN_PROCESS_FIXED: set_fixed_process(p_process && active); break;
+ case TWEEN_PROCESS_IDLE: set_process(p_process && active); break;
+ }
+
+ processing=p_process;
+}
+
+bool Tween::is_active() const {
+
+ return active;
+}
+
+void Tween::set_active(bool p_active) {
+
+ if (active==p_active)
+ return;
+
+ active=p_active;
+ _set_process(processing,true);
+}
+
+bool Tween::is_repeat() const {
+
+ return repeat;
+}
+
+void Tween::set_repeat(bool p_repeat) {
+
+ repeat = p_repeat;
+}
+
+void Tween::set_speed(float p_speed) {
+
+ speed_scale=p_speed;
+}
+
+float Tween::get_speed() const {
+
+ return speed_scale;
+}
+
+bool Tween::start() {
+
+ set_active(true);
+ _set_process(true);
+ return true;
+}
+
+bool Tween::reset(Node *p_node, String p_key) {
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ Node *node = get_node(data.path);
+ if(node == NULL)
+ continue;
+
+ if(node == p_node && data.key == p_key) {
+
+ data.elapsed = 0;
+ data.finish = false;
+ if(data.delay == 0)
+ _apply_tween_value(data, data.initial_val);
+ }
+ }
+ return true;
+}
+
+bool Tween::reset_all() {
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ data.elapsed = 0;
+ data.finish = false;
+ if(data.delay == 0)
+ _apply_tween_value(data, data.initial_val);
+ }
+ return true;
+}
+
+bool Tween::stop(Node *p_node, String p_key) {
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ Node *node = get_node(data.path);
+ if(node == NULL)
+ continue;
+ if(node == p_node && data.key == p_key)
+ data.active = false;
+ }
+ return true;
+}
+
+bool Tween::stop_all() {
+
+ set_active(false);
+ _set_process(false);
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ data.active = false;
+ }
+ return true;
+}
+
+bool Tween::resume(Node *p_node, String p_key) {
+
+ set_active(true);
+ _set_process(true);
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ Node *node = get_node(data.path);
+ if(node == NULL)
+ continue;
+ if(node == p_node && data.key == p_key)
+ data.active = true;
+ }
+ return true;
+}
+
+bool Tween::resume_all() {
+
+ set_active(true);
+ _set_process(true);
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ data.active = true;
+ }
+ return true;
+}
+
+bool Tween::remove(Node *p_node, String p_key) {
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+ Node *node = get_node(data.path);
+ if(node == NULL)
+ continue;
+ if(node == p_node && data.key == p_key) {
+ interpolates.erase(E);
+ return true;
+ }
+ }
+ return true;
+}
+
+bool Tween::remove_all() {
+
+ set_active(false);
+ _set_process(false);
+ interpolates.clear();
+ return true;
+}
+
+bool Tween::seek(real_t p_time) {
+
+ for(List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ InterpolateData& data = E->get();
+
+ data.elapsed = p_time;
+ if(data.elapsed < data.delay) {
+
+ data.finish = false;
+ continue;
+ }
+ else if(data.elapsed >= (data.delay + data.times_in_sec)) {
+
+ data.finish = true;
+ data.elapsed = (data.delay + data.times_in_sec);
+ } else
+ data.finish = false;
+
+ switch(data.type)
+ {
+ case INTER_PROPERTY:
+ case INTER_METHOD:
+ break;
+ case INTER_CALLBACK:
+ continue;
+ }
+
+ Variant result = _run_equation(data);
+
+ _apply_tween_value(data, result);
+ }
+ return true;
+}
+
+real_t Tween::tell() const {
+
+ real_t pos = 0;
+ for(const List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ const InterpolateData& data = E->get();
+ if(data.elapsed > pos)
+ pos = data.elapsed;
+ }
+ return pos;
+}
+
+real_t Tween::get_runtime() const {
+
+ real_t runtime = 0;
+ for(const List<InterpolateData>::Element *E=interpolates.front();E;E=E->next()) {
+
+ const InterpolateData& data = E->get();
+ real_t t = data.delay + data.times_in_sec;
+ if(t > runtime)
+ runtime = t;
+ }
+ return runtime;
+}
+
+bool Tween::_calc_delta_val(const Variant& p_initial_val, const Variant& p_final_val, Variant& p_delta_val) {
+
+ const Variant& initial_val = p_initial_val;
+ const Variant& final_val = p_final_val;
+ Variant& delta_val = p_delta_val;
+
+ switch(initial_val.get_type()) {
+ case Variant::INT:
+ delta_val = (int) final_val - (int) initial_val;
+ break;
+
+ case Variant::REAL:
+ delta_val = (real_t) final_val - (real_t) initial_val;
+ break;
+
+ case Variant::VECTOR2:
+ delta_val = final_val.operator Vector2() - initial_val.operator Vector2();
+ break;
+
+ case Variant::VECTOR3:
+ delta_val = final_val.operator Vector3() - initial_val.operator Vector3();
+ break;
+
+ case Variant::MATRIX3:
+ {
+ Matrix3 i = initial_val;
+ Matrix3 f = final_val;
+ delta_val = Matrix3(f.elements[0][0] - i.elements[0][0],
+ f.elements[0][1] - i.elements[0][1],
+ f.elements[0][2] - i.elements[0][2],
+ f.elements[1][0] - i.elements[1][0],
+ f.elements[1][1] - i.elements[1][1],
+ f.elements[1][2] - i.elements[1][2],
+ f.elements[2][0] - i.elements[2][0],
+ f.elements[2][1] - i.elements[2][1],
+ f.elements[2][2] - i.elements[2][2]
+ );
+ }
+ break;
+
+ case Variant::MATRIX32:
+ {
+ Matrix32 i = initial_val;
+ Matrix32 f = final_val;
+ Matrix32 d = Matrix32();
+ d[0][0] = f.elements[0][0] - i.elements[0][0];
+ d[0][1] = f.elements[0][1] - i.elements[0][1];
+ d[1][0] = f.elements[1][0] - i.elements[1][0];
+ d[1][1] = f.elements[1][1] - i.elements[1][1];
+ d[2][0] = f.elements[2][0] - i.elements[2][0];
+ d[2][1] = f.elements[2][1] - i.elements[2][1];
+ delta_val = d;
+ }
+ break;
+ case Variant::QUAT:
+ delta_val = final_val.operator Quat() - initial_val.operator Quat();
+ break;
+ case Variant::_AABB:
+ {
+ AABB i = initial_val;
+ AABB f = final_val;
+ delta_val = AABB(f.pos - i.pos, f.size - i.size);
+ }
+ break;
+ case Variant::TRANSFORM:
+ {
+ Transform i = initial_val;
+ Transform f = final_val;
+ Transform d;
+ d.set(f.basis.elements[0][0] - i.basis.elements[0][0],
+ f.basis.elements[0][1] - i.basis.elements[0][1],
+ f.basis.elements[0][2] - i.basis.elements[0][2],
+ f.basis.elements[1][0] - i.basis.elements[1][0],
+ f.basis.elements[1][1] - i.basis.elements[1][1],
+ f.basis.elements[1][2] - i.basis.elements[1][2],
+ f.basis.elements[2][0] - i.basis.elements[2][0],
+ f.basis.elements[2][1] - i.basis.elements[2][1],
+ f.basis.elements[2][2] - i.basis.elements[2][2],
+ f.origin.x - i.origin.x,
+ f.origin.y - i.origin.y,
+ f.origin.z - i.origin.z
+ );
+
+ delta_val = d;
+ }
+ break;
+ case Variant::COLOR:
+ {
+ Color i = initial_val;
+ Color f = final_val;
+ delta_val = Color(f.r - i.r, f.g - i.g, f.b - i.b, f.a - i.a);
+ }
+ break;
+
+ default:
+ ERR_PRINT("Invalid param type, except(int/real/vector2/vector/matrix/matrix32/quat/aabb/transform/color)");
+ return false;
+ };
+ return true;
+}
+
+bool Tween::interpolate_property(Node *p_node
+ , String p_property
+ , Variant p_initial_val
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ bool prop_valid = false;
+ p_node->get(p_property,&prop_valid);
+ ERR_FAIL_COND_V(!prop_valid, false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = INTER_PROPERTY;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_property;
+ data.initial_val = p_initial_val;
+ data.final_val = p_final_val;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ if(!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
+ return false;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+bool Tween::interpolate_method(Node *p_node
+ , String p_method
+ , Variant p_initial_val
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+ if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_initial_val.get_type() != p_final_val.get_type(), false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ ERR_FAIL_COND_V(!p_node->has_method(p_method), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = INTER_METHOD;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_method;
+ data.initial_val = p_initial_val;
+ data.final_val = p_final_val;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ if(!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
+ return false;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+bool Tween::interpolate_callback(Node *p_node
+ , String p_callback
+ , real_t p_times_in_sec
+ , Variant p_arg
+) {
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_times_in_sec < 0, false);
+
+ ERR_FAIL_COND_V(!p_node->has_method(p_callback), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = INTER_CALLBACK;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_callback;
+ data.times_in_sec = p_times_in_sec;
+ data.delay = 0;
+ data.arg = p_arg;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+bool Tween::follow_property(Node *p_node
+ , String p_property
+ , Variant p_initial_val
+ , Node *p_target
+ , String p_target_property
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ bool prop_valid = false;
+ p_node->get(p_property,&prop_valid);
+ ERR_FAIL_COND_V(!prop_valid, false);
+
+ bool target_prop_valid = false;
+ Variant target_val = p_target->get(p_target_property,&target_prop_valid);
+ ERR_FAIL_COND_V(!target_prop_valid, false);
+
+ // convert INT to REAL is better for interpolaters
+ if(target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
+ ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = FOLLOW_PROPERTY;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_property;
+ data.initial_val = p_initial_val;
+ data.target = p_target->get_path();
+ data.target_key = p_target_property;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+bool Tween::follow_method(Node *p_node
+ , String p_method
+ , Variant p_initial_val
+ , Node *p_target
+ , String p_target_method
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_initial_val.get_type() == Variant::INT) p_initial_val = p_initial_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_target == NULL, false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ ERR_FAIL_COND_V(!p_node->has_method(p_method), false);
+ ERR_FAIL_COND_V(!p_target->has_method(p_target_method), false);
+
+ Variant::CallError error;
+ Variant target_val = p_target->call(p_target_method, NULL, 0, error);
+ ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
+
+ // convert INT to REAL is better for interpolaters
+ if(target_val.get_type() == Variant::INT) target_val = target_val.operator real_t();
+ ERR_FAIL_COND_V(target_val.get_type() != p_initial_val.get_type(), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = FOLLOW_METHOD;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_method;
+ data.initial_val = p_initial_val;
+ data.target = p_target->get_path();
+ data.target_key = p_target_method;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+bool Tween::targeting_property(Node *p_node
+ , String p_property
+ , Node *p_initial
+ , String p_initial_property
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ bool prop_valid = false;
+ p_node->get(p_property,&prop_valid);
+ ERR_FAIL_COND_V(!prop_valid, false);
+
+ bool initial_prop_valid = false;
+ Variant initial_val = p_initial->get(p_initial_property,&initial_prop_valid);
+ ERR_FAIL_COND_V(!initial_prop_valid, false);
+
+ // convert INT to REAL is better for interpolaters
+ if(initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
+ ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = TARGETING_PROPERTY;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_property;
+ data.target = p_initial->get_path();
+ data.target_key = p_initial_property;
+ data.initial_val = initial_val;
+ data.final_val = p_final_val;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ if(!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
+ return false;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+
+bool Tween::targeting_method(Node *p_node
+ , String p_method
+ , Node *p_initial
+ , String p_initial_method
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay
+) {
+ // convert INT to REAL is better for interpolaters
+ if(p_final_val.get_type() == Variant::INT) p_final_val = p_final_val.operator real_t();
+
+ ERR_FAIL_COND_V(p_node == NULL, false);
+ ERR_FAIL_COND_V(p_initial == NULL, false);
+ ERR_FAIL_COND_V(p_times_in_sec <= 0, false);
+ ERR_FAIL_COND_V(p_trans_type < 0 || p_trans_type >= TRANS_COUNT, false);
+ ERR_FAIL_COND_V(p_ease_type < 0 || p_ease_type >= EASE_COUNT, false);
+ ERR_FAIL_COND_V(p_delay < 0, false);
+
+ ERR_FAIL_COND_V(!p_node->has_method(p_method), false);
+ ERR_FAIL_COND_V(!p_initial->has_method(p_initial_method), false);
+
+ Variant::CallError error;
+ Variant initial_val = p_initial->call(p_initial_method, NULL, 0, error);
+ ERR_FAIL_COND_V(error.error != Variant::CallError::CALL_OK, false);
+
+ // convert INT to REAL is better for interpolaters
+ if(initial_val.get_type() == Variant::INT) initial_val = initial_val.operator real_t();
+ ERR_FAIL_COND_V(initial_val.get_type() != p_final_val.get_type(), false);
+
+ InterpolateData data;
+ data.active = true;
+ data.type = TARGETING_METHOD;
+ data.finish = false;
+ data.elapsed = 0;
+
+ data.path = p_node->get_path();
+ data.key = p_method;
+ data.target = p_initial->get_path();
+ data.target_key = p_initial_method;
+ data.initial_val = initial_val;
+ data.final_val = p_final_val;
+ data.times_in_sec = p_times_in_sec;
+ data.trans_type = p_trans_type;
+ data.ease_type = p_ease_type;
+ data.delay = p_delay;
+
+ if(!_calc_delta_val(data.initial_val, data.final_val, data.delta_val))
+ return false;
+
+ interpolates.push_back(data);
+ return true;
+}
+
+Tween::Tween() {
+
+ //String autoplay;
+ tween_process_mode=TWEEN_PROCESS_IDLE;
+ processing=false;
+ active=false;
+ repeat=false;
+ speed_scale=1;
+}
+
+Tween::~Tween() {
+
+}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
new file mode 100644
index 0000000000..51d5fc9132
--- /dev/null
+++ b/scene/animation/tween.h
@@ -0,0 +1,239 @@
+/*************************************************************************/
+/* tween.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 TWEEN_H
+#define TWEEN_H
+
+#include "scene/main/node.h"
+
+
+class Tween : public Node {
+
+ OBJ_TYPE( Tween, Node );
+
+public:
+ enum TweenProcessMode {
+ TWEEN_PROCESS_FIXED,
+ TWEEN_PROCESS_IDLE,
+ };
+
+ enum TransitionType {
+ TRANS_LINEAR,
+ TRANS_SINE,
+ TRANS_QUINT,
+ TRANS_QUART,
+ TRANS_QUAD,
+ TRANS_EXPO,
+ TRANS_ELASTIC,
+ TRANS_CUBIC,
+ TRANS_CIRC,
+ TRANS_BOUNCE,
+ TRANS_BACK,
+
+ TRANS_COUNT,
+ };
+
+ enum EaseType {
+ EASE_IN,
+ EASE_OUT,
+ EASE_IN_OUT,
+ EASE_OUT_IN,
+
+ EASE_COUNT,
+ };
+
+private:
+ enum InterpolateType {
+
+ INTER_PROPERTY,
+ INTER_METHOD,
+ FOLLOW_PROPERTY,
+ FOLLOW_METHOD,
+ TARGETING_PROPERTY,
+ TARGETING_METHOD,
+ INTER_CALLBACK,
+ };
+
+ struct InterpolateData {
+ bool active;
+ InterpolateType type;
+ bool finish;
+ real_t elapsed;
+ NodePath path;
+ StringName key;
+ Variant initial_val;
+ Variant delta_val;
+ Variant final_val;
+ NodePath target;
+ StringName target_key;
+ real_t times_in_sec;
+ TransitionType trans_type;
+ EaseType ease_type;
+ real_t delay;
+ Variant arg;
+ };
+
+ String autoplay;
+ TweenProcessMode tween_process_mode;
+ bool processing;
+ bool active;
+ bool repeat;
+ float speed_scale;
+
+ List<InterpolateData> interpolates;
+
+ typedef real_t (*interpolater)(real_t t, real_t b, real_t c, real_t d);
+ static interpolater interpolaters[TRANS_COUNT][EASE_COUNT];
+
+ real_t _run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d);
+ Variant& _get_delta_val(InterpolateData& p_data);
+ Variant& _get_initial_val(InterpolateData& p_data);
+ Variant _run_equation(InterpolateData& p_data);
+ bool _calc_delta_val(const Variant& p_initial_val, const Variant& p_final_val, Variant& p_delta_val);
+ bool _apply_tween_value(InterpolateData& p_data, Variant& value);
+
+ void _tween_process(float p_delta);
+ void _set_process(bool p_process,bool p_force=false);
+
+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:
+
+ bool is_active() const;
+ void set_active(bool p_active);
+
+ bool is_repeat() const;
+ void set_repeat(bool p_repeat);
+
+ void set_tween_process_mode(TweenProcessMode p_mode);
+ TweenProcessMode get_tween_process_mode() const;
+
+ void set_speed(float p_speed);
+ float get_speed() const;
+
+ bool start();
+ bool reset(Node *p_node, String p_key);
+ bool reset_all();
+ bool stop(Node *p_node, String p_key);
+ bool stop_all();
+ bool resume(Node *p_node, String p_key);
+ bool resume_all();
+ bool remove(Node *p_node, String p_key);
+ bool remove_all();
+
+ bool seek(real_t p_time);
+ real_t tell() const;
+ real_t get_runtime() const;
+
+ bool interpolate_property(Node *p_node
+ , String p_property
+ , Variant p_initial_val
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ bool interpolate_method(Node *p_node
+ , String p_method
+ , Variant p_initial_val
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ bool interpolate_callback(Node *p_node
+ , String p_callback
+ , real_t p_times_in_sec
+ , Variant p_arg = Variant()
+ );
+
+ bool follow_property(Node *p_node
+ , String p_property
+ , Variant p_initial_val
+ , Node *p_target
+ , String p_target_property
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ bool follow_method(Node *p_node
+ , String p_method
+ , Variant p_initial_val
+ , Node *p_target
+ , String p_target_method
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ bool targeting_property(Node *p_node
+ , String p_property
+ , Node *p_initial
+ , String p_initial_property
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ bool targeting_method(Node *p_node
+ , String p_method
+ , Node *p_initial
+ , String p_initial_method
+ , Variant p_final_val
+ , real_t p_times_in_sec
+ , TransitionType p_trans_type
+ , EaseType p_ease_type
+ , real_t p_delay = 0
+ );
+
+ Tween();
+ ~Tween();
+};
+
+VARIANT_ENUM_CAST( Tween::TweenProcessMode );
+VARIANT_ENUM_CAST( Tween::TransitionType );
+VARIANT_ENUM_CAST( Tween::EaseType );
+
+#endif
+
diff --git a/scene/animation/tween_interpolaters.cpp b/scene/animation/tween_interpolaters.cpp
new file mode 100644
index 0000000000..7d0f2cd4e0
--- /dev/null
+++ b/scene/animation/tween_interpolaters.cpp
@@ -0,0 +1,407 @@
+/*************************************************************************/
+/* tween.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 "tween.h"
+
+const real_t pi = 3.1415926535898;
+
+///////////////////////////////////////////////////////////////////////////
+// linear
+///////////////////////////////////////////////////////////////////////////
+namespace linear {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * t / d + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * t / d + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * t / d + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * t / d + b;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// sine
+///////////////////////////////////////////////////////////////////////////
+namespace sine {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return -c * cos(t / d * (pi / 2)) + c + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * sin(t / d * (pi / 2)) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return -c / 2 * (cos(pi * t / d) - 1) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// quint
+///////////////////////////////////////////////////////////////////////////
+namespace quint {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * pow(t / d, 5) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * (pow(t / d - 1, 5) + 1) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ t = t / d * 2;
+ if (t < 1) return c / 2 * pow(t, 5) + b;
+ return c / 2 * (pow(t - 2, 5) + 2) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// quart
+///////////////////////////////////////////////////////////////////////////
+namespace quart {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * pow(t / d, 4) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return -c * (pow(t / d - 1, 4) - 1) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ t = t / d * 2;
+ if (t < 1) return c / 2 * pow(t, 4) + b;
+ return -c / 2 * (pow(t - 2, 4) - 2) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// quad
+///////////////////////////////////////////////////////////////////////////
+namespace quad {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * pow(t / d, 2) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ t = t / d;
+ return -c * t * (t - 2) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ t = t / d * 2;
+ if (t < 1) return c / 2 * pow(t, 2) + b;
+ return -c / 2 * ((t - 1) * (t - 3) - 1) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// expo
+///////////////////////////////////////////////////////////////////////////
+namespace expo {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == 0) return b;
+ return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == d) return b + c;
+ return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == 0) return b;
+ if (t == d) return b + c;
+ t = t / d * 2;
+ if (t < 1) return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005;
+ return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// elastic
+///////////////////////////////////////////////////////////////////////////
+namespace elastic {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == 0) return b;
+ if ((t /= d) == 1) return b + c;
+ float p = d * 0.3f;
+ float a = c;
+ float s = p / 4;
+ float postFix = a * pow(2,10 * (t -= 1)); // this is a fix, again, with post-increment operators
+ return -(postFix * sin((t * d - s) * (2 * pi) / p )) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == 0) return b;
+ if ((t /= d) == 1) return b + c;
+ float p = d * 0.3f;
+ float a = c;
+ float s = p / 4;
+ return (a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p ) + c + b);
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if (t == 0) return b;
+ if ((t /= d / 2) == 2) return b + c;
+ float p = d * (0.3f * 1.5f);
+ float a = c;
+ float s = p / 4;
+
+ if (t < 1) {
+ float postFix = a * pow(2, 10 * (t -= 1)); // postIncrement is evil
+ return -0.5f * (postFix * sin((t * d - s) * (2 * pi) / p)) + b;
+ }
+ float postFix = a * pow(2, -10 * (t -= 1)); // postIncrement is evil
+ return postFix * sin((t * d - s) * (2 * pi) / p ) * 0.5f + c + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// cubic
+///////////////////////////////////////////////////////////////////////////
+namespace cubic {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * (t /= d) * t * t + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * ((t = t / d - 1) * t * t + 1) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
+ return c / 2 * ((t -= 2) * t * t + 2) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// circ
+///////////////////////////////////////////////////////////////////////////
+namespace circ {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c * sqrt(1 - (t = t / d - 1) * t) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
+ return c / 2 * (sqrt(1 - t * (t -= 2)) + 1) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// bounce
+///////////////////////////////////////////////////////////////////////////
+namespace bounce {
+ static real_t out(real_t t, real_t b, real_t c, real_t d);
+
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return c - out(d - t, 0, c, d) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ if ((t /= d) < (1 / 2.75f)) {
+ return c*(7.5625f*t*t) + b;
+ } else if (t < (2/2.75f)) {
+ float postFix = t-=(1.5f/2.75f);
+ return c*(7.5625f*(postFix)*t + .75f) + b;
+ } else if (t < (2.5/2.75)) {
+ float postFix = t-=(2.25f/2.75f);
+ return c*(7.5625f*(postFix)*t + .9375f) + b;
+ } else {
+ float postFix = t-=(2.625f/2.75f);
+ return c*(7.5625f*(postFix)*t + .984375f) + b;
+ }
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? in(t * 2, b, c / 2, d)
+ : out((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+///////////////////////////////////////////////////////////////////////////
+// back
+///////////////////////////////////////////////////////////////////////////
+namespace back {
+ static real_t in(real_t t, real_t b, real_t c, real_t d)
+ {
+ float s = 1.70158f;
+ float postFix = t /= d;
+ return c * (postFix) * t * ((s + 1) * t - s) + b;
+ }
+
+ static real_t out(real_t t, real_t b, real_t c, real_t d)
+ {
+ float s = 1.70158f;
+ return c * ((t = t / d- 1) * t * ((s + 1) * t + s) + 1) + b;
+ }
+
+ static real_t in_out(real_t t, real_t b, real_t c, real_t d)
+ {
+ float s = 1.70158f;
+ if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525f)) + 1) * t - s)) + b;
+ float postFix = t -= 2;
+ return c / 2 * ((postFix) * t * (((s *= (1.525f)) + 1) * t + s) + 2) + b;
+ }
+
+ static real_t out_in(real_t t, real_t b, real_t c, real_t d)
+ {
+ return (t < d / 2)
+ ? out(t * 2, b, c / 2, d)
+ : in((t * 2) - d, b + c / 2, c / 2, d)
+ ;
+ }
+};
+
+Tween::interpolater Tween::interpolaters[Tween::TRANS_COUNT][Tween::EASE_COUNT] = {
+ { &linear::in, &linear::out, &linear::in_out, &linear::out_in },
+ { &sine::in, &sine::out, &sine::in_out, &sine::out_in },
+ { &quint::in, &quint::out, &quint::in_out, &quint::out_in },
+ { &quart::in, &quart::out, &quart::in_out, &quart::out_in },
+ { &quad::in, &quad::out, &quad::in_out, &quad::out_in },
+ { &expo::in, &expo::out, &expo::in_out, &expo::out_in },
+ { &elastic::in, &elastic::out, &elastic::in_out, &elastic::out_in },
+ { &cubic::in, &cubic::out, &cubic::in_out, &cubic::out_in },
+ { &circ::in, &circ::out, &circ::in_out, &circ::out_in },
+ { &bounce::in, &bounce::out, &bounce::in_out, &bounce::out_in },
+ { &back::in, &back::out, &back::in_out, &back::out_in },
+};
+
+real_t Tween::_run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d) {
+
+ interpolater cb = interpolaters[p_trans_type][p_ease_type];
+ ERR_FAIL_COND_V(cb == NULL, b);
+ return cb(t, b, c, d);
+}
+
diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp
index 3c95b102d7..2d6f3cd27a 100644
--- a/scene/gui/tab_container.cpp
+++ b/scene/gui/tab_container.cpp
@@ -113,7 +113,7 @@ void TabContainer::_input_event(const InputEvent& p_event) {
break;
}
- String s = c->has_meta("_tab_title")?String(XL_MESSAGE(String(c->get_meta("_tab_title")))):String(c->get_name());
+ String s = c->has_meta("_tab_name")?String(XL_MESSAGE(String(c->get_meta("_tab_name")))):String(c->get_name());
int tab_width=font->get_string_size(s).width;
if (c->has_meta("_tab_icon")) {
@@ -220,7 +220,7 @@ void TabContainer::_notification(int p_what) {
continue;
- String s = c->has_meta("_tab_title")?String(XL_MESSAGE(String(c->get_meta("_tab_title")))):String(c->get_name());
+ String s = c->has_meta("_tab_name")?String(XL_MESSAGE(String(c->get_meta("_tab_name")))):String(c->get_name());
w+=font->get_string_size(s).width;
if (c->has_meta("_tab_icon")) {
Ref<Texture> icon = c->get_meta("_tab_icon");
@@ -284,7 +284,7 @@ void TabContainer::_notification(int p_what) {
continue;
}
- String s = c->has_meta("_tab_title")?String(c->get_meta("_tab_title")):String(c->get_name());
+ String s = c->has_meta("_tab_name")?String(c->get_meta("_tab_name")):String(c->get_name());
int w=font->get_string_size(s).width;
Ref<Texture> icon;
if (c->has_meta("_tab_icon")) {
diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp
index b7c857b9c7..ae7a4d59a7 100644
--- a/scene/gui/tabs.cpp
+++ b/scene/gui/tabs.cpp
@@ -278,7 +278,8 @@ void Tabs::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_tab_title","tab_idx"),&Tabs::get_tab_title);
ObjectTypeDB::bind_method(_MD("set_tab_icon","tab_idx","icon:Texture"),&Tabs::set_tab_icon);
ObjectTypeDB::bind_method(_MD("get_tab_icon:Texture","tab_idx"),&Tabs::get_tab_icon);
- ObjectTypeDB::bind_method(_MD("remove_tab","tab_idx","icon:Texture"),&Tabs::remove_tab);
+ ObjectTypeDB::bind_method(_MD("remove_tab","tab_idx"),&Tabs::remove_tab);
+ ObjectTypeDB::bind_method(_MD("add_tab","title","icon:Texture"),&Tabs::add_tab);
ADD_SIGNAL(MethodInfo("tab_changed",PropertyInfo(Variant::INT,"tab")));
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 3566c1bfc4..0b797e7df3 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1354,13 +1354,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(cc);
- } else if (cursor.column==0) {
+ } else if (cursor.column==0) {
if (cursor.line>0) {
cursor_set_line(cursor.line-1);
cursor_set_column(text[cursor.line].length());
}
- } else {
+ } else {
cursor_set_column(cursor_get_column()-1);
}
@@ -1394,13 +1394,13 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(cc);
- } else if (cursor.column==text[cursor.line].length()) {
+ } else if (cursor.column==text[cursor.line].length()) {
if (cursor.line<text.size()-1) {
cursor_set_line(cursor.line+1);
cursor_set_column(0);
}
- } else {
+ } else {
cursor_set_column(cursor_get_column()+1);
}
@@ -1569,19 +1569,35 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
break;
}
- if (!selection.active)
- break;
+ if (!selection.active){
- String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- OS::get_singleton()->set_clipboard(clipboard);
+ String clipboard = text[cursor.line];
+ OS::get_singleton()->set_clipboard(clipboard);
+ cursor_set_line(cursor.line);
+ cursor_set_column(0);
+ _remove_text(cursor.line,0,cursor.line,text[cursor.line].length());
- cursor_set_line(selection.from_line);
- cursor_set_column(selection.from_column);
+ backspace_at_cursor();
+ update();
+ cursor_set_line(cursor.line+1);
+ cut_copy_line = true;
- _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- selection.active=false;
- selection.selecting_mode=Selection::MODE_NONE;
- update();
+ }
+ else
+ {
+
+ String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ OS::get_singleton()->set_clipboard(clipboard);
+
+ cursor_set_line(selection.from_line);
+ cursor_set_column(selection.from_column);
+
+ _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ selection.active=false;
+ selection.selecting_mode=Selection::MODE_NONE;
+ update();
+ cut_copy_line = false;
+ }
} break;
case KEY_C: {
@@ -1591,11 +1607,16 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
break;
}
- if (!selection.active)
- break;
-
- String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
- OS::get_singleton()->set_clipboard(clipboard);
+ if (!selection.active){
+ String clipboard = _base_get_text(cursor.line,0,cursor.line,text[cursor.line].length());
+ OS::get_singleton()->set_clipboard(clipboard);
+ cut_copy_line = true;
+ }
+ else{
+ String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column);
+ OS::get_singleton()->set_clipboard(clipboard);
+ cut_copy_line = false;
+ }
} break;
case KEY_Z: {
@@ -1625,6 +1646,12 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
cursor_set_column(selection.from_column);
}
+ else if (cut_copy_line)
+ {
+ cursor_set_column(0);
+ String ins="\n";
+ clipboard += ins;
+ }
_insert_text_at_cursor(clipboard);
@@ -1641,10 +1668,54 @@ void TextEdit::_input_event(const InputEvent& p_input_event) {
} break;
- default: {
-
- scancode_handled=false;
- } break;
+ case KEY_K:{
+ if (!k.mod.command || k.mod.shift || k.mod.alt) {
+ scancode_handled=false;
+ break;
+ }
+ else {
+ if (selection.active) {
+ int ini = selection.from_line;
+ int end = selection.to_line;
+ for (int i=ini; i<= end; i++)
+ {
+ _insert_text(i,0,"#");
+ }
+ }
+ else{
+ _insert_text(cursor.line,0,"#");
+ }
+ update();
+ }
+ break;}
+
+ case KEY_U:{
+ if (!k.mod.command || k.mod.shift || k.mod.alt) {
+ scancode_handled=false;
+ break;
+ }
+ else {
+ if (selection.active) {
+ int ini = selection.from_line;
+ int end = selection.to_line;
+ for (int i=ini; i<= end; i++)
+ {
+ if (text[i][0] == '#')
+ _remove_text(i,0,i,1);
+ }
+ }
+ else{
+ if (text[cursor.line][0] == '#')
+ _remove_text(cursor.line,0,cursor.line,1);
+ }
+ update();
+ }
+ break;}
+
+ default: {
+
+ scancode_handled=false;
+ } break;
}
@@ -3158,7 +3229,7 @@ TextEdit::TextEdit() {
current_op.type=TextOperation::TYPE_NONE;
undo_enabled=true;
- undo_stack_pos=NULL;
+ undo_stack_pos=NULL;
setting_text=false;
last_dblclk=0;
current_op.version=0;
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 7700bfd4d3..15c289a87e 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -162,7 +162,7 @@ class TextEdit : public Control {
TextOperation current_op;
List<TextOperation> undo_stack;
- List<TextOperation>::Element *undo_stack_pos;
+ List<TextOperation>::Element *undo_stack_pos;
void _clear_redo();
void _do_text_op(const TextOperation& p_op, bool p_reverse);
@@ -208,6 +208,7 @@ class TextEdit : public Control {
bool line_numbers;
bool auto_brace_completion_enabled;
+ bool cut_copy_line;
uint64_t last_dblclk;
@@ -336,7 +337,7 @@ public:
bool is_selection_active() const;
int get_selection_from_line() const;
- int get_selection_from_column() const;
+ int get_selection_from_column() const;
int get_selection_to_line() const;
int get_selection_to_column() const;
String get_selection_text() const;
@@ -347,7 +348,7 @@ public:
void undo();
void redo();
- void clear_undo_history();
+ void clear_undo_history();
void set_draw_tabs(bool p_draw);
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index fb85f0c6b7..25f04379ef 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -2787,7 +2787,7 @@ int Tree::get_item_offset(TreeItem *p_item) const {
ofs+=compute_item_height(it)+cache.vseparation;
- if (it->childs) {
+ if (it->childs && !it->collapsed) {
it=it->childs;
diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp
index e3bb50a9af..9a1c070529 100644
--- a/scene/gui/video_player.cpp
+++ b/scene/gui/video_player.cpp
@@ -46,6 +46,7 @@ void VideoPlayer::_notification(int p_notification) {
if (paused)
return;
+ stream->update(get_scene()->get_idle_process_time());
while (stream->get_pending_frame_count()) {
Image img = stream->pop_frame();
@@ -104,10 +105,6 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stop();
- if (stream_rid.is_valid())
- AudioServer::get_singleton()->free(stream_rid);
- stream_rid=RID();
-
texture = Ref<ImageTexture>(memnew(ImageTexture));
stream=p_stream;
@@ -115,7 +112,6 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) {
stream->set_loop(loops);
stream->set_paused(paused);
- stream_rid=AudioServer::get_singleton()->audio_stream_create(stream->get_audio_stream());
}
};
@@ -131,8 +127,6 @@ void VideoPlayer::play() {
if (stream.is_null())
return;
stream->play();
- AudioServer::get_singleton()->stream_set_active(stream_rid,true);
- AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
set_process(true);
};
@@ -143,7 +137,6 @@ void VideoPlayer::stop() {
if (stream.is_null())
return;
- AudioServer::get_singleton()->stream_set_active(stream_rid,false);
stream->stop();
set_process(false);
};
@@ -173,8 +166,6 @@ bool VideoPlayer::is_paused() const {
void VideoPlayer::set_volume(float p_vol) {
volume=p_vol;
- if (stream_rid.is_valid())
- AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume);
};
float VideoPlayer::get_volume() const {
@@ -213,6 +204,7 @@ float VideoPlayer::get_pos() const {
return stream->get_pos();
};
+
void VideoPlayer::set_autoplay(bool p_enable) {
autoplay=p_enable;
@@ -253,7 +245,7 @@ void VideoPlayer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("has_expand"), &VideoPlayer::has_expand );
- ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"AudioStream"), _SCS("set_stream"), _SCS("get_stream") );
+ ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"VideoStream"), _SCS("set_stream"), _SCS("get_stream") );
// ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") );
ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/autoplay"), _SCS("set_autoplay"), _SCS("has_autoplay") );
diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp
index 492c7633c1..bcdc50c880 100644
--- a/scene/main/scene_main_loop.cpp
+++ b/scene/main/scene_main_loop.cpp
@@ -481,8 +481,10 @@ bool SceneMainLoop::iteration(float p_time) {
_flush_transform_notifications();
MainLoop::iteration(p_time);
-
fixed_process_time=p_time;
+
+ emit_signal("fixed_frame");
+
_notify_group_pause("fixed_process",Node::NOTIFICATION_FIXED_PROCESS);
_flush_ugc();
_flush_transform_notifications();
@@ -507,6 +509,8 @@ bool SceneMainLoop::idle(float p_time){
idle_process_time=p_time;
+ emit_signal("idle_frame");
+
_flush_transform_notifications();
_notify_group_pause("idle_process",Node::NOTIFICATION_PROCESS);
@@ -580,6 +584,7 @@ void SceneMainLoop::_notification(int p_notification) {
break;
}
} break;
+ case NOTIFICATION_OS_MEMORY_WARNING:
case NOTIFICATION_WM_FOCUS_IN:
case NOTIFICATION_WM_FOCUS_OUT: {
@@ -1062,6 +1067,11 @@ SceneMainLoop::SceneMainLoop() {
ScriptDebugger::get_singleton()->set_request_scene_tree_message_func(_debugger_request_tree,this);
}
+ root->set_physics_object_picking(GLOBAL_DEF("physics/enable_object_picking",true));
+
+ ADD_SIGNAL( MethodInfo("idle_frame"));
+ ADD_SIGNAL( MethodInfo("fixed_frame"));
+
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index defa7da6ae..92dcef803c 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -36,6 +36,7 @@
#include "scene/gui/control.h"
#include "scene/3d/camera.h"
#include "scene/3d/spatial_indexer.h"
+#include "scene/3d/collision_object.h"
@@ -94,6 +95,8 @@ void Viewport::_update_stretch_transform() {
if (size_override_stretch && size_override) {
+ print_line("sive override size "+size_override_size);
+ print_line("rect size "+rect.size);
stretch_transform=Matrix32();
Size2 scale = rect.size/(size_override_size+size_override_margin*2);
stretch_transform.scale(scale);
@@ -233,6 +236,40 @@ void Viewport::update_worlds() {
find_world()->_update(get_scene()->get_frame());
}
+
+void Viewport::_test_new_mouseover(ObjectID new_collider) {
+
+ if (new_collider!=physics_object_over) {
+
+ if (physics_object_over) {
+ Object *obj = ObjectDB::get_instance(physics_object_over);
+ if (obj) {
+ CollisionObject *co = obj->cast_to<CollisionObject>();
+ if (co) {
+ co->_mouse_exit();
+ }
+ }
+ }
+
+ if (new_collider) {
+ Object *obj = ObjectDB::get_instance(new_collider);
+ if (obj) {
+ CollisionObject *co = obj->cast_to<CollisionObject>();
+ if (co) {
+ co->_mouse_enter();
+
+ }
+ }
+
+ }
+
+ physics_object_over=new_collider;
+
+ }
+
+
+}
+
void Viewport::_notification(int p_what) {
@@ -308,6 +345,155 @@ void Viewport::_notification(int p_what) {
remove_from_group("_viewports");
} break;
+ case NOTIFICATION_FIXED_PROCESS: {
+
+ if (physics_object_picking) {
+
+ Vector2 last_pos(1e20,1e20);
+ CollisionObject *last_object;
+ ObjectID last_id=0;
+ PhysicsDirectSpaceState::RayResult result;
+
+ bool motion_tested=false;
+
+ while(physics_picking_events.size()) {
+
+ InputEvent ev = physics_picking_events.front()->get();
+ physics_picking_events.pop_front();
+
+ Vector2 pos;
+ switch(ev.type) {
+ case InputEvent::MOUSE_MOTION: {
+ pos.x=ev.mouse_motion.x;
+ pos.y=ev.mouse_motion.y;
+ motion_tested=true;
+ physics_last_mousepos=pos;
+ } break;
+ case InputEvent::MOUSE_BUTTON: {
+ pos.x=ev.mouse_button.x;
+ pos.y=ev.mouse_button.y;
+
+ } break;
+ case InputEvent::SCREEN_DRAG: {
+ pos.x=ev.screen_drag.x;
+ pos.y=ev.screen_drag.y;
+ } break;
+ case InputEvent::SCREEN_TOUCH: {
+ pos.x=ev.screen_touch.x;
+ pos.y=ev.screen_touch.y;
+ } break;
+
+ }
+
+ bool captured=false;
+
+ if (physics_object_capture!=0) {
+
+
+ Object *obj = ObjectDB::get_instance(physics_object_capture);
+ if (obj) {
+ CollisionObject *co = obj->cast_to<CollisionObject>();
+ if (co) {
+ co->_input_event(ev,Vector3(),Vector3(),0);
+ captured=true;
+ if (ev.type==InputEvent::MOUSE_BUTTON && ev.mouse_button.button_index==1 && !ev.mouse_button.pressed) {
+ physics_object_capture=0;
+ }
+
+ } else {
+ physics_object_capture=0;
+ }
+ } else {
+ physics_object_capture=0;
+ }
+ }
+
+
+ if (captured) {
+ //none
+ } else if (pos==last_pos) {
+
+ if (last_id) {
+ if (ObjectDB::get_instance(last_id)) {
+ //good, exists
+ last_object->_input_event(ev,result.position,result.normal,result.shape);
+ if (last_object->get_capture_input_on_drag() && ev.type==InputEvent::MOUSE_BUTTON && ev.mouse_button.button_index==1 && ev.mouse_button.pressed) {
+ physics_object_capture=last_id;
+ }
+
+
+ }
+ }
+ } else {
+
+
+
+
+ if (camera) {
+
+ Vector3 from = camera->project_ray_origin(pos);
+ Vector3 dir = camera->project_ray_normal(pos);
+
+ PhysicsDirectSpaceState *space = PhysicsServer::get_singleton()->space_get_direct_state(find_world()->get_space());
+ if (space) {
+
+ bool col = space->intersect_ray(from,from+dir*10000,result,Set<RID>(),0xFFFFFFFF,0xFFFFFFFF);
+ ObjectID new_collider=0;
+ if (col) {
+ if (result.collider) {
+ CollisionObject *co = result.collider->cast_to<CollisionObject>();
+ if (co) {
+ co->_input_event(ev,result.position,result.normal,result.shape);
+ last_object=co;
+ last_id=result.collider_id;
+ new_collider=last_id;
+ if (co->get_capture_input_on_drag() && ev.type==InputEvent::MOUSE_BUTTON && ev.mouse_button.button_index==1 && ev.mouse_button.pressed) {
+ physics_object_capture=last_id;
+ }
+
+ }
+ }
+ }
+
+ if (ev.type==InputEvent::MOUSE_MOTION) {
+ _test_new_mouseover(new_collider);
+ }
+ }
+
+ last_pos=pos;
+ }
+ }
+ }
+
+ if (!motion_tested && camera && physics_last_mousepos!=Vector2(1e20,1e20)) {
+
+ //test anyway for mouseenter/exit because objects might move
+ Vector3 from = camera->project_ray_origin(physics_last_mousepos);
+ Vector3 dir = camera->project_ray_normal(physics_last_mousepos);
+
+ PhysicsDirectSpaceState *space = PhysicsServer::get_singleton()->space_get_direct_state(find_world()->get_space());
+ if (space) {
+
+ bool col = space->intersect_ray(from,from+dir*10000,result,Set<RID>(),0xFFFFFFFF,0xFFFFFFFF);
+ ObjectID new_collider=0;
+ if (col) {
+ if (result.collider) {
+ CollisionObject *co = result.collider->cast_to<CollisionObject>();
+ if (co) {
+ new_collider=result.collider_id;
+
+ }
+ }
+ }
+
+ _test_new_mouseover(new_collider);
+
+ }
+
+ }
+ }
+
+ } break;
}
}
@@ -786,6 +972,19 @@ bool Viewport::get_render_target_filter() const{
return (render_target_texture->get_flags()&Texture::FLAG_FILTER)!=0;
}
+void Viewport::set_render_target_gen_mipmaps(bool p_enable) {
+
+ //render_target_texture->set_flags(p_enable?int(Texture::FLAG_FILTER):int(0));
+ render_target_gen_mipmaps=p_enable;
+
+}
+
+bool Viewport::get_render_target_gen_mipmaps() const{
+
+ //return (render_target_texture->get_flags()&Texture::FLAG_FILTER)!=0;
+ return render_target_gen_mipmaps;
+}
+
Matrix32 Viewport::_get_input_pre_xform() const {
@@ -875,7 +1074,8 @@ void Viewport::_vp_input(const InputEvent& p_ev) {
void Viewport::_vp_unhandled_input(const InputEvent& p_ev) {
- if (render_target)
+
+ if (render_target && to_screen_rect==Rect2())
return; //if render target, can't get input events
//this one handles system input, p_ev are in system coordinates
@@ -904,6 +1104,15 @@ void Viewport::unhandled_input(const InputEvent& p_event) {
get_scene()->_call_input_pause(unhandled_key_input_group,"_unhandled_key_input",p_event);
//call_group(GROUP_CALL_REVERSE|GROUP_CALL_REALTIME|GROUP_CALL_MULIILEVEL,"unhandled_key_input","_unhandled_key_input",ev);
}
+
+
+ if (physics_object_picking && !get_scene()->input_handled) {
+
+ if (p_event.type==InputEvent::MOUSE_BUTTON || p_event.type==InputEvent::MOUSE_MOTION || p_event.type==InputEvent::SCREEN_DRAG || p_event.type==InputEvent::SCREEN_TOUCH) {
+ physics_picking_events.push_back(p_event);
+ }
+ }
+
}
void Viewport::set_use_own_world(bool p_world) {
@@ -960,6 +1169,22 @@ Rect2 Viewport::get_render_target_to_screen_rect() const{
return to_screen_rect;
}
+void Viewport::set_physics_object_picking(bool p_enable) {
+
+ physics_object_picking=p_enable;
+ set_fixed_process(physics_object_picking);
+ if (!physics_object_picking)
+ physics_picking_events.clear();
+
+
+}
+
+bool Viewport::get_physics_object_picking() {
+
+
+ return physics_object_picking;
+}
+
void Viewport::_bind_methods() {
@@ -1007,11 +1232,17 @@ void Viewport::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_render_target_filter","enable"), &Viewport::set_render_target_filter);
ObjectTypeDB::bind_method(_MD("get_render_target_filter"), &Viewport::get_render_target_filter);
+ ObjectTypeDB::bind_method(_MD("set_render_target_gen_mipmaps","enable"), &Viewport::set_render_target_gen_mipmaps);
+ ObjectTypeDB::bind_method(_MD("get_render_target_gen_mipmaps"), &Viewport::get_render_target_gen_mipmaps);
+
ObjectTypeDB::bind_method(_MD("set_render_target_update_mode","mode"), &Viewport::set_render_target_update_mode);
ObjectTypeDB::bind_method(_MD("get_render_target_update_mode"), &Viewport::get_render_target_update_mode);
ObjectTypeDB::bind_method(_MD("get_render_target_texture:RenderTargetTexture"), &Viewport::get_render_target_texture);
+ ObjectTypeDB::bind_method(_MD("set_physics_object_picking","enable"), &Viewport::set_physics_object_picking);
+ ObjectTypeDB::bind_method(_MD("get_physics_object_picking"), &Viewport::get_physics_object_picking);
+
ObjectTypeDB::bind_method(_MD("get_viewport"), &Viewport::get_viewport);
ObjectTypeDB::bind_method(_MD("input","local_event"), &Viewport::input);
ObjectTypeDB::bind_method(_MD("unhandled_input","local_event"), &Viewport::unhandled_input);
@@ -1021,6 +1252,7 @@ void Viewport::_bind_methods() {
ObjectTypeDB::bind_method(_MD("set_use_own_world","enable"), &Viewport::set_use_own_world);
ObjectTypeDB::bind_method(_MD("is_using_own_world"), &Viewport::is_using_own_world);
+ ObjectTypeDB::bind_method(_MD("get_camera:Camera"), &Viewport::get_camera);
ObjectTypeDB::bind_method(_MD("set_as_audio_listener","enable"), &Viewport::set_as_audio_listener);
ObjectTypeDB::bind_method(_MD("is_audio_listener","enable"), &Viewport::is_audio_listener);
@@ -1038,9 +1270,11 @@ void Viewport::_bind_methods() {
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"render_target/enabled"), _SCS("set_as_render_target"), _SCS("is_set_as_render_target") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"render_target/v_flip"), _SCS("set_render_target_vflip"), _SCS("get_render_target_vflip") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"render_target/filter"), _SCS("set_render_target_filter"), _SCS("get_render_target_filter") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"render_target/gen_mipmaps"), _SCS("set_render_target_gen_mipmaps"), _SCS("get_render_target_gen_mipmaps") );
ADD_PROPERTY( PropertyInfo(Variant::INT,"render_target/update_mode",PROPERTY_HINT_ENUM,"Disabled,Once,When Visible,Always"), _SCS("set_render_target_update_mode"), _SCS("get_render_target_update_mode") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"audio_listener/enable_2d"), _SCS("set_as_audio_listener_2d"), _SCS("is_audio_listener_2d") );
ADD_PROPERTY( PropertyInfo(Variant::BOOL,"audio_listener/enable_3d"), _SCS("set_as_audio_listener"), _SCS("is_audio_listener") );
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"physics/object_picking"), _SCS("set_physics_object_picking"), _SCS("get_physics_object_picking") );
ADD_SIGNAL(MethodInfo("size_changed"));
@@ -1070,11 +1304,17 @@ Viewport::Viewport() {
size_override=false;
size_override_stretch=false;
size_override_size=Size2(1,1);
+ render_target_gen_mipmaps=false;
render_target=false;
render_target_vflip=false;
render_target_update_mode=RENDER_TARGET_UPDATE_WHEN_VISIBLE;
render_target_texture = Ref<RenderTargetTexture>( memnew( RenderTargetTexture(this) ) );
+ physics_object_picking=false;
+ physics_object_capture=0;
+ physics_object_over=0;
+ physics_last_mousepos=Vector2(1e20,1e20);
+
String id=itos(get_instance_ID());
input_group = "_vp_input"+id;
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index d54b489843..5d68438f0d 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -114,6 +114,14 @@ friend class RenderTargetTexture;
bool transparent_bg;
bool render_target_vflip;
bool render_target_filter;
+ bool render_target_gen_mipmaps;
+
+ bool physics_object_picking;
+ List<InputEvent> physics_picking_events;
+ ObjectID physics_object_capture;
+ ObjectID physics_object_over;
+ Vector2 physics_last_mousepos;
+ void _test_new_mouseover(ObjectID new_collider);
void _update_rect();
@@ -214,6 +222,9 @@ public:
void set_render_target_filter(bool p_enable);
bool get_render_target_filter() const;
+ void set_render_target_gen_mipmaps(bool p_enable);
+ bool get_render_target_gen_mipmaps() const;
+
void set_render_target_update_mode(RenderTargetUpdateMode p_mode);
RenderTargetUpdateMode get_render_target_update_mode() const;
Ref<RenderTargetTexture> get_render_target_texture() const;
@@ -230,6 +241,9 @@ public:
void set_render_target_to_screen_rect(const Rect2& p_rect);
Rect2 get_render_target_to_screen_rect() const;
+ void set_physics_object_picking(bool p_enable);
+ bool get_physics_object_picking();
+
Viewport();
~Viewport();
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index a468f0a379..2a1cca6a3a 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -108,6 +108,7 @@
#include "scene/animation/animation_player.h"
#include "scene/animation/animation_tree_player.h"
+#include "scene/animation/tween.h"
#include "scene/main/scene_main_loop.h"
#include "scene/main/resource_preloader.h"
#include "scene/resources/packed_scene.h"
@@ -185,6 +186,7 @@
#include "scene/resources/environment.h"
#include "scene/3d/physics_body.h"
#include "scene/3d/car_body.h"
+#include "scene/3d/vehicle_body.h"
#include "scene/3d/body_shape.h"
#include "scene/3d/area.h"
#include "scene/3d/physics_joint.h"
@@ -196,6 +198,9 @@
#include "scene/3d/spatial_sample_player.h"
#include "scene/3d/spatial_stream_player.h"
#include "scene/3d/proximity_group.h"
+#include "scene/3d/navigation_mesh.h"
+#include "scene/3d/navigation.h"
+#include "scene/3d/collision_polygon.h"
#endif
#include "scene/scene_binds.h"
@@ -365,6 +370,7 @@ void register_scene_types() {
ObjectTypeDB::register_type<Spatial>();
ObjectTypeDB::register_type<Skeleton>();
ObjectTypeDB::register_type<AnimationPlayer>();
+ ObjectTypeDB::register_type<Tween>();
OS::get_singleton()->yield(); //may take time to init
@@ -388,17 +394,24 @@ void register_scene_types() {
ObjectTypeDB::register_type<Particles>();
ObjectTypeDB::register_type<Position3D>();
ObjectTypeDB::register_type<Quad>();
+ ObjectTypeDB::register_type<NavigationMeshInstance>();
+ ObjectTypeDB::register_type<NavigationMesh>();
+ ObjectTypeDB::register_type<Navigation>();
OS::get_singleton()->yield(); //may take time to init
ObjectTypeDB::register_virtual_type<CollisionObject>();
ObjectTypeDB::register_type<StaticBody>();
ObjectTypeDB::register_type<RigidBody>();
+ ObjectTypeDB::register_type<KinematicBody>();
ObjectTypeDB::register_type<CarBody>();
ObjectTypeDB::register_type<CarWheel>();
+ ObjectTypeDB::register_type<VehicleBody>();
+ ObjectTypeDB::register_type<VehicleWheel>();
ObjectTypeDB::register_type<Area>();
ObjectTypeDB::register_type<ProximityGroup>();
ObjectTypeDB::register_type<CollisionShape>();
+ ObjectTypeDB::register_type<CollisionPolygon>();
ObjectTypeDB::register_type<RayCast>();
ObjectTypeDB::register_virtual_type<EditableShape>();
ObjectTypeDB::register_type<EditableSphere>();
@@ -434,9 +447,17 @@ void register_scene_types() {
//ObjectTypeDB::register_type<BodyVolumeBox>();
//ObjectTypeDB::register_type<BodyVolumeCylinder>();
//ObjectTypeDB::register_type<BodyVolumeCapsule>();
- //ObjectTypeDB::register_virtual_type<PhysicsJoint>();
//ObjectTypeDB::register_type<PhysicsJointPin>();
+
+ ObjectTypeDB::register_virtual_type<Joint>();
+ ObjectTypeDB::register_type<PinJoint>();
+ ObjectTypeDB::register_type<HingeJoint>();
+ ObjectTypeDB::register_type<SliderJoint>();
+ ObjectTypeDB::register_type<ConeTwistJoint>();
+ ObjectTypeDB::register_type<Generic6DOFJoint>();
+
+
ObjectTypeDB::register_type<StreamPlayer>();
ObjectTypeDB::register_type<EventPlayer>();
diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp
index 7fa606f5da..67f45ced2b 100644
--- a/scene/resources/animation.cpp
+++ b/scene/resources/animation.cpp
@@ -1716,7 +1716,7 @@ void Animation::clear() {
}
-void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
+void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err) {
ERR_FAIL_INDEX(p_idx,tracks.size());
ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM);
@@ -1756,7 +1756,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
Vector3 s[2]={ v0, v2 };
real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
- if (d>pd.length()*p_allowed_err) {
+ if (d>pd.length()*p_alowed_linear_err) {
continue; //beyond allowed error for colinearity
}
@@ -1795,7 +1795,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
}
real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized()))/Math_PI;
- if (err_01>p_allowed_err) {
+ if (err_01>p_alowed_angular_err) {
//not rotating in the same axis
continue;
}
@@ -1841,7 +1841,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
Vector3 s[2]={ v0, v2 };
real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
- if (d>pd.length()*p_allowed_err) {
+ if (d>pd.length()*p_alowed_linear_err) {
continue; //beyond allowed error for colinearity
}
@@ -1866,7 +1866,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
if (t[k]==-1)
continue;
- if (Math::abs(lt-t[k])>p_allowed_err) {
+ if (Math::abs(lt-t[k])>p_alowed_linear_err) {
erase=false;
break;
}
@@ -1879,7 +1879,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
if (erase) {
- if (Math::abs(lt-c)>p_allowed_err) {
+ if (Math::abs(lt-c)>p_alowed_linear_err) {
//todo, evaluate changing the transition if this fails?
//this could be done as a second pass and would be
//able to optimize more
@@ -1905,7 +1905,7 @@ void Animation::_transform_track_optimize(int p_idx,float p_allowed_err) {
}
-void Animation::optimize(float p_allowed_err) {
+void Animation::optimize(float p_allowed_linear_err,float p_allowed_angular_err) {
int total_tt=0;
@@ -1913,7 +1913,7 @@ void Animation::optimize(float p_allowed_err) {
for(int i=0;i<tracks.size();i++) {
if (tracks[i]->type==TYPE_TRANSFORM)
- _transform_track_optimize(i,p_allowed_err);
+ _transform_track_optimize(i,p_allowed_linear_err,p_allowed_angular_err);
}
diff --git a/scene/resources/animation.h b/scene/resources/animation.h
index 4366bdaca8..4c4e2f0275 100644
--- a/scene/resources/animation.h
+++ b/scene/resources/animation.h
@@ -204,7 +204,7 @@ private:
return idxr;
}
- void _transform_track_optimize(int p_idx,float p_allowed_err=0.05);
+ void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01);
protected:
@@ -271,7 +271,7 @@ public:
void clear();
- void optimize(float p_allowed_err=0.05);
+ void optimize(float p_allowed_linear_err=0.05,float p_allowed_angular_err=0.01);
Animation();
~Animation();
diff --git a/scene/resources/baked_light.cpp b/scene/resources/baked_light.cpp
index 725ac1c946..647c8df5d4 100644
--- a/scene/resources/baked_light.cpp
+++ b/scene/resources/baked_light.cpp
@@ -5,6 +5,7 @@ void BakedLight::set_mode(Mode p_mode) {
mode=p_mode;
VS::get_singleton()->baked_light_set_mode(baked_light,(VS::BakedLightMode(p_mode)));
+
}
BakedLight::Mode BakedLight::get_mode() const{
@@ -23,56 +24,79 @@ DVector<uint8_t> BakedLight::get_octree() const {
}
-void BakedLight::_update_lightmaps() {
-
- VS::get_singleton()->baked_light_clear_lightmaps(baked_light);
- for(Map<int,Ref<Texture> >::Element *E=lightmaps.front();E;E=E->next()) {
- VS::get_singleton()->baked_light_add_lightmap(baked_light,E->get()->get_rid(),E->key());
- }
-}
-void BakedLight::add_lightmap(const Ref<Texture> p_texture,int p_id) {
+void BakedLight::add_lightmap(const Ref<Texture> &p_texture,Size2 p_gen_size) {
- ERR_FAIL_COND(!p_texture.is_valid());
- ERR_FAIL_COND(p_id<0);
- lightmaps[p_id]=p_texture;
- VS::get_singleton()->baked_light_add_lightmap(baked_light,p_texture->get_rid(),p_id);
+ LightMap lm;
+ lm.texture=p_texture;
+ lm.gen_size=p_gen_size;
+ lightmaps.push_back(lm);
+ _update_lightmaps();
+ _change_notify();
}
-void BakedLight::erase_lightmap(int p_id) {
+void BakedLight::set_lightmap_gen_size(int p_idx,const Size2& p_size){
- ERR_FAIL_COND(!lightmaps.has(p_id));
- lightmaps.erase(p_id);
+ ERR_FAIL_INDEX(p_idx,lightmaps.size());
+ lightmaps[p_idx].gen_size=p_size;
_update_lightmaps();
}
+Size2 BakedLight::get_lightmap_gen_size(int p_idx) const{
-void BakedLight::get_lightmaps(List<int> *r_lightmaps) {
+ ERR_FAIL_INDEX_V(p_idx,lightmaps.size(),Size2());
+ return lightmaps[p_idx].gen_size;
- for(Map<int,Ref<Texture> >::Element *E=lightmaps.front();E;E=E->next()) {
+}
+void BakedLight::set_lightmap_texture(int p_idx,const Ref<Texture> &p_texture){
- r_lightmaps->push_back(E->key());
- }
+ ERR_FAIL_INDEX(p_idx,lightmaps.size());
+ lightmaps[p_idx].texture=p_texture;
+ _update_lightmaps();
}
+Ref<Texture> BakedLight::get_lightmap_texture(int p_idx) const{
-Ref<Texture> BakedLight::get_lightmap_texture(int p_id) {
+ ERR_FAIL_INDEX_V(p_idx,lightmaps.size(),Ref<Texture>());
+ return lightmaps[p_idx].texture;
- if (!lightmaps.has(p_id))
- return Ref<Texture>();
-
- return lightmaps[p_id];
+}
+void BakedLight::erase_lightmap(int p_idx){
+ ERR_FAIL_INDEX(p_idx,lightmaps.size());
+ lightmaps.remove(p_idx);
+ _update_lightmaps();
+ _change_notify();
}
+int BakedLight::get_lightmaps_count() const{
-void BakedLight::clear_lightmaps() {
+ return lightmaps.size();
+}
+void BakedLight::clear_lightmaps(){
lightmaps.clear();
_update_lightmaps();
+ _change_notify();
+}
+
+
+
+void BakedLight::_update_lightmaps() {
+
+ VS::get_singleton()->baked_light_clear_lightmaps(baked_light);
+ for(int i=0;i<lightmaps.size();i++) {
+
+ RID tid;
+ if (lightmaps[i].texture.is_valid())
+ tid=lightmaps[i].texture->get_rid();
+ VS::get_singleton()->baked_light_add_lightmap(baked_light,tid,i);
+ }
}
+
+
RID BakedLight::get_rid() const {
return baked_light;
@@ -84,12 +108,11 @@ Array BakedLight::_get_lightmap_data() const {
ret.resize(lightmaps.size()*2);
int idx=0;
- for(Map<int,Ref<Texture> >::Element *E=lightmaps.front();E;E=E->next()) {
+ for(int i=0;i<lightmaps.size();i++) {
- ret[idx++]=E->key();
- ret[idx++]=E->get();
+ ret[idx++]=Size2(lightmaps[i].gen_size);
+ ret[idx++]=lightmaps[i].texture;
}
-
return ret;
}
@@ -99,11 +122,13 @@ void BakedLight::_set_lightmap_data(Array p_array){
lightmaps.clear();
for(int i=0;i<p_array.size();i+=2) {
- int id = p_array[i];
+ Size2 size = p_array[i];
Ref<Texture> tex = p_array[i+1];
- ERR_CONTINUE(id<0);
- ERR_CONTINUE(tex.is_null());
- lightmaps[id]=tex;
+// ERR_CONTINUE(tex.is_null());
+ LightMap lm;
+ lm.gen_size=size;
+ lm.texture=tex;
+ lightmaps.push_back(lm);
}
_update_lightmaps();
}
@@ -204,7 +229,7 @@ bool BakedLight::get_bake_flag(BakeFlags p_flags) const{
void BakedLight::set_format(Format p_format) {
format=p_format;
-
+ VS::get_singleton()->baked_light_set_lightmap_multiplier(baked_light,format==FORMAT_HDR8?8.0:1.0);
}
BakedLight::Format BakedLight::get_format() const{
@@ -212,6 +237,88 @@ BakedLight::Format BakedLight::get_format() const{
return format;
}
+void BakedLight::set_transfer_lightmaps_only_to_uv2(bool p_enable) {
+
+ transfer_only_uv2=p_enable;
+}
+
+bool BakedLight::get_transfer_lightmaps_only_to_uv2() const{
+
+ return transfer_only_uv2;
+}
+
+
+bool BakedLight::_set(const StringName& p_name, const Variant& p_value) {
+
+ String n = p_name;
+ if (!n.begins_with("lightmap"))
+ return false;
+ int idx = n.get_slice("/",1).to_int();
+ ERR_FAIL_COND_V(idx<0,false);
+ ERR_FAIL_COND_V(idx>lightmaps.size(),false);
+
+ String what = n.get_slice("/",2);
+ Ref<Texture> tex;
+ Size2 gens;
+
+ if (what=="texture")
+ tex=p_value;
+ else if (what=="gen_size")
+ gens=p_value;
+
+ if (idx==lightmaps.size()) {
+ if (tex.is_valid() || gens!=Size2())
+ add_lightmap(tex,gens);
+ } else {
+ if (tex.is_valid())
+ set_lightmap_texture(idx,tex);
+ else if (gens!=Size2())
+ set_lightmap_gen_size(idx,gens);
+ }
+
+
+ return true;
+}
+
+bool BakedLight::_get(const StringName& p_name,Variant &r_ret) const{
+
+ String n = p_name;
+ if (!n.begins_with("lightmap"))
+ return false;
+ int idx = n.get_slice("/",1).to_int();
+ ERR_FAIL_COND_V(idx<0,false);
+ ERR_FAIL_COND_V(idx>lightmaps.size(),false);
+
+ String what = n.get_slice("/",2);
+
+ if (what=="texture") {
+ if (idx==lightmaps.size())
+ r_ret=Ref<Texture>();
+ else
+ r_ret=lightmaps[idx].texture;
+
+ } else if (what=="gen_size") {
+
+ if (idx==lightmaps.size())
+ r_ret=Size2();
+ else
+ r_ret=Size2(lightmaps[idx].gen_size);
+ } else
+ return false;
+
+ return true;
+
+
+}
+void BakedLight::_get_property_list( List<PropertyInfo> *p_list) const{
+
+ for(int i=0;i<=lightmaps.size();i++) {
+
+ p_list->push_back(PropertyInfo(Variant::VECTOR2,"lightmaps/"+itos(i)+"/gen_size",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::OBJECT,"lightmaps/"+itos(i)+"/texture",PROPERTY_HINT_RESOURCE_TYPE,"Texture",PROPERTY_USAGE_EDITOR));
+ }
+}
+
void BakedLight::_bind_methods(){
@@ -222,7 +329,7 @@ void BakedLight::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_octree","octree"),&BakedLight::set_octree);
ObjectTypeDB::bind_method(_MD("get_octree"),&BakedLight::get_octree);
- ObjectTypeDB::bind_method(_MD("add_lightmap","texture:Texture","id"),&BakedLight::add_lightmap);
+ ObjectTypeDB::bind_method(_MD("add_lightmap","texture:Texture","gen_size"),&BakedLight::add_lightmap);
ObjectTypeDB::bind_method(_MD("erase_lightmap","id"),&BakedLight::erase_lightmap);
ObjectTypeDB::bind_method(_MD("clear_lightmaps"),&BakedLight::clear_lightmaps);
@@ -253,6 +360,11 @@ void BakedLight::_bind_methods(){
ObjectTypeDB::bind_method(_MD("set_format","format"),&BakedLight::set_format);
ObjectTypeDB::bind_method(_MD("get_format"),&BakedLight::get_format);
+ ObjectTypeDB::bind_method(_MD("set_transfer_lightmaps_only_to_uv2","enable"),&BakedLight::set_transfer_lightmaps_only_to_uv2);
+ ObjectTypeDB::bind_method(_MD("get_transfer_lightmaps_only_to_uv2"),&BakedLight::get_transfer_lightmaps_only_to_uv2);
+
+
+
ObjectTypeDB::bind_method(_MD("set_energy_multiplier","energy_multiplier"),&BakedLight::set_energy_multiplier);
ObjectTypeDB::bind_method(_MD("get_energy_multiplier"),&BakedLight::get_energy_multiplier);
@@ -276,6 +388,7 @@ void BakedLight::_bind_methods(){
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/specular"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_SPECULAR);
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/translucent"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_TRANSLUCENT);
ADD_PROPERTYI( PropertyInfo(Variant::BOOL,"baking_flags/conserve_energy"),_SCS("set_bake_flag"),_SCS("get_bake_flag"),BAKE_CONSERVE_ENERGY);
+ ADD_PROPERTY( PropertyInfo(Variant::BOOL,"lightmap/use_only_uv2"),_SCS("set_transfer_lightmaps_only_to_uv2"),_SCS("get_transfer_lightmaps_only_to_uv2"));
ADD_PROPERTY( PropertyInfo(Variant::RAW_ARRAY,"octree",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("set_octree"),_SCS("get_octree"));
ADD_PROPERTY( PropertyInfo(Variant::ARRAY,"lightmaps",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_NOEDITOR),_SCS("_set_lightmap_data"),_SCS("_get_lightmap_data"));
@@ -308,6 +421,7 @@ BakedLight::BakedLight() {
edge_damp=0.0;
normal_damp=0.0;
format=FORMAT_RGB;
+ transfer_only_uv2=false;
flags[BAKE_DIFFUSE]=true;
flags[BAKE_SPECULAR]=false;
diff --git a/scene/resources/baked_light.h b/scene/resources/baked_light.h
index df86f98c08..57ed7d7aee 100644
--- a/scene/resources/baked_light.h
+++ b/scene/resources/baked_light.h
@@ -33,7 +33,13 @@ private:
RID baked_light;
Mode mode;
- Map<int,Ref<Texture> > lightmaps;
+ struct LightMap {
+ Size2i gen_size;
+ Ref<Texture> texture;
+ };
+
+
+ Vector< LightMap> lightmaps;
//bake vars
int cell_subdiv;
@@ -45,6 +51,7 @@ private:
float edge_damp;
float normal_damp;
int bounces;
+ bool transfer_only_uv2;
Format format;
bool flags[BAKE_MAX];
@@ -54,6 +61,13 @@ private:
Array _get_lightmap_data() const;
void _set_lightmap_data(Array p_array);
+
+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:
@@ -91,16 +105,22 @@ public:
void set_format(Format p_margin);
Format get_format() const;
+ void set_transfer_lightmaps_only_to_uv2(bool p_enable);
+ bool get_transfer_lightmaps_only_to_uv2() const;
+
void set_mode(Mode p_mode);
Mode get_mode() const;
void set_octree(const DVector<uint8_t>& p_octree);
DVector<uint8_t> get_octree() const;
- void add_lightmap(const Ref<Texture> p_texture,int p_id);
- void erase_lightmap(int p_id);
- void get_lightmaps(List<int> *r_lightmaps);
- Ref<Texture> get_lightmap_texture(int p_id);
+ void add_lightmap(const Ref<Texture> &p_texture,Size2 p_gen_size=Size2(256,256));
+ void set_lightmap_gen_size(int p_idx,const Size2& p_size);
+ Size2 get_lightmap_gen_size(int p_idx) const;
+ void set_lightmap_texture(int p_idx,const Ref<Texture> &p_texture);
+ Ref<Texture> get_lightmap_texture(int p_idx) const;
+ void erase_lightmap(int p_idx);
+ int get_lightmaps_count() const;
void clear_lightmaps();
virtual RID get_rid() const;
diff --git a/scene/resources/video_stream.h b/scene/resources/video_stream.h
index 32bc8670f0..eafacce159 100644
--- a/scene/resources/video_stream.h
+++ b/scene/resources/video_stream.h
@@ -33,19 +33,37 @@
-class VideoStream : public AudioStreamResampled {
+class VideoStream : public Resource {
- OBJ_TYPE(VideoStream,AudioStreamResampled);
+ OBJ_TYPE(VideoStream,Resource);
protected:
static void _bind_methods();
public:
+ virtual void stop()=0;
+ virtual void play()=0;
+
+ virtual bool is_playing() const=0;
+
+ virtual void set_paused(bool p_paused)=0;
+ virtual bool is_paused(bool p_paused) const=0;
+
+ virtual void set_loop(bool p_enable)=0;
+ virtual bool has_loop() const=0;
+
+ virtual float get_length() const=0;
+
+ virtual float get_pos() const=0;
+ virtual void seek_pos(float p_time)=0;
+
virtual int get_pending_frame_count() const=0;
virtual Image pop_frame()=0;
virtual Image peek_frame() const=0;
+ virtual void update(float p_time)=0;
+
VideoStream();
};
diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp
index 0576d5a5b1..1d99eb6d1f 100644
--- a/scene/scene_string_names.cpp
+++ b/scene/scene_string_names.cpp
@@ -144,5 +144,7 @@ SceneStringNames::SceneStringNames() {
baked_light_changed = StaticCString::create("baked_light_changed");
_baked_light_changed = StaticCString::create("_baked_light_changed");
+ _mouse_enter=StaticCString::create("_mouse_enter");
+ _mouse_exit=StaticCString::create("_mouse_exit");
}
diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h
index d4de0555ed..dd4f8789c2 100644
--- a/scene/scene_string_names.h
+++ b/scene/scene_string_names.h
@@ -152,6 +152,10 @@ public:
StringName baked_light_changed;
StringName _baked_light_changed;
+ StringName _mouse_enter;
+ StringName _mouse_exit;
+
+
};