diff options
Diffstat (limited to 'scene/3d')
28 files changed, 673 insertions, 46 deletions
diff --git a/scene/3d/area.cpp b/scene/3d/area.cpp index 7d4235e051..a8a4122016 100644 --- a/scene/3d/area.cpp +++ b/scene/3d/area.cpp @@ -519,6 +519,60 @@ bool Area::overlaps_body(Node* p_body) const{ return E->get().in_tree; } +void Area::set_collision_mask(uint32_t p_mask) { + + collision_mask=p_mask; + PhysicsServer::get_singleton()->area_set_collision_mask(get_rid(),p_mask); +} + +uint32_t Area::get_collision_mask() const { + + return collision_mask; +} +void Area::set_layer_mask(uint32_t p_mask) { + + layer_mask=p_mask; + PhysicsServer::get_singleton()->area_set_layer_mask(get_rid(),p_mask); +} + +uint32_t Area::get_layer_mask() const { + + return layer_mask; +} + +void Area::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask|=1<<p_bit; + else + mask&=~(1<<p_bit); + set_collision_mask(mask); + +} + +bool Area::get_collision_mask_bit(int p_bit) const{ + + return get_collision_mask()&(1<<p_bit); +} + + +void Area::set_layer_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_layer_mask(); + if (p_value) + mask|=1<<p_bit; + else + mask&=~(1<<p_bit); + set_layer_mask(mask); + +} + +bool Area::get_layer_mask_bit(int p_bit) const{ + + return get_layer_mask()&(1<<p_bit); +} + void Area::_bind_methods() { @@ -552,6 +606,18 @@ 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_collision_mask","collision_mask"),&Area::set_collision_mask); + ObjectTypeDB::bind_method(_MD("get_collision_mask"),&Area::get_collision_mask); + + ObjectTypeDB::bind_method(_MD("set_layer_mask","layer_mask"),&Area::set_layer_mask); + ObjectTypeDB::bind_method(_MD("get_layer_mask"),&Area::get_layer_mask); + + ObjectTypeDB::bind_method(_MD("set_collision_mask_bit","bit","value"),&Area::set_collision_mask_bit); + ObjectTypeDB::bind_method(_MD("get_collision_mask_bit","bit"),&Area::get_collision_mask_bit); + + ObjectTypeDB::bind_method(_MD("set_layer_mask_bit","bit","value"),&Area::set_layer_mask_bit); + ObjectTypeDB::bind_method(_MD("get_layer_mask_bit","bit"),&Area::get_layer_mask_bit); + ObjectTypeDB::bind_method(_MD("set_monitorable","enable"),&Area::set_monitorable); ObjectTypeDB::bind_method(_MD("is_monitorable"),&Area::is_monitorable); @@ -562,8 +628,8 @@ void Area::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_overlapping_bodies"),&Area::get_overlapping_bodies); ObjectTypeDB::bind_method(_MD("get_overlapping_areas"),&Area::get_overlapping_areas); - ObjectTypeDB::bind_method(_MD("overlaps_body:PhysicsBody","body"),&Area::overlaps_body); - ObjectTypeDB::bind_method(_MD("overlaps_area:Area","area"),&Area::overlaps_area); + ObjectTypeDB::bind_method(_MD("overlaps_body","body"),&Area::overlaps_body); + ObjectTypeDB::bind_method(_MD("overlaps_area","area"),&Area::overlaps_area); ObjectTypeDB::bind_method(_MD("_body_inout"),&Area::_body_inout); ObjectTypeDB::bind_method(_MD("_area_inout"),&Area::_area_inout); @@ -589,6 +655,8 @@ void Area::_bind_methods() { 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,"monitorable"),_SCS("set_monitorable"),_SCS("is_monitorable")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"collision/layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask")); + ADD_PROPERTY( PropertyInfo(Variant::INT,"collision/mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_collision_mask"),_SCS("get_collision_mask")); } @@ -604,6 +672,8 @@ Area::Area() : CollisionObject(PhysicsServer::get_singleton()->area_create(),tru angular_damp=1; priority=0; monitoring=false; + collision_mask=1; + layer_mask=1; set_ray_pickable(false); set_enable_monitoring(true); set_monitorable(true); diff --git a/scene/3d/area.h b/scene/3d/area.h index c250d27fb1..440a7d2030 100644 --- a/scene/3d/area.h +++ b/scene/3d/area.h @@ -54,6 +54,8 @@ private: real_t gravity_distance_scale; real_t angular_damp; real_t linear_damp; + uint32_t collision_mask; + uint32_t layer_mask; int priority; bool monitoring; bool monitorable; @@ -157,6 +159,18 @@ public: void set_monitorable(bool p_enable); bool is_monitorable() const; + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_layer_mask(uint32_t p_mask); + uint32_t get_layer_mask() const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + + void set_layer_mask_bit(int p_bit, bool p_value); + bool get_layer_mask_bit(int p_bit) const; + Array get_overlapping_bodies() const; Array get_overlapping_areas() const; //function for script diff --git a/scene/3d/body_shape.cpp b/scene/3d/body_shape.cpp index 3a47371de3..e62ab394af 100644 --- a/scene/3d/body_shape.cpp +++ b/scene/3d/body_shape.cpp @@ -398,6 +398,19 @@ int CollisionShape::_get_update_shape_index() const{ return update_shape_index; } +String CollisionShape::get_configuration_warning() const { + + if (!get_parent()->cast_to<CollisionObject>()) { + return TTR("CollisionShape only serves to provide a collision shape to a CollisionObject derived node. Please only use it as a child of Area, StaticBody, RigidBody, KinematicBody, etc. to give them a shape."); + } + + if (!shape.is_valid()) { + return TTR("A shape must be provided for CollisionShape to function. Please create a shape resource for it!"); + } + + return String(); +} + void CollisionShape::_bind_methods() { diff --git a/scene/3d/body_shape.h b/scene/3d/body_shape.h index dd005c0edd..a3289bf26a 100644 --- a/scene/3d/body_shape.h +++ b/scene/3d/body_shape.h @@ -90,6 +90,9 @@ public: int get_collision_object_shape_index() const { return _get_update_shape_index(); } + + String get_configuration_warning() const; + CollisionShape(); ~CollisionShape(); }; diff --git a/scene/3d/bone_attachment.cpp b/scene/3d/bone_attachment.cpp index 1628ccc15e..56b61d40e2 100644 --- a/scene/3d/bone_attachment.cpp +++ b/scene/3d/bone_attachment.cpp @@ -80,6 +80,7 @@ void BoneAttachment::_check_bind() { int idx = sk->find_bone(bone_name); if (idx!=-1) { sk->bind_child_node_to_bone(idx,this);; + set_transform(sk->get_bone_global_pose(idx)); bound=true; } } diff --git a/scene/3d/collision_polygon.cpp b/scene/3d/collision_polygon.cpp index 3b14e1d767..e05f29714b 100644 --- a/scene/3d/collision_polygon.cpp +++ b/scene/3d/collision_polygon.cpp @@ -231,6 +231,19 @@ float CollisionPolygon::get_depth() const { return depth; } +String CollisionPolygon::get_configuration_warning() const { + + if (!get_parent()->cast_to<CollisionObject>()) { + return TTR("CollisionPolygon only serves to provide a collision shape to a CollisionObject derived node. Please only use it as a child of Area, StaticBody, RigidBody, KinematicBody, etc. to give them a shape."); + } + + if (polygon.empty()) { + return TTR("An empty CollisionPolygon has no effect on collision."); + + } + + return String(); +} void CollisionPolygon::_bind_methods() { diff --git a/scene/3d/collision_polygon.h b/scene/3d/collision_polygon.h index 9b9afea34f..3d190a02b3 100644 --- a/scene/3d/collision_polygon.h +++ b/scene/3d/collision_polygon.h @@ -55,6 +55,8 @@ public: int get_collision_object_first_shape() const { return shape_from; } int get_collision_object_last_shape() const { return shape_to; } + String get_configuration_warning() const; + CollisionPolygon(); }; diff --git a/scene/3d/listener.cpp b/scene/3d/listener.cpp new file mode 100644 index 0000000000..bf42a5c92e --- /dev/null +++ b/scene/3d/listener.cpp @@ -0,0 +1,167 @@ +#include "listener.h" + +#include "scene/resources/mesh.h" + +void Listener::_update_audio_listener_state() { + + +} + +void Listener::_request_listener_update() { + + _update_listener(); +} + +bool Listener::_set(const StringName& p_name, const Variant& p_value) { + + if (p_name == "current") { + if (p_value.operator bool()) { + make_current(); + } + else { + clear_current(); + } + } + else + return false; + + return true; +} +bool Listener::_get(const StringName& p_name,Variant &r_ret) const { + + if (p_name == "current") { + if (is_inside_tree() && get_tree()->is_node_being_edited(this)) { + r_ret = current; + } + else { + r_ret = is_current(); + } + } + else + return false; + + return true; +} + +void Listener::_get_property_list( List<PropertyInfo> *p_list) const { + + p_list->push_back( PropertyInfo( Variant::BOOL, "current" ) ); +} + +void Listener::_update_listener() { + + if (is_inside_tree() && is_current()) { + get_viewport()->_listener_transform_changed_notify(); + + } +} + +void Listener::_notification(int p_what) { + + switch(p_what) { + + case NOTIFICATION_ENTER_WORLD: { + bool first_listener = get_viewport()->_listener_add(this); + if (!get_tree()->is_node_being_edited(this) && (current || first_listener)) + make_current(); + } break; + case NOTIFICATION_TRANSFORM_CHANGED: { + _request_listener_update(); + } break; + case NOTIFICATION_EXIT_WORLD: { + + if (!get_tree()->is_node_being_edited(this)) { + if (is_current()) { + clear_current(); + current=true; //keep it true + + } else { + current=false; + } + } + + get_viewport()->_listener_remove(this); + + + } break; + + + } + +} + + +Transform Listener::get_listener_transform() const { + + return get_global_transform().orthonormalized(); +} + +void Listener::make_current() { + + current=true; + + if (!is_inside_tree()) + return; + + get_viewport()->_listener_set(this); +} + + + + +void Listener::clear_current() { + + current=false; + if (!is_inside_tree()) + return; + + if (get_viewport()->get_listener()==this) { + get_viewport()->_listener_set(NULL); + get_viewport()->_listener_make_next_current(this); + } + +} + +bool Listener::is_current() const { + + if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { + + return get_viewport()->get_listener()==this; + } else + return current; + + return false; +} + +bool Listener::_can_gizmo_scale() const { + + return false; +} + +RES Listener::_get_gizmo_geometry() const { + Ref<Mesh> mesh = memnew(Mesh); + + return mesh; +} + +void Listener::_bind_methods() { + + ObjectTypeDB::bind_method( _MD("make_current"),&Listener::make_current ); + ObjectTypeDB::bind_method( _MD("clear_current"),&Listener::clear_current ); + ObjectTypeDB::bind_method( _MD("is_current"),&Listener::is_current ); + ObjectTypeDB::bind_method( _MD("get_listener_transform"),&Listener::get_listener_transform ); +} + +Listener::Listener() { + + current=false; + force_change=false; + //active=false; +} + + +Listener::~Listener() { + +} + + diff --git a/scene/3d/listener.h b/scene/3d/listener.h new file mode 100644 index 0000000000..bf0281a8e0 --- /dev/null +++ b/scene/3d/listener.h @@ -0,0 +1,53 @@ +#ifndef LISTENER_H +#define LISTENER_H + + +#include "scene/3d/spatial.h" +#include "scene/main/viewport.h" + +class Listener : public Spatial { + + OBJ_TYPE(Listener, Spatial); +private: + + bool force_change; + bool current; + + RID scenario_id; + + virtual bool _can_gizmo_scale() const; + virtual RES _get_gizmo_geometry() const; + +friend class Viewport; + void _update_audio_listener_state(); +protected: + + void _update_listener(); + virtual void _request_listener_update(); + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + void _notification(int p_what); + + static void _bind_methods(); + +public: + + void make_current(); + void clear_current(); + bool is_current() const; + + virtual Transform get_listener_transform() const; + + void set_visible_layers(uint32_t p_layers); + uint32_t get_visible_layers() const; + + Vector<Plane> get_frustum() const; + + Listener(); + ~Listener(); + +}; + +#endif diff --git a/scene/3d/mesh_instance.cpp b/scene/3d/mesh_instance.cpp index cfe273fa20..ef956e8ad9 100644 --- a/scene/3d/mesh_instance.cpp +++ b/scene/3d/mesh_instance.cpp @@ -31,8 +31,8 @@ #include "skeleton.h" #include "physics_body.h" #include "body_shape.h" - - +#include "scene/scene_string_names.h" +#include "core_string_names.h" bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -43,13 +43,22 @@ bool MeshInstance::_set(const StringName& p_name, const Variant& p_value) { Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name); - if (!E) - return false; + if (E) { + E->get().value=p_value; + VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value); + return true; + } - E->get().value=p_value; - VisualServer::get_singleton()->instance_set_morph_target_weight(get_instance(),E->get().idx,E->get().value); + if (p_name.operator String().begins_with("material/")) { + int idx = p_name.operator String().get_slicec('/',1).to_int(); + if (idx>=materials.size() || idx<0) + return false; - return true; + set_surface_material(idx,p_value); + return true; + } + + return false; } bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const { @@ -59,12 +68,19 @@ bool MeshInstance::_get(const StringName& p_name,Variant &r_ret) const { return false; const Map<StringName,MorphTrack>::Element *E = morph_tracks.find(p_name); - if (!E) - return false; - - r_ret = E->get().value; + if (E) { + r_ret = E->get().value; + return true; + } - return true; + if (p_name.operator String().begins_with("material/")) { + int idx = p_name.operator String().get_slicec('/',1).to_int(); + if (idx>=materials.size() || idx<0) + return false; + r_ret=materials[idx]; + return true; + } + return false; } void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const { @@ -80,6 +96,12 @@ void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const { for(List<String>::Element *E=ls.front();E;E=E->next()) { p_list->push_back( PropertyInfo(Variant::REAL,E->get(),PROPERTY_HINT_RANGE,"0,1,0.01")); } + + if (mesh.is_valid()) { + for(int i=0;i<mesh->get_surface_count();i++) { + p_list->push_back( PropertyInfo(Variant::OBJECT, "material/"+itos(i), PROPERTY_HINT_RESOURCE_TYPE, "Material")); + } + } } @@ -87,6 +109,14 @@ void MeshInstance::_get_property_list( List<PropertyInfo> *p_list) const { void MeshInstance::set_mesh(const Ref<Mesh>& p_mesh) { + if (mesh==p_mesh) + return; + + if (mesh.is_valid()) { + mesh->disconnect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->_mesh_changed); + materials.clear(); + } + mesh=p_mesh; morph_tracks.clear(); @@ -100,13 +130,17 @@ void MeshInstance::set_mesh(const Ref<Mesh>& p_mesh) { mt.value=0; morph_tracks["morph/"+String(mesh->get_morph_target_name(i))]=mt; } + + mesh->connect(CoreStringNames::get_singleton()->changed,this,SceneStringNames::get_singleton()->_mesh_changed); + materials.resize(mesh->get_surface_count()); + set_base(mesh->get_rid()); } else { set_base(RID()); } - _change_notify("mesh"); + _change_notify(); } Ref<Mesh> MeshInstance::get_mesh() const { @@ -232,6 +266,32 @@ void MeshInstance::_notification(int p_what) { } +void MeshInstance::set_surface_material(int p_surface,const Ref<Material>& p_material) { + + ERR_FAIL_INDEX(p_surface,materials.size()); + + materials[p_surface]=p_material; + + if (materials[p_surface].is_valid()) + VS::get_singleton()->instance_set_surface_material(get_instance(),p_surface,materials[p_surface]->get_rid()); + else + VS::get_singleton()->instance_set_surface_material(get_instance(),p_surface,RID()); + +} + +Ref<Material> MeshInstance::get_surface_material(int p_surface) const { + + ERR_FAIL_INDEX_V(p_surface,materials.size(),Ref<Material>()); + + return materials[p_surface]; +} + + +void MeshInstance::_mesh_changed() { + + materials.resize( mesh->get_surface_count() ); +} + void MeshInstance::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_mesh","mesh:Mesh"),&MeshInstance::set_mesh); @@ -243,6 +303,7 @@ void MeshInstance::_bind_methods() { ObjectTypeDB::set_method_flags("MeshInstance","create_trimesh_collision",METHOD_FLAGS_DEFAULT); ObjectTypeDB::bind_method(_MD("create_convex_collision"),&MeshInstance::create_convex_collision); ObjectTypeDB::set_method_flags("MeshInstance","create_convex_collision",METHOD_FLAGS_DEFAULT); + ObjectTypeDB::bind_method(_MD("_mesh_changed"),&MeshInstance::_mesh_changed); ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "mesh/mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh" ), _SCS("set_mesh"), _SCS("get_mesh")); ADD_PROPERTY( PropertyInfo (Variant::NODE_PATH, "mesh/skeleton"), _SCS("set_skeleton_path"), _SCS("get_skeleton_path")); } diff --git a/scene/3d/mesh_instance.h b/scene/3d/mesh_instance.h index 7c605c2d6d..fd8faf38b4 100644 --- a/scene/3d/mesh_instance.h +++ b/scene/3d/mesh_instance.h @@ -50,7 +50,9 @@ class MeshInstance : public GeometryInstance { }; Map<StringName,MorphTrack> morph_tracks; + Vector<Ref<Material> > materials; + void _mesh_changed(); void _resolve_skeleton_path(); protected: @@ -69,6 +71,9 @@ public: void set_skeleton_path(const NodePath& p_skeleton); NodePath get_skeleton_path(); + void set_surface_material(int p_surface,const Ref<Material>& p_material); + Ref<Material> get_surface_material(int p_surface) const; + Node* create_trimesh_collision_node(); void create_trimesh_collision(); diff --git a/scene/3d/navigation_mesh.cpp b/scene/3d/navigation_mesh.cpp index a238a8ff22..3adf282f13 100644 --- a/scene/3d/navigation_mesh.cpp +++ b/scene/3d/navigation_mesh.cpp @@ -329,6 +329,7 @@ void NavigationMeshInstance::set_navigation_mesh(const Ref<NavigationMesh>& p_na nav_id = navigation->navmesh_create(navmesh,get_relative_transform(navigation),this); } update_gizmo(); + update_configuration_warning(); } @@ -337,6 +338,27 @@ Ref<NavigationMesh> NavigationMeshInstance::get_navigation_mesh() const{ return navmesh; } +String NavigationMeshInstance::get_configuration_warning() const { + + if (!is_visible() || !is_inside_tree()) + return String(); + + if (!navmesh.is_valid()) { + return TTR("A NavigationMesh resource must be set or created for this node to work."); + } + const Spatial *c=this; + while(c) { + + if (c->cast_to<Navigation>()) + return String(); + + c=c->get_parent()->cast_to<Spatial>(); + } + + return TTR("NavigationMeshInstance must be a child or grandchild to a Navigation node. It only provides navigation data."); +} + + void NavigationMeshInstance::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_navigation_mesh","navmesh"),&NavigationMeshInstance::set_navigation_mesh); diff --git a/scene/3d/navigation_mesh.h b/scene/3d/navigation_mesh.h index 1e53b2127a..cb3b5d95f6 100644 --- a/scene/3d/navigation_mesh.h +++ b/scene/3d/navigation_mesh.h @@ -78,6 +78,8 @@ public: void set_navigation_mesh(const Ref<NavigationMesh>& p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; + String get_configuration_warning() const; + NavigationMeshInstance(); }; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index 1a2665b6ad..243cb31aca 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -69,6 +69,50 @@ uint32_t PhysicsBody::get_layer_mask() const { return layer_mask; } +void PhysicsBody::set_collision_mask(uint32_t p_mask) { + + collision_mask=p_mask; + PhysicsServer::get_singleton()->body_set_collision_mask(get_rid(),p_mask); +} + +uint32_t PhysicsBody::get_collision_mask() const { + + return collision_mask; +} + +void PhysicsBody::set_collision_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_collision_mask(); + if (p_value) + mask|=1<<p_bit; + else + mask&=~(1<<p_bit); + set_collision_mask(mask); + +} + +bool PhysicsBody::get_collision_mask_bit(int p_bit) const{ + + return get_collision_mask()&(1<<p_bit); +} + + +void PhysicsBody::set_layer_mask_bit(int p_bit, bool p_value) { + + uint32_t mask = get_layer_mask(); + if (p_value) + mask|=1<<p_bit; + else + mask&=~(1<<p_bit); + set_layer_mask(mask); + +} + +bool PhysicsBody::get_layer_mask_bit(int p_bit) const{ + + return get_layer_mask()&(1<<p_bit); +} + void PhysicsBody::add_collision_exception_with(Node* p_node) { ERR_FAIL_NULL(p_node); @@ -92,17 +136,42 @@ void PhysicsBody::remove_collision_exception_with(Node* p_node) { PhysicsServer::get_singleton()->body_remove_collision_exception(get_rid(),physics_body->get_rid()); } +void PhysicsBody::_set_layers(uint32_t p_mask) { + set_layer_mask(p_mask); + set_collision_mask(p_mask); +} + +uint32_t PhysicsBody::_get_layers() const{ + + return get_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")); + + ObjectTypeDB::bind_method(_MD("set_collision_mask","mask"),&PhysicsBody::set_collision_mask); + ObjectTypeDB::bind_method(_MD("get_collision_mask"),&PhysicsBody::get_collision_mask); + + ObjectTypeDB::bind_method(_MD("set_collision_mask_bit","bit","value"),&PhysicsBody::set_collision_mask_bit); + ObjectTypeDB::bind_method(_MD("get_collision_mask_bit","bit"),&PhysicsBody::get_collision_mask_bit); + + ObjectTypeDB::bind_method(_MD("set_layer_mask_bit","bit","value"),&PhysicsBody::set_layer_mask_bit); + ObjectTypeDB::bind_method(_MD("get_layer_mask_bit","bit"),&PhysicsBody::get_layer_mask_bit); + + ObjectTypeDB::bind_method(_MD("_set_layers","mask"),&PhysicsBody::_set_layers); + ObjectTypeDB::bind_method(_MD("_get_layers"),&PhysicsBody::_get_layers); + + ADD_PROPERTY(PropertyInfo(Variant::INT,"layers",PROPERTY_HINT_ALL_FLAGS,"",0),_SCS("_set_layers"),_SCS("_get_layers")); //for backwards compat + ADD_PROPERTY(PropertyInfo(Variant::INT,"collision/layers",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"collision/mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_collision_mask"),_SCS("get_collision_mask")); } PhysicsBody::PhysicsBody(PhysicsServer::BodyMode p_mode) : CollisionObject( PhysicsServer::get_singleton()->body_create(p_mode), false) { layer_mask=1; + collision_mask=1; } @@ -419,7 +488,10 @@ void RigidBody::_direct_state_changed(Object *p_state) { set_global_transform(state->get_transform()); linear_velocity=state->get_linear_velocity(); angular_velocity=state->get_angular_velocity(); - sleeping=state->is_sleeping(); + if(sleeping!=state->is_sleeping()) { + sleeping=state->is_sleeping(); + emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); + } if (get_script_instance()) get_script_instance()->call("_integrate_forces",state); set_ignore_transform_notification(false); @@ -811,6 +883,7 @@ void RigidBody::_bind_methods() { ADD_SIGNAL( MethodInfo("body_exit_shape",PropertyInfo(Variant::INT,"body_id"),PropertyInfo(Variant::OBJECT,"body"),PropertyInfo(Variant::INT,"body_shape"),PropertyInfo(Variant::INT,"local_shape"))); ADD_SIGNAL( MethodInfo("body_enter",PropertyInfo(Variant::OBJECT,"body"))); ADD_SIGNAL( MethodInfo("body_exit",PropertyInfo(Variant::OBJECT,"body"))); + ADD_SIGNAL( MethodInfo("sleeping_state_changed")); BIND_CONSTANT( MODE_STATIC ); BIND_CONSTANT( MODE_KINEMATIC ); diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index da79d63f00..f95b4f017f 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -39,6 +39,11 @@ class PhysicsBody : public CollisionObject { OBJ_TYPE(PhysicsBody,CollisionObject); uint32_t layer_mask; + uint32_t collision_mask; + + void _set_layers(uint32_t p_mask); + uint32_t _get_layers() const; + protected: static void _bind_methods(); @@ -53,6 +58,15 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_layer_mask_bit(int p_bit, bool p_value); + bool get_layer_mask_bit(int p_bit) const; + + void set_collision_mask_bit(int p_bit, bool p_value); + bool get_collision_mask_bit(int p_bit) const; + void add_collision_exception_with(Node* p_node); //must be physicsbody void remove_collision_exception_with(Node* p_node); diff --git a/scene/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp index ab2c4fc8dc..1acda8d1f8 100644 --- a/scene/3d/ray_cast.cpp +++ b/scene/3d/ray_cast.cpp @@ -33,7 +33,7 @@ void RayCast::set_cast_to(const Vector3& p_point) { cast_to=p_point; - if (is_inside_tree() && get_tree()->is_editor_hint()) + if (is_inside_tree() && (get_tree()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) update_gizmo(); } @@ -43,6 +43,26 @@ Vector3 RayCast::get_cast_to() const{ return cast_to; } +void RayCast::set_layer_mask(uint32_t p_mask) { + + layer_mask=p_mask; +} + +uint32_t RayCast::get_layer_mask() const { + + return layer_mask; +} + +void RayCast::set_type_mask(uint32_t p_mask) { + + type_mask=p_mask; +} + +uint32_t RayCast::get_type_mask() const { + + return type_mask; +} + bool RayCast::is_colliding() const{ return collided; @@ -130,7 +150,7 @@ void RayCast::_notification(int p_what) { PhysicsDirectSpaceState::RayResult rr; - if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude)) { + if (dss->intersect_ray(gt.get_origin(),gt.xform(to),rr,exclude, layer_mask, type_mask)) { collided=true; against=rr.collider_id; @@ -206,8 +226,16 @@ void RayCast::_bind_methods() { ObjectTypeDB::bind_method(_MD("clear_exceptions"),&RayCast::clear_exceptions); + ObjectTypeDB::bind_method(_MD("set_layer_mask","mask"),&RayCast::set_layer_mask); + ObjectTypeDB::bind_method(_MD("get_layer_mask"),&RayCast::get_layer_mask); + + ObjectTypeDB::bind_method(_MD("set_type_mask","mask"),&RayCast::set_type_mask); + ObjectTypeDB::bind_method(_MD("get_type_mask"),&RayCast::get_type_mask); + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"enabled"),_SCS("set_enabled"),_SCS("is_enabled")); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3,"cast_to"),_SCS("set_cast_to"),_SCS("get_cast_to")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"layer_mask",PROPERTY_HINT_ALL_FLAGS),_SCS("set_layer_mask"),_SCS("get_layer_mask")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"type_mask",PROPERTY_HINT_FLAGS,"Static,Kinematic,Rigid,Character,Area"),_SCS("set_type_mask"),_SCS("get_type_mask")); } RayCast::RayCast() { @@ -216,5 +244,7 @@ RayCast::RayCast() { against=0; collided=false; against_shape=0; + layer_mask=1; + type_mask=PhysicsDirectSpaceState::TYPE_MASK_COLLISION; cast_to=Vector3(0,-1,0); } diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h index 520b4d5313..4f6514e61b 100644 --- a/scene/3d/ray_cast.h +++ b/scene/3d/ray_cast.h @@ -47,6 +47,9 @@ class RayCast : public Spatial { Set<RID> exclude; + uint32_t layer_mask; + uint32_t type_mask; + protected: void _notification(int p_what); @@ -59,6 +62,12 @@ public: void set_cast_to(const Vector3& p_point); Vector3 get_cast_to() const; + void set_layer_mask(uint32_t p_mask); + uint32_t get_layer_mask() const; + + void set_type_mask(uint32_t p_mask); + uint32_t get_type_mask() const; + bool is_colliding() const; Object *get_collider() const; int get_collider_shape() const; diff --git a/scene/3d/scenario_fx.cpp b/scene/3d/scenario_fx.cpp index 2e22ab36d3..f01c2263fb 100644 --- a/scene/3d/scenario_fx.cpp +++ b/scene/3d/scenario_fx.cpp @@ -40,12 +40,17 @@ void WorldEnvironment::_notification(int p_what) { WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); } get_world()->set_environment(environment); + add_to_group("_world_environment_"+itos(get_world()->get_scenario().get_id())); + } } else if (p_what==NOTIFICATION_EXIT_WORLD) { - if (environment.is_valid() && get_world()->get_environment()==environment) + if (environment.is_valid() && get_world()->get_environment()==environment) { get_world()->set_environment(Ref<Environment>()); + remove_from_group("_world_environment_"+itos(get_world()->get_scenario().get_id())); + + } } } @@ -53,6 +58,7 @@ void WorldEnvironment::set_environment(const Ref<Environment>& p_environment) { if (is_inside_world() && environment.is_valid() && get_world()->get_environment()==environment) { get_world()->set_environment(Ref<Environment>()); + remove_from_group("_world_environment_"+itos(get_world()->get_scenario().get_id())); //clean up } @@ -63,7 +69,11 @@ void WorldEnvironment::set_environment(const Ref<Environment>& p_environment) { WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); } get_world()->set_environment(environment); + add_to_group("_world_environment_"+itos(get_world()->get_scenario().get_id())); + } + + update_configuration_warning(); } Ref<Environment> WorldEnvironment::get_environment() const { @@ -71,6 +81,21 @@ Ref<Environment> WorldEnvironment::get_environment() const { return environment; } +String WorldEnvironment::get_configuration_warning() const { + + if (!is_visible() || !is_inside_tree() || !environment.is_valid()) + return String(); + + List<Node*> nodes; + get_tree()->get_nodes_in_group("_world_environment_"+itos(get_world()->get_scenario().get_id()),&nodes); + + if (nodes.size()>1) { + return TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); + } + + return String(); +} + void WorldEnvironment::_bind_methods() { diff --git a/scene/3d/scenario_fx.h b/scene/3d/scenario_fx.h index a3c13e03a1..a73c455918 100644 --- a/scene/3d/scenario_fx.h +++ b/scene/3d/scenario_fx.h @@ -51,6 +51,8 @@ public: void set_environment(const Ref<Environment>& p_environment); Ref<Environment> get_environment() const; + String get_configuration_warning() const; + WorldEnvironment(); }; diff --git a/scene/3d/spatial.cpp b/scene/3d/spatial.cpp index c2d318e8a7..6a9c655141 100644 --- a/scene/3d/spatial.cpp +++ b/scene/3d/spatial.cpp @@ -346,14 +346,14 @@ void Spatial::set_translation(const Vector3& p_translation) { } -void Spatial::set_rotation(const Vector3& p_euler){ +void Spatial::set_rotation(const Vector3& p_euler_rad){ if (data.dirty&DIRTY_VECTORS) { data.scale=data.local_transform.basis.get_scale(); data.dirty&=~DIRTY_VECTORS; } - data.rotation=p_euler; + data.rotation=p_euler_rad; data.dirty|=DIRTY_LOCAL; _propagate_transform_changed(this); if (data.notify_local_transform) { @@ -361,6 +361,18 @@ void Spatial::set_rotation(const Vector3& p_euler){ } } + +void Spatial::set_rotation_deg(const Vector3& p_euler_deg) { + + set_rotation(p_euler_deg * Math_PI / 180.0); +} + +void Spatial::_set_rotation_deg(const Vector3& p_euler_deg) { + + WARN_PRINT("Deprecated method Spatial._set_rotation_deg(): This method was renamed to set_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted."); + set_rotation_deg(p_euler_deg); +} + void Spatial::set_scale(const Vector3& p_scale){ if (data.dirty&DIRTY_VECTORS) { @@ -381,6 +393,7 @@ Vector3 Spatial::get_translation() const{ return data.local_transform.origin; } + Vector3 Spatial::get_rotation() const{ if (data.dirty&DIRTY_VECTORS) { @@ -391,6 +404,20 @@ Vector3 Spatial::get_rotation() const{ return data.rotation; } + +Vector3 Spatial::get_rotation_deg() const { + + return get_rotation() * 180.0 / Math_PI; +} + +// Kept for compatibility after rename to set_rotd. +// Could be removed after a couple releases. +Vector3 Spatial::_get_rotation_deg() const { + + WARN_PRINT("Deprecated method Spatial._get_rotation_deg(): This method was renamed to get_rotation_deg. Please adapt your code accordingly, as the old method will be obsoleted."); + return get_rotation_deg(); +} + Vector3 Spatial::get_scale() const{ if (data.dirty&DIRTY_VECTORS) { @@ -495,16 +522,6 @@ bool Spatial::is_set_as_toplevel() const{ return data.toplevel; } -void Spatial::_set_rotation_deg(const Vector3& p_deg) { - - set_rotation(p_deg * Math_PI / 180.0); -} - -Vector3 Spatial::_get_rotation_deg() const { - - return get_rotation() * 180.0 / Math_PI; -} - Ref<World> Spatial::get_world() const { ERR_FAIL_COND_V(!is_inside_world(),Ref<World>()); @@ -722,8 +739,10 @@ void Spatial::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_transform"), &Spatial::get_transform); ObjectTypeDB::bind_method(_MD("set_translation","translation"), &Spatial::set_translation); ObjectTypeDB::bind_method(_MD("get_translation"), &Spatial::get_translation); - ObjectTypeDB::bind_method(_MD("set_rotation","rotation"), &Spatial::set_rotation); + ObjectTypeDB::bind_method(_MD("set_rotation","rotation_rad"), &Spatial::set_rotation); ObjectTypeDB::bind_method(_MD("get_rotation"), &Spatial::get_rotation); + ObjectTypeDB::bind_method(_MD("set_rotation_deg","rotation_deg"), &Spatial::set_rotation_deg); + ObjectTypeDB::bind_method(_MD("get_rotation_deg"), &Spatial::get_rotation_deg); ObjectTypeDB::bind_method(_MD("set_scale","scale"), &Spatial::set_scale); ObjectTypeDB::bind_method(_MD("get_scale"), &Spatial::get_scale); ObjectTypeDB::bind_method(_MD("set_global_transform","global"), &Spatial::set_global_transform); @@ -732,9 +751,11 @@ void Spatial::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_ignore_transform_notification","enabled"), &Spatial::set_ignore_transform_notification); ObjectTypeDB::bind_method(_MD("set_as_toplevel","enable"), &Spatial::set_as_toplevel); ObjectTypeDB::bind_method(_MD("is_set_as_toplevel"), &Spatial::is_set_as_toplevel); + ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world); + + // TODO: Obsolete those two methods (old name) properly (GH-4397) ObjectTypeDB::bind_method(_MD("_set_rotation_deg","rotation_deg"), &Spatial::_set_rotation_deg); ObjectTypeDB::bind_method(_MD("_get_rotation_deg"), &Spatial::_get_rotation_deg); - ObjectTypeDB::bind_method(_MD("get_world:World"), &Spatial::get_world); #ifdef TOOLS_ENABLED ObjectTypeDB::bind_method(_MD("_update_gizmo"), &Spatial::_update_gizmo); @@ -789,7 +810,7 @@ void Spatial::_bind_methods() { //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), _SCS("set_global_transform"), _SCS("get_global_transform") ); ADD_PROPERTYNZ( PropertyInfo(Variant::TRANSFORM,"transform/local",PROPERTY_HINT_NONE,""), _SCS("set_transform"), _SCS("get_transform") ); ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/translation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("set_translation"), _SCS("get_translation") ); - ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_EDITOR), _SCS("_set_rotation_deg"), _SCS("_get_rotation_deg") ); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR3,"transform/rotation",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_PROPERTYNO( PropertyInfo(Variant::BOOL,"visibility/visible"), _SCS("_set_visible_"), _SCS("_is_visible_") ); diff --git a/scene/3d/spatial.h b/scene/3d/spatial.h index 50123b2d81..fdc9f95f0b 100644 --- a/scene/3d/spatial.h +++ b/scene/3d/spatial.h @@ -110,7 +110,8 @@ class Spatial : public Node { void _notify_dirty(); void _propagate_transform_changed(Spatial *p_origin); - void _set_rotation_deg(const Vector3& p_deg); + // Deprecated, should be removed in a future version. + void _set_rotation_deg(const Vector3& p_euler_deg); Vector3 _get_rotation_deg() const; void _propagate_visibility_changed(); @@ -144,11 +145,13 @@ public: Ref<World> get_world() const; void set_translation(const Vector3& p_translation); - void set_rotation(const Vector3& p_euler); + void set_rotation(const Vector3& p_euler_rad); + void set_rotation_deg(const Vector3& p_euler_deg); void set_scale(const Vector3& p_scale); Vector3 get_translation() const; Vector3 get_rotation() const; + Vector3 get_rotation_deg() const; Vector3 get_scale() const; void set_transform(const Transform& p_transform); diff --git a/scene/3d/spatial_sample_player.cpp b/scene/3d/spatial_sample_player.cpp index 7114fd4b77..0df921f208 100644 --- a/scene/3d/spatial_sample_player.cpp +++ b/scene/3d/spatial_sample_player.cpp @@ -104,6 +104,7 @@ void SpatialSamplePlayer::set_sample_library(const Ref<SampleLibrary>& p_library library=p_library; _change_notify(); + update_configuration_warning(); } Ref<SampleLibrary> SpatialSamplePlayer::get_sample_library() const { @@ -190,6 +191,16 @@ void SpatialSamplePlayer::stop_all() { } } +String SpatialSamplePlayer::get_configuration_warning() const { + + if (library.is_null()) { + return TTR("A SampleLibrary resource must be created or set in the 'samples' property in order for SpatialSamplePlayer to play sound."); + } + + return String(); +} + + void SpatialSamplePlayer::_bind_methods() { diff --git a/scene/3d/spatial_sample_player.h b/scene/3d/spatial_sample_player.h index 037cdc906a..257f6d0dc3 100644 --- a/scene/3d/spatial_sample_player.h +++ b/scene/3d/spatial_sample_player.h @@ -78,6 +78,7 @@ public: void stop_voice(VoiceID p_voice); void stop_all(); + String get_configuration_warning() const; SpatialSamplePlayer(); ~SpatialSamplePlayer(); diff --git a/scene/3d/spatial_stream_player.cpp b/scene/3d/spatial_stream_player.cpp index dfef0faf4b..11debb9bce 100644 --- a/scene/3d/spatial_stream_player.cpp +++ b/scene/3d/spatial_stream_player.cpp @@ -329,8 +329,8 @@ int SpatialStreamPlayer::get_buffering_msec() const{ void SpatialStreamPlayer::_bind_methods() { - ObjectTypeDB::bind_method(_MD("set_stream","stream:Stream"),&SpatialStreamPlayer::set_stream); - ObjectTypeDB::bind_method(_MD("get_stream:Stream"),&SpatialStreamPlayer::get_stream); + ObjectTypeDB::bind_method(_MD("set_stream","stream:AudioStream"),&SpatialStreamPlayer::set_stream); + ObjectTypeDB::bind_method(_MD("get_stream:AudioStream"),&SpatialStreamPlayer::get_stream); ObjectTypeDB::bind_method(_MD("play","offset"),&SpatialStreamPlayer::play,DEFVAL(0)); ObjectTypeDB::bind_method(_MD("stop"),&SpatialStreamPlayer::stop); diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index c7d1249a07..da83e9b6d2 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -606,11 +606,11 @@ void AnimatedSprite3D::_draw() { RID immediate = get_immediate(); VS::get_singleton()->immediate_clear(immediate); - if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) { + if (!frames.is_valid() || !frames->get_frame_count(animation) || frame<0 || frame>=frames->get_frame_count(animation)) { return; } - Ref<Texture> texture = frames->get_frame(frame); + Ref<Texture> texture = frames->get_frame(animation,frame); if (!texture.is_valid()) return; //no texuture no life Vector2 tsize = texture->get_size(); @@ -748,7 +748,7 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames>& p_sprite_frame if (frames.is_valid()) frames->connect("changed",this,"_queue_update"); - if (!frames.is_valid() || frame >=frames->get_frame_count()) { + if (!frames.is_valid() || frame >=frames->get_frame_count(animation)) { frame=0; } @@ -766,7 +766,7 @@ void AnimatedSprite3D::set_frame(int p_frame){ if (frames.is_null()) return; - ERR_FAIL_INDEX(p_frame,frames->get_frame_count()); + ERR_FAIL_INDEX(p_frame,frames->get_frame_count(animation)); if (frame==p_frame) return; @@ -783,11 +783,11 @@ int AnimatedSprite3D::get_frame() const{ Rect2 AnimatedSprite3D::get_item_rect() const { - if (!frames.is_valid() || !frames->get_frame_count() || frame<0 || frame>=frames->get_frame_count()) { + if (!frames.is_valid() || !frames->get_frame_count(animation) || frame<0 || frame>=frames->get_frame_count(animation)) { return Rect2(0,0,1,1); } - Ref<Texture> t = frames->get_frame(frame); + Ref<Texture> t = frames->get_frame(animation,frame); if (t.is_null()) return Rect2(0,0,1,1); Size2i s = t->get_size(); @@ -806,6 +806,8 @@ Rect2 AnimatedSprite3D::get_item_rect() const { AnimatedSprite3D::AnimatedSprite3D() { + animation="current"; frame=0; } + diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index fe8e1f6ebf..d8e589556c 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -158,12 +158,14 @@ public: // ~Sprite3D(); }; + class AnimatedSprite3D : public SpriteBase3D { OBJ_TYPE(AnimatedSprite3D,SpriteBase3D); Ref<SpriteFrames> frames; + StringName animation; int frame; protected: @@ -186,6 +188,7 @@ public: // ~AnimatedSprite3D(); }; + VARIANT_ENUM_CAST(SpriteBase3D::DrawFlags); VARIANT_ENUM_CAST(SpriteBase3D::AlphaCutMode); #endif // SPRITE_3D_H diff --git a/scene/3d/vehicle_body.cpp b/scene/3d/vehicle_body.cpp index ba30c118f0..e35ba11e84 100644 --- a/scene/3d/vehicle_body.cpp +++ b/scene/3d/vehicle_body.cpp @@ -936,7 +936,7 @@ void VehicleBody::_direct_state_changed(Object *p_state) { wheel.m_deltaRotation *= real_t(0.99);//damping of rotation when not in contact } - + linear_velocity = s->get_linear_velocity(); } void VehicleBody::set_mass(real_t p_mass) { @@ -990,6 +990,10 @@ float VehicleBody::get_steering() const{ return m_steeringValue; } +Vector3 VehicleBody::get_linear_velocity() +{ + return linear_velocity; +} void VehicleBody::_bind_methods(){ @@ -1008,6 +1012,8 @@ void VehicleBody::_bind_methods(){ ObjectTypeDB::bind_method(_MD("set_steering","steering"),&VehicleBody::set_steering); ObjectTypeDB::bind_method(_MD("get_steering"),&VehicleBody::get_steering); + ObjectTypeDB::bind_method(_MD("get_linear_velocity"),&VehicleBody::get_linear_velocity); + ObjectTypeDB::bind_method(_MD("_direct_state_changed"),&VehicleBody::_direct_state_changed); ADD_PROPERTY( PropertyInfo(Variant::REAL,"motion/engine_force",PROPERTY_HINT_RANGE,"0.00,1024.0,0.01"),_SCS("set_engine_force"),_SCS("get_engine_force")); diff --git a/scene/3d/vehicle_body.h b/scene/3d/vehicle_body.h index 285cca142d..b6ad88f15e 100644 --- a/scene/3d/vehicle_body.h +++ b/scene/3d/vehicle_body.h @@ -178,6 +178,7 @@ public: void set_steering(float p_steering); float get_steering() const; + Vector3 get_linear_velocity(); VehicleBody(); }; |