feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -60,8 +60,13 @@ bool CharacterBody3D::move_and_slide() {
|
|||
|
||||
// We need to check the platform_rid object still exists before accessing.
|
||||
// A valid RID is no guarantee that the object has not been deleted.
|
||||
if (ObjectDB::get_instance(platform_object_id)) {
|
||||
//this approach makes sure there is less delay between the actual body velocity and the one we saved
|
||||
|
||||
// We can only perform the ObjectDB lifetime check on Object derived objects.
|
||||
// Note that physics also creates RIDs for non-Object derived objects, these cannot
|
||||
// be lifetime checked through ObjectDB, and therefore there is a still a vulnerability
|
||||
// to dangling RIDs (access after free) in this scenario.
|
||||
if (platform_object_id.is_null() || ObjectDB::get_instance(platform_object_id)) {
|
||||
// This approach makes sure there is less delay between the actual body velocity and the one we saved.
|
||||
bs = PhysicsServer3D::get_singleton()->body_get_direct_state(platform_rid);
|
||||
}
|
||||
|
||||
|
|
@ -464,14 +469,14 @@ void CharacterBody3D::apply_floor_snap() {
|
|||
_set_collision_direction(result, result_state, CollisionState(true, false, false));
|
||||
|
||||
if (result_state.floor) {
|
||||
if (floor_stop_on_slope) {
|
||||
// move and collide may stray the object a bit because of pre un-stucking,
|
||||
// so only ensure that motion happens on floor direction in this case.
|
||||
if (result.travel.length() > margin) {
|
||||
result.travel = up_direction * up_direction.dot(result.travel);
|
||||
} else {
|
||||
result.travel = Vector3();
|
||||
}
|
||||
// Ensure that we only move the body along the up axis, because
|
||||
// move_and_collide may stray the object a bit when getting it unstuck.
|
||||
// Canceling this motion should not affect move_and_slide, as previous
|
||||
// calls to move_and_collide already took care of freeing the body.
|
||||
if (result.travel.length() > margin) {
|
||||
result.travel = up_direction * up_direction.dot(result.travel);
|
||||
} else {
|
||||
result.travel = Vector3();
|
||||
}
|
||||
|
||||
parameters.from.origin += result.travel;
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ void CollisionObject3D::_notification(int p_what) {
|
|||
|
||||
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
|
||||
Ref<World3D> world_ref = get_world_3d();
|
||||
ERR_FAIL_COND(!world_ref.is_valid());
|
||||
ERR_FAIL_COND(world_ref.is_null());
|
||||
RID space = world_ref->get_space();
|
||||
if (area) {
|
||||
PhysicsServer3D::get_singleton()->area_set_space(rid, space);
|
||||
|
|
@ -731,7 +731,7 @@ bool CollisionObject3D::get_capture_input_on_drag() const {
|
|||
}
|
||||
|
||||
PackedStringArray CollisionObject3D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node3D::get_configuration_warnings();
|
||||
|
||||
if (shapes.is_empty()) {
|
||||
warnings.push_back(RTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."));
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ void CollisionPolygon3D::_build_polygon() {
|
|||
|
||||
convex->set_points(cp);
|
||||
convex->set_margin(margin);
|
||||
convex->set_debug_color(debug_color);
|
||||
convex->set_debug_fill(debug_fill);
|
||||
collision_object->shape_owner_add_shape(owner_id, convex);
|
||||
collision_object->shape_owner_set_disabled(owner_id, disabled);
|
||||
}
|
||||
|
|
@ -157,6 +159,68 @@ bool CollisionPolygon3D::is_disabled() const {
|
|||
return disabled;
|
||||
}
|
||||
|
||||
Color CollisionPolygon3D::_get_default_debug_color() const {
|
||||
const SceneTree *st = SceneTree::get_singleton();
|
||||
return st ? st->get_debug_collisions_color() : Color(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void CollisionPolygon3D::set_debug_color(const Color &p_color) {
|
||||
if (debug_color == p_color) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_color = p_color;
|
||||
|
||||
update_gizmos();
|
||||
}
|
||||
|
||||
Color CollisionPolygon3D::get_debug_color() const {
|
||||
return debug_color;
|
||||
}
|
||||
|
||||
void CollisionPolygon3D::set_debug_fill_enabled(bool p_enable) {
|
||||
if (debug_fill == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_fill = p_enable;
|
||||
|
||||
update_gizmos();
|
||||
}
|
||||
|
||||
bool CollisionPolygon3D::get_debug_fill_enabled() const {
|
||||
return debug_fill;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
bool CollisionPolygon3D::_property_can_revert(const StringName &p_name) const {
|
||||
if (p_name == "debug_color") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollisionPolygon3D::_property_get_revert(const StringName &p_name, Variant &r_property) const {
|
||||
if (p_name == "debug_color") {
|
||||
r_property = _get_default_debug_color();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollisionPolygon3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "debug_color") {
|
||||
if (debug_color == _get_default_debug_color()) {
|
||||
p_property.usage = PROPERTY_USAGE_DEFAULT & ~PROPERTY_USAGE_STORAGE;
|
||||
} else {
|
||||
p_property.usage = PROPERTY_USAGE_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
real_t CollisionPolygon3D::get_margin() const {
|
||||
return margin;
|
||||
}
|
||||
|
|
@ -169,7 +233,7 @@ void CollisionPolygon3D::set_margin(real_t p_margin) {
|
|||
}
|
||||
|
||||
PackedStringArray CollisionPolygon3D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node3D::get_configuration_warnings();
|
||||
|
||||
if (!Object::cast_to<CollisionObject3D>(get_parent())) {
|
||||
warnings.push_back(RTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape."));
|
||||
|
|
@ -201,6 +265,12 @@ void CollisionPolygon3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon3D::set_disabled);
|
||||
ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon3D::is_disabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionPolygon3D::set_debug_color);
|
||||
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionPolygon3D::get_debug_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionPolygon3D::set_debug_fill_enabled);
|
||||
ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionPolygon3D::get_debug_fill_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_margin", "margin"), &CollisionPolygon3D::set_margin);
|
||||
ClassDB::bind_method(D_METHOD("get_margin"), &CollisionPolygon3D::get_margin);
|
||||
|
||||
|
|
@ -210,8 +280,15 @@ void CollisionPolygon3D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001,suffix:m"), "set_margin", "get_margin");
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color");
|
||||
// Default value depends on a project setting, override for doc generation purposes.
|
||||
ADD_PROPERTY_DEFAULT("debug_color", Color(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill");
|
||||
}
|
||||
|
||||
CollisionPolygon3D::CollisionPolygon3D() {
|
||||
set_notify_local_transform(true);
|
||||
debug_color = _get_default_debug_color();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
#define COLLISION_POLYGON_3D_H
|
||||
|
||||
#include "scene/3d/node_3d.h"
|
||||
#include "scene/resources/3d/shape_3d.h"
|
||||
|
||||
class CollisionObject3D;
|
||||
class CollisionPolygon3D : public Node3D {
|
||||
|
|
@ -47,6 +46,11 @@ protected:
|
|||
uint32_t owner_id = 0;
|
||||
CollisionObject3D *collision_object = nullptr;
|
||||
|
||||
Color debug_color;
|
||||
bool debug_fill = true;
|
||||
|
||||
Color _get_default_debug_color() const;
|
||||
|
||||
bool disabled = false;
|
||||
|
||||
void _build_polygon();
|
||||
|
|
@ -59,6 +63,12 @@ protected:
|
|||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool _property_can_revert(const StringName &p_name) const;
|
||||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
public:
|
||||
void set_depth(real_t p_depth);
|
||||
real_t get_depth() const;
|
||||
|
|
@ -69,6 +79,12 @@ public:
|
|||
void set_disabled(bool p_disabled);
|
||||
bool is_disabled() const;
|
||||
|
||||
void set_debug_color(const Color &p_color);
|
||||
Color get_debug_color() const;
|
||||
|
||||
void set_debug_fill_enabled(bool p_enable);
|
||||
bool get_debug_fill_enabled() const;
|
||||
|
||||
virtual AABB get_item_rect() const;
|
||||
|
||||
real_t get_margin() const;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@
|
|||
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/physics/character_body_3d.h"
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
#include "scene/3d/physics/vehicle_body_3d.h"
|
||||
#include "scene/resources/3d/concave_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/convex_polygon_shape_3d.h"
|
||||
|
|
@ -87,6 +86,7 @@ void CollisionShape3D::_notification(int p_what) {
|
|||
if (shape.is_valid()) {
|
||||
collision_object->shape_owner_add_shape(owner_id, shape);
|
||||
}
|
||||
|
||||
_update_in_shape_owner();
|
||||
}
|
||||
} break;
|
||||
|
|
@ -120,14 +120,14 @@ void CollisionShape3D::resource_changed(Ref<Resource> res) {
|
|||
#endif
|
||||
|
||||
PackedStringArray CollisionShape3D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node3D::get_configuration_warnings();
|
||||
|
||||
CollisionObject3D *col_object = Object::cast_to<CollisionObject3D>(get_parent());
|
||||
if (col_object == nullptr) {
|
||||
warnings.push_back(RTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node.\nPlease only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape."));
|
||||
}
|
||||
|
||||
if (!shape.is_valid()) {
|
||||
if (shape.is_null()) {
|
||||
warnings.push_back(RTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."));
|
||||
}
|
||||
|
||||
|
|
@ -166,11 +166,24 @@ void CollisionShape3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("get_shape"), &CollisionShape3D::get_shape);
|
||||
ClassDB::bind_method(D_METHOD("set_disabled", "enable"), &CollisionShape3D::set_disabled);
|
||||
ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionShape3D::is_disabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings);
|
||||
ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionShape3D::set_debug_color);
|
||||
ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionShape3D::get_debug_color);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionShape3D::set_debug_fill_enabled);
|
||||
ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionShape3D::get_debug_fill_enabled);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color");
|
||||
// Default value depends on a project setting, override for doc generation purposes.
|
||||
ADD_PROPERTY_DEFAULT("debug_color", Color(0.0, 0.0, 0.0, 0.0));
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill");
|
||||
}
|
||||
|
||||
void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) {
|
||||
|
|
@ -178,11 +191,27 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) {
|
|||
return;
|
||||
}
|
||||
if (shape.is_valid()) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
shape->disconnect_changed(callable_mp(this, &CollisionShape3D::_shape_changed));
|
||||
#endif // DEBUG_ENABLED
|
||||
shape->disconnect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos));
|
||||
}
|
||||
shape = p_shape;
|
||||
if (shape.is_valid()) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (shape->are_debug_properties_edited()) {
|
||||
set_debug_color(shape->get_debug_color());
|
||||
set_debug_fill_enabled(shape->get_debug_fill());
|
||||
} else {
|
||||
shape->set_debug_color(debug_color);
|
||||
shape->set_debug_fill(debug_fill);
|
||||
}
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
shape->connect_changed(callable_mp((Node3D *)this, &Node3D::update_gizmos));
|
||||
#ifdef DEBUG_ENABLED
|
||||
shape->connect_changed(callable_mp(this, &CollisionShape3D::_shape_changed));
|
||||
#endif // DEBUG_ENABLED
|
||||
}
|
||||
update_gizmos();
|
||||
if (collision_object) {
|
||||
|
|
@ -215,9 +244,85 @@ bool CollisionShape3D::is_disabled() const {
|
|||
return disabled;
|
||||
}
|
||||
|
||||
Color CollisionShape3D::_get_default_debug_color() const {
|
||||
const SceneTree *st = SceneTree::get_singleton();
|
||||
return st ? st->get_debug_collisions_color() : Color(0.0, 0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void CollisionShape3D::set_debug_color(const Color &p_color) {
|
||||
if (debug_color == p_color) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_color = p_color;
|
||||
|
||||
if (shape.is_valid()) {
|
||||
shape->set_debug_color(p_color);
|
||||
}
|
||||
}
|
||||
|
||||
Color CollisionShape3D::get_debug_color() const {
|
||||
return debug_color;
|
||||
}
|
||||
|
||||
void CollisionShape3D::set_debug_fill_enabled(bool p_enable) {
|
||||
if (debug_fill == p_enable) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug_fill = p_enable;
|
||||
|
||||
if (shape.is_valid()) {
|
||||
shape->set_debug_fill(p_enable);
|
||||
}
|
||||
}
|
||||
|
||||
bool CollisionShape3D::get_debug_fill_enabled() const {
|
||||
return debug_fill;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
|
||||
bool CollisionShape3D::_property_can_revert(const StringName &p_name) const {
|
||||
if (p_name == "debug_color") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CollisionShape3D::_property_get_revert(const StringName &p_name, Variant &r_property) const {
|
||||
if (p_name == "debug_color") {
|
||||
r_property = _get_default_debug_color();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollisionShape3D::_validate_property(PropertyInfo &p_property) const {
|
||||
if (p_property.name == "debug_color") {
|
||||
if (debug_color == _get_default_debug_color()) {
|
||||
p_property.usage = PROPERTY_USAGE_DEFAULT & ~PROPERTY_USAGE_STORAGE;
|
||||
} else {
|
||||
p_property.usage = PROPERTY_USAGE_DEFAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CollisionShape3D::_shape_changed() {
|
||||
if (shape->get_debug_color() != debug_color) {
|
||||
set_debug_color(shape->get_debug_color());
|
||||
}
|
||||
if (shape->get_debug_fill() != debug_fill) {
|
||||
set_debug_fill_enabled(shape->get_debug_fill());
|
||||
}
|
||||
}
|
||||
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
CollisionShape3D::CollisionShape3D() {
|
||||
//indicator = RenderingServer::get_singleton()->mesh_create();
|
||||
set_notify_local_transform(true);
|
||||
debug_color = _get_default_debug_color();
|
||||
}
|
||||
|
||||
CollisionShape3D::~CollisionShape3D() {
|
||||
|
|
|
|||
|
|
@ -43,6 +43,15 @@ class CollisionShape3D : public Node3D {
|
|||
uint32_t owner_id = 0;
|
||||
CollisionObject3D *collision_object = nullptr;
|
||||
|
||||
Color debug_color;
|
||||
bool debug_fill = true;
|
||||
|
||||
Color _get_default_debug_color() const;
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
void _shape_changed();
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
void resource_changed(Ref<Resource> res);
|
||||
#endif
|
||||
|
|
@ -55,6 +64,12 @@ protected:
|
|||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
bool _property_can_revert(const StringName &p_name) const;
|
||||
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
|
||||
void _validate_property(PropertyInfo &p_property) const;
|
||||
#endif // DEBUG_ENABLED
|
||||
|
||||
public:
|
||||
void make_convex_from_siblings();
|
||||
|
||||
|
|
@ -64,6 +79,12 @@ public:
|
|||
void set_disabled(bool p_disabled);
|
||||
bool is_disabled() const;
|
||||
|
||||
void set_debug_color(const Color &p_color);
|
||||
Color get_debug_color() const;
|
||||
|
||||
void set_debug_fill_enabled(bool p_enable);
|
||||
bool get_debug_fill_enabled() const;
|
||||
|
||||
PackedStringArray get_configuration_warnings() const override;
|
||||
|
||||
CollisionShape3D();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "kinematic_collision_3d.h"
|
||||
|
||||
#include "scene/3d/physics/character_body_3d.h"
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
|
||||
Vector3 KinematicCollision3D::get_travel() const {
|
||||
|
|
|
|||
|
|
@ -34,9 +34,6 @@
|
|||
#include "core/object/ref_counted.h"
|
||||
#include "servers/physics_server_3d.h"
|
||||
|
||||
class CharacterBody3D;
|
||||
class PhysicsBody3D;
|
||||
|
||||
class KinematicCollision3D : public RefCounted {
|
||||
GDCLASS(KinematicCollision3D, RefCounted);
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
/**************************************************************************/
|
||||
|
||||
#include "physical_bone_3d.h"
|
||||
|
||||
#include "scene/3d/physical_bone_simulator_3d.h"
|
||||
#ifndef DISABLE_DEPRECATED
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
#endif //_DISABLE_DEPRECATED
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@
|
|||
#ifndef PHYSICAL_BONE_3D_H
|
||||
#define PHYSICAL_BONE_3D_H
|
||||
|
||||
#include "scene/3d/physical_bone_simulator_3d.h"
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
#include "scene/3d/skeleton_3d.h"
|
||||
|
||||
class PhysicalBoneSimulator3D;
|
||||
|
||||
|
|
|
|||
|
|
@ -167,8 +167,7 @@ bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion
|
|||
PhysicsServer3D::MotionResult *r = nullptr;
|
||||
PhysicsServer3D::MotionResult temp_result;
|
||||
if (r_collision.is_valid()) {
|
||||
// Needs const_cast because method bindings don't support non-const Ref.
|
||||
r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result);
|
||||
r = &r_collision->result;
|
||||
} else {
|
||||
r = &temp_result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
#ifndef PHYSICS_BODY_3D_H
|
||||
#define PHYSICS_BODY_3D_H
|
||||
|
||||
#include "core/templates/vset.h"
|
||||
#include "scene/3d/physics/collision_object_3d.h"
|
||||
#include "scene/3d/physics/kinematic_collision_3d.h"
|
||||
#include "scene/resources/physics_material.h"
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "ray_cast_3d.h"
|
||||
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/physics/collision_object_3d.h"
|
||||
|
||||
void RayCast3D::set_target_position(const Vector3 &p_point) {
|
||||
|
|
@ -464,12 +463,12 @@ void RayCast3D::_create_debug_shape() {
|
|||
}
|
||||
|
||||
if (debug_mesh.is_null()) {
|
||||
debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
|
||||
debug_mesh.instantiate();
|
||||
}
|
||||
}
|
||||
|
||||
void RayCast3D::_update_debug_shape_material(bool p_check_collision) {
|
||||
if (!debug_material.is_valid()) {
|
||||
if (debug_material.is_null()) {
|
||||
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
|
||||
debug_material = material;
|
||||
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ void RigidBody3D::_reload_physics_characteristics() {
|
|||
}
|
||||
|
||||
PackedStringArray RigidBody3D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = CollisionObject3D::get_configuration_warnings();
|
||||
PackedStringArray warnings = PhysicsBody3D::get_configuration_warnings();
|
||||
|
||||
Vector3 scale = get_transform().get_basis().get_scale();
|
||||
if (ABS(scale.x - 1.0) > 0.05 || ABS(scale.y - 1.0) > 0.05 || ABS(scale.z - 1.0) > 0.05) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@
|
|||
#ifndef RIGID_BODY_3D_H
|
||||
#define RIGID_BODY_3D_H
|
||||
|
||||
#include "scene/3d/physics/static_body_3d.h"
|
||||
#include "core/templates/vset.h"
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
|
||||
class RigidBody3D : public PhysicsBody3D {
|
||||
GDCLASS(RigidBody3D, PhysicsBody3D);
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
#include "shape_cast_3d.h"
|
||||
|
||||
#include "scene/3d/mesh_instance_3d.h"
|
||||
#include "scene/3d/physics/collision_object_3d.h"
|
||||
#include "scene/resources/3d/concave_polygon_shape_3d.h"
|
||||
|
||||
|
|
@ -157,7 +156,7 @@ void ShapeCast3D::_bind_methods() {
|
|||
ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &ShapeCast3D::set_collide_with_bodies);
|
||||
ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &ShapeCast3D::is_collide_with_bodies_enabled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("_get_collision_result"), &ShapeCast3D::_get_collision_result);
|
||||
ClassDB::bind_method(D_METHOD("get_collision_result"), &ShapeCast3D::get_collision_result);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &ShapeCast3D::set_debug_shape_custom_color);
|
||||
ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &ShapeCast3D::get_debug_shape_custom_color);
|
||||
|
|
@ -169,7 +168,7 @@ void ShapeCast3D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,100,0.01,suffix:m"), "set_margin", "get_margin");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_results"), "set_max_results", "get_max_results");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "_get_collision_result");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "collision_result", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "", "get_collision_result");
|
||||
|
||||
ADD_GROUP("Collide With", "collide_with");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
|
||||
|
|
@ -475,7 +474,7 @@ bool ShapeCast3D::is_collide_with_bodies_enabled() const {
|
|||
return collide_with_bodies;
|
||||
}
|
||||
|
||||
Array ShapeCast3D::_get_collision_result() const {
|
||||
Array ShapeCast3D::get_collision_result() const {
|
||||
Array ret;
|
||||
|
||||
for (int i = 0; i < result.size(); ++i) {
|
||||
|
|
@ -499,7 +498,7 @@ void ShapeCast3D::_update_debug_shape_vertices() {
|
|||
debug_shape_vertices.clear();
|
||||
debug_line_vertices.clear();
|
||||
|
||||
if (!shape.is_null()) {
|
||||
if (shape.is_valid()) {
|
||||
debug_shape_vertices.append_array(shape->get_debug_mesh_lines());
|
||||
for (int i = 0; i < debug_shape_vertices.size(); i++) {
|
||||
debug_shape_vertices.set(i, debug_shape_vertices[i] + Vector3(target_position * get_closest_collision_safe_fraction()));
|
||||
|
|
@ -546,12 +545,12 @@ void ShapeCast3D::_create_debug_shape() {
|
|||
}
|
||||
|
||||
if (debug_mesh.is_null()) {
|
||||
debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
|
||||
debug_mesh.instantiate();
|
||||
}
|
||||
}
|
||||
|
||||
void ShapeCast3D::_update_debug_shape_material(bool p_check_collision) {
|
||||
if (!debug_material.is_valid()) {
|
||||
if (debug_material.is_null()) {
|
||||
Ref<StandardMaterial3D> material = memnew(StandardMaterial3D);
|
||||
debug_material = material;
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ class ShapeCast3D : public Node3D {
|
|||
real_t collision_safe_fraction = 1.0;
|
||||
real_t collision_unsafe_fraction = 1.0;
|
||||
|
||||
Array _get_collision_result() const;
|
||||
|
||||
RID debug_instance;
|
||||
Ref<ArrayMesh> debug_mesh;
|
||||
|
||||
|
|
@ -123,6 +121,7 @@ public:
|
|||
|
||||
Ref<StandardMaterial3D> get_debug_material();
|
||||
|
||||
Array get_collision_result() const;
|
||||
int get_collision_count() const;
|
||||
Object *get_collider(int p_idx) const;
|
||||
RID get_collider_rid(int p_idx) const;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,24 @@
|
|||
|
||||
#include "static_body_3d.h"
|
||||
|
||||
#include "core/math/convex_hull.h"
|
||||
#include "scene/resources/3d/box_shape_3d.h"
|
||||
#include "scene/resources/3d/capsule_shape_3d.h"
|
||||
#include "scene/resources/3d/concave_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/convex_polygon_shape_3d.h"
|
||||
#include "scene/resources/3d/cylinder_shape_3d.h"
|
||||
#include "scene/resources/3d/height_map_shape_3d.h"
|
||||
#include "scene/resources/3d/navigation_mesh_source_geometry_data_3d.h"
|
||||
#include "scene/resources/3d/primitive_meshes.h"
|
||||
#include "scene/resources/3d/shape_3d.h"
|
||||
#include "scene/resources/3d/sphere_shape_3d.h"
|
||||
#include "scene/resources/3d/world_boundary_shape_3d.h"
|
||||
#include "scene/resources/navigation_mesh.h"
|
||||
#include "servers/navigation_server_3d.h"
|
||||
|
||||
Callable StaticBody3D::_navmesh_source_geometry_parsing_callback;
|
||||
RID StaticBody3D::_navmesh_source_geometry_parser;
|
||||
|
||||
void StaticBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) {
|
||||
if (physics_material_override.is_valid()) {
|
||||
physics_material_override->disconnect_changed(callable_mp(this, &StaticBody3D::_reload_physics_characteristics));
|
||||
|
|
@ -77,6 +95,138 @@ void StaticBody3D::_reload_physics_characteristics() {
|
|||
}
|
||||
}
|
||||
|
||||
void StaticBody3D::navmesh_parse_init() {
|
||||
ERR_FAIL_NULL(NavigationServer3D::get_singleton());
|
||||
if (!_navmesh_source_geometry_parser.is_valid()) {
|
||||
_navmesh_source_geometry_parsing_callback = callable_mp_static(&StaticBody3D::navmesh_parse_source_geometry);
|
||||
_navmesh_source_geometry_parser = NavigationServer3D::get_singleton()->source_geometry_parser_create();
|
||||
NavigationServer3D::get_singleton()->source_geometry_parser_set_callback(_navmesh_source_geometry_parser, _navmesh_source_geometry_parsing_callback);
|
||||
}
|
||||
}
|
||||
|
||||
void StaticBody3D::navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node) {
|
||||
StaticBody3D *static_body = Object::cast_to<StaticBody3D>(p_node);
|
||||
|
||||
if (static_body == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
NavigationMesh::ParsedGeometryType parsed_geometry_type = p_navigation_mesh->get_parsed_geometry_type();
|
||||
uint32_t parsed_collision_mask = p_navigation_mesh->get_collision_mask();
|
||||
|
||||
if ((parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_STATIC_COLLIDERS || parsed_geometry_type == NavigationMesh::PARSED_GEOMETRY_BOTH) && (static_body->get_collision_layer() & parsed_collision_mask)) {
|
||||
List<uint32_t> shape_owners;
|
||||
static_body->get_shape_owners(&shape_owners);
|
||||
for (uint32_t shape_owner : shape_owners) {
|
||||
if (static_body->is_shape_owner_disabled(shape_owner)) {
|
||||
continue;
|
||||
}
|
||||
const int shape_count = static_body->shape_owner_get_shape_count(shape_owner);
|
||||
for (int shape_index = 0; shape_index < shape_count; shape_index++) {
|
||||
Ref<Shape3D> s = static_body->shape_owner_get_shape(shape_owner, shape_index);
|
||||
if (s.is_null()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Transform3D transform = static_body->get_global_transform() * static_body->shape_owner_get_transform(shape_owner);
|
||||
|
||||
BoxShape3D *box = Object::cast_to<BoxShape3D>(*s);
|
||||
if (box) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
BoxMesh::create_mesh_array(arr, box->get_size());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
CapsuleShape3D *capsule = Object::cast_to<CapsuleShape3D>(*s);
|
||||
if (capsule) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CapsuleMesh::create_mesh_array(arr, capsule->get_radius(), capsule->get_height());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
CylinderShape3D *cylinder = Object::cast_to<CylinderShape3D>(*s);
|
||||
if (cylinder) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
CylinderMesh::create_mesh_array(arr, cylinder->get_radius(), cylinder->get_radius(), cylinder->get_height());
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
SphereShape3D *sphere = Object::cast_to<SphereShape3D>(*s);
|
||||
if (sphere) {
|
||||
Array arr;
|
||||
arr.resize(RS::ARRAY_MAX);
|
||||
SphereMesh::create_mesh_array(arr, sphere->get_radius(), sphere->get_radius() * 2.0);
|
||||
p_source_geometry_data->add_mesh_array(arr, transform);
|
||||
}
|
||||
|
||||
ConcavePolygonShape3D *concave_polygon = Object::cast_to<ConcavePolygonShape3D>(*s);
|
||||
if (concave_polygon) {
|
||||
p_source_geometry_data->add_faces(concave_polygon->get_faces(), transform);
|
||||
}
|
||||
|
||||
ConvexPolygonShape3D *convex_polygon = Object::cast_to<ConvexPolygonShape3D>(*s);
|
||||
if (convex_polygon) {
|
||||
Vector<Vector3> varr = Variant(convex_polygon->get_points());
|
||||
Geometry3D::MeshData md;
|
||||
|
||||
Error err = ConvexHullComputer::convex_hull(varr, md);
|
||||
|
||||
if (err == OK) {
|
||||
PackedVector3Array faces;
|
||||
|
||||
for (const Geometry3D::MeshData::Face &face : md.faces) {
|
||||
for (uint32_t k = 2; k < face.indices.size(); ++k) {
|
||||
faces.push_back(md.vertices[face.indices[0]]);
|
||||
faces.push_back(md.vertices[face.indices[k - 1]]);
|
||||
faces.push_back(md.vertices[face.indices[k]]);
|
||||
}
|
||||
}
|
||||
|
||||
p_source_geometry_data->add_faces(faces, transform);
|
||||
}
|
||||
}
|
||||
|
||||
HeightMapShape3D *heightmap_shape = Object::cast_to<HeightMapShape3D>(*s);
|
||||
if (heightmap_shape) {
|
||||
int heightmap_depth = heightmap_shape->get_map_depth();
|
||||
int heightmap_width = heightmap_shape->get_map_width();
|
||||
|
||||
if (heightmap_depth >= 2 && heightmap_width >= 2) {
|
||||
const Vector<real_t> &map_data = heightmap_shape->get_map_data();
|
||||
|
||||
Vector2 heightmap_gridsize(heightmap_width - 1, heightmap_depth - 1);
|
||||
Vector3 start = Vector3(heightmap_gridsize.x, 0, heightmap_gridsize.y) * -0.5;
|
||||
|
||||
Vector<Vector3> vertex_array;
|
||||
vertex_array.resize((heightmap_depth - 1) * (heightmap_width - 1) * 6);
|
||||
Vector3 *vertex_array_ptrw = vertex_array.ptrw();
|
||||
const real_t *map_data_ptr = map_data.ptr();
|
||||
int vertex_index = 0;
|
||||
|
||||
for (int d = 0; d < heightmap_depth - 1; d++) {
|
||||
for (int w = 0; w < heightmap_width - 1; w++) {
|
||||
vertex_array_ptrw[vertex_index] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + w], d);
|
||||
vertex_array_ptrw[vertex_index + 1] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 2] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 3] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + w + 1], d);
|
||||
vertex_array_ptrw[vertex_index + 4] = start + Vector3(w + 1, map_data_ptr[(heightmap_width * d) + heightmap_width + w + 1], d + 1);
|
||||
vertex_array_ptrw[vertex_index + 5] = start + Vector3(w, map_data_ptr[(heightmap_width * d) + heightmap_width + w], d + 1);
|
||||
vertex_index += 6;
|
||||
}
|
||||
}
|
||||
if (vertex_array.size() > 0) {
|
||||
p_source_geometry_data->add_faces(vertex_array, transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StaticBody3D::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity);
|
||||
ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@
|
|||
|
||||
#include "scene/3d/physics/physics_body_3d.h"
|
||||
|
||||
class NavigationMesh;
|
||||
class NavigationMeshSourceGeometryData3D;
|
||||
|
||||
class StaticBody3D : public PhysicsBody3D {
|
||||
GDCLASS(StaticBody3D, PhysicsBody3D);
|
||||
|
||||
|
|
@ -59,6 +62,13 @@ public:
|
|||
|
||||
private:
|
||||
void _reload_physics_characteristics();
|
||||
|
||||
static Callable _navmesh_source_geometry_parsing_callback;
|
||||
static RID _navmesh_source_geometry_parser;
|
||||
|
||||
public:
|
||||
static void navmesh_parse_init();
|
||||
static void navmesh_parse_source_geometry(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_node);
|
||||
};
|
||||
|
||||
#endif // STATIC_BODY_3D_H
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ void VehicleWheel3D::_notification(int p_what) {
|
|||
}
|
||||
|
||||
PackedStringArray VehicleWheel3D::get_configuration_warnings() const {
|
||||
PackedStringArray warnings = Node::get_configuration_warnings();
|
||||
PackedStringArray warnings = Node3D::get_configuration_warnings();
|
||||
|
||||
if (!Object::cast_to<VehicleBody3D>(get_parent())) {
|
||||
warnings.push_back(RTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."));
|
||||
|
|
@ -219,6 +219,14 @@ bool VehicleWheel3D::is_in_contact() const {
|
|||
return m_raycastInfo.m_isInContact;
|
||||
}
|
||||
|
||||
Vector3 VehicleWheel3D::get_contact_point() const {
|
||||
return m_raycastInfo.m_contactPointWS;
|
||||
}
|
||||
|
||||
Vector3 VehicleWheel3D::get_contact_normal() const {
|
||||
return m_raycastInfo.m_contactNormalWS;
|
||||
}
|
||||
|
||||
Node3D *VehicleWheel3D::get_contact_body() const {
|
||||
return m_raycastInfo.m_groundObject;
|
||||
}
|
||||
|
|
@ -256,6 +264,8 @@ void VehicleWheel3D::_bind_methods() {
|
|||
|
||||
ClassDB::bind_method(D_METHOD("is_in_contact"), &VehicleWheel3D::is_in_contact);
|
||||
ClassDB::bind_method(D_METHOD("get_contact_body"), &VehicleWheel3D::get_contact_body);
|
||||
ClassDB::bind_method(D_METHOD("get_contact_point"), &VehicleWheel3D::get_contact_point);
|
||||
ClassDB::bind_method(D_METHOD("get_contact_normal"), &VehicleWheel3D::get_contact_normal);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_roll_influence", "roll_influence"), &VehicleWheel3D::set_roll_influence);
|
||||
ClassDB::bind_method(D_METHOD("get_roll_influence"), &VehicleWheel3D::get_roll_influence);
|
||||
|
|
@ -287,11 +297,11 @@ void VehicleWheel3D::_bind_methods() {
|
|||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wheel_friction_slip"), "set_friction_slip", "get_friction_slip");
|
||||
ADD_GROUP("Suspension", "suspension_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_travel", PROPERTY_HINT_NONE, "suffix:m"), "set_suspension_travel", "get_suspension_travel");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness"), "set_suspension_stiffness", "get_suspension_stiffness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_stiffness", PROPERTY_HINT_NONE, U"suffix:N/mm"), "set_suspension_stiffness", "get_suspension_stiffness");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "suspension_max_force", PROPERTY_HINT_NONE, U"suffix:kg\u22C5m/s\u00B2 (N)"), "set_suspension_max_force", "get_suspension_max_force");
|
||||
ADD_GROUP("Damping", "damping_");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression"), "set_damping_compression", "get_damping_compression");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_compression", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_compression", "get_damping_compression");
|
||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation", PROPERTY_HINT_NONE, U"suffix:N\u22C5s/mm"), "set_damping_relaxation", "get_damping_relaxation");
|
||||
}
|
||||
|
||||
void VehicleWheel3D::set_engine_force(real_t p_engine_force) {
|
||||
|
|
@ -532,7 +542,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const
|
|||
if (body2) {
|
||||
rel_pos2 = pos2 - body2->get_global_transform().origin;
|
||||
}
|
||||
//this jacobian entry could be re-used for all iterations
|
||||
// This Jacobian entry could be reused for all iterations.
|
||||
|
||||
Vector3 vel1 = s->get_linear_velocity() + (s->get_angular_velocity()).cross(rel_pos1); // * mPos);
|
||||
Vector3 vel2;
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ public:
|
|||
|
||||
bool is_in_contact() const;
|
||||
|
||||
Vector3 get_contact_point() const;
|
||||
|
||||
Vector3 get_contact_normal() const;
|
||||
|
||||
Node3D *get_contact_body() const;
|
||||
|
||||
void set_roll_influence(real_t p_value);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue