initial commit, 4.5 stable
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
Some checks failed
🔗 GHA / 📊 Static checks (push) Has been cancelled
🔗 GHA / 🤖 Android (push) Has been cancelled
🔗 GHA / 🍏 iOS (push) Has been cancelled
🔗 GHA / 🐧 Linux (push) Has been cancelled
🔗 GHA / 🍎 macOS (push) Has been cancelled
🔗 GHA / 🏁 Windows (push) Has been cancelled
🔗 GHA / 🌐 Web (push) Has been cancelled
This commit is contained in:
6
modules/godot_physics_2d/SCsub
Normal file
6
modules/godot_physics_2d/SCsub
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
from misc.utility.scons_hints import *
|
||||
|
||||
Import("env")
|
||||
|
||||
env.add_source_files(env.modules_sources, "*.cpp")
|
6
modules/godot_physics_2d/config.py
Normal file
6
modules/godot_physics_2d/config.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def can_build(env, platform):
|
||||
return not env["disable_physics_2d"]
|
||||
|
||||
|
||||
def configure(env):
|
||||
pass
|
314
modules/godot_physics_2d/godot_area_2d.cpp
Normal file
314
modules/godot_physics_2d/godot_area_2d.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
/**************************************************************************/
|
||||
/* godot_area_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_area_2d.h"
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
GodotArea2D::BodyKey::BodyKey(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
|
||||
rid = p_body->get_self();
|
||||
instance_id = p_body->get_instance_id();
|
||||
body_shape = p_body_shape;
|
||||
area_shape = p_area_shape;
|
||||
}
|
||||
|
||||
GodotArea2D::BodyKey::BodyKey(GodotArea2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
|
||||
rid = p_body->get_self();
|
||||
instance_id = p_body->get_instance_id();
|
||||
body_shape = p_body_shape;
|
||||
area_shape = p_area_shape;
|
||||
}
|
||||
|
||||
void GodotArea2D::_shapes_changed() {
|
||||
if (!moved_list.in_list() && get_space()) {
|
||||
get_space()->area_add_to_moved_list(&moved_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::set_transform(const Transform2D &p_transform) {
|
||||
if (!moved_list.in_list() && get_space()) {
|
||||
get_space()->area_add_to_moved_list(&moved_list);
|
||||
}
|
||||
|
||||
_set_transform(p_transform);
|
||||
_set_inv_transform(p_transform.affine_inverse());
|
||||
}
|
||||
|
||||
void GodotArea2D::set_space(GodotSpace2D *p_space) {
|
||||
if (get_space()) {
|
||||
if (monitor_query_list.in_list()) {
|
||||
get_space()->area_remove_from_monitor_query_list(&monitor_query_list);
|
||||
}
|
||||
if (moved_list.in_list()) {
|
||||
get_space()->area_remove_from_moved_list(&moved_list);
|
||||
}
|
||||
}
|
||||
|
||||
monitored_bodies.clear();
|
||||
monitored_areas.clear();
|
||||
|
||||
_set_space(p_space);
|
||||
}
|
||||
|
||||
void GodotArea2D::set_monitor_callback(const Callable &p_callback) {
|
||||
_unregister_shapes();
|
||||
|
||||
monitor_callback = p_callback;
|
||||
|
||||
monitored_bodies.clear();
|
||||
monitored_areas.clear();
|
||||
|
||||
_shape_changed();
|
||||
|
||||
if (!moved_list.in_list() && get_space()) {
|
||||
get_space()->area_add_to_moved_list(&moved_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::set_area_monitor_callback(const Callable &p_callback) {
|
||||
_unregister_shapes();
|
||||
|
||||
area_monitor_callback = p_callback;
|
||||
|
||||
monitored_bodies.clear();
|
||||
monitored_areas.clear();
|
||||
|
||||
_shape_changed();
|
||||
|
||||
if (!moved_list.in_list() && get_space()) {
|
||||
get_space()->area_add_to_moved_list(&moved_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::_set_space_override_mode(PhysicsServer2D::AreaSpaceOverrideMode &r_mode, PhysicsServer2D::AreaSpaceOverrideMode p_new_mode) {
|
||||
bool do_override = p_new_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
|
||||
if (do_override == (r_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED)) {
|
||||
return;
|
||||
}
|
||||
_unregister_shapes();
|
||||
r_mode = p_new_mode;
|
||||
_shape_changed();
|
||||
}
|
||||
|
||||
void GodotArea2D::set_param(PhysicsServer2D::AreaParameter p_param, const Variant &p_value) {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
|
||||
_set_space_override_mode(gravity_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY:
|
||||
gravity = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR:
|
||||
gravity_vector = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT:
|
||||
gravity_is_point = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
|
||||
gravity_point_unit_distance = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
|
||||
_set_space_override_mode(linear_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP:
|
||||
linear_damp = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
|
||||
_set_space_override_mode(angular_damping_override_mode, (PhysicsServer2D::AreaSpaceOverrideMode)(int)p_value);
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP:
|
||||
angular_damp = p_value;
|
||||
break;
|
||||
case PhysicsServer2D::AREA_PARAM_PRIORITY:
|
||||
priority = p_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Variant GodotArea2D::get_param(PhysicsServer2D::AreaParameter p_param) const {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE:
|
||||
return gravity_override_mode;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY:
|
||||
return gravity;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_VECTOR:
|
||||
return gravity_vector;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_IS_POINT:
|
||||
return gravity_is_point;
|
||||
case PhysicsServer2D::AREA_PARAM_GRAVITY_POINT_UNIT_DISTANCE:
|
||||
return gravity_point_unit_distance;
|
||||
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE:
|
||||
return linear_damping_override_mode;
|
||||
case PhysicsServer2D::AREA_PARAM_LINEAR_DAMP:
|
||||
return linear_damp;
|
||||
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE:
|
||||
return angular_damping_override_mode;
|
||||
case PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP:
|
||||
return angular_damp;
|
||||
case PhysicsServer2D::AREA_PARAM_PRIORITY:
|
||||
return priority;
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void GodotArea2D::_queue_monitor_update() {
|
||||
ERR_FAIL_NULL(get_space());
|
||||
|
||||
if (!monitor_query_list.in_list()) {
|
||||
get_space()->area_add_to_monitor_query_list(&monitor_query_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::set_monitorable(bool p_monitorable) {
|
||||
if (monitorable == p_monitorable) {
|
||||
return;
|
||||
}
|
||||
|
||||
monitorable = p_monitorable;
|
||||
_set_static(!monitorable);
|
||||
_shapes_changed();
|
||||
}
|
||||
|
||||
void GodotArea2D::call_queries() {
|
||||
if (!monitor_callback.is_null() && !monitored_bodies.is_empty()) {
|
||||
if (monitor_callback.is_valid()) {
|
||||
Variant res[5];
|
||||
Variant *resptr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
resptr[i] = &res[i];
|
||||
}
|
||||
|
||||
for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_bodies.begin(); E;) {
|
||||
if (E->value.state == 0) { // Nothing happened
|
||||
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
|
||||
++next;
|
||||
monitored_bodies.remove(E);
|
||||
E = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;
|
||||
res[1] = E->key.rid;
|
||||
res[2] = E->key.instance_id;
|
||||
res[3] = E->key.body_shape;
|
||||
res[4] = E->key.area_shape;
|
||||
|
||||
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
|
||||
++next;
|
||||
monitored_bodies.remove(E);
|
||||
E = next;
|
||||
|
||||
Callable::CallError ce;
|
||||
Variant ret;
|
||||
monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
|
||||
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(monitor_callback, (const Variant **)resptr, 5, ce));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
monitored_bodies.clear();
|
||||
monitor_callback = Callable();
|
||||
}
|
||||
}
|
||||
|
||||
if (!area_monitor_callback.is_null() && !monitored_areas.is_empty()) {
|
||||
if (area_monitor_callback.is_valid()) {
|
||||
Variant res[5];
|
||||
Variant *resptr[5];
|
||||
for (int i = 0; i < 5; i++) {
|
||||
resptr[i] = &res[i];
|
||||
}
|
||||
|
||||
for (HashMap<BodyKey, BodyState, BodyKey>::Iterator E = monitored_areas.begin(); E;) {
|
||||
if (E->value.state == 0) { // Nothing happened
|
||||
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
|
||||
++next;
|
||||
monitored_areas.remove(E);
|
||||
E = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
res[0] = E->value.state > 0 ? PhysicsServer2D::AREA_BODY_ADDED : PhysicsServer2D::AREA_BODY_REMOVED;
|
||||
res[1] = E->key.rid;
|
||||
res[2] = E->key.instance_id;
|
||||
res[3] = E->key.body_shape;
|
||||
res[4] = E->key.area_shape;
|
||||
|
||||
HashMap<BodyKey, BodyState, BodyKey>::Iterator next = E;
|
||||
++next;
|
||||
monitored_areas.remove(E);
|
||||
E = next;
|
||||
|
||||
Callable::CallError ce;
|
||||
Variant ret;
|
||||
area_monitor_callback.callp((const Variant **)resptr, 5, ret, ce);
|
||||
|
||||
if (ce.error != Callable::CallError::CALL_OK) {
|
||||
ERR_PRINT_ONCE("Error calling event callback method " + Variant::get_callable_error_text(area_monitor_callback, (const Variant **)resptr, 5, ce));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
monitored_areas.clear();
|
||||
area_monitor_callback = Callable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const {
|
||||
if (is_gravity_point()) {
|
||||
const real_t gr_unit_dist = get_gravity_point_unit_distance();
|
||||
Vector2 v = get_transform().xform(get_gravity_vector()) - p_position;
|
||||
if (gr_unit_dist > 0) {
|
||||
const real_t v_length_sq = v.length_squared();
|
||||
if (v_length_sq > 0) {
|
||||
const real_t gravity_strength = get_gravity() * gr_unit_dist * gr_unit_dist / v_length_sq;
|
||||
r_gravity = v.normalized() * gravity_strength;
|
||||
} else {
|
||||
r_gravity = Vector2();
|
||||
}
|
||||
} else {
|
||||
r_gravity = v.normalized() * get_gravity();
|
||||
}
|
||||
} else {
|
||||
r_gravity = get_gravity_vector() * get_gravity();
|
||||
}
|
||||
}
|
||||
|
||||
GodotArea2D::GodotArea2D() :
|
||||
GodotCollisionObject2D(TYPE_AREA),
|
||||
monitor_query_list(this),
|
||||
moved_list(this) {
|
||||
_set_static(true); //areas are not active by default
|
||||
}
|
||||
|
||||
GodotArea2D::~GodotArea2D() {
|
||||
}
|
188
modules/godot_physics_2d/godot_area_2d.h
Normal file
188
modules/godot_physics_2d/godot_area_2d.h
Normal file
@@ -0,0 +1,188 @@
|
||||
/**************************************************************************/
|
||||
/* godot_area_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_collision_object_2d.h"
|
||||
|
||||
#include "core/templates/self_list.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class GodotSpace2D;
|
||||
class GodotBody2D;
|
||||
class GodotConstraint2D;
|
||||
|
||||
class GodotArea2D : public GodotCollisionObject2D {
|
||||
PhysicsServer2D::AreaSpaceOverrideMode gravity_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
|
||||
PhysicsServer2D::AreaSpaceOverrideMode linear_damping_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
|
||||
PhysicsServer2D::AreaSpaceOverrideMode angular_damping_override_mode = PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED;
|
||||
|
||||
real_t gravity = 9.80665;
|
||||
Vector2 gravity_vector = Vector2(0, -1);
|
||||
bool gravity_is_point = false;
|
||||
real_t gravity_point_unit_distance = 0.0;
|
||||
real_t linear_damp = 0.1;
|
||||
real_t angular_damp = 1.0;
|
||||
int priority = 0;
|
||||
bool monitorable = false;
|
||||
|
||||
Callable monitor_callback;
|
||||
|
||||
Callable area_monitor_callback;
|
||||
|
||||
SelfList<GodotArea2D> monitor_query_list;
|
||||
SelfList<GodotArea2D> moved_list;
|
||||
|
||||
struct BodyKey {
|
||||
RID rid;
|
||||
ObjectID instance_id;
|
||||
uint32_t body_shape = 0;
|
||||
uint32_t area_shape = 0;
|
||||
|
||||
static uint32_t hash(const BodyKey &p_key) {
|
||||
uint32_t h = hash_one_uint64(p_key.rid.get_id());
|
||||
h = hash_murmur3_one_64(p_key.instance_id, h);
|
||||
h = hash_murmur3_one_32(p_key.area_shape, h);
|
||||
return hash_fmix32(hash_murmur3_one_32(p_key.body_shape, h));
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const BodyKey &p_key) const {
|
||||
return rid == p_key.rid && instance_id == p_key.instance_id && body_shape == p_key.body_shape && area_shape == p_key.area_shape;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ BodyKey() {}
|
||||
BodyKey(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
|
||||
BodyKey(GodotArea2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
|
||||
};
|
||||
|
||||
struct BodyState {
|
||||
int state = 0;
|
||||
_FORCE_INLINE_ void inc() { state++; }
|
||||
_FORCE_INLINE_ void dec() { state--; }
|
||||
};
|
||||
|
||||
HashMap<BodyKey, BodyState, BodyKey> monitored_bodies;
|
||||
HashMap<BodyKey, BodyState, BodyKey> monitored_areas;
|
||||
|
||||
HashSet<GodotConstraint2D *> constraints;
|
||||
|
||||
virtual void _shapes_changed() override;
|
||||
void _queue_monitor_update();
|
||||
|
||||
void _set_space_override_mode(PhysicsServer2D::AreaSpaceOverrideMode &r_mode, PhysicsServer2D::AreaSpaceOverrideMode p_new_mode);
|
||||
|
||||
public:
|
||||
void set_monitor_callback(const Callable &p_callback);
|
||||
_FORCE_INLINE_ bool has_monitor_callback() const { return monitor_callback.is_valid(); }
|
||||
|
||||
void set_area_monitor_callback(const Callable &p_callback);
|
||||
_FORCE_INLINE_ bool has_area_monitor_callback() const { return area_monitor_callback.is_valid(); }
|
||||
|
||||
_FORCE_INLINE_ void add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
|
||||
_FORCE_INLINE_ void remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape);
|
||||
|
||||
_FORCE_INLINE_ void add_area_to_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
|
||||
_FORCE_INLINE_ void remove_area_from_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape);
|
||||
|
||||
void set_param(PhysicsServer2D::AreaParameter p_param, const Variant &p_value);
|
||||
Variant get_param(PhysicsServer2D::AreaParameter p_param) const;
|
||||
|
||||
_FORCE_INLINE_ void set_gravity(real_t p_gravity) { gravity = p_gravity; }
|
||||
_FORCE_INLINE_ real_t get_gravity() const { return gravity; }
|
||||
|
||||
_FORCE_INLINE_ void set_gravity_vector(const Vector2 &p_gravity) { gravity_vector = p_gravity; }
|
||||
_FORCE_INLINE_ Vector2 get_gravity_vector() const { return gravity_vector; }
|
||||
|
||||
_FORCE_INLINE_ void set_gravity_as_point(bool p_enable) { gravity_is_point = p_enable; }
|
||||
_FORCE_INLINE_ bool is_gravity_point() const { return gravity_is_point; }
|
||||
|
||||
_FORCE_INLINE_ void set_gravity_point_unit_distance(real_t scale) { gravity_point_unit_distance = scale; }
|
||||
_FORCE_INLINE_ real_t get_gravity_point_unit_distance() const { return gravity_point_unit_distance; }
|
||||
|
||||
_FORCE_INLINE_ void set_linear_damp(real_t p_linear_damp) { linear_damp = p_linear_damp; }
|
||||
_FORCE_INLINE_ real_t get_linear_damp() const { return linear_damp; }
|
||||
|
||||
_FORCE_INLINE_ void set_angular_damp(real_t p_angular_damp) { angular_damp = p_angular_damp; }
|
||||
_FORCE_INLINE_ real_t get_angular_damp() const { return angular_damp; }
|
||||
|
||||
_FORCE_INLINE_ void set_priority(int p_priority) { priority = p_priority; }
|
||||
_FORCE_INLINE_ int get_priority() const { return priority; }
|
||||
|
||||
_FORCE_INLINE_ void add_constraint(GodotConstraint2D *p_constraint) { constraints.insert(p_constraint); }
|
||||
_FORCE_INLINE_ void remove_constraint(GodotConstraint2D *p_constraint) { constraints.erase(p_constraint); }
|
||||
_FORCE_INLINE_ const HashSet<GodotConstraint2D *> &get_constraints() const { return constraints; }
|
||||
_FORCE_INLINE_ void clear_constraints() { constraints.clear(); }
|
||||
|
||||
void set_monitorable(bool p_monitorable);
|
||||
_FORCE_INLINE_ bool is_monitorable() const { return monitorable; }
|
||||
|
||||
void set_transform(const Transform2D &p_transform);
|
||||
|
||||
void set_space(GodotSpace2D *p_space) override;
|
||||
|
||||
void call_queries();
|
||||
|
||||
void compute_gravity(const Vector2 &p_position, Vector2 &r_gravity) const;
|
||||
|
||||
GodotArea2D();
|
||||
~GodotArea2D();
|
||||
};
|
||||
|
||||
void GodotArea2D::add_body_to_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
|
||||
BodyKey bk(p_body, p_body_shape, p_area_shape);
|
||||
monitored_bodies[bk].inc();
|
||||
if (!monitor_query_list.in_list()) {
|
||||
_queue_monitor_update();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::remove_body_from_query(GodotBody2D *p_body, uint32_t p_body_shape, uint32_t p_area_shape) {
|
||||
BodyKey bk(p_body, p_body_shape, p_area_shape);
|
||||
monitored_bodies[bk].dec();
|
||||
if (get_space() && !monitor_query_list.in_list()) {
|
||||
_queue_monitor_update();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::add_area_to_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) {
|
||||
BodyKey bk(p_area, p_area_shape, p_self_shape);
|
||||
monitored_areas[bk].inc();
|
||||
if (!monitor_query_list.in_list()) {
|
||||
_queue_monitor_update();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotArea2D::remove_area_from_query(GodotArea2D *p_area, uint32_t p_area_shape, uint32_t p_self_shape) {
|
||||
BodyKey bk(p_area, p_area_shape, p_self_shape);
|
||||
monitored_areas[bk].dec();
|
||||
if (get_space() && !monitor_query_list.in_list()) {
|
||||
_queue_monitor_update();
|
||||
}
|
||||
}
|
203
modules/godot_physics_2d/godot_area_pair_2d.cpp
Normal file
203
modules/godot_physics_2d/godot_area_pair_2d.cpp
Normal file
@@ -0,0 +1,203 @@
|
||||
/**************************************************************************/
|
||||
/* godot_area_pair_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_area_pair_2d.h"
|
||||
#include "godot_collision_solver_2d.h"
|
||||
|
||||
bool GodotAreaPair2D::setup(real_t p_step) {
|
||||
bool result = false;
|
||||
if (area->collides_with(body) && GodotCollisionSolver2D::solve(body->get_shape(body_shape), body->get_transform() * body->get_shape_transform(body_shape), Vector2(), area->get_shape(area_shape), area->get_transform() * area->get_shape_transform(area_shape), Vector2(), nullptr, this)) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
process_collision = false;
|
||||
has_space_override = false;
|
||||
if (result != colliding) {
|
||||
if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
has_space_override = true;
|
||||
} else if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
has_space_override = true;
|
||||
} else if ((int)area->get_param(PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE) != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
has_space_override = true;
|
||||
}
|
||||
process_collision = has_space_override;
|
||||
|
||||
if (area->has_monitor_callback()) {
|
||||
process_collision = true;
|
||||
}
|
||||
|
||||
colliding = result;
|
||||
}
|
||||
|
||||
return process_collision;
|
||||
}
|
||||
|
||||
bool GodotAreaPair2D::pre_solve(real_t p_step) {
|
||||
if (!process_collision) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (colliding) {
|
||||
if (has_space_override) {
|
||||
body_has_attached_area = true;
|
||||
body->add_area(area);
|
||||
}
|
||||
|
||||
if (area->has_monitor_callback()) {
|
||||
area->add_body_to_query(body, body_shape, area_shape);
|
||||
}
|
||||
} else {
|
||||
if (has_space_override) {
|
||||
body_has_attached_area = false;
|
||||
body->remove_area(area);
|
||||
}
|
||||
|
||||
if (area->has_monitor_callback()) {
|
||||
area->remove_body_from_query(body, body_shape, area_shape);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Never do any post solving.
|
||||
}
|
||||
|
||||
void GodotAreaPair2D::solve(real_t p_step) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
GodotAreaPair2D::GodotAreaPair2D(GodotBody2D *p_body, int p_body_shape, GodotArea2D *p_area, int p_area_shape) {
|
||||
body = p_body;
|
||||
area = p_area;
|
||||
body_shape = p_body_shape;
|
||||
area_shape = p_area_shape;
|
||||
body->add_constraint(this, 0);
|
||||
area->add_constraint(this);
|
||||
if (p_body->get_mode() == PhysicsServer2D::BODY_MODE_KINEMATIC) { //need to be active to process pair
|
||||
p_body->set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
GodotAreaPair2D::~GodotAreaPair2D() {
|
||||
if (colliding) {
|
||||
if (body_has_attached_area) {
|
||||
body_has_attached_area = false;
|
||||
body->remove_area(area);
|
||||
}
|
||||
if (area->has_monitor_callback()) {
|
||||
area->remove_body_from_query(body, body_shape, area_shape);
|
||||
}
|
||||
}
|
||||
body->remove_constraint(this, 0);
|
||||
area->remove_constraint(this);
|
||||
}
|
||||
|
||||
//////////////////////////////////
|
||||
|
||||
bool GodotArea2Pair2D::setup(real_t p_step) {
|
||||
bool result_a = area_a->collides_with(area_b);
|
||||
bool result_b = area_b->collides_with(area_a);
|
||||
if ((result_a || result_b) && !GodotCollisionSolver2D::solve(area_a->get_shape(shape_a), area_a->get_transform() * area_a->get_shape_transform(shape_a), Vector2(), area_b->get_shape(shape_b), area_b->get_transform() * area_b->get_shape_transform(shape_b), Vector2(), nullptr, this)) {
|
||||
result_a = false;
|
||||
result_b = false;
|
||||
}
|
||||
|
||||
bool process_collision = false;
|
||||
|
||||
process_collision_a = false;
|
||||
if (result_a != colliding_a) {
|
||||
if (area_a->has_area_monitor_callback() && area_b_monitorable) {
|
||||
process_collision_a = true;
|
||||
process_collision = true;
|
||||
}
|
||||
colliding_a = result_a;
|
||||
}
|
||||
|
||||
process_collision_b = false;
|
||||
if (result_b != colliding_b) {
|
||||
if (area_b->has_area_monitor_callback() && area_a_monitorable) {
|
||||
process_collision_b = true;
|
||||
process_collision = true;
|
||||
}
|
||||
colliding_b = result_b;
|
||||
}
|
||||
|
||||
return process_collision;
|
||||
}
|
||||
|
||||
bool GodotArea2Pair2D::pre_solve(real_t p_step) {
|
||||
if (process_collision_a) {
|
||||
if (colliding_a) {
|
||||
area_a->add_area_to_query(area_b, shape_b, shape_a);
|
||||
} else {
|
||||
area_a->remove_area_from_query(area_b, shape_b, shape_a);
|
||||
}
|
||||
}
|
||||
|
||||
if (process_collision_b) {
|
||||
if (colliding_b) {
|
||||
area_b->add_area_to_query(area_a, shape_a, shape_b);
|
||||
} else {
|
||||
area_b->remove_area_from_query(area_a, shape_a, shape_b);
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Never do any post solving.
|
||||
}
|
||||
|
||||
void GodotArea2Pair2D::solve(real_t p_step) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
GodotArea2Pair2D::GodotArea2Pair2D(GodotArea2D *p_area_a, int p_shape_a, GodotArea2D *p_area_b, int p_shape_b) {
|
||||
area_a = p_area_a;
|
||||
area_b = p_area_b;
|
||||
shape_a = p_shape_a;
|
||||
shape_b = p_shape_b;
|
||||
area_a_monitorable = area_a->is_monitorable();
|
||||
area_b_monitorable = area_b->is_monitorable();
|
||||
area_a->add_constraint(this);
|
||||
area_b->add_constraint(this);
|
||||
}
|
||||
|
||||
GodotArea2Pair2D::~GodotArea2Pair2D() {
|
||||
if (colliding_a) {
|
||||
if (area_a->has_area_monitor_callback() && area_b_monitorable) {
|
||||
area_a->remove_area_from_query(area_b, shape_b, shape_a);
|
||||
}
|
||||
}
|
||||
|
||||
if (colliding_b) {
|
||||
if (area_b->has_area_monitor_callback() && area_a_monitorable) {
|
||||
area_b->remove_area_from_query(area_a, shape_a, shape_b);
|
||||
}
|
||||
}
|
||||
|
||||
area_a->remove_constraint(this);
|
||||
area_b->remove_constraint(this);
|
||||
}
|
75
modules/godot_physics_2d/godot_area_pair_2d.h
Normal file
75
modules/godot_physics_2d/godot_area_pair_2d.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/**************************************************************************/
|
||||
/* godot_area_pair_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_area_2d.h"
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_constraint_2d.h"
|
||||
|
||||
class GodotAreaPair2D : public GodotConstraint2D {
|
||||
GodotBody2D *body = nullptr;
|
||||
GodotArea2D *area = nullptr;
|
||||
int body_shape = 0;
|
||||
int area_shape = 0;
|
||||
bool colliding = false;
|
||||
bool has_space_override = false;
|
||||
bool process_collision = false;
|
||||
bool body_has_attached_area = false;
|
||||
|
||||
public:
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
GodotAreaPair2D(GodotBody2D *p_body, int p_body_shape, GodotArea2D *p_area, int p_area_shape);
|
||||
~GodotAreaPair2D();
|
||||
};
|
||||
|
||||
class GodotArea2Pair2D : public GodotConstraint2D {
|
||||
GodotArea2D *area_a = nullptr;
|
||||
GodotArea2D *area_b = nullptr;
|
||||
int shape_a = 0;
|
||||
int shape_b = 0;
|
||||
bool colliding_a = false;
|
||||
bool colliding_b = false;
|
||||
bool process_collision_a = false;
|
||||
bool process_collision_b = false;
|
||||
bool area_a_monitorable;
|
||||
bool area_b_monitorable;
|
||||
|
||||
public:
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
GodotArea2Pair2D(GodotArea2D *p_area_a, int p_shape_a, GodotArea2D *p_area_b, int p_shape_b);
|
||||
~GodotArea2Pair2D();
|
||||
};
|
763
modules/godot_physics_2d/godot_body_2d.cpp
Normal file
763
modules/godot_physics_2d/godot_body_2d.cpp
Normal file
@@ -0,0 +1,763 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_body_2d.h"
|
||||
|
||||
#include "godot_area_2d.h"
|
||||
#include "godot_body_direct_state_2d.h"
|
||||
#include "godot_constraint_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
void GodotBody2D::_mass_properties_changed() {
|
||||
if (get_space() && !mass_properties_update_list.in_list()) {
|
||||
get_space()->body_add_to_mass_properties_update_list(&mass_properties_update_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotBody2D::update_mass_properties() {
|
||||
//update shapes and motions
|
||||
|
||||
switch (mode) {
|
||||
case PhysicsServer2D::BODY_MODE_RIGID: {
|
||||
real_t total_area = 0;
|
||||
for (int i = 0; i < get_shape_count(); i++) {
|
||||
if (is_shape_disabled(i)) {
|
||||
continue;
|
||||
}
|
||||
total_area += get_shape_aabb(i).get_area();
|
||||
}
|
||||
|
||||
if (calculate_center_of_mass) {
|
||||
// We have to recompute the center of mass.
|
||||
center_of_mass_local = Vector2();
|
||||
|
||||
if (total_area != 0.0) {
|
||||
for (int i = 0; i < get_shape_count(); i++) {
|
||||
if (is_shape_disabled(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t area = get_shape_aabb(i).get_area();
|
||||
|
||||
real_t mass_new = area * mass / total_area;
|
||||
|
||||
// NOTE: we assume that the shape origin is also its center of mass.
|
||||
center_of_mass_local += mass_new * get_shape_transform(i).get_origin();
|
||||
}
|
||||
|
||||
center_of_mass_local /= mass;
|
||||
}
|
||||
}
|
||||
|
||||
if (calculate_inertia) {
|
||||
inertia = 0;
|
||||
|
||||
for (int i = 0; i < get_shape_count(); i++) {
|
||||
if (is_shape_disabled(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const GodotShape2D *shape = get_shape(i);
|
||||
|
||||
real_t area = get_shape_aabb(i).get_area();
|
||||
if (area == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t mass_new = area * mass / total_area;
|
||||
|
||||
Transform2D mtx = get_shape_transform(i);
|
||||
Vector2 scale = mtx.get_scale();
|
||||
Vector2 shape_origin = mtx.get_origin() - center_of_mass_local;
|
||||
inertia += shape->get_moment_of_inertia(mass_new, scale) + mass_new * shape_origin.length_squared();
|
||||
}
|
||||
}
|
||||
|
||||
_inv_inertia = inertia > 0.0 ? (1.0 / inertia) : 0.0;
|
||||
|
||||
if (mass) {
|
||||
_inv_mass = 1.0 / mass;
|
||||
} else {
|
||||
_inv_mass = 0;
|
||||
}
|
||||
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_MODE_KINEMATIC:
|
||||
case PhysicsServer2D::BODY_MODE_STATIC: {
|
||||
_inv_inertia = 0;
|
||||
_inv_mass = 0;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_MODE_RIGID_LINEAR: {
|
||||
_inv_inertia = 0;
|
||||
_inv_mass = 1.0 / mass;
|
||||
|
||||
} break;
|
||||
}
|
||||
|
||||
_update_transform_dependent();
|
||||
}
|
||||
|
||||
void GodotBody2D::reset_mass_properties() {
|
||||
calculate_inertia = true;
|
||||
calculate_center_of_mass = true;
|
||||
_mass_properties_changed();
|
||||
}
|
||||
|
||||
void GodotBody2D::set_active(bool p_active) {
|
||||
if (active == p_active) {
|
||||
return;
|
||||
}
|
||||
|
||||
active = p_active;
|
||||
|
||||
if (active) {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
// Static bodies can't be active.
|
||||
active = false;
|
||||
} else if (get_space()) {
|
||||
get_space()->body_add_to_active_list(&active_list);
|
||||
}
|
||||
} else if (get_space()) {
|
||||
get_space()->body_remove_from_active_list(&active_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotBody2D::set_param(PhysicsServer2D::BodyParameter p_param, const Variant &p_value) {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::BODY_PARAM_BOUNCE: {
|
||||
bounce = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_FRICTION: {
|
||||
friction = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_MASS: {
|
||||
real_t mass_value = p_value;
|
||||
ERR_FAIL_COND(mass_value <= 0);
|
||||
mass = mass_value;
|
||||
if (mode >= PhysicsServer2D::BODY_MODE_RIGID) {
|
||||
_mass_properties_changed();
|
||||
}
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_INERTIA: {
|
||||
real_t inertia_value = p_value;
|
||||
if (inertia_value <= 0.0) {
|
||||
calculate_inertia = true;
|
||||
if (mode == PhysicsServer2D::BODY_MODE_RIGID) {
|
||||
_mass_properties_changed();
|
||||
}
|
||||
} else {
|
||||
calculate_inertia = false;
|
||||
inertia = inertia_value;
|
||||
if (mode == PhysicsServer2D::BODY_MODE_RIGID) {
|
||||
_inv_inertia = 1.0 / inertia;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: {
|
||||
calculate_center_of_mass = false;
|
||||
center_of_mass_local = p_value;
|
||||
_update_transform_dependent();
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: {
|
||||
if (Math::is_zero_approx(gravity_scale)) {
|
||||
wakeup();
|
||||
}
|
||||
gravity_scale = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE: {
|
||||
int mode_value = p_value;
|
||||
linear_damp_mode = (PhysicsServer2D::BodyDampMode)mode_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE: {
|
||||
int mode_value = p_value;
|
||||
angular_damp_mode = (PhysicsServer2D::BodyDampMode)mode_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_LINEAR_DAMP: {
|
||||
linear_damp = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP: {
|
||||
angular_damp = p_value;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Variant GodotBody2D::get_param(PhysicsServer2D::BodyParameter p_param) const {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::BODY_PARAM_BOUNCE: {
|
||||
return bounce;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_FRICTION: {
|
||||
return friction;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_MASS: {
|
||||
return mass;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_INERTIA: {
|
||||
return inertia;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_CENTER_OF_MASS: {
|
||||
return center_of_mass_local;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_GRAVITY_SCALE: {
|
||||
return gravity_scale;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_LINEAR_DAMP_MODE: {
|
||||
return linear_damp_mode;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP_MODE: {
|
||||
return angular_damp_mode;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_LINEAR_DAMP: {
|
||||
return linear_damp;
|
||||
}
|
||||
case PhysicsServer2D::BODY_PARAM_ANGULAR_DAMP: {
|
||||
return angular_damp;
|
||||
}
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GodotBody2D::set_mode(PhysicsServer2D::BodyMode p_mode) {
|
||||
PhysicsServer2D::BodyMode prev = mode;
|
||||
mode = p_mode;
|
||||
|
||||
switch (p_mode) {
|
||||
//CLEAR UP EVERYTHING IN CASE IT NOT WORKS!
|
||||
case PhysicsServer2D::BODY_MODE_STATIC:
|
||||
case PhysicsServer2D::BODY_MODE_KINEMATIC: {
|
||||
_set_inv_transform(get_transform().affine_inverse());
|
||||
_inv_mass = 0;
|
||||
_inv_inertia = 0;
|
||||
_set_static(p_mode == PhysicsServer2D::BODY_MODE_STATIC);
|
||||
set_active(p_mode == PhysicsServer2D::BODY_MODE_KINEMATIC && contacts.size());
|
||||
linear_velocity = Vector2();
|
||||
angular_velocity = 0;
|
||||
if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC && prev != mode) {
|
||||
first_time_kinematic = true;
|
||||
}
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_MODE_RIGID: {
|
||||
_inv_mass = mass > 0 ? (1.0 / mass) : 0;
|
||||
if (!calculate_inertia) {
|
||||
_inv_inertia = 1.0 / inertia;
|
||||
}
|
||||
_mass_properties_changed();
|
||||
_set_static(false);
|
||||
set_active(true);
|
||||
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_MODE_RIGID_LINEAR: {
|
||||
_inv_mass = mass > 0 ? (1.0 / mass) : 0;
|
||||
_inv_inertia = 0;
|
||||
angular_velocity = 0;
|
||||
_set_static(false);
|
||||
set_active(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsServer2D::BodyMode GodotBody2D::get_mode() const {
|
||||
return mode;
|
||||
}
|
||||
|
||||
void GodotBody2D::_shapes_changed() {
|
||||
_mass_properties_changed();
|
||||
wakeup();
|
||||
wakeup_neighbours();
|
||||
}
|
||||
|
||||
void GodotBody2D::set_state(PhysicsServer2D::BodyState p_state, const Variant &p_variant) {
|
||||
switch (p_state) {
|
||||
case PhysicsServer2D::BODY_STATE_TRANSFORM: {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
new_transform = p_variant;
|
||||
//wakeup_neighbours();
|
||||
set_active(true);
|
||||
if (first_time_kinematic) {
|
||||
_set_transform(p_variant);
|
||||
_set_inv_transform(get_transform().affine_inverse());
|
||||
first_time_kinematic = false;
|
||||
}
|
||||
} else if (mode == PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
_set_transform(p_variant);
|
||||
_set_inv_transform(get_transform().affine_inverse());
|
||||
wakeup_neighbours();
|
||||
} else {
|
||||
Transform2D t = p_variant;
|
||||
t.orthonormalize();
|
||||
new_transform = get_transform(); //used as old to compute motion
|
||||
if (t == new_transform) {
|
||||
break;
|
||||
}
|
||||
_set_transform(t);
|
||||
_set_inv_transform(get_transform().inverse());
|
||||
_update_transform_dependent();
|
||||
}
|
||||
wakeup();
|
||||
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY: {
|
||||
linear_velocity = p_variant;
|
||||
constant_linear_velocity = linear_velocity;
|
||||
wakeup();
|
||||
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY: {
|
||||
angular_velocity = p_variant;
|
||||
constant_angular_velocity = angular_velocity;
|
||||
wakeup();
|
||||
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_STATE_SLEEPING: {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_STATIC || mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
break;
|
||||
}
|
||||
bool do_sleep = p_variant;
|
||||
if (do_sleep) {
|
||||
linear_velocity = Vector2();
|
||||
//biased_linear_velocity=Vector3();
|
||||
angular_velocity = 0;
|
||||
//biased_angular_velocity=Vector3();
|
||||
set_active(false);
|
||||
} else {
|
||||
if (mode != PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
set_active(true);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_STATE_CAN_SLEEP: {
|
||||
can_sleep = p_variant;
|
||||
if (mode >= PhysicsServer2D::BODY_MODE_RIGID && !active && !can_sleep) {
|
||||
set_active(true);
|
||||
}
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
Variant GodotBody2D::get_state(PhysicsServer2D::BodyState p_state) const {
|
||||
switch (p_state) {
|
||||
case PhysicsServer2D::BODY_STATE_TRANSFORM: {
|
||||
return get_transform();
|
||||
}
|
||||
case PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY: {
|
||||
return linear_velocity;
|
||||
}
|
||||
case PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY: {
|
||||
return angular_velocity;
|
||||
}
|
||||
case PhysicsServer2D::BODY_STATE_SLEEPING: {
|
||||
return !is_active();
|
||||
}
|
||||
case PhysicsServer2D::BODY_STATE_CAN_SLEEP: {
|
||||
return can_sleep;
|
||||
}
|
||||
}
|
||||
|
||||
return Variant();
|
||||
}
|
||||
|
||||
void GodotBody2D::set_space(GodotSpace2D *p_space) {
|
||||
if (get_space()) {
|
||||
wakeup_neighbours();
|
||||
|
||||
if (mass_properties_update_list.in_list()) {
|
||||
get_space()->body_remove_from_mass_properties_update_list(&mass_properties_update_list);
|
||||
}
|
||||
if (active_list.in_list()) {
|
||||
get_space()->body_remove_from_active_list(&active_list);
|
||||
}
|
||||
if (direct_state_query_list.in_list()) {
|
||||
get_space()->body_remove_from_state_query_list(&direct_state_query_list);
|
||||
}
|
||||
}
|
||||
|
||||
_set_space(p_space);
|
||||
|
||||
if (get_space()) {
|
||||
_mass_properties_changed();
|
||||
|
||||
if (active && !active_list.in_list()) {
|
||||
get_space()->body_add_to_active_list(&active_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotBody2D::_update_transform_dependent() {
|
||||
center_of_mass = get_transform().basis_xform(center_of_mass_local);
|
||||
}
|
||||
|
||||
void GodotBody2D::integrate_forces(real_t p_step) {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL(get_space());
|
||||
|
||||
int ac = areas.size();
|
||||
|
||||
bool gravity_done = false;
|
||||
bool linear_damp_done = false;
|
||||
bool angular_damp_done = false;
|
||||
|
||||
bool stopped = false;
|
||||
|
||||
gravity = Vector2(0, 0);
|
||||
|
||||
total_linear_damp = 0.0;
|
||||
total_angular_damp = 0.0;
|
||||
|
||||
// Combine gravity and damping from overlapping areas in priority order.
|
||||
if (ac) {
|
||||
areas.sort();
|
||||
const AreaCMP *aa = &areas[0];
|
||||
for (int i = ac - 1; i >= 0 && !stopped; i--) {
|
||||
if (!gravity_done) {
|
||||
PhysicsServer2D::AreaSpaceOverrideMode area_gravity_mode = (PhysicsServer2D::AreaSpaceOverrideMode)(int)aa[i].area->get_param(PhysicsServer2D::AREA_PARAM_GRAVITY_OVERRIDE_MODE);
|
||||
if (area_gravity_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
Vector2 area_gravity;
|
||||
aa[i].area->compute_gravity(get_transform().get_origin(), area_gravity);
|
||||
switch (area_gravity_mode) {
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
|
||||
gravity += area_gravity;
|
||||
gravity_done = area_gravity_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE;
|
||||
} break;
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
|
||||
gravity = area_gravity;
|
||||
gravity_done = area_gravity_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!linear_damp_done) {
|
||||
PhysicsServer2D::AreaSpaceOverrideMode area_linear_damp_mode = (PhysicsServer2D::AreaSpaceOverrideMode)(int)aa[i].area->get_param(PhysicsServer2D::AREA_PARAM_LINEAR_DAMP_OVERRIDE_MODE);
|
||||
if (area_linear_damp_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
real_t area_linear_damp = aa[i].area->get_linear_damp();
|
||||
switch (area_linear_damp_mode) {
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
|
||||
total_linear_damp += area_linear_damp;
|
||||
linear_damp_done = area_linear_damp_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE;
|
||||
} break;
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
|
||||
total_linear_damp = area_linear_damp;
|
||||
linear_damp_done = area_linear_damp_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!angular_damp_done) {
|
||||
PhysicsServer2D::AreaSpaceOverrideMode area_angular_damp_mode = (PhysicsServer2D::AreaSpaceOverrideMode)(int)aa[i].area->get_param(PhysicsServer2D::AREA_PARAM_ANGULAR_DAMP_OVERRIDE_MODE);
|
||||
if (area_angular_damp_mode != PhysicsServer2D::AREA_SPACE_OVERRIDE_DISABLED) {
|
||||
real_t area_angular_damp = aa[i].area->get_angular_damp();
|
||||
switch (area_angular_damp_mode) {
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE: {
|
||||
total_angular_damp += area_angular_damp;
|
||||
angular_damp_done = area_angular_damp_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_COMBINE_REPLACE;
|
||||
} break;
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE:
|
||||
case PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE_COMBINE: {
|
||||
total_angular_damp = area_angular_damp;
|
||||
angular_damp_done = area_angular_damp_mode == PhysicsServer2D::AREA_SPACE_OVERRIDE_REPLACE;
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stopped = gravity_done && linear_damp_done && angular_damp_done;
|
||||
}
|
||||
}
|
||||
|
||||
// Add default gravity and damping from space area.
|
||||
if (!stopped) {
|
||||
GodotArea2D *default_area = get_space()->get_default_area();
|
||||
ERR_FAIL_NULL(default_area);
|
||||
|
||||
if (!gravity_done) {
|
||||
Vector2 default_gravity;
|
||||
default_area->compute_gravity(get_transform().get_origin(), default_gravity);
|
||||
gravity += default_gravity;
|
||||
}
|
||||
|
||||
if (!linear_damp_done) {
|
||||
total_linear_damp += default_area->get_linear_damp();
|
||||
}
|
||||
|
||||
if (!angular_damp_done) {
|
||||
total_angular_damp += default_area->get_angular_damp();
|
||||
}
|
||||
}
|
||||
|
||||
// Override linear damping with body's value.
|
||||
switch (linear_damp_mode) {
|
||||
case PhysicsServer2D::BODY_DAMP_MODE_COMBINE: {
|
||||
total_linear_damp += linear_damp;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_DAMP_MODE_REPLACE: {
|
||||
total_linear_damp = linear_damp;
|
||||
} break;
|
||||
}
|
||||
|
||||
// Override angular damping with body's value.
|
||||
switch (angular_damp_mode) {
|
||||
case PhysicsServer2D::BODY_DAMP_MODE_COMBINE: {
|
||||
total_angular_damp += angular_damp;
|
||||
} break;
|
||||
case PhysicsServer2D::BODY_DAMP_MODE_REPLACE: {
|
||||
total_angular_damp = angular_damp;
|
||||
} break;
|
||||
}
|
||||
|
||||
gravity *= gravity_scale;
|
||||
|
||||
prev_linear_velocity = linear_velocity;
|
||||
prev_angular_velocity = angular_velocity;
|
||||
|
||||
Vector2 motion;
|
||||
bool do_motion = false;
|
||||
|
||||
if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
//compute motion, angular and etc. velocities from prev transform
|
||||
motion = new_transform.get_origin() - get_transform().get_origin();
|
||||
linear_velocity = constant_linear_velocity + motion / p_step;
|
||||
|
||||
real_t rot = new_transform.get_rotation() - get_transform().get_rotation();
|
||||
angular_velocity = constant_angular_velocity + std::remainder(rot, 2.0 * Math::PI) / p_step;
|
||||
|
||||
do_motion = true;
|
||||
|
||||
} else {
|
||||
if (!omit_force_integration) {
|
||||
//overridden by direct state query
|
||||
|
||||
Vector2 force = gravity * mass + applied_force + constant_force;
|
||||
real_t torque = applied_torque + constant_torque;
|
||||
|
||||
real_t damp = 1.0 - p_step * total_linear_damp;
|
||||
|
||||
if (damp < 0) { // reached zero in the given time
|
||||
damp = 0;
|
||||
}
|
||||
|
||||
real_t angular_damp_new = 1.0 - p_step * total_angular_damp;
|
||||
|
||||
if (angular_damp_new < 0) { // reached zero in the given time
|
||||
angular_damp_new = 0;
|
||||
}
|
||||
|
||||
linear_velocity *= damp;
|
||||
angular_velocity *= angular_damp_new;
|
||||
|
||||
linear_velocity += _inv_mass * force * p_step;
|
||||
angular_velocity += _inv_inertia * torque * p_step;
|
||||
}
|
||||
|
||||
if (continuous_cd_mode != PhysicsServer2D::CCD_MODE_DISABLED) {
|
||||
motion = linear_velocity * p_step;
|
||||
do_motion = true;
|
||||
}
|
||||
}
|
||||
|
||||
applied_force = Vector2();
|
||||
applied_torque = 0.0;
|
||||
|
||||
biased_angular_velocity = 0.0;
|
||||
biased_linear_velocity = Vector2();
|
||||
|
||||
if (do_motion) { //shapes temporarily extend for raycast
|
||||
_update_shapes_with_motion(motion);
|
||||
}
|
||||
|
||||
contact_count = 0;
|
||||
}
|
||||
|
||||
void GodotBody2D::integrate_velocities(real_t p_step) {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL(get_space());
|
||||
|
||||
if (fi_callback_data || body_state_callback.is_valid()) {
|
||||
get_space()->body_add_to_state_query_list(&direct_state_query_list);
|
||||
}
|
||||
|
||||
if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
_set_transform(new_transform, false);
|
||||
_set_inv_transform(new_transform.affine_inverse());
|
||||
if (contacts.is_empty() && linear_velocity == Vector2() && angular_velocity == 0) {
|
||||
set_active(false); //stopped moving, deactivate
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
real_t total_angular_velocity = angular_velocity + biased_angular_velocity;
|
||||
Vector2 total_linear_velocity = linear_velocity + biased_linear_velocity;
|
||||
|
||||
real_t angle_delta = total_angular_velocity * p_step;
|
||||
real_t angle = get_transform().get_rotation() + angle_delta;
|
||||
Vector2 pos = get_transform().get_origin() + total_linear_velocity * p_step;
|
||||
|
||||
if (center_of_mass.length_squared() > CMP_EPSILON2) {
|
||||
// Calculate displacement due to center of mass offset.
|
||||
pos += center_of_mass - center_of_mass.rotated(angle_delta);
|
||||
}
|
||||
|
||||
_set_transform(Transform2D(angle, pos), continuous_cd_mode == PhysicsServer2D::CCD_MODE_DISABLED);
|
||||
_set_inv_transform(get_transform().inverse());
|
||||
|
||||
if (continuous_cd_mode != PhysicsServer2D::CCD_MODE_DISABLED) {
|
||||
new_transform = get_transform();
|
||||
}
|
||||
|
||||
_update_transform_dependent();
|
||||
}
|
||||
|
||||
void GodotBody2D::wakeup_neighbours() {
|
||||
for (const Pair<GodotConstraint2D *, int> &E : constraint_list) {
|
||||
const GodotConstraint2D *c = E.first;
|
||||
GodotBody2D **n = c->get_body_ptr();
|
||||
int bc = c->get_body_count();
|
||||
|
||||
for (int i = 0; i < bc; i++) {
|
||||
if (i == E.second) {
|
||||
continue;
|
||||
}
|
||||
GodotBody2D *b = n[i];
|
||||
if (b->mode < PhysicsServer2D::BODY_MODE_RIGID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!b->is_active()) {
|
||||
b->set_active(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotBody2D::call_queries() {
|
||||
Variant direct_state_variant = get_direct_state();
|
||||
|
||||
if (fi_callback_data) {
|
||||
if (!fi_callback_data->callable.is_valid()) {
|
||||
set_force_integration_callback(Callable());
|
||||
} else {
|
||||
const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata };
|
||||
|
||||
Callable::CallError ce;
|
||||
Variant rv;
|
||||
if (fi_callback_data->udata.get_type() != Variant::NIL) {
|
||||
fi_callback_data->callable.callp(vp, 2, rv, ce);
|
||||
|
||||
} else {
|
||||
fi_callback_data->callable.callp(vp, 1, rv, ce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (body_state_callback.is_valid()) {
|
||||
body_state_callback.call(direct_state_variant);
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotBody2D::sleep_test(real_t p_step) {
|
||||
if (mode == PhysicsServer2D::BODY_MODE_STATIC || mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
return true;
|
||||
} else if (!can_sleep) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ERR_FAIL_NULL_V(get_space(), true);
|
||||
|
||||
if (Math::abs(angular_velocity) < get_space()->get_body_angular_velocity_sleep_threshold() && Math::abs(linear_velocity.length_squared()) < get_space()->get_body_linear_velocity_sleep_threshold() * get_space()->get_body_linear_velocity_sleep_threshold()) {
|
||||
still_time += p_step;
|
||||
|
||||
return still_time > get_space()->get_body_time_to_sleep();
|
||||
} else {
|
||||
still_time = 0; //maybe this should be set to 0 on set_active?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GodotBody2D::set_state_sync_callback(const Callable &p_callable) {
|
||||
body_state_callback = p_callable;
|
||||
}
|
||||
|
||||
void GodotBody2D::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) {
|
||||
if (p_callable.is_valid()) {
|
||||
if (!fi_callback_data) {
|
||||
fi_callback_data = memnew(ForceIntegrationCallbackData);
|
||||
}
|
||||
fi_callback_data->callable = p_callable;
|
||||
fi_callback_data->udata = p_udata;
|
||||
} else if (fi_callback_data) {
|
||||
memdelete(fi_callback_data);
|
||||
fi_callback_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
GodotPhysicsDirectBodyState2D *GodotBody2D::get_direct_state() {
|
||||
if (!direct_state) {
|
||||
direct_state = memnew(GodotPhysicsDirectBodyState2D);
|
||||
direct_state->body = this;
|
||||
}
|
||||
return direct_state;
|
||||
}
|
||||
|
||||
GodotBody2D::GodotBody2D() :
|
||||
GodotCollisionObject2D(TYPE_BODY),
|
||||
active_list(this),
|
||||
mass_properties_update_list(this),
|
||||
direct_state_query_list(this) {
|
||||
_set_static(false);
|
||||
}
|
||||
|
||||
GodotBody2D::~GodotBody2D() {
|
||||
if (fi_callback_data) {
|
||||
memdelete(fi_callback_data);
|
||||
}
|
||||
if (direct_state) {
|
||||
memdelete(direct_state);
|
||||
}
|
||||
}
|
387
modules/godot_physics_2d/godot_body_2d.h
Normal file
387
modules/godot_physics_2d/godot_body_2d.h
Normal file
@@ -0,0 +1,387 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_area_2d.h"
|
||||
#include "godot_collision_object_2d.h"
|
||||
|
||||
#include "core/templates/list.h"
|
||||
#include "core/templates/pair.h"
|
||||
#include "core/templates/vset.h"
|
||||
|
||||
class GodotConstraint2D;
|
||||
class GodotPhysicsDirectBodyState2D;
|
||||
|
||||
class GodotBody2D : public GodotCollisionObject2D {
|
||||
PhysicsServer2D::BodyMode mode = PhysicsServer2D::BODY_MODE_RIGID;
|
||||
|
||||
Vector2 biased_linear_velocity;
|
||||
real_t biased_angular_velocity = 0.0;
|
||||
|
||||
Vector2 linear_velocity;
|
||||
real_t angular_velocity = 0.0;
|
||||
|
||||
Vector2 prev_linear_velocity;
|
||||
real_t prev_angular_velocity = 0.0;
|
||||
|
||||
Vector2 constant_linear_velocity;
|
||||
real_t constant_angular_velocity = 0.0;
|
||||
|
||||
PhysicsServer2D::BodyDampMode linear_damp_mode = PhysicsServer2D::BODY_DAMP_MODE_COMBINE;
|
||||
PhysicsServer2D::BodyDampMode angular_damp_mode = PhysicsServer2D::BODY_DAMP_MODE_COMBINE;
|
||||
|
||||
real_t linear_damp = 0.0;
|
||||
real_t angular_damp = 0.0;
|
||||
|
||||
real_t total_linear_damp = 0.0;
|
||||
real_t total_angular_damp = 0.0;
|
||||
|
||||
real_t gravity_scale = 1.0;
|
||||
|
||||
real_t bounce = 0.0;
|
||||
real_t friction = 1.0;
|
||||
|
||||
real_t mass = 1.0;
|
||||
real_t _inv_mass = 1.0;
|
||||
|
||||
real_t inertia = 0.0;
|
||||
real_t _inv_inertia = 0.0;
|
||||
|
||||
Vector2 center_of_mass_local;
|
||||
Vector2 center_of_mass;
|
||||
|
||||
bool calculate_inertia = true;
|
||||
bool calculate_center_of_mass = true;
|
||||
|
||||
Vector2 gravity;
|
||||
|
||||
real_t still_time = 0.0;
|
||||
|
||||
Vector2 applied_force;
|
||||
real_t applied_torque = 0.0;
|
||||
|
||||
Vector2 constant_force;
|
||||
real_t constant_torque = 0.0;
|
||||
|
||||
SelfList<GodotBody2D> active_list;
|
||||
SelfList<GodotBody2D> mass_properties_update_list;
|
||||
SelfList<GodotBody2D> direct_state_query_list;
|
||||
|
||||
VSet<RID> exceptions;
|
||||
PhysicsServer2D::CCDMode continuous_cd_mode = PhysicsServer2D::CCD_MODE_DISABLED;
|
||||
bool omit_force_integration = false;
|
||||
bool active = true;
|
||||
bool can_sleep = true;
|
||||
bool first_time_kinematic = false;
|
||||
void _mass_properties_changed();
|
||||
virtual void _shapes_changed() override;
|
||||
Transform2D new_transform;
|
||||
|
||||
List<Pair<GodotConstraint2D *, int>> constraint_list;
|
||||
|
||||
struct AreaCMP {
|
||||
GodotArea2D *area = nullptr;
|
||||
int refCount = 0;
|
||||
_FORCE_INLINE_ bool operator==(const AreaCMP &p_cmp) const { return area->get_self() == p_cmp.area->get_self(); }
|
||||
_FORCE_INLINE_ bool operator<(const AreaCMP &p_cmp) const { return area->get_priority() < p_cmp.area->get_priority(); }
|
||||
_FORCE_INLINE_ AreaCMP() {}
|
||||
_FORCE_INLINE_ AreaCMP(GodotArea2D *p_area) {
|
||||
area = p_area;
|
||||
refCount = 1;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<AreaCMP> areas;
|
||||
|
||||
struct Contact {
|
||||
Vector2 local_pos;
|
||||
Vector2 local_normal;
|
||||
Vector2 local_velocity_at_pos;
|
||||
real_t depth = 0.0;
|
||||
int local_shape = 0;
|
||||
Vector2 collider_pos;
|
||||
int collider_shape = 0;
|
||||
ObjectID collider_instance_id;
|
||||
RID collider;
|
||||
Vector2 collider_velocity_at_pos;
|
||||
Vector2 impulse;
|
||||
};
|
||||
|
||||
Vector<Contact> contacts; //no contacts by default
|
||||
int contact_count = 0;
|
||||
|
||||
Callable body_state_callback;
|
||||
|
||||
struct ForceIntegrationCallbackData {
|
||||
Callable callable;
|
||||
Variant udata;
|
||||
};
|
||||
|
||||
ForceIntegrationCallbackData *fi_callback_data = nullptr;
|
||||
|
||||
GodotPhysicsDirectBodyState2D *direct_state = nullptr;
|
||||
|
||||
uint64_t island_step = 0;
|
||||
|
||||
void _update_transform_dependent();
|
||||
|
||||
friend class GodotPhysicsDirectBodyState2D; // i give up, too many functions to expose
|
||||
|
||||
public:
|
||||
void set_state_sync_callback(const Callable &p_callable);
|
||||
void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant());
|
||||
|
||||
GodotPhysicsDirectBodyState2D *get_direct_state();
|
||||
|
||||
_FORCE_INLINE_ void add_area(GodotArea2D *p_area) {
|
||||
int index = areas.find(AreaCMP(p_area));
|
||||
if (index > -1) {
|
||||
areas.write[index].refCount += 1;
|
||||
} else {
|
||||
areas.ordered_insert(AreaCMP(p_area));
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void remove_area(GodotArea2D *p_area) {
|
||||
int index = areas.find(AreaCMP(p_area));
|
||||
if (index > -1) {
|
||||
areas.write[index].refCount -= 1;
|
||||
if (areas[index].refCount < 1) {
|
||||
areas.remove_at(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void set_max_contacts_reported(int p_size) {
|
||||
ERR_FAIL_INDEX(p_size, MAX_CONTACTS_REPORTED_2D_MAX);
|
||||
contacts.resize(p_size);
|
||||
contact_count = 0;
|
||||
if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC && p_size) {
|
||||
set_active(true);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ int get_max_contacts_reported() const { return contacts.size(); }
|
||||
|
||||
_FORCE_INLINE_ bool can_report_contacts() const { return !contacts.is_empty(); }
|
||||
_FORCE_INLINE_ void add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_local_velocity_at_pos, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse);
|
||||
|
||||
_FORCE_INLINE_ void add_exception(const RID &p_exception) { exceptions.insert(p_exception); }
|
||||
_FORCE_INLINE_ void remove_exception(const RID &p_exception) { exceptions.erase(p_exception); }
|
||||
_FORCE_INLINE_ bool has_exception(const RID &p_exception) const { return exceptions.has(p_exception); }
|
||||
_FORCE_INLINE_ const VSet<RID> &get_exceptions() const { return exceptions; }
|
||||
|
||||
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
|
||||
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
|
||||
|
||||
_FORCE_INLINE_ void add_constraint(GodotConstraint2D *p_constraint, int p_pos) { constraint_list.push_back({ p_constraint, p_pos }); }
|
||||
_FORCE_INLINE_ void remove_constraint(GodotConstraint2D *p_constraint, int p_pos) { constraint_list.erase({ p_constraint, p_pos }); }
|
||||
const List<Pair<GodotConstraint2D *, int>> &get_constraint_list() const { return constraint_list; }
|
||||
_FORCE_INLINE_ void clear_constraint_list() { constraint_list.clear(); }
|
||||
|
||||
_FORCE_INLINE_ void set_omit_force_integration(bool p_omit_force_integration) { omit_force_integration = p_omit_force_integration; }
|
||||
_FORCE_INLINE_ bool get_omit_force_integration() const { return omit_force_integration; }
|
||||
|
||||
_FORCE_INLINE_ void set_linear_velocity(const Vector2 &p_velocity) { linear_velocity = p_velocity; }
|
||||
_FORCE_INLINE_ Vector2 get_linear_velocity() const { return linear_velocity; }
|
||||
|
||||
_FORCE_INLINE_ void set_angular_velocity(real_t p_velocity) { angular_velocity = p_velocity; }
|
||||
_FORCE_INLINE_ real_t get_angular_velocity() const { return angular_velocity; }
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_prev_linear_velocity() const { return prev_linear_velocity; }
|
||||
_FORCE_INLINE_ real_t get_prev_angular_velocity() const { return prev_angular_velocity; }
|
||||
|
||||
_FORCE_INLINE_ void set_biased_linear_velocity(const Vector2 &p_velocity) { biased_linear_velocity = p_velocity; }
|
||||
_FORCE_INLINE_ Vector2 get_biased_linear_velocity() const { return biased_linear_velocity; }
|
||||
|
||||
_FORCE_INLINE_ void set_biased_angular_velocity(real_t p_velocity) { biased_angular_velocity = p_velocity; }
|
||||
_FORCE_INLINE_ real_t get_biased_angular_velocity() const { return biased_angular_velocity; }
|
||||
|
||||
_FORCE_INLINE_ void apply_central_impulse(const Vector2 &p_impulse) {
|
||||
linear_velocity += p_impulse * _inv_mass;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) {
|
||||
linear_velocity += p_impulse * _inv_mass;
|
||||
angular_velocity += _inv_inertia * (p_position - center_of_mass).cross(p_impulse);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_torque_impulse(real_t p_torque) {
|
||||
angular_velocity += _inv_inertia * p_torque;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_bias_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2(), real_t p_max_delta_av = -1.0) {
|
||||
biased_linear_velocity += p_impulse * _inv_mass;
|
||||
if (p_max_delta_av != 0.0) {
|
||||
real_t delta_av = _inv_inertia * (p_position - center_of_mass).cross(p_impulse);
|
||||
if (p_max_delta_av > 0 && delta_av > p_max_delta_av) {
|
||||
delta_av = p_max_delta_av;
|
||||
}
|
||||
biased_angular_velocity += delta_av;
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_central_force(const Vector2 &p_force) {
|
||||
applied_force += p_force;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) {
|
||||
applied_force += p_force;
|
||||
applied_torque += (p_position - center_of_mass).cross(p_force);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void apply_torque(real_t p_torque) {
|
||||
applied_torque += p_torque;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_constant_central_force(const Vector2 &p_force) {
|
||||
constant_force += p_force;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_constant_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) {
|
||||
constant_force += p_force;
|
||||
constant_torque += (p_position - center_of_mass).cross(p_force);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void add_constant_torque(real_t p_torque) {
|
||||
constant_torque += p_torque;
|
||||
}
|
||||
|
||||
void set_constant_force(const Vector2 &p_force) { constant_force = p_force; }
|
||||
Vector2 get_constant_force() const { return constant_force; }
|
||||
|
||||
void set_constant_torque(real_t p_torque) { constant_torque = p_torque; }
|
||||
real_t get_constant_torque() const { return constant_torque; }
|
||||
|
||||
void set_active(bool p_active);
|
||||
_FORCE_INLINE_ bool is_active() const { return active; }
|
||||
|
||||
_FORCE_INLINE_ void wakeup() {
|
||||
if ((!get_space()) || mode == PhysicsServer2D::BODY_MODE_STATIC || mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
return;
|
||||
}
|
||||
set_active(true);
|
||||
}
|
||||
|
||||
void set_param(PhysicsServer2D::BodyParameter p_param, const Variant &p_value);
|
||||
Variant get_param(PhysicsServer2D::BodyParameter p_param) const;
|
||||
|
||||
void set_mode(PhysicsServer2D::BodyMode p_mode);
|
||||
PhysicsServer2D::BodyMode get_mode() const;
|
||||
|
||||
void set_state(PhysicsServer2D::BodyState p_state, const Variant &p_variant);
|
||||
Variant get_state(PhysicsServer2D::BodyState p_state) const;
|
||||
|
||||
_FORCE_INLINE_ void set_continuous_collision_detection_mode(PhysicsServer2D::CCDMode p_mode) { continuous_cd_mode = p_mode; }
|
||||
_FORCE_INLINE_ PhysicsServer2D::CCDMode get_continuous_collision_detection_mode() const { return continuous_cd_mode; }
|
||||
|
||||
void set_space(GodotSpace2D *p_space) override;
|
||||
|
||||
void update_mass_properties();
|
||||
void reset_mass_properties();
|
||||
|
||||
_FORCE_INLINE_ const Vector2 &get_center_of_mass() const { return center_of_mass; }
|
||||
_FORCE_INLINE_ const Vector2 &get_center_of_mass_local() const { return center_of_mass_local; }
|
||||
_FORCE_INLINE_ real_t get_inv_mass() const { return _inv_mass; }
|
||||
_FORCE_INLINE_ real_t get_inv_inertia() const { return _inv_inertia; }
|
||||
_FORCE_INLINE_ real_t get_friction() const { return friction; }
|
||||
_FORCE_INLINE_ real_t get_bounce() const { return bounce; }
|
||||
|
||||
void integrate_forces(real_t p_step);
|
||||
void integrate_velocities(real_t p_step);
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_velocity_in_local_point(const Vector2 &rel_pos) const {
|
||||
return linear_velocity + Vector2(-angular_velocity * rel_pos.y, angular_velocity * rel_pos.x);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_motion() const {
|
||||
if (mode > PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
return new_transform.get_origin() - get_transform().get_origin();
|
||||
} else if (mode == PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
return get_transform().get_origin() - new_transform.get_origin(); //kinematic simulates forward
|
||||
}
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
void call_queries();
|
||||
void wakeup_neighbours();
|
||||
|
||||
bool sleep_test(real_t p_step);
|
||||
|
||||
GodotBody2D();
|
||||
~GodotBody2D();
|
||||
};
|
||||
|
||||
//add contact inline
|
||||
|
||||
void GodotBody2D::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_normal, real_t p_depth, int p_local_shape, const Vector2 &p_local_velocity_at_pos, const Vector2 &p_collider_pos, int p_collider_shape, ObjectID p_collider_instance_id, const RID &p_collider, const Vector2 &p_collider_velocity_at_pos, const Vector2 &p_impulse) {
|
||||
int c_max = contacts.size();
|
||||
|
||||
if (c_max == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Contact *c = contacts.ptrw();
|
||||
|
||||
int idx = -1;
|
||||
|
||||
if (contact_count < c_max) {
|
||||
idx = contact_count++;
|
||||
} else {
|
||||
real_t least_depth = 1e20;
|
||||
int least_deep = -1;
|
||||
for (int i = 0; i < c_max; i++) {
|
||||
if (i == 0 || c[i].depth < least_depth) {
|
||||
least_deep = i;
|
||||
least_depth = c[i].depth;
|
||||
}
|
||||
}
|
||||
|
||||
if (least_deep >= 0 && least_depth < p_depth) {
|
||||
idx = least_deep;
|
||||
}
|
||||
if (idx == -1) {
|
||||
return; //none least deepe than this
|
||||
}
|
||||
}
|
||||
|
||||
c[idx].local_pos = p_local_pos;
|
||||
c[idx].local_normal = p_local_normal;
|
||||
c[idx].local_velocity_at_pos = p_local_velocity_at_pos;
|
||||
c[idx].depth = p_depth;
|
||||
c[idx].local_shape = p_local_shape;
|
||||
c[idx].collider_pos = p_collider_pos;
|
||||
c[idx].collider_shape = p_collider_shape;
|
||||
c[idx].collider_instance_id = p_collider_instance_id;
|
||||
c[idx].collider = p_collider;
|
||||
c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos;
|
||||
c[idx].impulse = p_impulse;
|
||||
}
|
244
modules/godot_physics_2d/godot_body_direct_state_2d.cpp
Normal file
244
modules/godot_physics_2d/godot_body_direct_state_2d.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_direct_state_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_body_direct_state_2d.h"
|
||||
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_total_gravity() const {
|
||||
return body->gravity;
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_total_angular_damp() const {
|
||||
return body->total_angular_damp;
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_total_linear_damp() const {
|
||||
return body->total_linear_damp;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_center_of_mass() const {
|
||||
return body->get_center_of_mass();
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_center_of_mass_local() const {
|
||||
return body->get_center_of_mass_local();
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_inverse_mass() const {
|
||||
return body->get_inv_mass();
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_inverse_inertia() const {
|
||||
return body->get_inv_inertia();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_linear_velocity(const Vector2 &p_velocity) {
|
||||
body->wakeup();
|
||||
body->set_linear_velocity(p_velocity);
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_linear_velocity() const {
|
||||
return body->get_linear_velocity();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_angular_velocity(real_t p_velocity) {
|
||||
body->wakeup();
|
||||
body->set_angular_velocity(p_velocity);
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_angular_velocity() const {
|
||||
return body->get_angular_velocity();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_transform(const Transform2D &p_transform) {
|
||||
body->set_state(PhysicsServer2D::BODY_STATE_TRANSFORM, p_transform);
|
||||
}
|
||||
|
||||
Transform2D GodotPhysicsDirectBodyState2D::get_transform() const {
|
||||
return body->get_transform();
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_velocity_at_local_position(const Vector2 &p_position) const {
|
||||
return body->get_velocity_in_local_point(p_position);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_central_impulse(const Vector2 &p_impulse) {
|
||||
body->wakeup();
|
||||
body->apply_central_impulse(p_impulse);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) {
|
||||
body->wakeup();
|
||||
body->apply_impulse(p_impulse, p_position);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_torque_impulse(real_t p_torque) {
|
||||
body->wakeup();
|
||||
body->apply_torque_impulse(p_torque);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_central_force(const Vector2 &p_force) {
|
||||
body->wakeup();
|
||||
body->apply_central_force(p_force);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_force(const Vector2 &p_force, const Vector2 &p_position) {
|
||||
body->wakeup();
|
||||
body->apply_force(p_force, p_position);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::apply_torque(real_t p_torque) {
|
||||
body->wakeup();
|
||||
body->apply_torque(p_torque);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::add_constant_central_force(const Vector2 &p_force) {
|
||||
body->wakeup();
|
||||
body->add_constant_central_force(p_force);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::add_constant_force(const Vector2 &p_force, const Vector2 &p_position) {
|
||||
body->wakeup();
|
||||
body->add_constant_force(p_force, p_position);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::add_constant_torque(real_t p_torque) {
|
||||
body->wakeup();
|
||||
body->add_constant_torque(p_torque);
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_constant_force(const Vector2 &p_force) {
|
||||
if (!p_force.is_zero_approx()) {
|
||||
body->wakeup();
|
||||
}
|
||||
body->set_constant_force(p_force);
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_constant_force() const {
|
||||
return body->get_constant_force();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_constant_torque(real_t p_torque) {
|
||||
if (!Math::is_zero_approx(p_torque)) {
|
||||
body->wakeup();
|
||||
}
|
||||
body->set_constant_torque(p_torque);
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_constant_torque() const {
|
||||
return body->get_constant_torque();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_sleep_state(bool p_enable) {
|
||||
body->set_active(!p_enable);
|
||||
}
|
||||
|
||||
bool GodotPhysicsDirectBodyState2D::is_sleeping() const {
|
||||
return !body->is_active();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_collision_layer(uint32_t p_layer) {
|
||||
body->set_collision_layer(p_layer);
|
||||
}
|
||||
|
||||
uint32_t GodotPhysicsDirectBodyState2D::get_collision_layer() const {
|
||||
return body->get_collision_layer();
|
||||
}
|
||||
|
||||
void GodotPhysicsDirectBodyState2D::set_collision_mask(uint32_t p_mask) {
|
||||
body->set_collision_mask(p_mask);
|
||||
}
|
||||
|
||||
uint32_t GodotPhysicsDirectBodyState2D::get_collision_mask() const {
|
||||
return body->get_collision_mask();
|
||||
}
|
||||
|
||||
int GodotPhysicsDirectBodyState2D::get_contact_count() const {
|
||||
return body->contact_count;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_local_position(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].local_pos;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_local_normal(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].local_normal;
|
||||
}
|
||||
|
||||
int GodotPhysicsDirectBodyState2D::get_contact_local_shape(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1);
|
||||
return body->contacts[p_contact_idx].local_shape;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_local_velocity_at_position(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].local_velocity_at_pos;
|
||||
}
|
||||
|
||||
RID GodotPhysicsDirectBodyState2D::get_contact_collider(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID());
|
||||
return body->contacts[p_contact_idx].collider;
|
||||
}
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_collider_position(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].collider_pos;
|
||||
}
|
||||
|
||||
ObjectID GodotPhysicsDirectBodyState2D::get_contact_collider_id(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID());
|
||||
return body->contacts[p_contact_idx].collider_instance_id;
|
||||
}
|
||||
|
||||
int GodotPhysicsDirectBodyState2D::get_contact_collider_shape(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0);
|
||||
return body->contacts[p_contact_idx].collider_shape;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_collider_velocity_at_position(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].collider_velocity_at_pos;
|
||||
}
|
||||
|
||||
Vector2 GodotPhysicsDirectBodyState2D::get_contact_impulse(int p_contact_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2());
|
||||
return body->contacts[p_contact_idx].impulse;
|
||||
}
|
||||
|
||||
PhysicsDirectSpaceState2D *GodotPhysicsDirectBodyState2D::get_space_state() {
|
||||
return body->get_space()->get_direct_state();
|
||||
}
|
||||
|
||||
real_t GodotPhysicsDirectBodyState2D::get_step() const {
|
||||
return body->get_space()->get_last_step();
|
||||
}
|
107
modules/godot_physics_2d/godot_body_direct_state_2d.h
Normal file
107
modules/godot_physics_2d/godot_body_direct_state_2d.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_direct_state_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class GodotBody2D;
|
||||
|
||||
class GodotPhysicsDirectBodyState2D : public PhysicsDirectBodyState2D {
|
||||
GDCLASS(GodotPhysicsDirectBodyState2D, PhysicsDirectBodyState2D);
|
||||
|
||||
public:
|
||||
GodotBody2D *body = nullptr;
|
||||
|
||||
virtual Vector2 get_total_gravity() const override;
|
||||
virtual real_t get_total_angular_damp() const override;
|
||||
virtual real_t get_total_linear_damp() const override;
|
||||
|
||||
virtual Vector2 get_center_of_mass() const override;
|
||||
virtual Vector2 get_center_of_mass_local() const override;
|
||||
virtual real_t get_inverse_mass() const override;
|
||||
virtual real_t get_inverse_inertia() const override;
|
||||
|
||||
virtual void set_linear_velocity(const Vector2 &p_velocity) override;
|
||||
virtual Vector2 get_linear_velocity() const override;
|
||||
|
||||
virtual void set_angular_velocity(real_t p_velocity) override;
|
||||
virtual real_t get_angular_velocity() const override;
|
||||
|
||||
virtual void set_transform(const Transform2D &p_transform) override;
|
||||
virtual Transform2D get_transform() const override;
|
||||
|
||||
virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override;
|
||||
|
||||
virtual void apply_central_impulse(const Vector2 &p_impulse) override;
|
||||
virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override;
|
||||
virtual void apply_torque_impulse(real_t p_torque) override;
|
||||
|
||||
virtual void apply_central_force(const Vector2 &p_force) override;
|
||||
virtual void apply_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override;
|
||||
virtual void apply_torque(real_t p_torque) override;
|
||||
|
||||
virtual void add_constant_central_force(const Vector2 &p_force) override;
|
||||
virtual void add_constant_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override;
|
||||
virtual void add_constant_torque(real_t p_torque) override;
|
||||
|
||||
virtual void set_constant_force(const Vector2 &p_force) override;
|
||||
virtual Vector2 get_constant_force() const override;
|
||||
|
||||
virtual void set_constant_torque(real_t p_torque) override;
|
||||
virtual real_t get_constant_torque() const override;
|
||||
|
||||
virtual void set_sleep_state(bool p_enable) override;
|
||||
virtual bool is_sleeping() const override;
|
||||
|
||||
virtual void set_collision_layer(uint32_t p_layer) override;
|
||||
virtual uint32_t get_collision_layer() const override;
|
||||
|
||||
virtual void set_collision_mask(uint32_t p_mask) override;
|
||||
virtual uint32_t get_collision_mask() const override;
|
||||
|
||||
virtual int get_contact_count() const override;
|
||||
|
||||
virtual Vector2 get_contact_local_position(int p_contact_idx) const override;
|
||||
virtual Vector2 get_contact_local_normal(int p_contact_idx) const override;
|
||||
virtual int get_contact_local_shape(int p_contact_idx) const override;
|
||||
virtual Vector2 get_contact_local_velocity_at_position(int p_contact_idx) const override;
|
||||
|
||||
virtual RID get_contact_collider(int p_contact_idx) const override;
|
||||
virtual Vector2 get_contact_collider_position(int p_contact_idx) const override;
|
||||
virtual ObjectID get_contact_collider_id(int p_contact_idx) const override;
|
||||
virtual int get_contact_collider_shape(int p_contact_idx) const override;
|
||||
virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override;
|
||||
virtual Vector2 get_contact_impulse(int p_contact_idx) const override;
|
||||
|
||||
virtual PhysicsDirectSpaceState2D *get_space_state() override;
|
||||
|
||||
virtual real_t get_step() const override;
|
||||
};
|
610
modules/godot_physics_2d/godot_body_pair_2d.cpp
Normal file
610
modules/godot_physics_2d/godot_body_pair_2d.cpp
Normal file
@@ -0,0 +1,610 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_pair_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_body_pair_2d.h"
|
||||
|
||||
#include "godot_collision_solver_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
#define ACCUMULATE_IMPULSES
|
||||
|
||||
#define MIN_VELOCITY 0.001
|
||||
#define MAX_BIAS_ROTATION (Math::PI / 8)
|
||||
|
||||
void GodotBodyPair2D::_add_contact(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_self) {
|
||||
GodotBodyPair2D *self = static_cast<GodotBodyPair2D *>(p_self);
|
||||
|
||||
self->_contact_added_callback(p_point_A, p_point_B);
|
||||
}
|
||||
|
||||
void GodotBodyPair2D::_contact_added_callback(const Vector2 &p_point_A, const Vector2 &p_point_B) {
|
||||
Vector2 local_A = A->get_inv_transform().basis_xform(p_point_A);
|
||||
Vector2 local_B = B->get_inv_transform().basis_xform(p_point_B - offset_B);
|
||||
|
||||
int new_index = contact_count;
|
||||
|
||||
ERR_FAIL_COND(new_index >= (MAX_CONTACTS + 1));
|
||||
|
||||
Contact contact;
|
||||
contact.local_A = local_A;
|
||||
contact.local_B = local_B;
|
||||
contact.normal = (p_point_A - p_point_B).normalized();
|
||||
contact.used = true;
|
||||
|
||||
// Attempt to determine if the contact will be reused.
|
||||
real_t recycle_radius_2 = space->get_contact_recycle_radius() * space->get_contact_recycle_radius();
|
||||
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
Contact &c = contacts[i];
|
||||
if (c.local_A.distance_squared_to(local_A) < (recycle_radius_2) &&
|
||||
c.local_B.distance_squared_to(local_B) < (recycle_radius_2)) {
|
||||
contact.acc_normal_impulse = c.acc_normal_impulse;
|
||||
contact.acc_tangent_impulse = c.acc_tangent_impulse;
|
||||
contact.acc_bias_impulse = c.acc_bias_impulse;
|
||||
contact.acc_bias_impulse_center_of_mass = c.acc_bias_impulse_center_of_mass;
|
||||
c = contact;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out if the contact amount must be reduced to fit the new contact.
|
||||
if (new_index == MAX_CONTACTS) {
|
||||
// Remove the contact with the minimum depth.
|
||||
|
||||
const Transform2D &transform_A = A->get_transform();
|
||||
const Transform2D &transform_B = B->get_transform();
|
||||
|
||||
int least_deep = -1;
|
||||
real_t min_depth;
|
||||
|
||||
// Start with depth for new contact.
|
||||
{
|
||||
Vector2 global_A = transform_A.basis_xform(contact.local_A);
|
||||
Vector2 global_B = transform_B.basis_xform(contact.local_B) + offset_B;
|
||||
|
||||
Vector2 axis = global_A - global_B;
|
||||
min_depth = axis.dot(contact.normal);
|
||||
}
|
||||
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
const Contact &c = contacts[i];
|
||||
Vector2 global_A = transform_A.basis_xform(c.local_A);
|
||||
Vector2 global_B = transform_B.basis_xform(c.local_B) + offset_B;
|
||||
|
||||
Vector2 axis = global_A - global_B;
|
||||
real_t depth = axis.dot(c.normal);
|
||||
|
||||
if (depth < min_depth) {
|
||||
min_depth = depth;
|
||||
least_deep = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (least_deep > -1) {
|
||||
// Replace the least deep contact by the new one.
|
||||
contacts[least_deep] = contact;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
contacts[new_index] = contact;
|
||||
contact_count++;
|
||||
}
|
||||
|
||||
void GodotBodyPair2D::_validate_contacts() {
|
||||
// Make sure to erase contacts that are no longer valid.
|
||||
real_t max_separation = space->get_contact_max_separation();
|
||||
real_t max_separation2 = max_separation * max_separation;
|
||||
|
||||
const Transform2D &transform_A = A->get_transform();
|
||||
const Transform2D &transform_B = B->get_transform();
|
||||
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
Contact &c = contacts[i];
|
||||
|
||||
bool erase = false;
|
||||
if (!c.used) {
|
||||
// Was left behind in previous frame.
|
||||
erase = true;
|
||||
} else {
|
||||
c.used = false;
|
||||
|
||||
Vector2 global_A = transform_A.basis_xform(c.local_A);
|
||||
Vector2 global_B = transform_B.basis_xform(c.local_B) + offset_B;
|
||||
Vector2 axis = global_A - global_B;
|
||||
real_t depth = axis.dot(c.normal);
|
||||
|
||||
if (depth < -max_separation || (global_B + c.normal * depth - global_A).length_squared() > max_separation2) {
|
||||
erase = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (erase) {
|
||||
// Contact no longer needed, remove.
|
||||
|
||||
if ((i + 1) < contact_count) {
|
||||
// Swap with the last one.
|
||||
SWAP(contacts[i], contacts[contact_count - 1]);
|
||||
}
|
||||
|
||||
i--;
|
||||
contact_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// `_test_ccd` prevents tunneling by slowing down a high velocity body that is about to collide so
|
||||
// that next frame it will be at an appropriate location to collide (i.e. slight overlap).
|
||||
// WARNING: The way velocity is adjusted down to cause a collision means the momentum will be
|
||||
// weaker than it should for a bounce!
|
||||
// Process: Only proceed if body A's motion is high relative to its size.
|
||||
// Cast forward along motion vector to see if A is going to enter/pass B's collider next frame, only proceed if it does.
|
||||
// Adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
|
||||
bool GodotBodyPair2D::_test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, const Transform2D &p_xform_A, GodotBody2D *p_B, int p_shape_B, const Transform2D &p_xform_B) {
|
||||
Vector2 motion = p_A->get_linear_velocity() * p_step;
|
||||
real_t mlen = motion.length();
|
||||
if (mlen < CMP_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 mnormal = motion / mlen;
|
||||
|
||||
real_t min = 0.0, max = 0.0;
|
||||
p_A->get_shape(p_shape_A)->project_rangev(mnormal, p_xform_A, min, max);
|
||||
|
||||
// Did it move enough in this direction to even attempt raycast?
|
||||
// Let's say it should move more than 1/3 the size of the object in that axis.
|
||||
bool fast_object = mlen > (max - min) * 0.3;
|
||||
if (!fast_object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A is moving fast enough that tunneling might occur. See if it's really about to collide.
|
||||
|
||||
// Roughly predict body B's position in the next frame (ignoring collisions).
|
||||
Transform2D predicted_xform_B = p_xform_B.translated(p_B->get_linear_velocity() * p_step);
|
||||
|
||||
// Cast a segment from support in motion normal, in the same direction of motion by motion length.
|
||||
// Support point will the farthest forward collision point along the movement vector.
|
||||
// i.e. the point that should hit B first if any collision does occur.
|
||||
|
||||
// convert mnormal into body A's local xform because get_support requires (and returns) local coordinates.
|
||||
int a;
|
||||
Vector2 s[2];
|
||||
p_A->get_shape(p_shape_A)->get_supports(p_xform_A.basis_xform_inv(mnormal).normalized(), s, a);
|
||||
Vector2 from = p_xform_A.xform(s[0]);
|
||||
// Back up 10% of the per-frame motion behind the support point and use that as the beginning of our cast.
|
||||
// This should ensure the calculated new velocity will really cause a bit of overlap instead of just getting us very close.
|
||||
Vector2 to = from + motion;
|
||||
|
||||
Transform2D from_inv = predicted_xform_B.affine_inverse();
|
||||
|
||||
// Back up 10% of the per-frame motion behind the support point and use that as the beginning of our cast.
|
||||
// At high speeds, this may mean we're actually casting from well behind the body instead of inside it, which is odd. But it still works out.
|
||||
Vector2 local_from = from_inv.xform(from - motion * 0.1);
|
||||
Vector2 local_to = from_inv.xform(to);
|
||||
|
||||
Vector2 rpos, rnorm;
|
||||
if (!p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm)) {
|
||||
// there was no hit. Since the segment is the length of per-frame motion, this means the bodies will not
|
||||
// actually collide yet on next frame. We'll probably check again next frame once they're closer.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check one-way collision based on motion direction.
|
||||
if (p_A->get_shape(p_shape_A)->allows_one_way_collision() && p_B->is_shape_set_as_one_way_collision(p_shape_B)) {
|
||||
Vector2 direction = predicted_xform_B.columns[1].normalized();
|
||||
if (direction.dot(mnormal) < CMP_EPSILON) {
|
||||
collided = false;
|
||||
oneway_disabled = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Shorten the linear velocity so it does not hit, but gets close enough,
|
||||
// next frame will hit softly or soft enough.
|
||||
Vector2 hitpos = predicted_xform_B.xform(rpos);
|
||||
|
||||
real_t newlen = hitpos.distance_to(from) + (max - min) * 0.01; // adding 1% of body length to the distance between collision and support point should cause body A's support point to arrive just within B's collider next frame.
|
||||
p_A->set_linear_velocity(mnormal * (newlen / p_step));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
real_t combine_bounce(GodotBody2D *A, GodotBody2D *B) {
|
||||
return CLAMP(A->get_bounce() + B->get_bounce(), 0, 1);
|
||||
}
|
||||
|
||||
real_t combine_friction(GodotBody2D *A, GodotBody2D *B) {
|
||||
return Math::abs(MIN(A->get_friction(), B->get_friction()));
|
||||
}
|
||||
|
||||
bool GodotBodyPair2D::setup(real_t p_step) {
|
||||
check_ccd = false;
|
||||
|
||||
if (!A->interacts_with(B) || A->has_exception(B->get_self()) || B->has_exception(A->get_self())) {
|
||||
collided = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
collide_A = (A->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC) && A->collides_with(B);
|
||||
collide_B = (B->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC) && B->collides_with(A);
|
||||
|
||||
report_contacts_only = false;
|
||||
if (!collide_A && !collide_B) {
|
||||
if ((A->get_max_contacts_reported() > 0) || (B->get_max_contacts_reported() > 0)) {
|
||||
report_contacts_only = true;
|
||||
} else {
|
||||
collided = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//use local A coordinates to avoid numerical issues on collision detection
|
||||
offset_B = B->get_transform().get_origin() - A->get_transform().get_origin();
|
||||
|
||||
_validate_contacts();
|
||||
|
||||
const Vector2 &offset_A = A->get_transform().get_origin();
|
||||
Transform2D xform_Au = A->get_transform().untranslated();
|
||||
Transform2D xform_A = xform_Au * A->get_shape_transform(shape_A);
|
||||
|
||||
Transform2D xform_Bu = B->get_transform();
|
||||
xform_Bu.columns[2] -= offset_A;
|
||||
Transform2D xform_B = xform_Bu * B->get_shape_transform(shape_B);
|
||||
|
||||
GodotShape2D *shape_A_ptr = A->get_shape(shape_A);
|
||||
GodotShape2D *shape_B_ptr = B->get_shape(shape_B);
|
||||
|
||||
Vector2 motion_A, motion_B;
|
||||
|
||||
if (A->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_SHAPE) {
|
||||
motion_A = A->get_motion();
|
||||
}
|
||||
if (B->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_SHAPE) {
|
||||
motion_B = B->get_motion();
|
||||
}
|
||||
|
||||
bool prev_collided = collided;
|
||||
|
||||
collided = GodotCollisionSolver2D::solve(shape_A_ptr, xform_A, motion_A, shape_B_ptr, xform_B, motion_B, _add_contact, this, &sep_axis);
|
||||
if (!collided) {
|
||||
oneway_disabled = false;
|
||||
|
||||
if (A->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_A) {
|
||||
check_ccd = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (B->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_B) {
|
||||
check_ccd = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (oneway_disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!prev_collided) {
|
||||
if (shape_B_ptr->allows_one_way_collision() && A->is_shape_set_as_one_way_collision(shape_A)) {
|
||||
Vector2 direction = xform_A.columns[1].normalized();
|
||||
bool valid = false;
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
Contact &c = contacts[i];
|
||||
if (c.normal.dot(direction) > -CMP_EPSILON) { // Greater (normal inverted).
|
||||
continue;
|
||||
}
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
if (!valid) {
|
||||
collided = false;
|
||||
oneway_disabled = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (shape_A_ptr->allows_one_way_collision() && B->is_shape_set_as_one_way_collision(shape_B)) {
|
||||
Vector2 direction = xform_B.columns[1].normalized();
|
||||
bool valid = false;
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
Contact &c = contacts[i];
|
||||
if (c.normal.dot(direction) < CMP_EPSILON) { // Less (normal ok).
|
||||
continue;
|
||||
}
|
||||
valid = true;
|
||||
break;
|
||||
}
|
||||
if (!valid) {
|
||||
collided = false;
|
||||
oneway_disabled = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GodotBodyPair2D::pre_solve(real_t p_step) {
|
||||
if (oneway_disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!collided) {
|
||||
if (check_ccd) {
|
||||
const Vector2 &offset_A = A->get_transform().get_origin();
|
||||
Transform2D xform_Au = A->get_transform().untranslated();
|
||||
Transform2D xform_A = xform_Au * A->get_shape_transform(shape_A);
|
||||
|
||||
Transform2D xform_Bu = B->get_transform();
|
||||
xform_Bu.columns[2] -= offset_A;
|
||||
Transform2D xform_B = xform_Bu * B->get_shape_transform(shape_B);
|
||||
|
||||
if (A->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_A) {
|
||||
_test_ccd(p_step, A, shape_A, xform_A, B, shape_B, xform_B);
|
||||
}
|
||||
|
||||
if (B->get_continuous_collision_detection_mode() == PhysicsServer2D::CCD_MODE_CAST_RAY && collide_B) {
|
||||
_test_ccd(p_step, B, shape_B, xform_B, A, shape_A, xform_A);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
real_t max_penetration = space->get_contact_max_allowed_penetration();
|
||||
|
||||
real_t bias = space->get_contact_bias();
|
||||
|
||||
GodotShape2D *shape_A_ptr = A->get_shape(shape_A);
|
||||
GodotShape2D *shape_B_ptr = B->get_shape(shape_B);
|
||||
|
||||
if (shape_A_ptr->get_custom_bias() || shape_B_ptr->get_custom_bias()) {
|
||||
if (shape_A_ptr->get_custom_bias() == 0) {
|
||||
bias = shape_B_ptr->get_custom_bias();
|
||||
} else if (shape_B_ptr->get_custom_bias() == 0) {
|
||||
bias = shape_A_ptr->get_custom_bias();
|
||||
} else {
|
||||
bias = (shape_B_ptr->get_custom_bias() + shape_A_ptr->get_custom_bias()) * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
real_t inv_dt = 1.0 / p_step;
|
||||
|
||||
bool do_process = false;
|
||||
|
||||
const Vector2 &offset_A = A->get_transform().get_origin();
|
||||
const Transform2D &transform_A = A->get_transform();
|
||||
const Transform2D &transform_B = B->get_transform();
|
||||
|
||||
real_t inv_inertia_A = collide_A ? A->get_inv_inertia() : 0.0;
|
||||
real_t inv_inertia_B = collide_B ? B->get_inv_inertia() : 0.0;
|
||||
|
||||
real_t inv_mass_A = collide_A ? A->get_inv_mass() : 0.0;
|
||||
real_t inv_mass_B = collide_B ? B->get_inv_mass() : 0.0;
|
||||
|
||||
for (int i = 0; i < contact_count; i++) {
|
||||
Contact &c = contacts[i];
|
||||
c.active = false;
|
||||
|
||||
Vector2 global_A = transform_A.basis_xform(c.local_A);
|
||||
Vector2 global_B = transform_B.basis_xform(c.local_B) + offset_B;
|
||||
|
||||
Vector2 axis = global_A - global_B;
|
||||
real_t depth = axis.dot(c.normal);
|
||||
|
||||
if (depth <= 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_ENABLED
|
||||
if (space->is_debugging_contacts()) {
|
||||
space->add_debug_contact(global_A + offset_A);
|
||||
space->add_debug_contact(global_B + offset_A);
|
||||
}
|
||||
#endif
|
||||
|
||||
c.rA = global_A - A->get_center_of_mass();
|
||||
c.rB = global_B - B->get_center_of_mass() - offset_B;
|
||||
|
||||
// Precompute normal mass, tangent mass, and bias.
|
||||
real_t rnA = c.rA.dot(c.normal);
|
||||
real_t rnB = c.rB.dot(c.normal);
|
||||
real_t kNormal = inv_mass_A + inv_mass_B;
|
||||
kNormal += inv_inertia_A * (c.rA.dot(c.rA) - rnA * rnA) + inv_inertia_B * (c.rB.dot(c.rB) - rnB * rnB);
|
||||
c.mass_normal = 1.0f / kNormal;
|
||||
|
||||
Vector2 tangent = c.normal.orthogonal();
|
||||
real_t rtA = c.rA.dot(tangent);
|
||||
real_t rtB = c.rB.dot(tangent);
|
||||
real_t kTangent = inv_mass_A + inv_mass_B;
|
||||
kTangent += inv_inertia_A * (c.rA.dot(c.rA) - rtA * rtA) + inv_inertia_B * (c.rB.dot(c.rB) - rtB * rtB);
|
||||
c.mass_tangent = 1.0f / kTangent;
|
||||
|
||||
c.bias = -bias * inv_dt * MIN(0.0f, -depth + max_penetration);
|
||||
c.depth = depth;
|
||||
|
||||
Vector2 P = c.acc_normal_impulse * c.normal + c.acc_tangent_impulse * tangent;
|
||||
|
||||
c.acc_impulse -= P;
|
||||
|
||||
if (A->can_report_contacts() || B->can_report_contacts()) {
|
||||
Vector2 crB = Vector2(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x) + B->get_linear_velocity();
|
||||
Vector2 crA = Vector2(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x) + A->get_linear_velocity();
|
||||
if (A->can_report_contacts()) {
|
||||
A->add_contact(global_A + offset_A, -c.normal, depth, shape_A, crA, global_B + offset_A, shape_B, B->get_instance_id(), B->get_self(), crB, c.acc_impulse);
|
||||
}
|
||||
if (B->can_report_contacts()) {
|
||||
B->add_contact(global_B + offset_A, c.normal, depth, shape_B, crB, global_A + offset_A, shape_A, A->get_instance_id(), A->get_self(), crA, c.acc_impulse);
|
||||
}
|
||||
}
|
||||
|
||||
if (report_contacts_only) {
|
||||
collided = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef ACCUMULATE_IMPULSES
|
||||
{
|
||||
// Apply normal + friction impulse
|
||||
if (collide_A) {
|
||||
A->apply_impulse(-P, c.rA + A->get_center_of_mass());
|
||||
}
|
||||
if (collide_B) {
|
||||
B->apply_impulse(P, c.rB + B->get_center_of_mass());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
c.bounce = combine_bounce(A, B);
|
||||
if (c.bounce) {
|
||||
Vector2 crA(-A->get_prev_angular_velocity() * c.rA.y, A->get_prev_angular_velocity() * c.rA.x);
|
||||
Vector2 crB(-B->get_prev_angular_velocity() * c.rB.y, B->get_prev_angular_velocity() * c.rB.x);
|
||||
Vector2 dv = B->get_prev_linear_velocity() + crB - A->get_prev_linear_velocity() - crA;
|
||||
c.bounce = c.bounce * dv.dot(c.normal);
|
||||
}
|
||||
|
||||
c.active = true;
|
||||
do_process = true;
|
||||
}
|
||||
|
||||
return do_process;
|
||||
}
|
||||
|
||||
void GodotBodyPair2D::solve(real_t p_step) {
|
||||
if (!collided || oneway_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const real_t max_bias_av = MAX_BIAS_ROTATION / p_step;
|
||||
|
||||
real_t inv_mass_A = collide_A ? A->get_inv_mass() : 0.0;
|
||||
real_t inv_mass_B = collide_B ? B->get_inv_mass() : 0.0;
|
||||
|
||||
for (int i = 0; i < contact_count; ++i) {
|
||||
Contact &c = contacts[i];
|
||||
|
||||
if (!c.active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Relative velocity at contact
|
||||
|
||||
Vector2 crA(-A->get_angular_velocity() * c.rA.y, A->get_angular_velocity() * c.rA.x);
|
||||
Vector2 crB(-B->get_angular_velocity() * c.rB.y, B->get_angular_velocity() * c.rB.x);
|
||||
Vector2 dv = B->get_linear_velocity() + crB - A->get_linear_velocity() - crA;
|
||||
|
||||
Vector2 crbA(-A->get_biased_angular_velocity() * c.rA.y, A->get_biased_angular_velocity() * c.rA.x);
|
||||
Vector2 crbB(-B->get_biased_angular_velocity() * c.rB.y, B->get_biased_angular_velocity() * c.rB.x);
|
||||
Vector2 dbv = B->get_biased_linear_velocity() + crbB - A->get_biased_linear_velocity() - crbA;
|
||||
|
||||
real_t vn = dv.dot(c.normal);
|
||||
real_t vbn = dbv.dot(c.normal);
|
||||
|
||||
Vector2 tangent = c.normal.orthogonal();
|
||||
real_t vt = dv.dot(tangent);
|
||||
|
||||
real_t jbn = (c.bias - vbn) * c.mass_normal;
|
||||
real_t jbnOld = c.acc_bias_impulse;
|
||||
c.acc_bias_impulse = MAX(jbnOld + jbn, 0.0f);
|
||||
|
||||
Vector2 jb = c.normal * (c.acc_bias_impulse - jbnOld);
|
||||
|
||||
if (collide_A) {
|
||||
A->apply_bias_impulse(-jb, c.rA + A->get_center_of_mass(), max_bias_av);
|
||||
}
|
||||
if (collide_B) {
|
||||
B->apply_bias_impulse(jb, c.rB + B->get_center_of_mass(), max_bias_av);
|
||||
}
|
||||
|
||||
crbA = Vector2(-A->get_biased_angular_velocity() * c.rA.y, A->get_biased_angular_velocity() * c.rA.x);
|
||||
crbB = Vector2(-B->get_biased_angular_velocity() * c.rB.y, B->get_biased_angular_velocity() * c.rB.x);
|
||||
dbv = B->get_biased_linear_velocity() + crbB - A->get_biased_linear_velocity() - crbA;
|
||||
|
||||
vbn = dbv.dot(c.normal);
|
||||
|
||||
if (Math::abs(-vbn + c.bias) > MIN_VELOCITY) {
|
||||
real_t jbn_com = (-vbn + c.bias) / (inv_mass_A + inv_mass_B);
|
||||
real_t jbnOld_com = c.acc_bias_impulse_center_of_mass;
|
||||
c.acc_bias_impulse_center_of_mass = MAX(jbnOld_com + jbn_com, 0.0f);
|
||||
|
||||
Vector2 jb_com = c.normal * (c.acc_bias_impulse_center_of_mass - jbnOld_com);
|
||||
|
||||
if (collide_A) {
|
||||
A->apply_bias_impulse(-jb_com, A->get_center_of_mass(), 0.0f);
|
||||
}
|
||||
if (collide_B) {
|
||||
B->apply_bias_impulse(jb_com, B->get_center_of_mass(), 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
real_t jn = -(c.bounce + vn) * c.mass_normal;
|
||||
real_t jnOld = c.acc_normal_impulse;
|
||||
c.acc_normal_impulse = MAX(jnOld + jn, 0.0f);
|
||||
|
||||
real_t friction = combine_friction(A, B);
|
||||
|
||||
real_t jtMax = friction * c.acc_normal_impulse;
|
||||
real_t jt = -vt * c.mass_tangent;
|
||||
real_t jtOld = c.acc_tangent_impulse;
|
||||
c.acc_tangent_impulse = CLAMP(jtOld + jt, -jtMax, jtMax);
|
||||
|
||||
Vector2 j = c.normal * (c.acc_normal_impulse - jnOld) + tangent * (c.acc_tangent_impulse - jtOld);
|
||||
|
||||
if (collide_A) {
|
||||
A->apply_impulse(-j, c.rA + A->get_center_of_mass());
|
||||
}
|
||||
if (collide_B) {
|
||||
B->apply_impulse(j, c.rB + B->get_center_of_mass());
|
||||
}
|
||||
c.acc_impulse -= j;
|
||||
}
|
||||
}
|
||||
|
||||
GodotBodyPair2D::GodotBodyPair2D(GodotBody2D *p_A, int p_shape_A, GodotBody2D *p_B, int p_shape_B) :
|
||||
GodotConstraint2D(_arr, 2) {
|
||||
A = p_A;
|
||||
B = p_B;
|
||||
shape_A = p_shape_A;
|
||||
shape_B = p_shape_B;
|
||||
space = A->get_space();
|
||||
A->add_constraint(this, 0);
|
||||
B->add_constraint(this, 1);
|
||||
}
|
||||
|
||||
GodotBodyPair2D::~GodotBodyPair2D() {
|
||||
A->remove_constraint(this, 0);
|
||||
B->remove_constraint(this, 1);
|
||||
}
|
98
modules/godot_physics_2d/godot_body_pair_2d.h
Normal file
98
modules/godot_physics_2d/godot_body_pair_2d.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**************************************************************************/
|
||||
/* godot_body_pair_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_constraint_2d.h"
|
||||
|
||||
class GodotBodyPair2D : public GodotConstraint2D {
|
||||
enum {
|
||||
MAX_CONTACTS = 2
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
GodotBody2D *A;
|
||||
GodotBody2D *B;
|
||||
};
|
||||
|
||||
GodotBody2D *_arr[2] = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
int shape_A = 0;
|
||||
int shape_B = 0;
|
||||
|
||||
bool collide_A = false;
|
||||
bool collide_B = false;
|
||||
|
||||
GodotSpace2D *space = nullptr;
|
||||
|
||||
struct Contact {
|
||||
Vector2 position;
|
||||
Vector2 normal;
|
||||
Vector2 local_A, local_B;
|
||||
Vector2 acc_impulse; // accumulated impulse
|
||||
real_t acc_normal_impulse = 0.0; // accumulated normal impulse (Pn)
|
||||
real_t acc_tangent_impulse = 0.0; // accumulated tangent impulse (Pt)
|
||||
real_t acc_bias_impulse = 0.0; // accumulated normal impulse for position bias (Pnb)
|
||||
real_t acc_bias_impulse_center_of_mass = 0.0; // accumulated normal impulse for position bias applied to com
|
||||
real_t mass_normal, mass_tangent = 0.0;
|
||||
real_t bias = 0.0;
|
||||
|
||||
real_t depth = 0.0;
|
||||
bool active = false;
|
||||
bool used = false;
|
||||
Vector2 rA, rB;
|
||||
real_t bounce = 0.0;
|
||||
};
|
||||
|
||||
Vector2 offset_B; //use local A coordinates to avoid numerical issues on collision detection
|
||||
|
||||
Vector2 sep_axis;
|
||||
Contact contacts[MAX_CONTACTS];
|
||||
int contact_count = 0;
|
||||
bool collided = false;
|
||||
bool check_ccd = false;
|
||||
bool oneway_disabled = false;
|
||||
bool report_contacts_only = false;
|
||||
|
||||
bool _test_ccd(real_t p_step, GodotBody2D *p_A, int p_shape_A, const Transform2D &p_xform_A, GodotBody2D *p_B, int p_shape_B, const Transform2D &p_xform_B);
|
||||
void _validate_contacts();
|
||||
static void _add_contact(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_self);
|
||||
_FORCE_INLINE_ void _contact_added_callback(const Vector2 &p_point_A, const Vector2 &p_point_B);
|
||||
|
||||
public:
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
GodotBodyPair2D(GodotBody2D *p_A, int p_shape_A, GodotBody2D *p_B, int p_shape_B);
|
||||
~GodotBodyPair2D();
|
||||
};
|
36
modules/godot_physics_2d/godot_broad_phase_2d.cpp
Normal file
36
modules/godot_physics_2d/godot_broad_phase_2d.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/**************************************************************************/
|
||||
/* godot_broad_phase_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_broad_phase_2d.h"
|
||||
|
||||
GodotBroadPhase2D::CreateFunction GodotBroadPhase2D::create_func = nullptr;
|
||||
|
||||
GodotBroadPhase2D::~GodotBroadPhase2D() {
|
||||
}
|
68
modules/godot_physics_2d/godot_broad_phase_2d.h
Normal file
68
modules/godot_physics_2d/godot_broad_phase_2d.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/**************************************************************************/
|
||||
/* godot_broad_phase_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/math/math_funcs.h"
|
||||
#include "core/math/rect2.h"
|
||||
|
||||
class GodotCollisionObject2D;
|
||||
|
||||
class GodotBroadPhase2D {
|
||||
public:
|
||||
typedef GodotBroadPhase2D *(*CreateFunction)();
|
||||
|
||||
static CreateFunction create_func;
|
||||
|
||||
typedef uint32_t ID;
|
||||
|
||||
typedef void *(*PairCallback)(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_userdata);
|
||||
typedef void (*UnpairCallback)(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_data, void *p_userdata);
|
||||
|
||||
// 0 is an invalid ID
|
||||
virtual ID create(GodotCollisionObject2D *p_object_, int p_subindex = 0, const Rect2 &p_aabb = Rect2(), bool p_static = false) = 0;
|
||||
virtual void move(ID p_id, const Rect2 &p_aabb) = 0;
|
||||
virtual void set_static(ID p_id, bool p_static) = 0;
|
||||
virtual void remove(ID p_id) = 0;
|
||||
|
||||
virtual GodotCollisionObject2D *get_object(ID p_id) const = 0;
|
||||
virtual bool is_static(ID p_id) const = 0;
|
||||
virtual int get_subindex(ID p_id) const = 0;
|
||||
|
||||
virtual int cull_segment(const Vector2 &p_from, const Vector2 &p_to, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices = nullptr) = 0;
|
||||
virtual int cull_aabb(const Rect2 &p_aabb, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices = nullptr) = 0;
|
||||
|
||||
virtual void set_pair_callback(PairCallback p_pair_callback, void *p_userdata) = 0;
|
||||
virtual void set_unpair_callback(UnpairCallback p_unpair_callback, void *p_userdata) = 0;
|
||||
|
||||
virtual void update() = 0;
|
||||
|
||||
virtual ~GodotBroadPhase2D();
|
||||
};
|
123
modules/godot_physics_2d/godot_broad_phase_2d_bvh.cpp
Normal file
123
modules/godot_physics_2d/godot_broad_phase_2d_bvh.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
/**************************************************************************/
|
||||
/* godot_broad_phase_2d_bvh.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_broad_phase_2d_bvh.h"
|
||||
#include "godot_collision_object_2d.h"
|
||||
|
||||
GodotBroadPhase2D::ID GodotBroadPhase2DBVH::create(GodotCollisionObject2D *p_object, int p_subindex, const Rect2 &p_aabb, bool p_static) {
|
||||
uint32_t tree_id = p_static ? TREE_STATIC : TREE_DYNAMIC;
|
||||
uint32_t tree_collision_mask = p_static ? TREE_FLAG_DYNAMIC : (TREE_FLAG_STATIC | TREE_FLAG_DYNAMIC);
|
||||
ID oid = bvh.create(p_object, true, tree_id, tree_collision_mask, p_aabb, p_subindex); // Pair everything, don't care?
|
||||
return oid + 1;
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::move(ID p_id, const Rect2 &p_aabb) {
|
||||
ERR_FAIL_COND(!p_id);
|
||||
bvh.move(p_id - 1, p_aabb);
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::set_static(ID p_id, bool p_static) {
|
||||
ERR_FAIL_COND(!p_id);
|
||||
uint32_t tree_id = p_static ? TREE_STATIC : TREE_DYNAMIC;
|
||||
uint32_t tree_collision_mask = p_static ? TREE_FLAG_DYNAMIC : (TREE_FLAG_STATIC | TREE_FLAG_DYNAMIC);
|
||||
bvh.set_tree(p_id - 1, tree_id, tree_collision_mask, false);
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::remove(ID p_id) {
|
||||
ERR_FAIL_COND(!p_id);
|
||||
bvh.erase(p_id - 1);
|
||||
}
|
||||
|
||||
GodotCollisionObject2D *GodotBroadPhase2DBVH::get_object(ID p_id) const {
|
||||
ERR_FAIL_COND_V(!p_id, nullptr);
|
||||
GodotCollisionObject2D *it = bvh.get(p_id - 1);
|
||||
ERR_FAIL_NULL_V(it, nullptr);
|
||||
return it;
|
||||
}
|
||||
|
||||
bool GodotBroadPhase2DBVH::is_static(ID p_id) const {
|
||||
ERR_FAIL_COND_V(!p_id, false);
|
||||
uint32_t tree_id = bvh.get_tree_id(p_id - 1);
|
||||
return tree_id == 0;
|
||||
}
|
||||
|
||||
int GodotBroadPhase2DBVH::get_subindex(ID p_id) const {
|
||||
ERR_FAIL_COND_V(!p_id, 0);
|
||||
return bvh.get_subindex(p_id - 1);
|
||||
}
|
||||
|
||||
int GodotBroadPhase2DBVH::cull_segment(const Vector2 &p_from, const Vector2 &p_to, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices) {
|
||||
return bvh.cull_segment(p_from, p_to, p_results, p_max_results, nullptr, 0xFFFFFFFF, p_result_indices);
|
||||
}
|
||||
|
||||
int GodotBroadPhase2DBVH::cull_aabb(const Rect2 &p_aabb, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices) {
|
||||
return bvh.cull_aabb(p_aabb, p_results, p_max_results, nullptr, 0xFFFFFFFF, p_result_indices);
|
||||
}
|
||||
|
||||
void *GodotBroadPhase2DBVH::_pair_callback(void *self, uint32_t p_A, GodotCollisionObject2D *p_object_A, int subindex_A, uint32_t p_B, GodotCollisionObject2D *p_object_B, int subindex_B) {
|
||||
GodotBroadPhase2DBVH *bpo = static_cast<GodotBroadPhase2DBVH *>(self);
|
||||
if (!bpo->pair_callback) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return bpo->pair_callback(p_object_A, subindex_A, p_object_B, subindex_B, bpo->pair_userdata);
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::_unpair_callback(void *self, uint32_t p_A, GodotCollisionObject2D *p_object_A, int subindex_A, uint32_t p_B, GodotCollisionObject2D *p_object_B, int subindex_B, void *pairdata) {
|
||||
GodotBroadPhase2DBVH *bpo = static_cast<GodotBroadPhase2DBVH *>(self);
|
||||
if (!bpo->unpair_callback) {
|
||||
return;
|
||||
}
|
||||
|
||||
bpo->unpair_callback(p_object_A, subindex_A, p_object_B, subindex_B, pairdata, bpo->unpair_userdata);
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::set_pair_callback(PairCallback p_pair_callback, void *p_userdata) {
|
||||
pair_callback = p_pair_callback;
|
||||
pair_userdata = p_userdata;
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::set_unpair_callback(UnpairCallback p_unpair_callback, void *p_userdata) {
|
||||
unpair_callback = p_unpair_callback;
|
||||
unpair_userdata = p_userdata;
|
||||
}
|
||||
|
||||
void GodotBroadPhase2DBVH::update() {
|
||||
bvh.update();
|
||||
}
|
||||
|
||||
GodotBroadPhase2D *GodotBroadPhase2DBVH::_create() {
|
||||
return memnew(GodotBroadPhase2DBVH);
|
||||
}
|
||||
|
||||
GodotBroadPhase2DBVH::GodotBroadPhase2DBVH() {
|
||||
bvh.set_pair_callback(_pair_callback, this);
|
||||
bvh.set_unpair_callback(_unpair_callback, this);
|
||||
}
|
98
modules/godot_physics_2d/godot_broad_phase_2d_bvh.h
Normal file
98
modules/godot_physics_2d/godot_broad_phase_2d_bvh.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/**************************************************************************/
|
||||
/* godot_broad_phase_2d_bvh.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_broad_phase_2d.h"
|
||||
|
||||
#include "core/math/bvh.h"
|
||||
#include "core/math/rect2.h"
|
||||
#include "core/math/vector2.h"
|
||||
|
||||
class GodotBroadPhase2DBVH : public GodotBroadPhase2D {
|
||||
template <typename T>
|
||||
class UserPairTestFunction {
|
||||
public:
|
||||
static bool user_pair_check(const T *p_a, const T *p_b) {
|
||||
// return false if no collision, decided by masks etc
|
||||
return p_a->interacts_with(p_b);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class UserCullTestFunction {
|
||||
public:
|
||||
static bool user_cull_check(const T *p_a, const T *p_b) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
enum Tree {
|
||||
TREE_STATIC = 0,
|
||||
TREE_DYNAMIC = 1,
|
||||
};
|
||||
|
||||
enum TreeFlag {
|
||||
TREE_FLAG_STATIC = 1 << TREE_STATIC,
|
||||
TREE_FLAG_DYNAMIC = 1 << TREE_DYNAMIC,
|
||||
};
|
||||
|
||||
BVH_Manager<GodotCollisionObject2D, 2, true, 128, UserPairTestFunction<GodotCollisionObject2D>, UserCullTestFunction<GodotCollisionObject2D>, Rect2, Vector2> bvh;
|
||||
|
||||
static void *_pair_callback(void *, uint32_t, GodotCollisionObject2D *, int, uint32_t, GodotCollisionObject2D *, int);
|
||||
static void _unpair_callback(void *, uint32_t, GodotCollisionObject2D *, int, uint32_t, GodotCollisionObject2D *, int, void *);
|
||||
|
||||
PairCallback pair_callback = nullptr;
|
||||
void *pair_userdata = nullptr;
|
||||
UnpairCallback unpair_callback = nullptr;
|
||||
void *unpair_userdata = nullptr;
|
||||
|
||||
public:
|
||||
// 0 is an invalid ID
|
||||
virtual ID create(GodotCollisionObject2D *p_object, int p_subindex = 0, const Rect2 &p_aabb = Rect2(), bool p_static = false) override;
|
||||
virtual void move(ID p_id, const Rect2 &p_aabb) override;
|
||||
virtual void set_static(ID p_id, bool p_static) override;
|
||||
virtual void remove(ID p_id) override;
|
||||
|
||||
virtual GodotCollisionObject2D *get_object(ID p_id) const override;
|
||||
virtual bool is_static(ID p_id) const override;
|
||||
virtual int get_subindex(ID p_id) const override;
|
||||
|
||||
virtual int cull_segment(const Vector2 &p_from, const Vector2 &p_to, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices = nullptr) override;
|
||||
virtual int cull_aabb(const Rect2 &p_aabb, GodotCollisionObject2D **p_results, int p_max_results, int *p_result_indices = nullptr) override;
|
||||
|
||||
virtual void set_pair_callback(PairCallback p_pair_callback, void *p_userdata) override;
|
||||
virtual void set_unpair_callback(UnpairCallback p_unpair_callback, void *p_userdata) override;
|
||||
|
||||
virtual void update() override;
|
||||
|
||||
static GodotBroadPhase2D *_create();
|
||||
GodotBroadPhase2DBVH();
|
||||
};
|
244
modules/godot_physics_2d/godot_collision_object_2d.cpp
Normal file
244
modules/godot_physics_2d/godot_collision_object_2d.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
/**************************************************************************/
|
||||
/* godot_collision_object_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_collision_object_2d.h"
|
||||
#include "godot_physics_server_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
void GodotCollisionObject2D::add_shape(GodotShape2D *p_shape, const Transform2D &p_transform, bool p_disabled) {
|
||||
Shape s;
|
||||
s.shape = p_shape;
|
||||
s.xform = p_transform;
|
||||
s.xform_inv = s.xform.affine_inverse();
|
||||
s.bpid = 0; //needs update
|
||||
s.disabled = p_disabled;
|
||||
s.one_way_collision = false;
|
||||
s.one_way_collision_margin = 0;
|
||||
shapes.push_back(s);
|
||||
p_shape->add_owner(this);
|
||||
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::set_shape(int p_index, GodotShape2D *p_shape) {
|
||||
ERR_FAIL_INDEX(p_index, shapes.size());
|
||||
shapes[p_index].shape->remove_owner(this);
|
||||
shapes.write[p_index].shape = p_shape;
|
||||
|
||||
p_shape->add_owner(this);
|
||||
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::set_shape_transform(int p_index, const Transform2D &p_transform) {
|
||||
ERR_FAIL_INDEX(p_index, shapes.size());
|
||||
|
||||
shapes.write[p_index].xform = p_transform;
|
||||
shapes.write[p_index].xform_inv = p_transform.affine_inverse();
|
||||
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::set_shape_disabled(int p_idx, bool p_disabled) {
|
||||
ERR_FAIL_INDEX(p_idx, shapes.size());
|
||||
|
||||
GodotCollisionObject2D::Shape &shape = shapes.write[p_idx];
|
||||
if (shape.disabled == p_disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
shape.disabled = p_disabled;
|
||||
|
||||
if (!space) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_disabled && shape.bpid != 0) {
|
||||
space->get_broadphase()->remove(shape.bpid);
|
||||
shape.bpid = 0;
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
} else if (!p_disabled && shape.bpid == 0) {
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::remove_shape(GodotShape2D *p_shape) {
|
||||
//remove a shape, all the times it appears
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
if (shapes[i].shape == p_shape) {
|
||||
remove_shape(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::remove_shape(int p_index) {
|
||||
//remove anything from shape to be erased to end, so subindices don't change
|
||||
ERR_FAIL_INDEX(p_index, shapes.size());
|
||||
for (int i = p_index; i < shapes.size(); i++) {
|
||||
if (shapes[i].bpid == 0) {
|
||||
continue;
|
||||
}
|
||||
//should never get here with a null owner
|
||||
space->get_broadphase()->remove(shapes[i].bpid);
|
||||
shapes.write[i].bpid = 0;
|
||||
}
|
||||
shapes[p_index].shape->remove_owner(this);
|
||||
shapes.remove_at(p_index);
|
||||
|
||||
if (!pending_shape_update_list.in_list()) {
|
||||
GodotPhysicsServer2D::godot_singleton->pending_shape_update_list.add(&pending_shape_update_list);
|
||||
}
|
||||
// _update_shapes();
|
||||
// _shapes_changed();
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_set_static(bool p_static) {
|
||||
if (_static == p_static) {
|
||||
return;
|
||||
}
|
||||
_static = p_static;
|
||||
|
||||
if (!space) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < get_shape_count(); i++) {
|
||||
const Shape &s = shapes[i];
|
||||
if (s.bpid > 0) {
|
||||
space->get_broadphase()->set_static(s.bpid, _static);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_unregister_shapes() {
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Shape &s = shapes.write[i];
|
||||
if (s.bpid > 0) {
|
||||
space->get_broadphase()->remove(s.bpid);
|
||||
s.bpid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_update_shapes() {
|
||||
if (!space) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Shape &s = shapes.write[i];
|
||||
if (s.disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//not quite correct, should compute the next matrix..
|
||||
Rect2 shape_aabb = s.shape->get_aabb();
|
||||
Transform2D xform = transform * s.xform;
|
||||
shape_aabb = xform.xform(shape_aabb);
|
||||
shape_aabb.grow_by((s.aabb_cache.size.x + s.aabb_cache.size.y) * 0.5 * 0.05);
|
||||
s.aabb_cache = shape_aabb;
|
||||
|
||||
if (s.bpid == 0) {
|
||||
s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
|
||||
space->get_broadphase()->set_static(s.bpid, _static);
|
||||
}
|
||||
|
||||
space->get_broadphase()->move(s.bpid, shape_aabb);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_update_shapes_with_motion(const Vector2 &p_motion) {
|
||||
if (!space) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Shape &s = shapes.write[i];
|
||||
if (s.disabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//not quite correct, should compute the next matrix..
|
||||
Rect2 shape_aabb = s.shape->get_aabb();
|
||||
Transform2D xform = transform * s.xform;
|
||||
shape_aabb = xform.xform(shape_aabb);
|
||||
shape_aabb = shape_aabb.merge(Rect2(shape_aabb.position + p_motion, shape_aabb.size)); //use motion
|
||||
s.aabb_cache = shape_aabb;
|
||||
|
||||
if (s.bpid == 0) {
|
||||
s.bpid = space->get_broadphase()->create(this, i, shape_aabb, _static);
|
||||
space->get_broadphase()->set_static(s.bpid, _static);
|
||||
}
|
||||
|
||||
space->get_broadphase()->move(s.bpid, shape_aabb);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_set_space(GodotSpace2D *p_space) {
|
||||
GodotSpace2D *old_space = space;
|
||||
space = p_space;
|
||||
|
||||
if (old_space) {
|
||||
old_space->remove_object(this);
|
||||
|
||||
for (int i = 0; i < shapes.size(); i++) {
|
||||
Shape &s = shapes.write[i];
|
||||
if (s.bpid) {
|
||||
old_space->get_broadphase()->remove(s.bpid);
|
||||
s.bpid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (space) {
|
||||
space->add_object(this);
|
||||
_update_shapes();
|
||||
}
|
||||
}
|
||||
|
||||
void GodotCollisionObject2D::_shape_changed() {
|
||||
_update_shapes();
|
||||
_shapes_changed();
|
||||
}
|
||||
|
||||
GodotCollisionObject2D::GodotCollisionObject2D(Type p_type) :
|
||||
pending_shape_update_list(this) {
|
||||
type = p_type;
|
||||
}
|
195
modules/godot_physics_2d/godot_collision_object_2d.h
Normal file
195
modules/godot_physics_2d/godot_collision_object_2d.h
Normal file
@@ -0,0 +1,195 @@
|
||||
/**************************************************************************/
|
||||
/* godot_collision_object_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_broad_phase_2d.h"
|
||||
#include "godot_shape_2d.h"
|
||||
|
||||
#include "core/templates/self_list.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class GodotSpace2D;
|
||||
|
||||
class GodotCollisionObject2D : public GodotShapeOwner2D {
|
||||
public:
|
||||
enum Type {
|
||||
TYPE_AREA,
|
||||
TYPE_BODY
|
||||
};
|
||||
|
||||
private:
|
||||
Type type;
|
||||
RID self;
|
||||
ObjectID instance_id;
|
||||
ObjectID canvas_instance_id;
|
||||
bool pickable = true;
|
||||
|
||||
struct Shape {
|
||||
Transform2D xform;
|
||||
Transform2D xform_inv;
|
||||
GodotBroadPhase2D::ID bpid = 0;
|
||||
Rect2 aabb_cache; //for rayqueries
|
||||
GodotShape2D *shape = nullptr;
|
||||
bool disabled = false;
|
||||
bool one_way_collision = false;
|
||||
real_t one_way_collision_margin = 0.0;
|
||||
};
|
||||
|
||||
Vector<Shape> shapes;
|
||||
GodotSpace2D *space = nullptr;
|
||||
Transform2D transform;
|
||||
Transform2D inv_transform;
|
||||
uint32_t collision_mask = 1;
|
||||
uint32_t collision_layer = 1;
|
||||
real_t collision_priority = 1.0;
|
||||
bool _static = true;
|
||||
|
||||
SelfList<GodotCollisionObject2D> pending_shape_update_list;
|
||||
|
||||
void _update_shapes();
|
||||
|
||||
protected:
|
||||
void _update_shapes_with_motion(const Vector2 &p_motion);
|
||||
void _unregister_shapes();
|
||||
|
||||
_FORCE_INLINE_ void _set_transform(const Transform2D &p_transform, bool p_update_shapes = true) {
|
||||
transform = p_transform;
|
||||
if (p_update_shapes) {
|
||||
_update_shapes();
|
||||
}
|
||||
}
|
||||
_FORCE_INLINE_ void _set_inv_transform(const Transform2D &p_transform) { inv_transform = p_transform; }
|
||||
void _set_static(bool p_static);
|
||||
|
||||
virtual void _shapes_changed() = 0;
|
||||
void _set_space(GodotSpace2D *p_space);
|
||||
|
||||
GodotCollisionObject2D(Type p_type);
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
|
||||
_FORCE_INLINE_ RID get_self() const { return self; }
|
||||
|
||||
_FORCE_INLINE_ void set_instance_id(const ObjectID &p_instance_id) { instance_id = p_instance_id; }
|
||||
_FORCE_INLINE_ ObjectID get_instance_id() const { return instance_id; }
|
||||
|
||||
_FORCE_INLINE_ void set_canvas_instance_id(const ObjectID &p_canvas_instance_id) { canvas_instance_id = p_canvas_instance_id; }
|
||||
_FORCE_INLINE_ ObjectID get_canvas_instance_id() const { return canvas_instance_id; }
|
||||
|
||||
void _shape_changed() override;
|
||||
|
||||
_FORCE_INLINE_ Type get_type() const { return type; }
|
||||
void add_shape(GodotShape2D *p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false);
|
||||
void set_shape(int p_index, GodotShape2D *p_shape);
|
||||
void set_shape_transform(int p_index, const Transform2D &p_transform);
|
||||
|
||||
_FORCE_INLINE_ int get_shape_count() const { return shapes.size(); }
|
||||
_FORCE_INLINE_ GodotShape2D *get_shape(int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, shapes.size());
|
||||
return shapes[p_index].shape;
|
||||
}
|
||||
_FORCE_INLINE_ const Transform2D &get_shape_transform(int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, shapes.size());
|
||||
return shapes[p_index].xform;
|
||||
}
|
||||
_FORCE_INLINE_ const Transform2D &get_shape_inv_transform(int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, shapes.size());
|
||||
return shapes[p_index].xform_inv;
|
||||
}
|
||||
_FORCE_INLINE_ const Rect2 &get_shape_aabb(int p_index) const {
|
||||
CRASH_BAD_INDEX(p_index, shapes.size());
|
||||
return shapes[p_index].aabb_cache;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ const Transform2D &get_transform() const { return transform; }
|
||||
_FORCE_INLINE_ const Transform2D &get_inv_transform() const { return inv_transform; }
|
||||
_FORCE_INLINE_ GodotSpace2D *get_space() const { return space; }
|
||||
|
||||
void set_shape_disabled(int p_idx, bool p_disabled);
|
||||
_FORCE_INLINE_ bool is_shape_disabled(int p_idx) const {
|
||||
ERR_FAIL_INDEX_V(p_idx, shapes.size(), false);
|
||||
return shapes[p_idx].disabled;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void set_shape_as_one_way_collision(int p_idx, bool p_one_way_collision, real_t p_margin) {
|
||||
CRASH_BAD_INDEX(p_idx, shapes.size());
|
||||
shapes.write[p_idx].one_way_collision = p_one_way_collision;
|
||||
shapes.write[p_idx].one_way_collision_margin = p_margin;
|
||||
}
|
||||
_FORCE_INLINE_ bool is_shape_set_as_one_way_collision(int p_idx) const {
|
||||
CRASH_BAD_INDEX(p_idx, shapes.size());
|
||||
return shapes[p_idx].one_way_collision;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ real_t get_shape_one_way_collision_margin(int p_idx) const {
|
||||
CRASH_BAD_INDEX(p_idx, shapes.size());
|
||||
return shapes[p_idx].one_way_collision_margin;
|
||||
}
|
||||
|
||||
void set_collision_mask(uint32_t p_mask) {
|
||||
collision_mask = p_mask;
|
||||
_shape_changed();
|
||||
}
|
||||
_FORCE_INLINE_ uint32_t get_collision_mask() const { return collision_mask; }
|
||||
|
||||
void set_collision_layer(uint32_t p_layer) {
|
||||
collision_layer = p_layer;
|
||||
_shape_changed();
|
||||
}
|
||||
_FORCE_INLINE_ uint32_t get_collision_layer() const { return collision_layer; }
|
||||
|
||||
_FORCE_INLINE_ void set_collision_priority(real_t p_priority) {
|
||||
ERR_FAIL_COND_MSG(p_priority <= 0, "Priority must be greater than 0.");
|
||||
collision_priority = p_priority;
|
||||
_shape_changed();
|
||||
}
|
||||
_FORCE_INLINE_ real_t get_collision_priority() const { return collision_priority; }
|
||||
|
||||
void remove_shape(GodotShape2D *p_shape) override;
|
||||
void remove_shape(int p_index);
|
||||
|
||||
virtual void set_space(GodotSpace2D *p_space) = 0;
|
||||
|
||||
_FORCE_INLINE_ bool is_static() const { return _static; }
|
||||
|
||||
void set_pickable(bool p_pickable) { pickable = p_pickable; }
|
||||
_FORCE_INLINE_ bool is_pickable() const { return pickable; }
|
||||
|
||||
_FORCE_INLINE_ bool collides_with(GodotCollisionObject2D *p_other) const {
|
||||
return p_other->collision_layer & collision_mask;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool interacts_with(const GodotCollisionObject2D *p_other) const {
|
||||
return collision_layer & p_other->collision_mask || p_other->collision_layer & collision_mask;
|
||||
}
|
||||
|
||||
virtual ~GodotCollisionObject2D() {}
|
||||
};
|
274
modules/godot_physics_2d/godot_collision_solver_2d.cpp
Normal file
274
modules/godot_physics_2d/godot_collision_solver_2d.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/**************************************************************************/
|
||||
/* godot_collision_solver_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_collision_solver_2d.h"
|
||||
#include "godot_collision_solver_2d_sat.h"
|
||||
|
||||
#define collision_solver sat_2d_calculate_penetration
|
||||
//#define collision_solver gjk_epa_calculate_penetration
|
||||
|
||||
bool GodotCollisionSolver2D::solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin) {
|
||||
const GodotWorldBoundaryShape2D *world_boundary = static_cast<const GodotWorldBoundaryShape2D *>(p_shape_A);
|
||||
if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_WORLD_BOUNDARY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 n = p_transform_A.basis_xform(world_boundary->get_normal()).normalized();
|
||||
Vector2 p = p_transform_A.xform(world_boundary->get_normal() * world_boundary->get_d());
|
||||
real_t d = n.dot(p);
|
||||
|
||||
Vector2 supports[2];
|
||||
int support_count;
|
||||
|
||||
p_shape_B->get_supports(p_transform_B.affine_inverse().basis_xform(-n).normalized(), supports, support_count);
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (int i = 0; i < support_count; i++) {
|
||||
supports[i] += p_margin * supports[i].normalized();
|
||||
supports[i] = p_transform_B.xform(supports[i]);
|
||||
supports[i] += p_motion_B;
|
||||
real_t pd = n.dot(supports[i]);
|
||||
if (pd >= d) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
|
||||
Vector2 support_A = supports[i] - n * (pd - d);
|
||||
|
||||
if (p_result_callback) {
|
||||
if (p_swap_result) {
|
||||
p_result_callback(supports[i], support_A, p_userdata);
|
||||
} else {
|
||||
p_result_callback(support_A, supports[i], p_userdata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
bool GodotCollisionSolver2D::solve_separation_ray(const GodotShape2D *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin) {
|
||||
const GodotSeparationRayShape2D *ray = static_cast<const GodotSeparationRayShape2D *>(p_shape_A);
|
||||
if (p_shape_B->get_type() == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 from = p_transform_A.get_origin();
|
||||
Vector2 to = from + p_transform_A[1] * (ray->get_length() + p_margin);
|
||||
if (p_motion_A != Vector2()) {
|
||||
//not the best but should be enough
|
||||
Vector2 normal = (to - from).normalized();
|
||||
to += normal * MAX(0.0, normal.dot(p_motion_A));
|
||||
}
|
||||
Vector2 support_A = to;
|
||||
|
||||
Transform2D invb = p_transform_B.affine_inverse();
|
||||
from = invb.xform(from);
|
||||
to = invb.xform(to);
|
||||
|
||||
Vector2 p, n;
|
||||
if (!p_shape_B->intersect_segment(from, to, p, n)) {
|
||||
if (r_sep_axis) {
|
||||
*r_sep_axis = p_transform_A[1].normalized();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Discard contacts when the ray is fully contained inside the shape.
|
||||
if (n == Vector2()) {
|
||||
if (r_sep_axis) {
|
||||
*r_sep_axis = p_transform_A[1].normalized();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Discard contacts in the wrong direction.
|
||||
if (n.dot(from - to) < CMP_EPSILON) {
|
||||
if (r_sep_axis) {
|
||||
*r_sep_axis = p_transform_A[1].normalized();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector2 support_B = p_transform_B.xform(p);
|
||||
if (ray->get_slide_on_slope()) {
|
||||
Vector2 global_n = invb.basis_xform_inv(n).normalized();
|
||||
support_B = support_A + (support_B - support_A).length() * global_n;
|
||||
}
|
||||
|
||||
if (p_result_callback) {
|
||||
if (p_swap_result) {
|
||||
p_result_callback(support_B, support_A, p_userdata);
|
||||
} else {
|
||||
p_result_callback(support_A, support_B, p_userdata);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct _ConcaveCollisionInfo2D {
|
||||
const Transform2D *transform_A = nullptr;
|
||||
const GodotShape2D *shape_A = nullptr;
|
||||
const Transform2D *transform_B = nullptr;
|
||||
Vector2 motion_A;
|
||||
Vector2 motion_B;
|
||||
real_t margin_A = 0.0;
|
||||
real_t margin_B = 0.0;
|
||||
GodotCollisionSolver2D::CallbackResult result_callback = nullptr;
|
||||
void *userdata = nullptr;
|
||||
bool swap_result = false;
|
||||
bool collided = false;
|
||||
int aabb_tests = 0;
|
||||
int collisions = 0;
|
||||
Vector2 *sep_axis = nullptr;
|
||||
};
|
||||
|
||||
bool GodotCollisionSolver2D::concave_callback(void *p_userdata, GodotShape2D *p_convex) {
|
||||
_ConcaveCollisionInfo2D &cinfo = *(static_cast<_ConcaveCollisionInfo2D *>(p_userdata));
|
||||
cinfo.aabb_tests++;
|
||||
|
||||
bool collided = collision_solver(cinfo.shape_A, *cinfo.transform_A, cinfo.motion_A, p_convex, *cinfo.transform_B, cinfo.motion_B, cinfo.result_callback, cinfo.userdata, cinfo.swap_result, cinfo.sep_axis, cinfo.margin_A, cinfo.margin_B);
|
||||
if (!collided) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cinfo.collided = true;
|
||||
cinfo.collisions++;
|
||||
|
||||
// Stop at first collision if contacts are not needed.
|
||||
return !cinfo.result_callback;
|
||||
}
|
||||
|
||||
bool GodotCollisionSolver2D::solve_concave(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) {
|
||||
const GodotConcaveShape2D *concave_B = static_cast<const GodotConcaveShape2D *>(p_shape_B);
|
||||
|
||||
_ConcaveCollisionInfo2D cinfo;
|
||||
cinfo.transform_A = &p_transform_A;
|
||||
cinfo.shape_A = p_shape_A;
|
||||
cinfo.transform_B = &p_transform_B;
|
||||
cinfo.motion_A = p_motion_A;
|
||||
cinfo.result_callback = p_result_callback;
|
||||
cinfo.userdata = p_userdata;
|
||||
cinfo.swap_result = p_swap_result;
|
||||
cinfo.collided = false;
|
||||
cinfo.collisions = 0;
|
||||
cinfo.sep_axis = r_sep_axis;
|
||||
cinfo.margin_A = p_margin_A;
|
||||
cinfo.margin_B = p_margin_B;
|
||||
|
||||
cinfo.aabb_tests = 0;
|
||||
|
||||
Transform2D rel_transform = p_transform_A;
|
||||
rel_transform.columns[2] -= p_transform_B.get_origin();
|
||||
|
||||
// Quickly compute a local Rect2.
|
||||
Rect2 local_aabb;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Vector2 axis(p_transform_B.columns[i]);
|
||||
real_t axis_scale = 1.0 / axis.length();
|
||||
axis *= axis_scale;
|
||||
|
||||
real_t smin = 0.0, smax = 0.0;
|
||||
p_shape_A->project_rangev(axis, rel_transform, smin, smax);
|
||||
smin *= axis_scale;
|
||||
smax *= axis_scale;
|
||||
|
||||
local_aabb.position[i] = smin;
|
||||
local_aabb.size[i] = smax - smin;
|
||||
}
|
||||
// In case of motion, expand the Rect2 in the motion direction.
|
||||
if (p_motion_A != Vector2()) {
|
||||
Rect2 moved_aabb = local_aabb;
|
||||
moved_aabb.position += p_motion_A;
|
||||
local_aabb = local_aabb.merge(moved_aabb);
|
||||
}
|
||||
|
||||
concave_B->cull(local_aabb, concave_callback, &cinfo);
|
||||
|
||||
return cinfo.collided;
|
||||
}
|
||||
|
||||
bool GodotCollisionSolver2D::solve(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis, real_t p_margin_A, real_t p_margin_B) {
|
||||
PhysicsServer2D::ShapeType type_A = p_shape_A->get_type();
|
||||
PhysicsServer2D::ShapeType type_B = p_shape_B->get_type();
|
||||
bool concave_A = p_shape_A->is_concave();
|
||||
bool concave_B = p_shape_B->is_concave();
|
||||
real_t margin_A = p_margin_A, margin_B = p_margin_B;
|
||||
|
||||
bool swap = false;
|
||||
|
||||
if (type_A > type_B) {
|
||||
SWAP(type_A, type_B);
|
||||
SWAP(concave_A, concave_B);
|
||||
SWAP(margin_A, margin_B);
|
||||
swap = true;
|
||||
}
|
||||
|
||||
if (type_A == PhysicsServer2D::SHAPE_WORLD_BOUNDARY) {
|
||||
if (type_B == PhysicsServer2D::SHAPE_WORLD_BOUNDARY) {
|
||||
WARN_PRINT_ONCE("Collisions between world boundaries are not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
return solve_static_world_boundary(p_shape_B, p_transform_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, p_margin_A);
|
||||
} else {
|
||||
return solve_static_world_boundary(p_shape_A, p_transform_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, p_margin_B);
|
||||
}
|
||||
|
||||
} else if (type_A == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
|
||||
if (type_B == PhysicsServer2D::SHAPE_SEPARATION_RAY) {
|
||||
WARN_PRINT_ONCE("Collisions between two rays are not supported.");
|
||||
return false; //no ray-ray
|
||||
}
|
||||
|
||||
if (swap) {
|
||||
return solve_separation_ray(p_shape_B, p_motion_B, p_transform_B, p_shape_A, p_transform_A, p_result_callback, p_userdata, true, r_sep_axis, p_margin_B);
|
||||
} else {
|
||||
return solve_separation_ray(p_shape_A, p_motion_A, p_transform_A, p_shape_B, p_transform_B, p_result_callback, p_userdata, false, r_sep_axis, p_margin_A);
|
||||
}
|
||||
|
||||
} else if (concave_B) {
|
||||
if (concave_A) {
|
||||
WARN_PRINT_ONCE("Collisions between two concave shapes are not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!swap) {
|
||||
return solve_concave(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B);
|
||||
} else {
|
||||
return solve_concave(p_shape_B, p_transform_B, p_motion_B, p_shape_A, p_transform_A, p_motion_A, p_result_callback, p_userdata, true, r_sep_axis, margin_A, margin_B);
|
||||
}
|
||||
|
||||
} else {
|
||||
return collision_solver(p_shape_A, p_transform_A, p_motion_A, p_shape_B, p_transform_B, p_motion_B, p_result_callback, p_userdata, false, r_sep_axis, margin_A, margin_B);
|
||||
}
|
||||
}
|
47
modules/godot_physics_2d/godot_collision_solver_2d.h
Normal file
47
modules/godot_physics_2d/godot_collision_solver_2d.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/**************************************************************************/
|
||||
/* godot_collision_solver_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_shape_2d.h"
|
||||
|
||||
class GodotCollisionSolver2D {
|
||||
public:
|
||||
typedef void (*CallbackResult)(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata);
|
||||
|
||||
private:
|
||||
static bool solve_static_world_boundary(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, real_t p_margin = 0);
|
||||
static bool concave_callback(void *p_userdata, GodotShape2D *p_convex);
|
||||
static bool solve_concave(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
|
||||
static bool solve_separation_ray(const GodotShape2D *p_shape_A, const Vector2 &p_motion_A, const Transform2D &p_transform_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, CallbackResult p_result_callback, void *p_userdata, bool p_swap_result, Vector2 *r_sep_axis = nullptr, real_t p_margin = 0);
|
||||
|
||||
public:
|
||||
static bool solve(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, CallbackResult p_result_callback, void *p_userdata, Vector2 *r_sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
|
||||
};
|
1404
modules/godot_physics_2d/godot_collision_solver_2d_sat.cpp
Normal file
1404
modules/godot_physics_2d/godot_collision_solver_2d_sat.cpp
Normal file
File diff suppressed because it is too large
Load Diff
35
modules/godot_physics_2d/godot_collision_solver_2d_sat.h
Normal file
35
modules/godot_physics_2d/godot_collision_solver_2d_sat.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/**************************************************************************/
|
||||
/* godot_collision_solver_2d_sat.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_collision_solver_2d.h"
|
||||
|
||||
bool sat_2d_calculate_penetration(const GodotShape2D *p_shape_A, const Transform2D &p_transform_A, const Vector2 &p_motion_A, const GodotShape2D *p_shape_B, const Transform2D &p_transform_B, const Vector2 &p_motion_B, GodotCollisionSolver2D::CallbackResult p_result_callback, void *p_userdata, bool p_swap = false, Vector2 *sep_axis = nullptr, real_t p_margin_A = 0, real_t p_margin_B = 0);
|
67
modules/godot_physics_2d/godot_constraint_2d.h
Normal file
67
modules/godot_physics_2d/godot_constraint_2d.h
Normal file
@@ -0,0 +1,67 @@
|
||||
/**************************************************************************/
|
||||
/* godot_constraint_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_body_2d.h"
|
||||
|
||||
class GodotConstraint2D {
|
||||
GodotBody2D **_body_ptr;
|
||||
int _body_count;
|
||||
uint64_t island_step = 0;
|
||||
bool disabled_collisions_between_bodies = true;
|
||||
|
||||
RID self;
|
||||
|
||||
protected:
|
||||
GodotConstraint2D(GodotBody2D **p_body_ptr = nullptr, int p_body_count = 0) {
|
||||
_body_ptr = p_body_ptr;
|
||||
_body_count = p_body_count;
|
||||
}
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
|
||||
_FORCE_INLINE_ RID get_self() const { return self; }
|
||||
|
||||
_FORCE_INLINE_ uint64_t get_island_step() const { return island_step; }
|
||||
_FORCE_INLINE_ void set_island_step(uint64_t p_step) { island_step = p_step; }
|
||||
|
||||
_FORCE_INLINE_ GodotBody2D **get_body_ptr() const { return _body_ptr; }
|
||||
_FORCE_INLINE_ int get_body_count() const { return _body_count; }
|
||||
|
||||
_FORCE_INLINE_ void disable_collisions_between_bodies(const bool p_disabled) { disabled_collisions_between_bodies = p_disabled; }
|
||||
_FORCE_INLINE_ bool is_disabled_collisions_between_bodies() const { return disabled_collisions_between_bodies; }
|
||||
|
||||
virtual bool setup(real_t p_step) = 0;
|
||||
virtual bool pre_solve(real_t p_step) = 0;
|
||||
virtual void solve(real_t p_step) = 0;
|
||||
|
||||
virtual ~GodotConstraint2D() {}
|
||||
};
|
595
modules/godot_physics_2d/godot_joints_2d.cpp
Normal file
595
modules/godot_physics_2d/godot_joints_2d.cpp
Normal file
@@ -0,0 +1,595 @@
|
||||
/**************************************************************************/
|
||||
/* godot_joints_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_joints_2d.h"
|
||||
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
//based on chipmunk joint constraints
|
||||
|
||||
/* Copyright (c) 2007 Scott Lembcke
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
void GodotJoint2D::copy_settings_from(GodotJoint2D *p_joint) {
|
||||
set_self(p_joint->get_self());
|
||||
set_max_force(p_joint->get_max_force());
|
||||
set_bias(p_joint->get_bias());
|
||||
set_max_bias(p_joint->get_max_bias());
|
||||
disable_collisions_between_bodies(p_joint->is_disabled_collisions_between_bodies());
|
||||
}
|
||||
|
||||
static inline real_t k_scalar(GodotBody2D *a, GodotBody2D *b, const Vector2 &rA, const Vector2 &rB, const Vector2 &n) {
|
||||
real_t value = 0.0;
|
||||
|
||||
{
|
||||
value += a->get_inv_mass();
|
||||
real_t rcn = (rA - a->get_center_of_mass()).cross(n);
|
||||
value += a->get_inv_inertia() * rcn * rcn;
|
||||
}
|
||||
|
||||
if (b) {
|
||||
value += b->get_inv_mass();
|
||||
real_t rcn = (rB - b->get_center_of_mass()).cross(n);
|
||||
value += b->get_inv_inertia() * rcn * rcn;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline Vector2
|
||||
relative_velocity(GodotBody2D *a, GodotBody2D *b, Vector2 rA, Vector2 rB) {
|
||||
Vector2 sum = a->get_linear_velocity() - (rA - a->get_center_of_mass()).orthogonal() * a->get_angular_velocity();
|
||||
if (b) {
|
||||
return (b->get_linear_velocity() - (rB - b->get_center_of_mass()).orthogonal() * b->get_angular_velocity()) - sum;
|
||||
} else {
|
||||
return -sum;
|
||||
}
|
||||
}
|
||||
|
||||
static inline real_t
|
||||
normal_relative_velocity(GodotBody2D *a, GodotBody2D *b, Vector2 rA, Vector2 rB, Vector2 n) {
|
||||
return relative_velocity(a, b, rA, rB).dot(n);
|
||||
}
|
||||
|
||||
bool GodotPinJoint2D::setup(real_t p_step) {
|
||||
dynamic_A = (A->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
dynamic_B = (B->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
|
||||
if (!dynamic_A && !dynamic_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GodotSpace2D *space = A->get_space();
|
||||
ERR_FAIL_NULL_V(space, false);
|
||||
|
||||
rA = A->get_transform().basis_xform(anchor_A);
|
||||
rB = B ? B->get_transform().basis_xform(anchor_B) : anchor_B;
|
||||
|
||||
real_t B_inv_mass = B ? B->get_inv_mass() : 0.0;
|
||||
|
||||
Transform2D K1;
|
||||
K1[0].x = A->get_inv_mass() + B_inv_mass;
|
||||
K1[1].x = 0.0f;
|
||||
K1[0].y = 0.0f;
|
||||
K1[1].y = A->get_inv_mass() + B_inv_mass;
|
||||
|
||||
Vector2 r1 = rA - A->get_center_of_mass();
|
||||
|
||||
Transform2D K2;
|
||||
K2[0].x = A->get_inv_inertia() * r1.y * r1.y;
|
||||
K2[1].x = -A->get_inv_inertia() * r1.x * r1.y;
|
||||
K2[0].y = -A->get_inv_inertia() * r1.x * r1.y;
|
||||
K2[1].y = A->get_inv_inertia() * r1.x * r1.x;
|
||||
|
||||
Transform2D K;
|
||||
K[0] = K1[0] + K2[0];
|
||||
K[1] = K1[1] + K2[1];
|
||||
|
||||
if (B) {
|
||||
Vector2 r2 = rB - B->get_center_of_mass();
|
||||
|
||||
Transform2D K3;
|
||||
K3[0].x = B->get_inv_inertia() * r2.y * r2.y;
|
||||
K3[1].x = -B->get_inv_inertia() * r2.x * r2.y;
|
||||
K3[0].y = -B->get_inv_inertia() * r2.x * r2.y;
|
||||
K3[1].y = B->get_inv_inertia() * r2.x * r2.x;
|
||||
|
||||
K[0] += K3[0];
|
||||
K[1] += K3[1];
|
||||
}
|
||||
|
||||
K[0].x += softness;
|
||||
K[1].y += softness;
|
||||
|
||||
M = K.affine_inverse();
|
||||
|
||||
Vector2 gA = rA + A->get_transform().get_origin();
|
||||
Vector2 gB = B ? rB + B->get_transform().get_origin() : rB;
|
||||
|
||||
Vector2 delta = gB - gA;
|
||||
|
||||
bias = delta * -(get_bias() == 0 ? space->get_constraint_bias() : get_bias()) * (1.0 / p_step);
|
||||
|
||||
// Compute max impulse.
|
||||
jn_max = get_max_force() * p_step;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline Vector2 custom_cross(const Vector2 &p_vec, real_t p_other) {
|
||||
return Vector2(p_other * p_vec.y, -p_other * p_vec.x);
|
||||
}
|
||||
|
||||
bool GodotPinJoint2D::pre_solve(real_t p_step) {
|
||||
// Apply accumulated impulse.
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-P, rA);
|
||||
}
|
||||
if (B && dynamic_B) {
|
||||
B->apply_impulse(P, rB);
|
||||
}
|
||||
// Angle limits joint pre_solve step taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpRotaryLimitJoint.c
|
||||
real_t i_sum_local = A->get_inv_inertia();
|
||||
if (B) {
|
||||
i_sum_local += B->get_inv_inertia();
|
||||
}
|
||||
i_sum = 1.0 / (i_sum_local);
|
||||
if (angular_limit_enabled && B) {
|
||||
Vector2 diff_vector = B->get_transform().get_origin() - A->get_transform().get_origin();
|
||||
diff_vector = diff_vector.rotated(-initial_angle);
|
||||
real_t dist = diff_vector.angle();
|
||||
real_t pdist = 0.0;
|
||||
if (dist > angular_limit_upper) {
|
||||
pdist = dist - angular_limit_upper;
|
||||
} else if (dist < angular_limit_lower) {
|
||||
pdist = dist - angular_limit_lower;
|
||||
}
|
||||
real_t error_bias = Math::pow(1.0 - 0.15, 60.0);
|
||||
// Calculate bias velocity.
|
||||
bias_velocity = -CLAMP((-1.0 - Math::pow(error_bias, p_step)) * pdist / p_step, -get_max_bias(), get_max_bias());
|
||||
// If the bias velocity is 0, the joint is not at a limit.
|
||||
if (bias_velocity >= -CMP_EPSILON && bias_velocity <= CMP_EPSILON) {
|
||||
j_acc = 0;
|
||||
is_joint_at_limit = false;
|
||||
} else {
|
||||
is_joint_at_limit = true;
|
||||
}
|
||||
} else {
|
||||
bias_velocity = 0.0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GodotPinJoint2D::solve(real_t p_step) {
|
||||
// Compute relative velocity.
|
||||
Vector2 vA = A->get_linear_velocity() - custom_cross(rA - A->get_center_of_mass(), A->get_angular_velocity());
|
||||
|
||||
Vector2 rel_vel;
|
||||
if (B) {
|
||||
rel_vel = B->get_linear_velocity() - custom_cross(rB - B->get_center_of_mass(), B->get_angular_velocity()) - vA;
|
||||
} else {
|
||||
rel_vel = -vA;
|
||||
}
|
||||
// Angle limits joint solve step taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpRotaryLimitJoint.c
|
||||
if ((angular_limit_enabled || motor_enabled) && B) {
|
||||
// Compute relative rotational velocity.
|
||||
real_t wr = B->get_angular_velocity() - A->get_angular_velocity();
|
||||
// Motor solve part taken from https://github.com/slembcke/Chipmunk2D/blob/d0239ef4599b3688a5a336373f7d0a68426414ba/src/cpSimpleMotor.c
|
||||
if (motor_enabled) {
|
||||
wr -= motor_target_velocity;
|
||||
}
|
||||
real_t j_max = jn_max;
|
||||
|
||||
// Compute normal impulse.
|
||||
real_t j = -(bias_velocity + wr) * i_sum;
|
||||
real_t j_old = j_acc;
|
||||
// Only enable the limits if we have to.
|
||||
if (angular_limit_enabled && is_joint_at_limit) {
|
||||
if (bias_velocity < 0.0) {
|
||||
j_acc = CLAMP(j_old + j, 0.0, j_max);
|
||||
} else {
|
||||
j_acc = CLAMP(j_old + j, -j_max, 0.0);
|
||||
}
|
||||
} else {
|
||||
j_acc = CLAMP(j_old + j, -j_max, j_max);
|
||||
}
|
||||
j = j_acc - j_old;
|
||||
A->apply_torque_impulse(-j * A->get_inv_inertia());
|
||||
B->apply_torque_impulse(j * B->get_inv_inertia());
|
||||
}
|
||||
|
||||
Vector2 impulse = M.basis_xform(bias - rel_vel - Vector2(softness, softness) * P);
|
||||
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-impulse, rA);
|
||||
}
|
||||
if (B && dynamic_B) {
|
||||
B->apply_impulse(impulse, rB);
|
||||
}
|
||||
|
||||
P += impulse;
|
||||
}
|
||||
|
||||
void GodotPinJoint2D::set_param(PhysicsServer2D::PinJointParam p_param, real_t p_value) {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::PIN_JOINT_SOFTNESS: {
|
||||
softness = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::PIN_JOINT_LIMIT_UPPER: {
|
||||
angular_limit_upper = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::PIN_JOINT_LIMIT_LOWER: {
|
||||
angular_limit_lower = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::PIN_JOINT_MOTOR_TARGET_VELOCITY: {
|
||||
motor_target_velocity = p_value;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
real_t GodotPinJoint2D::get_param(PhysicsServer2D::PinJointParam p_param) const {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::PIN_JOINT_SOFTNESS: {
|
||||
return softness;
|
||||
}
|
||||
case PhysicsServer2D::PIN_JOINT_LIMIT_UPPER: {
|
||||
return angular_limit_upper;
|
||||
}
|
||||
case PhysicsServer2D::PIN_JOINT_LIMIT_LOWER: {
|
||||
return angular_limit_lower;
|
||||
}
|
||||
case PhysicsServer2D::PIN_JOINT_MOTOR_TARGET_VELOCITY: {
|
||||
return motor_target_velocity;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_V(0);
|
||||
}
|
||||
|
||||
void GodotPinJoint2D::set_flag(PhysicsServer2D::PinJointFlag p_flag, bool p_enabled) {
|
||||
switch (p_flag) {
|
||||
case PhysicsServer2D::PIN_JOINT_FLAG_ANGULAR_LIMIT_ENABLED: {
|
||||
angular_limit_enabled = p_enabled;
|
||||
} break;
|
||||
case PhysicsServer2D::PIN_JOINT_FLAG_MOTOR_ENABLED: {
|
||||
motor_enabled = p_enabled;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotPinJoint2D::get_flag(PhysicsServer2D::PinJointFlag p_flag) const {
|
||||
switch (p_flag) {
|
||||
case PhysicsServer2D::PIN_JOINT_FLAG_ANGULAR_LIMIT_ENABLED: {
|
||||
return angular_limit_enabled;
|
||||
}
|
||||
case PhysicsServer2D::PIN_JOINT_FLAG_MOTOR_ENABLED: {
|
||||
return motor_enabled;
|
||||
}
|
||||
}
|
||||
ERR_FAIL_V(false);
|
||||
}
|
||||
|
||||
GodotPinJoint2D::GodotPinJoint2D(const Vector2 &p_pos, GodotBody2D *p_body_a, GodotBody2D *p_body_b) :
|
||||
GodotJoint2D(_arr, p_body_b ? 2 : 1) {
|
||||
A = p_body_a;
|
||||
B = p_body_b;
|
||||
anchor_A = p_body_a->get_inv_transform().xform(p_pos);
|
||||
anchor_B = p_body_b ? p_body_b->get_inv_transform().xform(p_pos) : p_pos;
|
||||
|
||||
p_body_a->add_constraint(this, 0);
|
||||
if (p_body_b) {
|
||||
p_body_b->add_constraint(this, 1);
|
||||
initial_angle = A->get_transform().get_origin().angle_to_point(B->get_transform().get_origin());
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
|
||||
static inline void
|
||||
k_tensor(GodotBody2D *a, GodotBody2D *b, Vector2 r1, Vector2 r2, Vector2 *k1, Vector2 *k2) {
|
||||
// calculate mass matrix
|
||||
// If I wasn't lazy and wrote a proper matrix class, this wouldn't be so gross...
|
||||
real_t k11, k12, k21, k22;
|
||||
real_t m_sum = a->get_inv_mass() + b->get_inv_mass();
|
||||
|
||||
// start with I*m_sum
|
||||
k11 = m_sum;
|
||||
k12 = 0.0f;
|
||||
k21 = 0.0f;
|
||||
k22 = m_sum;
|
||||
|
||||
r1 -= a->get_center_of_mass();
|
||||
r2 -= b->get_center_of_mass();
|
||||
|
||||
// add the influence from r1
|
||||
real_t a_i_inv = a->get_inv_inertia();
|
||||
real_t r1xsq = r1.x * r1.x * a_i_inv;
|
||||
real_t r1ysq = r1.y * r1.y * a_i_inv;
|
||||
real_t r1nxy = -r1.x * r1.y * a_i_inv;
|
||||
k11 += r1ysq;
|
||||
k12 += r1nxy;
|
||||
k21 += r1nxy;
|
||||
k22 += r1xsq;
|
||||
|
||||
// add the influence from r2
|
||||
real_t b_i_inv = b->get_inv_inertia();
|
||||
real_t r2xsq = r2.x * r2.x * b_i_inv;
|
||||
real_t r2ysq = r2.y * r2.y * b_i_inv;
|
||||
real_t r2nxy = -r2.x * r2.y * b_i_inv;
|
||||
k11 += r2ysq;
|
||||
k12 += r2nxy;
|
||||
k21 += r2nxy;
|
||||
k22 += r2xsq;
|
||||
|
||||
// invert
|
||||
real_t determinant = k11 * k22 - k12 * k21;
|
||||
ERR_FAIL_COND(determinant == 0.0);
|
||||
|
||||
real_t det_inv = 1.0f / determinant;
|
||||
*k1 = Vector2(k22 * det_inv, -k12 * det_inv);
|
||||
*k2 = Vector2(-k21 * det_inv, k11 * det_inv);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ Vector2
|
||||
mult_k(const Vector2 &vr, const Vector2 &k1, const Vector2 &k2) {
|
||||
return Vector2(vr.dot(k1), vr.dot(k2));
|
||||
}
|
||||
|
||||
bool GodotGrooveJoint2D::setup(real_t p_step) {
|
||||
dynamic_A = (A->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
dynamic_B = (B->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
|
||||
if (!dynamic_A && !dynamic_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GodotSpace2D *space = A->get_space();
|
||||
ERR_FAIL_NULL_V(space, false);
|
||||
|
||||
// calculate endpoints in worldspace
|
||||
Vector2 ta = A->get_transform().xform(A_groove_1);
|
||||
Vector2 tb = A->get_transform().xform(A_groove_2);
|
||||
|
||||
// calculate axis
|
||||
Vector2 n = -(tb - ta).orthogonal().normalized();
|
||||
real_t d = ta.dot(n);
|
||||
|
||||
xf_normal = n;
|
||||
rB = B->get_transform().basis_xform(B_anchor);
|
||||
|
||||
// calculate tangential distance along the axis of rB
|
||||
real_t td = (B->get_transform().get_origin() + rB).cross(n);
|
||||
// calculate clamping factor and rB
|
||||
if (td <= ta.cross(n)) {
|
||||
clamp = 1.0f;
|
||||
rA = ta - A->get_transform().get_origin();
|
||||
} else if (td >= tb.cross(n)) {
|
||||
clamp = -1.0f;
|
||||
rA = tb - A->get_transform().get_origin();
|
||||
} else {
|
||||
clamp = 0.0f;
|
||||
//joint->r1 = cpvsub(cpvadd(cpvmult(cpvperp(n), -td), cpvmult(n, d)), a->p);
|
||||
rA = ((-n.orthogonal() * -td) + n * d) - A->get_transform().get_origin();
|
||||
}
|
||||
|
||||
// Calculate mass tensor
|
||||
k_tensor(A, B, rA, rB, &k1, &k2);
|
||||
|
||||
// compute max impulse
|
||||
jn_max = get_max_force() * p_step;
|
||||
|
||||
// calculate bias velocity
|
||||
//cpVect delta = cpvsub(cpvadd(b->p, joint->r2), cpvadd(a->p, joint->r1));
|
||||
//joint->bias = cpvclamp(cpvmult(delta, -joint->constraint.biasCoef*dt_inv), joint->constraint.maxBias);
|
||||
|
||||
Vector2 delta = (B->get_transform().get_origin() + rB) - (A->get_transform().get_origin() + rA);
|
||||
|
||||
real_t _b = get_bias();
|
||||
gbias = (delta * -(_b == 0 ? space->get_constraint_bias() : _b) * (1.0 / p_step)).limit_length(get_max_bias());
|
||||
|
||||
correct = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GodotGrooveJoint2D::pre_solve(real_t p_step) {
|
||||
// Apply accumulated impulse.
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-jn_acc, rA);
|
||||
}
|
||||
if (dynamic_B) {
|
||||
B->apply_impulse(jn_acc, rB);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GodotGrooveJoint2D::solve(real_t p_step) {
|
||||
// compute impulse
|
||||
Vector2 vr = relative_velocity(A, B, rA, rB);
|
||||
|
||||
Vector2 j = mult_k(gbias - vr, k1, k2);
|
||||
Vector2 jOld = jn_acc;
|
||||
j += jOld;
|
||||
|
||||
jn_acc = (((clamp * j.cross(xf_normal)) > 0) ? j : j.project(xf_normal)).limit_length(jn_max);
|
||||
|
||||
j = jn_acc - jOld;
|
||||
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-j, rA);
|
||||
}
|
||||
if (dynamic_B) {
|
||||
B->apply_impulse(j, rB);
|
||||
}
|
||||
}
|
||||
|
||||
GodotGrooveJoint2D::GodotGrooveJoint2D(const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, GodotBody2D *p_body_a, GodotBody2D *p_body_b) :
|
||||
GodotJoint2D(_arr, 2) {
|
||||
A = p_body_a;
|
||||
B = p_body_b;
|
||||
|
||||
A_groove_1 = A->get_inv_transform().xform(p_a_groove1);
|
||||
A_groove_2 = A->get_inv_transform().xform(p_a_groove2);
|
||||
B_anchor = B->get_inv_transform().xform(p_b_anchor);
|
||||
A_groove_normal = -(A_groove_2 - A_groove_1).normalized().orthogonal();
|
||||
|
||||
A->add_constraint(this, 0);
|
||||
B->add_constraint(this, 1);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
//////////////////////////////////////////////
|
||||
|
||||
bool GodotDampedSpringJoint2D::setup(real_t p_step) {
|
||||
dynamic_A = (A->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
dynamic_B = (B->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC);
|
||||
|
||||
if (!dynamic_A && !dynamic_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rA = A->get_transform().basis_xform(anchor_A);
|
||||
rB = B->get_transform().basis_xform(anchor_B);
|
||||
|
||||
Vector2 delta = (B->get_transform().get_origin() + rB) - (A->get_transform().get_origin() + rA);
|
||||
real_t dist = delta.length();
|
||||
|
||||
if (dist) {
|
||||
n = delta / dist;
|
||||
} else {
|
||||
n = Vector2();
|
||||
}
|
||||
|
||||
real_t k = k_scalar(A, B, rA, rB, n);
|
||||
n_mass = 1.0f / k;
|
||||
|
||||
target_vrn = 0.0f;
|
||||
v_coef = 1.0f - Math::exp(-damping * (p_step)*k);
|
||||
|
||||
// Calculate spring force.
|
||||
real_t f_spring = (rest_length - dist) * stiffness;
|
||||
j = n * f_spring * (p_step);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GodotDampedSpringJoint2D::pre_solve(real_t p_step) {
|
||||
// Apply spring force.
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-j, rA);
|
||||
}
|
||||
if (dynamic_B) {
|
||||
B->apply_impulse(j, rB);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GodotDampedSpringJoint2D::solve(real_t p_step) {
|
||||
// compute relative velocity
|
||||
real_t vrn = normal_relative_velocity(A, B, rA, rB, n) - target_vrn;
|
||||
|
||||
// compute velocity loss from drag
|
||||
// not 100% certain this is derived correctly, though it makes sense
|
||||
real_t v_damp = -vrn * v_coef;
|
||||
target_vrn = vrn + v_damp;
|
||||
Vector2 j_new = n * v_damp * n_mass;
|
||||
|
||||
if (dynamic_A) {
|
||||
A->apply_impulse(-j_new, rA);
|
||||
}
|
||||
if (dynamic_B) {
|
||||
B->apply_impulse(j_new, rB);
|
||||
}
|
||||
}
|
||||
|
||||
void GodotDampedSpringJoint2D::set_param(PhysicsServer2D::DampedSpringParam p_param, real_t p_value) {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::DAMPED_SPRING_REST_LENGTH: {
|
||||
rest_length = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::DAMPED_SPRING_DAMPING: {
|
||||
damping = p_value;
|
||||
} break;
|
||||
case PhysicsServer2D::DAMPED_SPRING_STIFFNESS: {
|
||||
stiffness = p_value;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
real_t GodotDampedSpringJoint2D::get_param(PhysicsServer2D::DampedSpringParam p_param) const {
|
||||
switch (p_param) {
|
||||
case PhysicsServer2D::DAMPED_SPRING_REST_LENGTH: {
|
||||
return rest_length;
|
||||
} break;
|
||||
case PhysicsServer2D::DAMPED_SPRING_DAMPING: {
|
||||
return damping;
|
||||
} break;
|
||||
case PhysicsServer2D::DAMPED_SPRING_STIFFNESS: {
|
||||
return stiffness;
|
||||
} break;
|
||||
}
|
||||
|
||||
ERR_FAIL_V(0);
|
||||
}
|
||||
|
||||
GodotDampedSpringJoint2D::GodotDampedSpringJoint2D(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, GodotBody2D *p_body_a, GodotBody2D *p_body_b) :
|
||||
GodotJoint2D(_arr, 2) {
|
||||
A = p_body_a;
|
||||
B = p_body_b;
|
||||
anchor_A = A->get_inv_transform().xform(p_anchor_a);
|
||||
anchor_B = B->get_inv_transform().xform(p_anchor_b);
|
||||
|
||||
rest_length = p_anchor_a.distance_to(p_anchor_b);
|
||||
|
||||
A->add_constraint(this, 0);
|
||||
B->add_constraint(this, 1);
|
||||
}
|
189
modules/godot_physics_2d/godot_joints_2d.h
Normal file
189
modules/godot_physics_2d/godot_joints_2d.h
Normal file
@@ -0,0 +1,189 @@
|
||||
/**************************************************************************/
|
||||
/* godot_joints_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_constraint_2d.h"
|
||||
|
||||
class GodotJoint2D : public GodotConstraint2D {
|
||||
real_t bias = 0;
|
||||
real_t max_bias = 3.40282e+38;
|
||||
real_t max_force = 3.40282e+38;
|
||||
|
||||
protected:
|
||||
bool dynamic_A = false;
|
||||
bool dynamic_B = false;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_max_force(real_t p_force) { max_force = p_force; }
|
||||
_FORCE_INLINE_ real_t get_max_force() const { return max_force; }
|
||||
|
||||
_FORCE_INLINE_ void set_bias(real_t p_bias) { bias = p_bias; }
|
||||
_FORCE_INLINE_ real_t get_bias() const { return bias; }
|
||||
|
||||
_FORCE_INLINE_ void set_max_bias(real_t p_bias) { max_bias = p_bias; }
|
||||
_FORCE_INLINE_ real_t get_max_bias() const { return max_bias; }
|
||||
|
||||
virtual bool setup(real_t p_step) override { return false; }
|
||||
virtual bool pre_solve(real_t p_step) override { return false; }
|
||||
virtual void solve(real_t p_step) override {}
|
||||
|
||||
void copy_settings_from(GodotJoint2D *p_joint);
|
||||
|
||||
virtual PhysicsServer2D::JointType get_type() const { return PhysicsServer2D::JOINT_TYPE_MAX; }
|
||||
GodotJoint2D(GodotBody2D **p_body_ptr = nullptr, int p_body_count = 0) :
|
||||
GodotConstraint2D(p_body_ptr, p_body_count) {}
|
||||
|
||||
virtual ~GodotJoint2D() {
|
||||
for (int i = 0; i < get_body_count(); i++) {
|
||||
GodotBody2D *body = get_body_ptr()[i];
|
||||
if (body) {
|
||||
body->remove_constraint(this, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class GodotPinJoint2D : public GodotJoint2D {
|
||||
union {
|
||||
struct {
|
||||
GodotBody2D *A;
|
||||
GodotBody2D *B;
|
||||
};
|
||||
|
||||
GodotBody2D *_arr[2] = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
Transform2D M;
|
||||
Vector2 rA, rB;
|
||||
Vector2 anchor_A;
|
||||
Vector2 anchor_B;
|
||||
Vector2 bias;
|
||||
real_t initial_angle = 0.0;
|
||||
real_t bias_velocity = 0.0;
|
||||
real_t jn_max = 0.0;
|
||||
real_t j_acc = 0.0;
|
||||
real_t i_sum = 0.0;
|
||||
Vector2 P;
|
||||
real_t softness = 0.0;
|
||||
real_t angular_limit_lower = 0.0;
|
||||
real_t angular_limit_upper = 0.0;
|
||||
real_t motor_target_velocity = 0.0;
|
||||
bool is_joint_at_limit = false;
|
||||
bool motor_enabled = false;
|
||||
bool angular_limit_enabled = false;
|
||||
|
||||
public:
|
||||
virtual PhysicsServer2D::JointType get_type() const override { return PhysicsServer2D::JOINT_TYPE_PIN; }
|
||||
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
void set_param(PhysicsServer2D::PinJointParam p_param, real_t p_value);
|
||||
real_t get_param(PhysicsServer2D::PinJointParam p_param) const;
|
||||
|
||||
void set_flag(PhysicsServer2D::PinJointFlag p_flag, bool p_enabled);
|
||||
bool get_flag(PhysicsServer2D::PinJointFlag p_flag) const;
|
||||
|
||||
GodotPinJoint2D(const Vector2 &p_pos, GodotBody2D *p_body_a, GodotBody2D *p_body_b = nullptr);
|
||||
};
|
||||
|
||||
class GodotGrooveJoint2D : public GodotJoint2D {
|
||||
union {
|
||||
struct {
|
||||
GodotBody2D *A;
|
||||
GodotBody2D *B;
|
||||
};
|
||||
|
||||
GodotBody2D *_arr[2] = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
Vector2 A_groove_1;
|
||||
Vector2 A_groove_2;
|
||||
Vector2 A_groove_normal;
|
||||
Vector2 B_anchor;
|
||||
Vector2 jn_acc;
|
||||
Vector2 gbias;
|
||||
real_t jn_max = 0.0;
|
||||
real_t clamp = 0.0;
|
||||
Vector2 xf_normal;
|
||||
Vector2 rA, rB;
|
||||
Vector2 k1, k2;
|
||||
|
||||
bool correct = false;
|
||||
|
||||
public:
|
||||
virtual PhysicsServer2D::JointType get_type() const override { return PhysicsServer2D::JOINT_TYPE_GROOVE; }
|
||||
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
GodotGrooveJoint2D(const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, GodotBody2D *p_body_a, GodotBody2D *p_body_b);
|
||||
};
|
||||
|
||||
class GodotDampedSpringJoint2D : public GodotJoint2D {
|
||||
union {
|
||||
struct {
|
||||
GodotBody2D *A;
|
||||
GodotBody2D *B;
|
||||
};
|
||||
|
||||
GodotBody2D *_arr[2] = { nullptr, nullptr };
|
||||
};
|
||||
|
||||
Vector2 anchor_A;
|
||||
Vector2 anchor_B;
|
||||
|
||||
real_t rest_length = 0.0;
|
||||
real_t damping = 1.5;
|
||||
real_t stiffness = 20.0;
|
||||
|
||||
Vector2 rA, rB;
|
||||
Vector2 n;
|
||||
Vector2 j;
|
||||
real_t n_mass = 0.0;
|
||||
real_t target_vrn = 0.0;
|
||||
real_t v_coef = 0.0;
|
||||
|
||||
public:
|
||||
virtual PhysicsServer2D::JointType get_type() const override { return PhysicsServer2D::JOINT_TYPE_DAMPED_SPRING; }
|
||||
|
||||
virtual bool setup(real_t p_step) override;
|
||||
virtual bool pre_solve(real_t p_step) override;
|
||||
virtual void solve(real_t p_step) override;
|
||||
|
||||
void set_param(PhysicsServer2D::DampedSpringParam p_param, real_t p_value);
|
||||
real_t get_param(PhysicsServer2D::DampedSpringParam p_param) const;
|
||||
|
||||
GodotDampedSpringJoint2D(const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, GodotBody2D *p_body_a, GodotBody2D *p_body_b);
|
||||
};
|
1456
modules/godot_physics_2d/godot_physics_server_2d.cpp
Normal file
1456
modules/godot_physics_2d/godot_physics_server_2d.cpp
Normal file
File diff suppressed because it is too large
Load Diff
307
modules/godot_physics_2d/godot_physics_server_2d.h
Normal file
307
modules/godot_physics_2d/godot_physics_server_2d.h
Normal file
@@ -0,0 +1,307 @@
|
||||
/**************************************************************************/
|
||||
/* godot_physics_server_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_joints_2d.h"
|
||||
#include "godot_shape_2d.h"
|
||||
#include "godot_space_2d.h"
|
||||
#include "godot_step_2d.h"
|
||||
|
||||
#include "core/templates/rid_owner.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class GodotPhysicsServer2D : public PhysicsServer2D {
|
||||
GDCLASS(GodotPhysicsServer2D, PhysicsServer2D);
|
||||
|
||||
friend class GodotPhysicsDirectSpaceState2D;
|
||||
friend class GodotPhysicsDirectBodyState2D;
|
||||
bool active = true;
|
||||
bool doing_sync = false;
|
||||
|
||||
int island_count = 0;
|
||||
int active_objects = 0;
|
||||
int collision_pairs = 0;
|
||||
|
||||
bool using_threads = false;
|
||||
|
||||
bool flushing_queries = false;
|
||||
|
||||
GodotStep2D *stepper = nullptr;
|
||||
HashSet<GodotSpace2D *> active_spaces;
|
||||
|
||||
mutable RID_PtrOwner<GodotShape2D, true> shape_owner;
|
||||
mutable RID_PtrOwner<GodotSpace2D, true> space_owner;
|
||||
mutable RID_PtrOwner<GodotArea2D, true> area_owner;
|
||||
mutable RID_PtrOwner<GodotBody2D, true> body_owner{ 65536, 1048576 };
|
||||
mutable RID_PtrOwner<GodotJoint2D, true> joint_owner;
|
||||
|
||||
static GodotPhysicsServer2D *godot_singleton;
|
||||
|
||||
friend class GodotCollisionObject2D;
|
||||
SelfList<GodotCollisionObject2D>::List pending_shape_update_list;
|
||||
void _update_shapes();
|
||||
|
||||
RID _shape_create(ShapeType p_shape);
|
||||
|
||||
public:
|
||||
struct CollCbkData {
|
||||
Vector2 valid_dir;
|
||||
real_t valid_depth = 0.0;
|
||||
int max = 0;
|
||||
int amount = 0;
|
||||
int passed = 0;
|
||||
int invalid_by_dir = 0;
|
||||
Vector2 *ptr = nullptr;
|
||||
};
|
||||
|
||||
virtual RID world_boundary_shape_create() override;
|
||||
virtual RID separation_ray_shape_create() override;
|
||||
virtual RID segment_shape_create() override;
|
||||
virtual RID circle_shape_create() override;
|
||||
virtual RID rectangle_shape_create() override;
|
||||
virtual RID capsule_shape_create() override;
|
||||
virtual RID convex_polygon_shape_create() override;
|
||||
virtual RID concave_polygon_shape_create() override;
|
||||
|
||||
static void _shape_col_cbk(const Vector2 &p_point_A, const Vector2 &p_point_B, void *p_userdata);
|
||||
|
||||
virtual void shape_set_data(RID p_shape, const Variant &p_data) override;
|
||||
virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias) override;
|
||||
|
||||
virtual ShapeType shape_get_type(RID p_shape) const override;
|
||||
virtual Variant shape_get_data(RID p_shape) const override;
|
||||
virtual real_t shape_get_custom_solver_bias(RID p_shape) const override;
|
||||
|
||||
virtual bool shape_collide(RID p_shape_A, const Transform2D &p_xform_A, const Vector2 &p_motion_A, RID p_shape_B, const Transform2D &p_xform_B, const Vector2 &p_motion_B, Vector2 *r_results, int p_result_max, int &r_result_count) override;
|
||||
|
||||
/* SPACE API */
|
||||
|
||||
virtual RID space_create() override;
|
||||
virtual void space_set_active(RID p_space, bool p_active) override;
|
||||
virtual bool space_is_active(RID p_space) const override;
|
||||
virtual void space_step(RID p_space, real_t p_delta) override;
|
||||
virtual void space_flush_queries(RID p_space) override;
|
||||
|
||||
virtual void space_set_param(RID p_space, SpaceParameter p_param, real_t p_value) override;
|
||||
virtual real_t space_get_param(RID p_space, SpaceParameter p_param) const override;
|
||||
|
||||
virtual void space_set_debug_contacts(RID p_space, int p_max_contacts) override;
|
||||
virtual Vector<Vector2> space_get_contacts(RID p_space) const override;
|
||||
virtual int space_get_contact_count(RID p_space) const override;
|
||||
|
||||
// this function only works on physics process, errors and returns null otherwise
|
||||
virtual PhysicsDirectSpaceState2D *space_get_direct_state(RID p_space) override;
|
||||
|
||||
/* AREA API */
|
||||
|
||||
virtual RID area_create() override;
|
||||
|
||||
virtual void area_set_space(RID p_area, RID p_space) override;
|
||||
virtual RID area_get_space(RID p_area) const override;
|
||||
|
||||
virtual void area_add_shape(RID p_area, RID p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false) override;
|
||||
virtual void area_set_shape(RID p_area, int p_shape_idx, RID p_shape) override;
|
||||
virtual void area_set_shape_transform(RID p_area, int p_shape_idx, const Transform2D &p_transform) override;
|
||||
|
||||
virtual int area_get_shape_count(RID p_area) const override;
|
||||
virtual RID area_get_shape(RID p_area, int p_shape_idx) const override;
|
||||
virtual Transform2D area_get_shape_transform(RID p_area, int p_shape_idx) const override;
|
||||
|
||||
virtual void area_set_shape_disabled(RID p_area, int p_shape, bool p_disabled) override;
|
||||
|
||||
virtual void area_remove_shape(RID p_area, int p_shape_idx) override;
|
||||
virtual void area_clear_shapes(RID p_area) override;
|
||||
|
||||
virtual void area_attach_object_instance_id(RID p_area, ObjectID p_id) override;
|
||||
virtual ObjectID area_get_object_instance_id(RID p_area) const override;
|
||||
|
||||
virtual void area_attach_canvas_instance_id(RID p_area, ObjectID p_id) override;
|
||||
virtual ObjectID area_get_canvas_instance_id(RID p_area) const override;
|
||||
|
||||
virtual void area_set_param(RID p_area, AreaParameter p_param, const Variant &p_value) override;
|
||||
virtual void area_set_transform(RID p_area, const Transform2D &p_transform) override;
|
||||
|
||||
virtual Variant area_get_param(RID p_area, AreaParameter p_param) const override;
|
||||
virtual Transform2D area_get_transform(RID p_area) const override;
|
||||
virtual void area_set_monitorable(RID p_area, bool p_monitorable) override;
|
||||
|
||||
virtual void area_set_collision_layer(RID p_area, uint32_t p_layer) override;
|
||||
virtual uint32_t area_get_collision_layer(RID p_area) const override;
|
||||
|
||||
virtual void area_set_collision_mask(RID p_area, uint32_t p_mask) override;
|
||||
virtual uint32_t area_get_collision_mask(RID p_area) const override;
|
||||
|
||||
virtual void area_set_monitor_callback(RID p_area, const Callable &p_callback) override;
|
||||
virtual void area_set_area_monitor_callback(RID p_area, const Callable &p_callback) override;
|
||||
|
||||
virtual void area_set_pickable(RID p_area, bool p_pickable) override;
|
||||
|
||||
/* BODY API */
|
||||
|
||||
// create a body of a given type
|
||||
virtual RID body_create() override;
|
||||
|
||||
virtual void body_set_space(RID p_body, RID p_space) override;
|
||||
virtual RID body_get_space(RID p_body) const override;
|
||||
|
||||
virtual void body_set_mode(RID p_body, BodyMode p_mode) override;
|
||||
virtual BodyMode body_get_mode(RID p_body) const override;
|
||||
|
||||
virtual void body_add_shape(RID p_body, RID p_shape, const Transform2D &p_transform = Transform2D(), bool p_disabled = false) override;
|
||||
virtual void body_set_shape(RID p_body, int p_shape_idx, RID p_shape) override;
|
||||
virtual void body_set_shape_transform(RID p_body, int p_shape_idx, const Transform2D &p_transform) override;
|
||||
|
||||
virtual int body_get_shape_count(RID p_body) const override;
|
||||
virtual RID body_get_shape(RID p_body, int p_shape_idx) const override;
|
||||
virtual Transform2D body_get_shape_transform(RID p_body, int p_shape_idx) const override;
|
||||
|
||||
virtual void body_remove_shape(RID p_body, int p_shape_idx) override;
|
||||
virtual void body_clear_shapes(RID p_body) override;
|
||||
|
||||
virtual void body_set_shape_disabled(RID p_body, int p_shape_idx, bool p_disabled) override;
|
||||
virtual void body_set_shape_as_one_way_collision(RID p_body, int p_shape_idx, bool p_enable, real_t p_margin) override;
|
||||
|
||||
virtual void body_attach_object_instance_id(RID p_body, ObjectID p_id) override;
|
||||
virtual ObjectID body_get_object_instance_id(RID p_body) const override;
|
||||
|
||||
virtual void body_attach_canvas_instance_id(RID p_body, ObjectID p_id) override;
|
||||
virtual ObjectID body_get_canvas_instance_id(RID p_body) const override;
|
||||
|
||||
virtual void body_set_continuous_collision_detection_mode(RID p_body, CCDMode p_mode) override;
|
||||
virtual CCDMode body_get_continuous_collision_detection_mode(RID p_body) const override;
|
||||
|
||||
virtual void body_set_collision_layer(RID p_body, uint32_t p_layer) override;
|
||||
virtual uint32_t body_get_collision_layer(RID p_body) const override;
|
||||
|
||||
virtual void body_set_collision_mask(RID p_body, uint32_t p_mask) override;
|
||||
virtual uint32_t body_get_collision_mask(RID p_body) const override;
|
||||
|
||||
virtual void body_set_collision_priority(RID p_body, real_t p_priority) override;
|
||||
virtual real_t body_get_collision_priority(RID p_body) const override;
|
||||
|
||||
virtual void body_set_param(RID p_body, BodyParameter p_param, const Variant &p_value) override;
|
||||
virtual Variant body_get_param(RID p_body, BodyParameter p_param) const override;
|
||||
|
||||
virtual void body_reset_mass_properties(RID p_body) override;
|
||||
|
||||
virtual void body_set_state(RID p_body, BodyState p_state, const Variant &p_variant) override;
|
||||
virtual Variant body_get_state(RID p_body, BodyState p_state) const override;
|
||||
|
||||
virtual void body_apply_central_impulse(RID p_body, const Vector2 &p_impulse) override;
|
||||
virtual void body_apply_torque_impulse(RID p_body, real_t p_torque) override;
|
||||
virtual void body_apply_impulse(RID p_body, const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override;
|
||||
|
||||
virtual void body_apply_central_force(RID p_body, const Vector2 &p_force) override;
|
||||
virtual void body_apply_force(RID p_body, const Vector2 &p_force, const Vector2 &p_position = Vector2()) override;
|
||||
virtual void body_apply_torque(RID p_body, real_t p_torque) override;
|
||||
|
||||
virtual void body_add_constant_central_force(RID p_body, const Vector2 &p_force) override;
|
||||
virtual void body_add_constant_force(RID p_body, const Vector2 &p_force, const Vector2 &p_position = Vector2()) override;
|
||||
virtual void body_add_constant_torque(RID p_body, real_t p_torque) override;
|
||||
|
||||
virtual void body_set_constant_force(RID p_body, const Vector2 &p_force) override;
|
||||
virtual Vector2 body_get_constant_force(RID p_body) const override;
|
||||
|
||||
virtual void body_set_constant_torque(RID p_body, real_t p_torque) override;
|
||||
virtual real_t body_get_constant_torque(RID p_body) const override;
|
||||
|
||||
virtual void body_set_axis_velocity(RID p_body, const Vector2 &p_axis_velocity) override;
|
||||
|
||||
virtual void body_add_collision_exception(RID p_body, RID p_body_b) override;
|
||||
virtual void body_remove_collision_exception(RID p_body, RID p_body_b) override;
|
||||
virtual void body_get_collision_exceptions(RID p_body, List<RID> *p_exceptions) override;
|
||||
|
||||
virtual void body_set_contacts_reported_depth_threshold(RID p_body, real_t p_threshold) override;
|
||||
virtual real_t body_get_contacts_reported_depth_threshold(RID p_body) const override;
|
||||
|
||||
virtual void body_set_omit_force_integration(RID p_body, bool p_omit) override;
|
||||
virtual bool body_is_omitting_force_integration(RID p_body) const override;
|
||||
|
||||
virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override;
|
||||
virtual int body_get_max_contacts_reported(RID p_body) const override;
|
||||
|
||||
virtual void body_set_state_sync_callback(RID p_body, const Callable &p_callable) override;
|
||||
virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override;
|
||||
|
||||
virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override;
|
||||
|
||||
virtual void body_set_pickable(RID p_body, bool p_pickable) override;
|
||||
|
||||
virtual bool body_test_motion(RID p_body, const MotionParameters &p_parameters, MotionResult *r_result = nullptr) override;
|
||||
|
||||
// this function only works on physics process, errors and returns null otherwise
|
||||
virtual PhysicsDirectBodyState2D *body_get_direct_state(RID p_body) override;
|
||||
|
||||
/* JOINT API */
|
||||
|
||||
virtual RID joint_create() override;
|
||||
|
||||
virtual void joint_clear(RID p_joint) override;
|
||||
|
||||
virtual void joint_set_param(RID p_joint, JointParam p_param, real_t p_value) override;
|
||||
virtual real_t joint_get_param(RID p_joint, JointParam p_param) const override;
|
||||
|
||||
virtual void joint_disable_collisions_between_bodies(RID p_joint, const bool p_disabled) override;
|
||||
virtual bool joint_is_disabled_collisions_between_bodies(RID p_joint) const override;
|
||||
|
||||
virtual void joint_make_pin(RID p_joint, const Vector2 &p_anchor, RID p_body_a, RID p_body_b = RID()) override;
|
||||
virtual void joint_make_groove(RID p_joint, const Vector2 &p_a_groove1, const Vector2 &p_a_groove2, const Vector2 &p_b_anchor, RID p_body_a, RID p_body_b) override;
|
||||
virtual void joint_make_damped_spring(RID p_joint, const Vector2 &p_anchor_a, const Vector2 &p_anchor_b, RID p_body_a, RID p_body_b = RID()) override;
|
||||
|
||||
virtual void pin_joint_set_flag(RID p_joint, PinJointFlag p_flag, bool p_enabled) override;
|
||||
virtual bool pin_joint_get_flag(RID p_joint, PinJointFlag p_flag) const override;
|
||||
virtual void pin_joint_set_param(RID p_joint, PinJointParam p_param, real_t p_value) override;
|
||||
virtual real_t pin_joint_get_param(RID p_joint, PinJointParam p_param) const override;
|
||||
virtual void damped_spring_joint_set_param(RID p_joint, DampedSpringParam p_param, real_t p_value) override;
|
||||
virtual real_t damped_spring_joint_get_param(RID p_joint, DampedSpringParam p_param) const override;
|
||||
|
||||
virtual JointType joint_get_type(RID p_joint) const override;
|
||||
|
||||
/* MISC */
|
||||
|
||||
virtual void free(RID p_rid) override;
|
||||
|
||||
virtual void set_active(bool p_active) override;
|
||||
virtual void init() override;
|
||||
virtual void step(real_t p_step) override;
|
||||
virtual void sync() override;
|
||||
virtual void flush_queries() override;
|
||||
virtual void end_sync() override;
|
||||
virtual void finish() override;
|
||||
|
||||
virtual bool is_flushing_queries() const override { return flushing_queries; }
|
||||
|
||||
int get_process_info(ProcessInfo p_info) override;
|
||||
|
||||
virtual int space_get_last_process_info(RID p_space, ProcessInfo p_info) override;
|
||||
GodotPhysicsServer2D(bool p_using_threads = false);
|
||||
~GodotPhysicsServer2D() {}
|
||||
};
|
984
modules/godot_physics_2d/godot_shape_2d.cpp
Normal file
984
modules/godot_physics_2d/godot_shape_2d.cpp
Normal file
@@ -0,0 +1,984 @@
|
||||
/**************************************************************************/
|
||||
/* godot_shape_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_shape_2d.h"
|
||||
|
||||
#include "core/math/geometry_2d.h"
|
||||
#include "core/templates/sort_array.h"
|
||||
|
||||
void GodotShape2D::configure(const Rect2 &p_aabb) {
|
||||
aabb = p_aabb;
|
||||
configured = true;
|
||||
for (const KeyValue<GodotShapeOwner2D *, int> &E : owners) {
|
||||
E.key->_shape_changed();
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 GodotShape2D::get_support(const Vector2 &p_normal) const {
|
||||
Vector2 res[2];
|
||||
int amnt;
|
||||
get_supports(p_normal, res, amnt);
|
||||
return res[0];
|
||||
}
|
||||
|
||||
void GodotShape2D::add_owner(GodotShapeOwner2D *p_owner) {
|
||||
HashMap<GodotShapeOwner2D *, int>::Iterator E = owners.find(p_owner);
|
||||
if (E) {
|
||||
E->value++;
|
||||
} else {
|
||||
owners[p_owner] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void GodotShape2D::remove_owner(GodotShapeOwner2D *p_owner) {
|
||||
HashMap<GodotShapeOwner2D *, int>::Iterator E = owners.find(p_owner);
|
||||
ERR_FAIL_COND(!E);
|
||||
E->value--;
|
||||
if (E->value == 0) {
|
||||
owners.remove(E);
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotShape2D::is_owner(GodotShapeOwner2D *p_owner) const {
|
||||
return owners.has(p_owner);
|
||||
}
|
||||
|
||||
const HashMap<GodotShapeOwner2D *, int> &GodotShape2D::get_owners() const {
|
||||
return owners;
|
||||
}
|
||||
|
||||
GodotShape2D::~GodotShape2D() {
|
||||
ERR_FAIL_COND(owners.size());
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotWorldBoundaryShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
r_amount = 0;
|
||||
}
|
||||
|
||||
bool GodotWorldBoundaryShape2D::contains_point(const Vector2 &p_point) const {
|
||||
return normal.dot(p_point) < d;
|
||||
}
|
||||
|
||||
bool GodotWorldBoundaryShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
Vector2 segment = p_begin - p_end;
|
||||
real_t den = normal.dot(segment);
|
||||
|
||||
//printf("den is %i\n",den);
|
||||
if (Math::abs(den) <= CMP_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
real_t dist = (normal.dot(p_begin) - d) / den;
|
||||
//printf("dist is %i\n",dist);
|
||||
|
||||
if (dist < -CMP_EPSILON || dist > (1.0 + CMP_EPSILON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r_point = p_begin + segment * -dist;
|
||||
r_normal = normal;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
real_t GodotWorldBoundaryShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GodotWorldBoundaryShape2D::set_data(const Variant &p_data) {
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY);
|
||||
Array arr = p_data;
|
||||
ERR_FAIL_COND(arr.size() != 2);
|
||||
normal = arr[0];
|
||||
d = arr[1];
|
||||
configure(Rect2(Vector2(-1e15, -1e15), Vector2(1e15 * 2, 1e15 * 2)));
|
||||
}
|
||||
|
||||
Variant GodotWorldBoundaryShape2D::get_data() const {
|
||||
Array arr;
|
||||
arr.resize(2);
|
||||
arr[0] = normal;
|
||||
arr[1] = d;
|
||||
return arr;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotSeparationRayShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
r_amount = 1;
|
||||
|
||||
if (p_normal.y > 0) {
|
||||
*r_supports = Vector2(0, length);
|
||||
} else {
|
||||
*r_supports = Vector2();
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotSeparationRayShape2D::contains_point(const Vector2 &p_point) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GodotSeparationRayShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
return false; //rays can't be intersected
|
||||
}
|
||||
|
||||
real_t GodotSeparationRayShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
return 0; //rays are mass-less
|
||||
}
|
||||
|
||||
void GodotSeparationRayShape2D::set_data(const Variant &p_data) {
|
||||
Dictionary d = p_data;
|
||||
length = d["length"];
|
||||
slide_on_slope = d["slide_on_slope"];
|
||||
configure(Rect2(0, 0, 0.001, length));
|
||||
}
|
||||
|
||||
Variant GodotSeparationRayShape2D::get_data() const {
|
||||
Dictionary d;
|
||||
d["length"] = length;
|
||||
d["slide_on_slope"] = slide_on_slope;
|
||||
return d;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotSegmentShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
if (Math::abs(p_normal.dot(n)) > segment_is_valid_support_threshold) {
|
||||
r_supports[0] = a;
|
||||
r_supports[1] = b;
|
||||
r_amount = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
real_t dp = p_normal.dot(b - a);
|
||||
if (dp > 0) {
|
||||
*r_supports = b;
|
||||
} else {
|
||||
*r_supports = a;
|
||||
}
|
||||
r_amount = 1;
|
||||
}
|
||||
|
||||
bool GodotSegmentShape2D::contains_point(const Vector2 &p_point) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GodotSegmentShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
if (!Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &r_point)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n.dot(p_begin) > n.dot(a)) {
|
||||
r_normal = n;
|
||||
} else {
|
||||
r_normal = -n;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
real_t GodotSegmentShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
return p_mass * ((a * p_scale).distance_squared_to(b * p_scale)) / 12;
|
||||
}
|
||||
|
||||
void GodotSegmentShape2D::set_data(const Variant &p_data) {
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::RECT2);
|
||||
|
||||
Rect2 r = p_data;
|
||||
a = r.position;
|
||||
b = r.size;
|
||||
n = (b - a).orthogonal();
|
||||
|
||||
Rect2 aabb_new;
|
||||
aabb_new.position = a;
|
||||
aabb_new.expand_to(b);
|
||||
if (aabb_new.size.x == 0) {
|
||||
aabb_new.size.x = 0.001;
|
||||
}
|
||||
if (aabb_new.size.y == 0) {
|
||||
aabb_new.size.y = 0.001;
|
||||
}
|
||||
configure(aabb_new);
|
||||
}
|
||||
|
||||
Variant GodotSegmentShape2D::get_data() const {
|
||||
Rect2 r;
|
||||
r.position = a;
|
||||
r.size = b;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotCircleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
r_amount = 1;
|
||||
*r_supports = p_normal * radius;
|
||||
}
|
||||
|
||||
bool GodotCircleShape2D::contains_point(const Vector2 &p_point) const {
|
||||
return p_point.length_squared() < radius * radius;
|
||||
}
|
||||
|
||||
bool GodotCircleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
Vector2 line_vec = p_end - p_begin;
|
||||
|
||||
real_t a, b, c;
|
||||
|
||||
a = line_vec.dot(line_vec);
|
||||
b = 2 * p_begin.dot(line_vec);
|
||||
c = p_begin.dot(p_begin) - radius * radius;
|
||||
|
||||
real_t sqrtterm = b * b - 4 * a * c;
|
||||
|
||||
if (sqrtterm < 0) {
|
||||
return false;
|
||||
}
|
||||
sqrtterm = Math::sqrt(sqrtterm);
|
||||
real_t res = (-b - sqrtterm) / (2 * a);
|
||||
|
||||
if (res < 0 || res > 1 + CMP_EPSILON) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r_point = p_begin + line_vec * res;
|
||||
r_normal = r_point.normalized();
|
||||
return true;
|
||||
}
|
||||
|
||||
real_t GodotCircleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
real_t a = radius * p_scale.x;
|
||||
real_t b = radius * p_scale.y;
|
||||
return p_mass * (a * a + b * b) / 4;
|
||||
}
|
||||
|
||||
void GodotCircleShape2D::set_data(const Variant &p_data) {
|
||||
ERR_FAIL_COND(!p_data.is_num());
|
||||
radius = p_data;
|
||||
configure(Rect2(-radius, -radius, radius * 2, radius * 2));
|
||||
}
|
||||
|
||||
Variant GodotCircleShape2D::get_data() const {
|
||||
return radius;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotRectangleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Vector2 ag;
|
||||
ag[i] = 1.0;
|
||||
real_t dp = ag.dot(p_normal);
|
||||
if (Math::abs(dp) <= segment_is_valid_support_threshold) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t sgn = dp > 0 ? 1.0 : -1.0;
|
||||
|
||||
r_amount = 2;
|
||||
|
||||
r_supports[0][i] = half_extents[i] * sgn;
|
||||
r_supports[0][i ^ 1] = half_extents[i ^ 1];
|
||||
|
||||
r_supports[1][i] = half_extents[i] * sgn;
|
||||
r_supports[1][i ^ 1] = -half_extents[i ^ 1];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* USE POINT */
|
||||
|
||||
r_amount = 1;
|
||||
r_supports[0] = Vector2(
|
||||
(p_normal.x < 0) ? -half_extents.x : half_extents.x,
|
||||
(p_normal.y < 0) ? -half_extents.y : half_extents.y);
|
||||
}
|
||||
|
||||
bool GodotRectangleShape2D::contains_point(const Vector2 &p_point) const {
|
||||
real_t x = p_point.x;
|
||||
real_t y = p_point.y;
|
||||
real_t edge_x = half_extents.x;
|
||||
real_t edge_y = half_extents.y;
|
||||
return (x >= -edge_x) && (x < edge_x) && (y >= -edge_y) && (y < edge_y);
|
||||
}
|
||||
|
||||
bool GodotRectangleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
return get_aabb().intersects_segment(p_begin, p_end, &r_point, &r_normal);
|
||||
}
|
||||
|
||||
real_t GodotRectangleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
Vector2 he2 = half_extents * 2 * p_scale;
|
||||
return p_mass * he2.dot(he2) / 12.0;
|
||||
}
|
||||
|
||||
void GodotRectangleShape2D::set_data(const Variant &p_data) {
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::VECTOR2);
|
||||
|
||||
half_extents = p_data;
|
||||
configure(Rect2(-half_extents, half_extents * 2.0));
|
||||
}
|
||||
|
||||
Variant GodotRectangleShape2D::get_data() const {
|
||||
return half_extents;
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotCapsuleShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
Vector2 n = p_normal;
|
||||
|
||||
real_t h = height * 0.5 - radius; // half-height of the rectangle part
|
||||
|
||||
if (h > 0 && Math::abs(n.x) > segment_is_valid_support_threshold) {
|
||||
// make it flat
|
||||
n.y = 0.0;
|
||||
n.x = SIGN(n.x) * radius;
|
||||
|
||||
r_amount = 2;
|
||||
r_supports[0] = n;
|
||||
r_supports[0].y += h;
|
||||
r_supports[1] = n;
|
||||
r_supports[1].y -= h;
|
||||
} else {
|
||||
n *= radius;
|
||||
n.y += (n.y > 0) ? h : -h;
|
||||
r_amount = 1;
|
||||
*r_supports = n;
|
||||
}
|
||||
}
|
||||
|
||||
bool GodotCapsuleShape2D::contains_point(const Vector2 &p_point) const {
|
||||
Vector2 p = p_point;
|
||||
p.y = Math::abs(p.y);
|
||||
p.y -= height * 0.5 - radius;
|
||||
if (p.y < 0) {
|
||||
p.y = 0;
|
||||
}
|
||||
|
||||
return p.length_squared() < radius * radius;
|
||||
}
|
||||
|
||||
bool GodotCapsuleShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
real_t d = 1e10;
|
||||
Vector2 n = (p_end - p_begin).normalized();
|
||||
bool collided = false;
|
||||
|
||||
//try spheres
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Vector2 begin = p_begin;
|
||||
Vector2 end = p_end;
|
||||
real_t ofs = (i == 0) ? -height * 0.5 + radius : height * 0.5 - radius;
|
||||
begin.y += ofs;
|
||||
end.y += ofs;
|
||||
|
||||
Vector2 line_vec = end - begin;
|
||||
|
||||
real_t a, b, c;
|
||||
|
||||
a = line_vec.dot(line_vec);
|
||||
b = 2 * begin.dot(line_vec);
|
||||
c = begin.dot(begin) - radius * radius;
|
||||
|
||||
real_t sqrtterm = b * b - 4 * a * c;
|
||||
|
||||
if (sqrtterm < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sqrtterm = Math::sqrt(sqrtterm);
|
||||
real_t res = (-b - sqrtterm) / (2 * a);
|
||||
|
||||
if (res < 0 || res > 1 + CMP_EPSILON) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 point = begin + line_vec * res;
|
||||
Vector2 pointf(point.x, point.y - ofs);
|
||||
real_t pd = n.dot(pointf);
|
||||
if (pd < d) {
|
||||
r_point = pointf;
|
||||
r_normal = point.normalized();
|
||||
d = pd;
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 rpos, rnorm;
|
||||
if (Rect2(Point2(-radius, -height * 0.5 + radius), Size2(radius * 2.0, height - radius * 2)).intersects_segment(p_begin, p_end, &rpos, &rnorm)) {
|
||||
real_t pd = n.dot(rpos);
|
||||
if (pd < d) {
|
||||
r_point = rpos;
|
||||
r_normal = rnorm;
|
||||
d = pd;
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
|
||||
//return get_aabb().intersects_segment(p_begin,p_end,&r_point,&r_normal);
|
||||
return collided; //todo
|
||||
}
|
||||
|
||||
real_t GodotCapsuleShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
Vector2 he2 = Vector2(radius * 2, height) * p_scale;
|
||||
return p_mass * he2.dot(he2) / 12.0;
|
||||
}
|
||||
|
||||
void GodotCapsuleShape2D::set_data(const Variant &p_data) {
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::ARRAY && p_data.get_type() != Variant::VECTOR2);
|
||||
|
||||
if (p_data.get_type() == Variant::ARRAY) {
|
||||
Array arr = p_data;
|
||||
ERR_FAIL_COND(arr.size() != 2);
|
||||
height = arr[0];
|
||||
radius = arr[1];
|
||||
} else {
|
||||
Point2 p = p_data;
|
||||
radius = p.x;
|
||||
height = p.y;
|
||||
}
|
||||
|
||||
Point2 he(radius, height * 0.5);
|
||||
configure(Rect2(-he, he * 2));
|
||||
}
|
||||
|
||||
Variant GodotCapsuleShape2D::get_data() const {
|
||||
return Point2(height, radius);
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
/*********************************************************/
|
||||
|
||||
void GodotConvexPolygonShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
int support_idx = -1;
|
||||
real_t d = -1e10;
|
||||
r_amount = 0;
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
//test point
|
||||
real_t ld = p_normal.dot(points[i].pos);
|
||||
if (ld > d) {
|
||||
support_idx = i;
|
||||
d = ld;
|
||||
}
|
||||
|
||||
//test segment
|
||||
if (points[i].normal.dot(p_normal) > segment_is_valid_support_threshold) {
|
||||
r_amount = 2;
|
||||
r_supports[0] = points[i].pos;
|
||||
r_supports[1] = points[(i + 1) % point_count].pos;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND_MSG(support_idx == -1, "Convex polygon shape support not found.");
|
||||
|
||||
r_amount = 1;
|
||||
r_supports[0] = points[support_idx].pos;
|
||||
}
|
||||
|
||||
bool GodotConvexPolygonShape2D::contains_point(const Vector2 &p_point) const {
|
||||
bool out = false;
|
||||
bool in = false;
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
real_t d = points[i].normal.dot(p_point) - points[i].normal.dot(points[i].pos);
|
||||
if (d > 0) {
|
||||
out = true;
|
||||
} else {
|
||||
in = true;
|
||||
}
|
||||
}
|
||||
|
||||
return in != out;
|
||||
}
|
||||
|
||||
bool GodotConvexPolygonShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
Vector2 n = (p_end - p_begin).normalized();
|
||||
real_t d = 1e10;
|
||||
bool inters = false;
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
Vector2 res;
|
||||
|
||||
if (!Geometry2D::segment_intersects_segment(p_begin, p_end, points[i].pos, points[(i + 1) % point_count].pos, &res)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
real_t nd = n.dot(res);
|
||||
if (nd < d) {
|
||||
d = nd;
|
||||
r_point = res;
|
||||
r_normal = points[i].normal;
|
||||
inters = true;
|
||||
}
|
||||
}
|
||||
|
||||
return inters;
|
||||
}
|
||||
|
||||
real_t GodotConvexPolygonShape2D::get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const {
|
||||
ERR_FAIL_COND_V_MSG(point_count == 0, 0, "Convex polygon shape has no points.");
|
||||
Rect2 aabb_new;
|
||||
aabb_new.position = points[0].pos * p_scale;
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
aabb_new.expand_to(points[i].pos * p_scale);
|
||||
}
|
||||
|
||||
return p_mass * aabb_new.size.dot(aabb_new.size) / 12.0;
|
||||
}
|
||||
|
||||
void GodotConvexPolygonShape2D::set_data(const Variant &p_data) {
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT64_ARRAY);
|
||||
#else
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT32_ARRAY);
|
||||
#endif
|
||||
|
||||
if (points) {
|
||||
memdelete_arr(points);
|
||||
}
|
||||
points = nullptr;
|
||||
point_count = 0;
|
||||
|
||||
if (p_data.get_type() == Variant::PACKED_VECTOR2_ARRAY) {
|
||||
Vector<Vector2> arr = p_data;
|
||||
ERR_FAIL_COND(arr.is_empty());
|
||||
point_count = arr.size();
|
||||
points = memnew_arr(Point, point_count);
|
||||
const Vector2 *r = arr.ptr();
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
points[i].pos = r[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
Vector2 p = points[i].pos;
|
||||
Vector2 pn = points[(i + 1) % point_count].pos;
|
||||
points[i].normal = (pn - p).orthogonal().normalized();
|
||||
}
|
||||
} else {
|
||||
Vector<real_t> dvr = p_data;
|
||||
point_count = dvr.size() / 4;
|
||||
ERR_FAIL_COND(point_count == 0);
|
||||
|
||||
points = memnew_arr(Point, point_count);
|
||||
const real_t *r = dvr.ptr();
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
int idx = i << 2;
|
||||
points[i].pos.x = r[idx + 0];
|
||||
points[i].pos.y = r[idx + 1];
|
||||
points[i].normal.x = r[idx + 2];
|
||||
points[i].normal.y = r[idx + 3];
|
||||
}
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(point_count == 0);
|
||||
Rect2 aabb_new;
|
||||
aabb_new.position = points[0].pos;
|
||||
for (int i = 1; i < point_count; i++) {
|
||||
aabb_new.expand_to(points[i].pos);
|
||||
}
|
||||
|
||||
configure(aabb_new);
|
||||
}
|
||||
|
||||
Variant GodotConvexPolygonShape2D::get_data() const {
|
||||
Vector<Vector2> dvr;
|
||||
|
||||
dvr.resize(point_count);
|
||||
|
||||
for (int i = 0; i < point_count; i++) {
|
||||
dvr.set(i, points[i].pos);
|
||||
}
|
||||
|
||||
return dvr;
|
||||
}
|
||||
|
||||
GodotConvexPolygonShape2D::~GodotConvexPolygonShape2D() {
|
||||
if (points) {
|
||||
memdelete_arr(points);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
void GodotConcavePolygonShape2D::get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const {
|
||||
real_t d = -1e10;
|
||||
int idx = -1;
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
real_t ld = p_normal.dot(points[i]);
|
||||
if (ld > d) {
|
||||
d = ld;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
r_amount = 1;
|
||||
ERR_FAIL_COND(idx == -1);
|
||||
*r_supports = points[idx];
|
||||
}
|
||||
|
||||
bool GodotConcavePolygonShape2D::contains_point(const Vector2 &p_point) const {
|
||||
return false; //sorry
|
||||
}
|
||||
|
||||
bool GodotConcavePolygonShape2D::intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const {
|
||||
if (segments.is_empty() || points.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * bvh_depth);
|
||||
|
||||
enum {
|
||||
TEST_AABB_BIT = 0,
|
||||
VISIT_LEFT_BIT = 1,
|
||||
VISIT_RIGHT_BIT = 2,
|
||||
VISIT_DONE_BIT = 3,
|
||||
VISITED_BIT_SHIFT = 29,
|
||||
NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
|
||||
VISITED_BIT_MASK = ~NODE_IDX_MASK,
|
||||
|
||||
};
|
||||
|
||||
Vector2 n = (p_end - p_begin).normalized();
|
||||
real_t d = 1e10;
|
||||
bool inters = false;
|
||||
|
||||
/*
|
||||
for(int i=0;i<bvh_depth;i++)
|
||||
stack[i]=0;
|
||||
*/
|
||||
|
||||
int level = 0;
|
||||
|
||||
const Segment *segmentptr = &segments[0];
|
||||
const Vector2 *pointptr = &points[0];
|
||||
const BVH *bvhptr = &bvh[0];
|
||||
|
||||
stack[0] = 0;
|
||||
while (true) {
|
||||
uint32_t node = stack[level] & NODE_IDX_MASK;
|
||||
const BVH &bvh2 = bvhptr[node];
|
||||
bool done = false;
|
||||
|
||||
switch (stack[level] >> VISITED_BIT_SHIFT) {
|
||||
case TEST_AABB_BIT: {
|
||||
bool valid = bvh2.aabb.intersects_segment(p_begin, p_end);
|
||||
if (!valid) {
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
|
||||
} else {
|
||||
if (bvh2.left < 0) {
|
||||
const Segment &s = segmentptr[bvh2.right];
|
||||
Vector2 a = pointptr[s.points[0]];
|
||||
Vector2 b = pointptr[s.points[1]];
|
||||
|
||||
Vector2 res;
|
||||
|
||||
if (Geometry2D::segment_intersects_segment(p_begin, p_end, a, b, &res)) {
|
||||
real_t nd = n.dot(res);
|
||||
if (nd < d) {
|
||||
d = nd;
|
||||
r_point = res;
|
||||
r_normal = (b - a).orthogonal().normalized();
|
||||
inters = true;
|
||||
}
|
||||
}
|
||||
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
|
||||
} else {
|
||||
stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case VISIT_LEFT_BIT: {
|
||||
stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
|
||||
stack[level + 1] = bvh2.left | TEST_AABB_BIT;
|
||||
level++;
|
||||
}
|
||||
continue;
|
||||
case VISIT_RIGHT_BIT: {
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
stack[level + 1] = bvh2.right | TEST_AABB_BIT;
|
||||
level++;
|
||||
}
|
||||
continue;
|
||||
case VISIT_DONE_BIT: {
|
||||
if (level == 0) {
|
||||
done = true;
|
||||
break;
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (inters) {
|
||||
if (n.dot(r_normal) > 0) {
|
||||
r_normal = -r_normal;
|
||||
}
|
||||
}
|
||||
|
||||
return inters;
|
||||
}
|
||||
|
||||
int GodotConcavePolygonShape2D::_generate_bvh(BVH *p_bvh, int p_len, int p_depth) {
|
||||
if (p_len == 1) {
|
||||
bvh_depth = MAX(p_depth, bvh_depth);
|
||||
bvh.push_back(*p_bvh);
|
||||
return bvh.size() - 1;
|
||||
}
|
||||
|
||||
//else sort best
|
||||
|
||||
Rect2 global_aabb = p_bvh[0].aabb;
|
||||
for (int i = 1; i < p_len; i++) {
|
||||
global_aabb = global_aabb.merge(p_bvh[i].aabb);
|
||||
}
|
||||
|
||||
if (global_aabb.size.x > global_aabb.size.y) {
|
||||
SortArray<BVH, BVH_CompareX> sort;
|
||||
sort.sort(p_bvh, p_len);
|
||||
|
||||
} else {
|
||||
SortArray<BVH, BVH_CompareY> sort;
|
||||
sort.sort(p_bvh, p_len);
|
||||
}
|
||||
|
||||
int median = p_len / 2;
|
||||
|
||||
BVH node;
|
||||
node.aabb = global_aabb;
|
||||
int node_idx = bvh.size();
|
||||
bvh.push_back(node);
|
||||
|
||||
int l = _generate_bvh(p_bvh, median, p_depth + 1);
|
||||
int r = _generate_bvh(&p_bvh[median], p_len - median, p_depth + 1);
|
||||
bvh.write[node_idx].left = l;
|
||||
bvh.write[node_idx].right = r;
|
||||
|
||||
return node_idx;
|
||||
}
|
||||
|
||||
void GodotConcavePolygonShape2D::set_data(const Variant &p_data) {
|
||||
#ifdef REAL_T_IS_DOUBLE
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT64_ARRAY);
|
||||
#else
|
||||
ERR_FAIL_COND(p_data.get_type() != Variant::PACKED_VECTOR2_ARRAY && p_data.get_type() != Variant::PACKED_FLOAT32_ARRAY);
|
||||
#endif
|
||||
|
||||
Rect2 aabb_new;
|
||||
|
||||
if (p_data.get_type() == Variant::PACKED_VECTOR2_ARRAY) {
|
||||
Vector<Vector2> p2arr = p_data;
|
||||
int len = p2arr.size();
|
||||
ERR_FAIL_COND(len % 2);
|
||||
|
||||
segments.clear();
|
||||
points.clear();
|
||||
bvh.clear();
|
||||
bvh_depth = 1;
|
||||
|
||||
if (len == 0) {
|
||||
configure(aabb_new);
|
||||
return;
|
||||
}
|
||||
|
||||
const Vector2 *arr = p2arr.ptr();
|
||||
|
||||
HashMap<Point2, int> pointmap;
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
Point2 p1 = arr[i];
|
||||
Point2 p2 = arr[i + 1];
|
||||
int idx_p1, idx_p2;
|
||||
|
||||
if (pointmap.has(p1)) {
|
||||
idx_p1 = pointmap[p1];
|
||||
} else {
|
||||
idx_p1 = pointmap.size();
|
||||
pointmap[p1] = idx_p1;
|
||||
}
|
||||
|
||||
if (pointmap.has(p2)) {
|
||||
idx_p2 = pointmap[p2];
|
||||
} else {
|
||||
idx_p2 = pointmap.size();
|
||||
pointmap[p2] = idx_p2;
|
||||
}
|
||||
|
||||
Segment s;
|
||||
s.points[0] = idx_p1;
|
||||
s.points[1] = idx_p2;
|
||||
segments.push_back(s);
|
||||
}
|
||||
|
||||
points.resize(pointmap.size());
|
||||
aabb_new.position = pointmap.begin()->key;
|
||||
for (const KeyValue<Point2, int> &E : pointmap) {
|
||||
aabb_new.expand_to(E.key);
|
||||
points.write[E.value] = E.key;
|
||||
}
|
||||
|
||||
Vector<BVH> main_vbh;
|
||||
main_vbh.resize(segments.size());
|
||||
for (int i = 0; i < main_vbh.size(); i++) {
|
||||
main_vbh.write[i].aabb.position = points[segments[i].points[0]];
|
||||
main_vbh.write[i].aabb.expand_to(points[segments[i].points[1]]);
|
||||
main_vbh.write[i].left = -1;
|
||||
main_vbh.write[i].right = i;
|
||||
}
|
||||
|
||||
_generate_bvh(main_vbh.ptrw(), main_vbh.size(), 1);
|
||||
|
||||
} else {
|
||||
//dictionary with arrays
|
||||
}
|
||||
|
||||
configure(aabb_new);
|
||||
}
|
||||
|
||||
Variant GodotConcavePolygonShape2D::get_data() const {
|
||||
Vector<Vector2> rsegments;
|
||||
int len = segments.size();
|
||||
rsegments.resize(len * 2);
|
||||
Vector2 *w = rsegments.ptrw();
|
||||
for (int i = 0; i < len; i++) {
|
||||
w[(i << 1) + 0] = points[segments[i].points[0]];
|
||||
w[(i << 1) + 1] = points[segments[i].points[1]];
|
||||
}
|
||||
|
||||
return rsegments;
|
||||
}
|
||||
|
||||
void GodotConcavePolygonShape2D::cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const {
|
||||
uint32_t *stack = (uint32_t *)alloca(sizeof(int) * bvh_depth);
|
||||
|
||||
enum {
|
||||
TEST_AABB_BIT = 0,
|
||||
VISIT_LEFT_BIT = 1,
|
||||
VISIT_RIGHT_BIT = 2,
|
||||
VISIT_DONE_BIT = 3,
|
||||
VISITED_BIT_SHIFT = 29,
|
||||
NODE_IDX_MASK = (1 << VISITED_BIT_SHIFT) - 1,
|
||||
VISITED_BIT_MASK = ~NODE_IDX_MASK,
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
for(int i=0;i<bvh_depth;i++)
|
||||
stack[i]=0;
|
||||
*/
|
||||
|
||||
if (segments.is_empty() || points.is_empty() || bvh.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int level = 0;
|
||||
|
||||
const Segment *segmentptr = &segments[0];
|
||||
const Vector2 *pointptr = &points[0];
|
||||
const BVH *bvhptr = &bvh[0];
|
||||
|
||||
stack[0] = 0;
|
||||
while (true) {
|
||||
uint32_t node = stack[level] & NODE_IDX_MASK;
|
||||
const BVH &bvh2 = bvhptr[node];
|
||||
|
||||
switch (stack[level] >> VISITED_BIT_SHIFT) {
|
||||
case TEST_AABB_BIT: {
|
||||
bool valid = p_local_aabb.intersects(bvh2.aabb);
|
||||
if (!valid) {
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
|
||||
} else {
|
||||
if (bvh2.left < 0) {
|
||||
const Segment &s = segmentptr[bvh2.right];
|
||||
Vector2 a = pointptr[s.points[0]];
|
||||
Vector2 b = pointptr[s.points[1]];
|
||||
|
||||
GodotSegmentShape2D ss(a, b, (b - a).orthogonal().normalized());
|
||||
|
||||
if (p_callback(p_userdata, &ss)) {
|
||||
return;
|
||||
}
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
|
||||
} else {
|
||||
stack[level] = (VISIT_LEFT_BIT << VISITED_BIT_SHIFT) | node;
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
case VISIT_LEFT_BIT: {
|
||||
stack[level] = (VISIT_RIGHT_BIT << VISITED_BIT_SHIFT) | node;
|
||||
stack[level + 1] = bvh2.left | TEST_AABB_BIT;
|
||||
level++;
|
||||
}
|
||||
continue;
|
||||
case VISIT_RIGHT_BIT: {
|
||||
stack[level] = (VISIT_DONE_BIT << VISITED_BIT_SHIFT) | node;
|
||||
stack[level + 1] = bvh2.right | TEST_AABB_BIT;
|
||||
level++;
|
||||
}
|
||||
continue;
|
||||
case VISIT_DONE_BIT: {
|
||||
if (level == 0) {
|
||||
return;
|
||||
} else {
|
||||
level--;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
536
modules/godot_physics_2d/godot_shape_2d.h
Normal file
536
modules/godot_physics_2d/godot_shape_2d.h
Normal file
@@ -0,0 +1,536 @@
|
||||
/**************************************************************************/
|
||||
/* godot_shape_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "servers/physics_server_2d.h"
|
||||
|
||||
class GodotShape2D;
|
||||
|
||||
class GodotShapeOwner2D {
|
||||
public:
|
||||
virtual void _shape_changed() = 0;
|
||||
virtual void remove_shape(GodotShape2D *p_shape) = 0;
|
||||
|
||||
virtual ~GodotShapeOwner2D() {}
|
||||
};
|
||||
|
||||
class GodotShape2D {
|
||||
RID self;
|
||||
Rect2 aabb;
|
||||
bool configured = false;
|
||||
real_t custom_bias = 0.0;
|
||||
|
||||
HashMap<GodotShapeOwner2D *, int> owners;
|
||||
|
||||
protected:
|
||||
const double segment_is_valid_support_threshold = 0.99998;
|
||||
const double segment_is_valid_support_threshold_lower =
|
||||
Math::sqrt(1.0 - segment_is_valid_support_threshold * segment_is_valid_support_threshold);
|
||||
|
||||
void configure(const Rect2 &p_aabb);
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
|
||||
_FORCE_INLINE_ RID get_self() const { return self; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const = 0;
|
||||
|
||||
_FORCE_INLINE_ Rect2 get_aabb() const { return aabb; }
|
||||
_FORCE_INLINE_ bool is_configured() const { return configured; }
|
||||
|
||||
virtual bool allows_one_way_collision() const { return true; }
|
||||
|
||||
virtual bool is_concave() const { return false; }
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const = 0;
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const = 0;
|
||||
virtual void project_range_castv(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const = 0;
|
||||
virtual Vector2 get_support(const Vector2 &p_normal) const;
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const = 0;
|
||||
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const = 0;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const = 0;
|
||||
virtual void set_data(const Variant &p_data) = 0;
|
||||
virtual Variant get_data() const = 0;
|
||||
|
||||
_FORCE_INLINE_ void set_custom_bias(real_t p_bias) { custom_bias = p_bias; }
|
||||
_FORCE_INLINE_ real_t get_custom_bias() const { return custom_bias; }
|
||||
|
||||
void add_owner(GodotShapeOwner2D *p_owner);
|
||||
void remove_owner(GodotShapeOwner2D *p_owner);
|
||||
bool is_owner(GodotShapeOwner2D *p_owner) const;
|
||||
const HashMap<GodotShapeOwner2D *, int> &get_owners() const;
|
||||
|
||||
_FORCE_INLINE_ void get_supports_transformed_cast(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_xform, Vector2 *r_supports, int &r_amount) const {
|
||||
get_supports(p_xform.basis_xform_inv(p_normal).normalized(), r_supports, r_amount);
|
||||
for (int i = 0; i < r_amount; i++) {
|
||||
r_supports[i] = p_xform.xform(r_supports[i]);
|
||||
}
|
||||
|
||||
if (r_amount == 1) {
|
||||
if (Math::abs(p_normal.dot(p_cast.normalized())) < segment_is_valid_support_threshold_lower) {
|
||||
//make line because they are parallel
|
||||
r_amount = 2;
|
||||
r_supports[1] = r_supports[0] + p_cast;
|
||||
} else if (p_cast.dot(p_normal) > 0) {
|
||||
//normal points towards cast, add cast
|
||||
r_supports[0] += p_cast;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (Math::abs(p_normal.dot(p_cast.normalized())) < segment_is_valid_support_threshold_lower) {
|
||||
//optimize line and make it larger because they are parallel
|
||||
if ((r_supports[1] - r_supports[0]).dot(p_cast) > 0) {
|
||||
//larger towards 1
|
||||
r_supports[1] += p_cast;
|
||||
} else {
|
||||
//larger towards 0
|
||||
r_supports[0] += p_cast;
|
||||
}
|
||||
} else if (p_cast.dot(p_normal) > 0) {
|
||||
//normal points towards cast, add cast
|
||||
r_supports[0] += p_cast;
|
||||
r_supports[1] += p_cast;
|
||||
}
|
||||
}
|
||||
}
|
||||
GodotShape2D() {}
|
||||
virtual ~GodotShape2D();
|
||||
};
|
||||
|
||||
//let the optimizer do the magic
|
||||
#define DEFAULT_PROJECT_RANGE_CAST \
|
||||
virtual void project_range_castv(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { \
|
||||
project_range_cast(p_cast, p_normal, p_transform, r_min, r_max); \
|
||||
} \
|
||||
_FORCE_INLINE_ void project_range_cast(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const { \
|
||||
real_t mina, maxa; \
|
||||
real_t minb, maxb; \
|
||||
Transform2D ofsb = p_transform; \
|
||||
ofsb.columns[2] += p_cast; \
|
||||
project_range(p_normal, p_transform, mina, maxa); \
|
||||
project_range(p_normal, ofsb, minb, maxb); \
|
||||
r_min = MIN(mina, minb); \
|
||||
r_max = MAX(maxa, maxb); \
|
||||
}
|
||||
|
||||
class GodotWorldBoundaryShape2D : public GodotShape2D {
|
||||
Vector2 normal;
|
||||
real_t d = 0.0;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ Vector2 get_normal() const { return normal; }
|
||||
_FORCE_INLINE_ real_t get_d() const { return d; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_WORLD_BOUNDARY; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
//real large
|
||||
r_min = -1e10;
|
||||
r_max = 1e10;
|
||||
}
|
||||
|
||||
virtual void project_range_castv(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override {
|
||||
project_range_cast(p_cast, p_normal, p_transform, r_min, r_max);
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ void project_range_cast(const Vector2 &p_cast, const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
//real large
|
||||
r_min = -1e10;
|
||||
r_max = 1e10;
|
||||
}
|
||||
};
|
||||
|
||||
class GodotSeparationRayShape2D : public GodotShape2D {
|
||||
real_t length = 0.0;
|
||||
bool slide_on_slope = false;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ real_t get_length() const { return length; }
|
||||
_FORCE_INLINE_ bool get_slide_on_slope() const { return slide_on_slope; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_SEPARATION_RAY; }
|
||||
|
||||
virtual bool allows_one_way_collision() const override { return false; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
//real large
|
||||
r_max = p_normal.dot(p_transform.get_origin());
|
||||
r_min = p_normal.dot(p_transform.xform(Vector2(0, length)));
|
||||
if (r_max < r_min) {
|
||||
SWAP(r_max, r_min);
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
|
||||
_FORCE_INLINE_ GodotSeparationRayShape2D() {}
|
||||
_FORCE_INLINE_ GodotSeparationRayShape2D(real_t p_length) { length = p_length; }
|
||||
};
|
||||
|
||||
class GodotSegmentShape2D : public GodotShape2D {
|
||||
Vector2 a;
|
||||
Vector2 b;
|
||||
Vector2 n;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ const Vector2 &get_a() const { return a; }
|
||||
_FORCE_INLINE_ const Vector2 &get_b() const { return b; }
|
||||
_FORCE_INLINE_ const Vector2 &get_normal() const { return n; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_SEGMENT; }
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_xformed_normal(const Transform2D &p_xform) const {
|
||||
return (p_xform.xform(b) - p_xform.xform(a)).normalized().orthogonal();
|
||||
}
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
//real large
|
||||
r_max = p_normal.dot(p_transform.xform(a));
|
||||
r_min = p_normal.dot(p_transform.xform(b));
|
||||
if (r_max < r_min) {
|
||||
SWAP(r_max, r_min);
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
|
||||
_FORCE_INLINE_ GodotSegmentShape2D() {}
|
||||
_FORCE_INLINE_ GodotSegmentShape2D(const Vector2 &p_a, const Vector2 &p_b, const Vector2 &p_n) {
|
||||
a = p_a;
|
||||
b = p_b;
|
||||
n = p_n;
|
||||
}
|
||||
};
|
||||
|
||||
class GodotCircleShape2D : public GodotShape2D {
|
||||
real_t radius;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ const real_t &get_radius() const { return radius; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_CIRCLE; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
//real large
|
||||
real_t d = p_normal.dot(p_transform.get_origin());
|
||||
|
||||
// figure out scale at point
|
||||
Vector2 local_normal = p_transform.basis_xform_inv(p_normal);
|
||||
real_t scale = local_normal.length();
|
||||
|
||||
r_min = d - (radius)*scale;
|
||||
r_max = d + (radius)*scale;
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
};
|
||||
|
||||
class GodotRectangleShape2D : public GodotShape2D {
|
||||
Vector2 half_extents;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ const Vector2 &get_half_extents() const { return half_extents; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_RECTANGLE; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
// no matter the angle, the box is mirrored anyway
|
||||
r_max = -1e20;
|
||||
r_min = 1e20;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
real_t d = p_normal.dot(p_transform.xform(Vector2(((i & 1) * 2 - 1) * half_extents.x, ((i >> 1) * 2 - 1) * half_extents.y)));
|
||||
|
||||
if (d > r_max) {
|
||||
r_max = d;
|
||||
}
|
||||
if (d < r_min) {
|
||||
r_min = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_circle_axis(const Transform2D &p_xform, const Transform2D &p_xform_inv, const Vector2 &p_circle) const {
|
||||
Vector2 local_v = p_xform_inv.xform(p_circle);
|
||||
|
||||
Vector2 he(
|
||||
(local_v.x < 0) ? -half_extents.x : half_extents.x,
|
||||
(local_v.y < 0) ? -half_extents.y : half_extents.y);
|
||||
|
||||
return (p_xform.xform(he) - p_circle).normalized();
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Vector2 get_box_axis(const Transform2D &p_xform, const Transform2D &p_xform_inv, const GodotRectangleShape2D *p_B, const Transform2D &p_B_xform, const Transform2D &p_B_xform_inv) const {
|
||||
Vector2 a, b;
|
||||
|
||||
{
|
||||
Vector2 local_v = p_xform_inv.xform(p_B_xform.get_origin());
|
||||
|
||||
Vector2 he(
|
||||
(local_v.x < 0) ? -half_extents.x : half_extents.x,
|
||||
(local_v.y < 0) ? -half_extents.y : half_extents.y);
|
||||
|
||||
a = p_xform.xform(he);
|
||||
}
|
||||
{
|
||||
Vector2 local_v = p_B_xform_inv.xform(p_xform.get_origin());
|
||||
|
||||
Vector2 he(
|
||||
(local_v.x < 0) ? -p_B->half_extents.x : p_B->half_extents.x,
|
||||
(local_v.y < 0) ? -p_B->half_extents.y : p_B->half_extents.y);
|
||||
|
||||
b = p_B_xform.xform(he);
|
||||
}
|
||||
|
||||
return (a - b).normalized();
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
};
|
||||
|
||||
class GodotCapsuleShape2D : public GodotShape2D {
|
||||
real_t radius = 0.0;
|
||||
real_t height = 0.0;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ const real_t &get_radius() const { return radius; }
|
||||
_FORCE_INLINE_ const real_t &get_height() const { return height; }
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_CAPSULE; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
// no matter the angle, the box is mirrored anyway
|
||||
Vector2 n = p_transform.basis_xform_inv(p_normal).normalized();
|
||||
real_t h = height * 0.5 - radius;
|
||||
|
||||
n *= radius;
|
||||
n.y += (n.y > 0) ? h : -h;
|
||||
|
||||
r_max = p_normal.dot(p_transform.xform(n));
|
||||
r_min = p_normal.dot(p_transform.xform(-n));
|
||||
|
||||
if (r_max < r_min) {
|
||||
SWAP(r_max, r_min);
|
||||
}
|
||||
|
||||
//ERR_FAIL_COND( r_max < r_min );
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
};
|
||||
|
||||
class GodotConvexPolygonShape2D : public GodotShape2D {
|
||||
struct Point {
|
||||
Vector2 pos;
|
||||
Vector2 normal; //normal to next segment
|
||||
};
|
||||
|
||||
Point *points = nullptr;
|
||||
int point_count = 0;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ int get_point_count() const { return point_count; }
|
||||
_FORCE_INLINE_ const Vector2 &get_point(int p_idx) const { return points[p_idx].pos; }
|
||||
_FORCE_INLINE_ const Vector2 &get_segment_normal(int p_idx) const { return points[p_idx].normal; }
|
||||
_FORCE_INLINE_ Vector2 get_xformed_segment_normal(const Transform2D &p_xform, int p_idx) const {
|
||||
Vector2 a = points[p_idx].pos;
|
||||
p_idx++;
|
||||
Vector2 b = points[p_idx == point_count ? 0 : p_idx].pos;
|
||||
return (p_xform.xform(b) - p_xform.xform(a)).normalized().orthogonal();
|
||||
}
|
||||
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_CONVEX_POLYGON; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override { project_range(p_normal, p_transform, r_min, r_max); }
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override;
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
_FORCE_INLINE_ void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
if (!points || point_count <= 0) {
|
||||
r_min = r_max = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
r_min = r_max = p_normal.dot(p_transform.xform(points[0].pos));
|
||||
for (int i = 1; i < point_count; i++) {
|
||||
real_t d = p_normal.dot(p_transform.xform(points[i].pos));
|
||||
if (d > r_max) {
|
||||
r_max = d;
|
||||
}
|
||||
if (d < r_min) {
|
||||
r_min = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
|
||||
GodotConvexPolygonShape2D() {}
|
||||
~GodotConvexPolygonShape2D();
|
||||
};
|
||||
|
||||
class GodotConcaveShape2D : public GodotShape2D {
|
||||
public:
|
||||
virtual bool is_concave() const override { return true; }
|
||||
|
||||
// Returns true to stop the query.
|
||||
typedef bool (*QueryCallback)(void *p_userdata, GodotShape2D *p_convex);
|
||||
|
||||
virtual void cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const = 0;
|
||||
};
|
||||
|
||||
class GodotConcavePolygonShape2D : public GodotConcaveShape2D {
|
||||
struct Segment {
|
||||
int points[2] = {};
|
||||
};
|
||||
|
||||
Vector<Segment> segments;
|
||||
Vector<Point2> points;
|
||||
|
||||
struct BVH {
|
||||
Rect2 aabb;
|
||||
int left = 0, right = 0;
|
||||
};
|
||||
|
||||
Vector<BVH> bvh;
|
||||
int bvh_depth = 0;
|
||||
|
||||
struct BVH_CompareX {
|
||||
_FORCE_INLINE_ bool operator()(const BVH &a, const BVH &b) const {
|
||||
return (a.aabb.position.x + a.aabb.size.x * 0.5) < (b.aabb.position.x + b.aabb.size.x * 0.5);
|
||||
}
|
||||
};
|
||||
|
||||
struct BVH_CompareY {
|
||||
_FORCE_INLINE_ bool operator()(const BVH &a, const BVH &b) const {
|
||||
return (a.aabb.position.y + a.aabb.size.y * 0.5) < (b.aabb.position.y + b.aabb.size.y * 0.5);
|
||||
}
|
||||
};
|
||||
|
||||
int _generate_bvh(BVH *p_bvh, int p_len, int p_depth);
|
||||
|
||||
public:
|
||||
virtual PhysicsServer2D::ShapeType get_type() const override { return PhysicsServer2D::SHAPE_CONCAVE_POLYGON; }
|
||||
|
||||
virtual void project_rangev(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const override {
|
||||
r_min = 0;
|
||||
r_max = 0;
|
||||
ERR_FAIL_MSG("Unsupported call to project_rangev in GodotConcavePolygonShape2D");
|
||||
}
|
||||
|
||||
void project_range(const Vector2 &p_normal, const Transform2D &p_transform, real_t &r_min, real_t &r_max) const {
|
||||
r_min = 0;
|
||||
r_max = 0;
|
||||
ERR_FAIL_MSG("Unsupported call to project_range in GodotConcavePolygonShape2D");
|
||||
}
|
||||
|
||||
virtual void get_supports(const Vector2 &p_normal, Vector2 *r_supports, int &r_amount) const override;
|
||||
|
||||
virtual bool contains_point(const Vector2 &p_point) const override;
|
||||
virtual bool intersect_segment(const Vector2 &p_begin, const Vector2 &p_end, Vector2 &r_point, Vector2 &r_normal) const override;
|
||||
|
||||
virtual real_t get_moment_of_inertia(real_t p_mass, const Size2 &p_scale) const override { return 0; }
|
||||
|
||||
virtual void set_data(const Variant &p_data) override;
|
||||
virtual Variant get_data() const override;
|
||||
|
||||
virtual void cull(const Rect2 &p_local_aabb, QueryCallback p_callback, void *p_userdata) const override;
|
||||
|
||||
DEFAULT_PROJECT_RANGE_CAST
|
||||
};
|
||||
|
||||
#undef DEFAULT_PROJECT_RANGE_CAST
|
1241
modules/godot_physics_2d/godot_space_2d.cpp
Normal file
1241
modules/godot_physics_2d/godot_space_2d.cpp
Normal file
File diff suppressed because it is too large
Load Diff
207
modules/godot_physics_2d/godot_space_2d.h
Normal file
207
modules/godot_physics_2d/godot_space_2d.h
Normal file
@@ -0,0 +1,207 @@
|
||||
/**************************************************************************/
|
||||
/* godot_space_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_area_2d.h"
|
||||
#include "godot_body_2d.h"
|
||||
#include "godot_broad_phase_2d.h"
|
||||
#include "godot_collision_object_2d.h"
|
||||
|
||||
#include "core/typedefs.h"
|
||||
|
||||
class GodotPhysicsDirectSpaceState2D : public PhysicsDirectSpaceState2D {
|
||||
GDCLASS(GodotPhysicsDirectSpaceState2D, PhysicsDirectSpaceState2D);
|
||||
|
||||
public:
|
||||
GodotSpace2D *space = nullptr;
|
||||
|
||||
virtual int intersect_point(const PointParameters &p_parameters, ShapeResult *r_results, int p_result_max) override;
|
||||
virtual bool intersect_ray(const RayParameters &p_parameters, RayResult &r_result) override;
|
||||
virtual int intersect_shape(const ShapeParameters &p_parameters, ShapeResult *r_results, int p_result_max) override;
|
||||
virtual bool cast_motion(const ShapeParameters &p_parameters, real_t &p_closest_safe, real_t &p_closest_unsafe) override;
|
||||
virtual bool collide_shape(const ShapeParameters &p_parameters, Vector2 *r_results, int p_result_max, int &r_result_count) override;
|
||||
virtual bool rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) override;
|
||||
|
||||
GodotPhysicsDirectSpaceState2D() {}
|
||||
};
|
||||
|
||||
class GodotSpace2D {
|
||||
public:
|
||||
enum ElapsedTime {
|
||||
ELAPSED_TIME_INTEGRATE_FORCES,
|
||||
ELAPSED_TIME_GENERATE_ISLANDS,
|
||||
ELAPSED_TIME_SETUP_CONSTRAINTS,
|
||||
ELAPSED_TIME_SOLVE_CONSTRAINTS,
|
||||
ELAPSED_TIME_INTEGRATE_VELOCITIES,
|
||||
ELAPSED_TIME_MAX
|
||||
|
||||
};
|
||||
|
||||
private:
|
||||
struct ExcludedShapeSW {
|
||||
GodotShape2D *local_shape = nullptr;
|
||||
const GodotCollisionObject2D *against_object = nullptr;
|
||||
int against_shape_index = 0;
|
||||
};
|
||||
|
||||
uint64_t elapsed_time[ELAPSED_TIME_MAX] = {};
|
||||
|
||||
GodotPhysicsDirectSpaceState2D *direct_access = nullptr;
|
||||
RID self;
|
||||
|
||||
GodotBroadPhase2D *broadphase = nullptr;
|
||||
SelfList<GodotBody2D>::List active_list;
|
||||
SelfList<GodotBody2D>::List mass_properties_update_list;
|
||||
SelfList<GodotBody2D>::List state_query_list;
|
||||
SelfList<GodotArea2D>::List monitor_query_list;
|
||||
SelfList<GodotArea2D>::List area_moved_list;
|
||||
|
||||
static void *_broadphase_pair(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_self);
|
||||
static void _broadphase_unpair(GodotCollisionObject2D *A, int p_subindex_A, GodotCollisionObject2D *B, int p_subindex_B, void *p_data, void *p_self);
|
||||
|
||||
HashSet<GodotCollisionObject2D *> objects;
|
||||
|
||||
GodotArea2D *area = nullptr;
|
||||
|
||||
int solver_iterations = 0;
|
||||
|
||||
real_t contact_recycle_radius = 0.0;
|
||||
real_t contact_max_separation = 0.0;
|
||||
real_t contact_max_allowed_penetration = 0.0;
|
||||
real_t contact_bias = 0.0;
|
||||
real_t constraint_bias = 0.0;
|
||||
|
||||
enum {
|
||||
INTERSECTION_QUERY_MAX = 2048
|
||||
};
|
||||
|
||||
GodotCollisionObject2D *intersection_query_results[INTERSECTION_QUERY_MAX];
|
||||
int intersection_query_subindex_results[INTERSECTION_QUERY_MAX];
|
||||
|
||||
real_t body_linear_velocity_sleep_threshold = 0.0;
|
||||
real_t body_angular_velocity_sleep_threshold = 0.0;
|
||||
real_t body_time_to_sleep = 0.0;
|
||||
|
||||
bool locked = false;
|
||||
|
||||
real_t last_step = 0.001;
|
||||
|
||||
int island_count = 0;
|
||||
int active_objects = 0;
|
||||
int collision_pairs = 0;
|
||||
|
||||
int _cull_aabb_for_body(GodotBody2D *p_body, const Rect2 &p_aabb);
|
||||
|
||||
Vector<Vector2> contact_debug;
|
||||
int contact_debug_count = 0;
|
||||
|
||||
friend class GodotPhysicsDirectSpaceState2D;
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ void set_self(const RID &p_self) { self = p_self; }
|
||||
_FORCE_INLINE_ RID get_self() const { return self; }
|
||||
|
||||
void set_default_area(GodotArea2D *p_area) { area = p_area; }
|
||||
GodotArea2D *get_default_area() const { return area; }
|
||||
|
||||
const SelfList<GodotBody2D>::List &get_active_body_list() const;
|
||||
void body_add_to_active_list(SelfList<GodotBody2D> *p_body);
|
||||
void body_remove_from_active_list(SelfList<GodotBody2D> *p_body);
|
||||
void body_add_to_mass_properties_update_list(SelfList<GodotBody2D> *p_body);
|
||||
void body_remove_from_mass_properties_update_list(SelfList<GodotBody2D> *p_body);
|
||||
void area_add_to_moved_list(SelfList<GodotArea2D> *p_area);
|
||||
void area_remove_from_moved_list(SelfList<GodotArea2D> *p_area);
|
||||
const SelfList<GodotArea2D>::List &get_moved_area_list() const;
|
||||
|
||||
void body_add_to_state_query_list(SelfList<GodotBody2D> *p_body);
|
||||
void body_remove_from_state_query_list(SelfList<GodotBody2D> *p_body);
|
||||
|
||||
void area_add_to_monitor_query_list(SelfList<GodotArea2D> *p_area);
|
||||
void area_remove_from_monitor_query_list(SelfList<GodotArea2D> *p_area);
|
||||
|
||||
GodotBroadPhase2D *get_broadphase();
|
||||
|
||||
void add_object(GodotCollisionObject2D *p_object);
|
||||
void remove_object(GodotCollisionObject2D *p_object);
|
||||
const HashSet<GodotCollisionObject2D *> &get_objects() const;
|
||||
|
||||
_FORCE_INLINE_ int get_solver_iterations() const { return solver_iterations; }
|
||||
_FORCE_INLINE_ real_t get_contact_recycle_radius() const { return contact_recycle_radius; }
|
||||
_FORCE_INLINE_ real_t get_contact_max_separation() const { return contact_max_separation; }
|
||||
_FORCE_INLINE_ real_t get_contact_max_allowed_penetration() const { return contact_max_allowed_penetration; }
|
||||
_FORCE_INLINE_ real_t get_contact_bias() const { return contact_bias; }
|
||||
_FORCE_INLINE_ real_t get_constraint_bias() const { return constraint_bias; }
|
||||
_FORCE_INLINE_ real_t get_body_linear_velocity_sleep_threshold() const { return body_linear_velocity_sleep_threshold; }
|
||||
_FORCE_INLINE_ real_t get_body_angular_velocity_sleep_threshold() const { return body_angular_velocity_sleep_threshold; }
|
||||
_FORCE_INLINE_ real_t get_body_time_to_sleep() const { return body_time_to_sleep; }
|
||||
|
||||
void update();
|
||||
void setup();
|
||||
void call_queries();
|
||||
|
||||
bool is_locked() const;
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
real_t get_last_step() const { return last_step; }
|
||||
void set_last_step(real_t p_step) { last_step = p_step; }
|
||||
|
||||
void set_param(PhysicsServer2D::SpaceParameter p_param, real_t p_value);
|
||||
real_t get_param(PhysicsServer2D::SpaceParameter p_param) const;
|
||||
|
||||
void set_island_count(int p_island_count) { island_count = p_island_count; }
|
||||
int get_island_count() const { return island_count; }
|
||||
|
||||
void set_active_objects(int p_active_objects) { active_objects = p_active_objects; }
|
||||
int get_active_objects() const { return active_objects; }
|
||||
|
||||
int get_collision_pairs() const { return collision_pairs; }
|
||||
|
||||
bool test_body_motion(GodotBody2D *p_body, const PhysicsServer2D::MotionParameters &p_parameters, PhysicsServer2D::MotionResult *r_result);
|
||||
|
||||
void set_debug_contacts(int p_amount) { contact_debug.resize(p_amount); }
|
||||
_FORCE_INLINE_ bool is_debugging_contacts() const { return !contact_debug.is_empty(); }
|
||||
_FORCE_INLINE_ void add_debug_contact(const Vector2 &p_contact) {
|
||||
if (contact_debug_count < contact_debug.size()) {
|
||||
contact_debug.write[contact_debug_count++] = p_contact;
|
||||
}
|
||||
}
|
||||
_FORCE_INLINE_ Vector<Vector2> get_debug_contacts() { return contact_debug; }
|
||||
_FORCE_INLINE_ int get_debug_contact_count() { return contact_debug_count; }
|
||||
|
||||
GodotPhysicsDirectSpaceState2D *get_direct_state();
|
||||
|
||||
void set_elapsed_time(ElapsedTime p_time, uint64_t p_msec) { elapsed_time[p_time] = p_msec; }
|
||||
uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; }
|
||||
|
||||
GodotSpace2D();
|
||||
~GodotSpace2D();
|
||||
};
|
307
modules/godot_physics_2d/godot_step_2d.cpp
Normal file
307
modules/godot_physics_2d/godot_step_2d.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
/**************************************************************************/
|
||||
/* godot_step_2d.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "godot_step_2d.h"
|
||||
|
||||
#include "core/object/worker_thread_pool.h"
|
||||
#include "core/os/os.h"
|
||||
#include "godot_constraint_2d.h"
|
||||
|
||||
#define BODY_ISLAND_COUNT_RESERVE 128
|
||||
#define BODY_ISLAND_SIZE_RESERVE 512
|
||||
#define ISLAND_COUNT_RESERVE 128
|
||||
#define ISLAND_SIZE_RESERVE 512
|
||||
#define CONSTRAINT_COUNT_RESERVE 1024
|
||||
|
||||
void GodotStep2D::_populate_island(GodotBody2D *p_body, LocalVector<GodotBody2D *> &p_body_island, LocalVector<GodotConstraint2D *> &p_constraint_island) {
|
||||
p_body->set_island_step(_step);
|
||||
|
||||
if (p_body->get_mode() > PhysicsServer2D::BODY_MODE_KINEMATIC) {
|
||||
// Only rigid bodies are tested for activation.
|
||||
p_body_island.push_back(p_body);
|
||||
}
|
||||
|
||||
for (const Pair<GodotConstraint2D *, int> &E : p_body->get_constraint_list()) {
|
||||
GodotConstraint2D *constraint = E.first;
|
||||
if (constraint->get_island_step() == _step) {
|
||||
continue; // Already processed.
|
||||
}
|
||||
constraint->set_island_step(_step);
|
||||
p_constraint_island.push_back(constraint);
|
||||
all_constraints.push_back(constraint);
|
||||
|
||||
for (int i = 0; i < constraint->get_body_count(); i++) {
|
||||
if (i == E.second) {
|
||||
continue;
|
||||
}
|
||||
GodotBody2D *other_body = constraint->get_body_ptr()[i];
|
||||
if (other_body->get_island_step() == _step) {
|
||||
continue; // Already processed.
|
||||
}
|
||||
if (other_body->get_mode() == PhysicsServer2D::BODY_MODE_STATIC) {
|
||||
continue; // Static bodies don't connect islands.
|
||||
}
|
||||
_populate_island(other_body, p_body_island, p_constraint_island);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotStep2D::_setup_constraint(uint32_t p_constraint_index, void *p_userdata) {
|
||||
GodotConstraint2D *constraint = all_constraints[p_constraint_index];
|
||||
constraint->setup(delta);
|
||||
}
|
||||
|
||||
void GodotStep2D::_pre_solve_island(LocalVector<GodotConstraint2D *> &p_constraint_island) const {
|
||||
uint32_t constraint_count = p_constraint_island.size();
|
||||
uint32_t valid_constraint_count = 0;
|
||||
for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
|
||||
GodotConstraint2D *constraint = p_constraint_island[constraint_index];
|
||||
if (p_constraint_island[constraint_index]->pre_solve(delta)) {
|
||||
// Keep this constraint for solving.
|
||||
p_constraint_island[valid_constraint_count++] = constraint;
|
||||
}
|
||||
}
|
||||
p_constraint_island.resize(valid_constraint_count);
|
||||
}
|
||||
|
||||
void GodotStep2D::_solve_island(uint32_t p_island_index, void *p_userdata) const {
|
||||
const LocalVector<GodotConstraint2D *> &constraint_island = constraint_islands[p_island_index];
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
uint32_t constraint_count = constraint_island.size();
|
||||
for (uint32_t constraint_index = 0; constraint_index < constraint_count; ++constraint_index) {
|
||||
constraint_island[constraint_index]->solve(delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotStep2D::_check_suspend(LocalVector<GodotBody2D *> &p_body_island) const {
|
||||
bool can_sleep = true;
|
||||
|
||||
uint32_t body_count = p_body_island.size();
|
||||
for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
|
||||
GodotBody2D *body = p_body_island[body_index];
|
||||
|
||||
if (!body->sleep_test(delta)) {
|
||||
can_sleep = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Put all to sleep or wake up everyone.
|
||||
for (uint32_t body_index = 0; body_index < body_count; ++body_index) {
|
||||
GodotBody2D *body = p_body_island[body_index];
|
||||
|
||||
bool active = body->is_active();
|
||||
|
||||
if (active == can_sleep) {
|
||||
body->set_active(!can_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GodotStep2D::step(GodotSpace2D *p_space, real_t p_delta) {
|
||||
p_space->lock(); // can't access space during this
|
||||
|
||||
p_space->setup(); //update inertias, etc
|
||||
|
||||
p_space->set_last_step(p_delta);
|
||||
|
||||
iterations = p_space->get_solver_iterations();
|
||||
delta = p_delta;
|
||||
|
||||
const SelfList<GodotBody2D>::List *body_list = &p_space->get_active_body_list();
|
||||
|
||||
/* INTEGRATE FORCES */
|
||||
|
||||
uint64_t profile_begtime = OS::get_singleton()->get_ticks_usec();
|
||||
uint64_t profile_endtime = 0;
|
||||
|
||||
int active_count = 0;
|
||||
|
||||
const SelfList<GodotBody2D> *b = body_list->first();
|
||||
while (b) {
|
||||
b->self()->integrate_forces(p_delta);
|
||||
b = b->next();
|
||||
active_count++;
|
||||
}
|
||||
|
||||
p_space->set_active_objects(active_count);
|
||||
|
||||
// Update the broadphase to register collision pairs.
|
||||
p_space->update();
|
||||
|
||||
{ //profile
|
||||
profile_endtime = OS::get_singleton()->get_ticks_usec();
|
||||
p_space->set_elapsed_time(GodotSpace2D::ELAPSED_TIME_INTEGRATE_FORCES, profile_endtime - profile_begtime);
|
||||
profile_begtime = profile_endtime;
|
||||
}
|
||||
|
||||
/* GENERATE CONSTRAINT ISLANDS FOR MOVING AREAS */
|
||||
|
||||
uint32_t island_count = 0;
|
||||
|
||||
const SelfList<GodotArea2D>::List &aml = p_space->get_moved_area_list();
|
||||
|
||||
while (aml.first()) {
|
||||
for (GodotConstraint2D *E : aml.first()->self()->get_constraints()) {
|
||||
GodotConstraint2D *constraint = E;
|
||||
if (constraint->get_island_step() == _step) {
|
||||
continue;
|
||||
}
|
||||
constraint->set_island_step(_step);
|
||||
|
||||
// Each constraint can be on a separate island for areas as there's no solving phase.
|
||||
++island_count;
|
||||
if (constraint_islands.size() < island_count) {
|
||||
constraint_islands.resize(island_count);
|
||||
}
|
||||
LocalVector<GodotConstraint2D *> &constraint_island = constraint_islands[island_count - 1];
|
||||
constraint_island.clear();
|
||||
|
||||
all_constraints.push_back(constraint);
|
||||
constraint_island.push_back(constraint);
|
||||
}
|
||||
p_space->area_remove_from_moved_list((SelfList<GodotArea2D> *)aml.first()); //faster to remove here
|
||||
}
|
||||
|
||||
/* GENERATE CONSTRAINT ISLANDS FOR ACTIVE RIGID BODIES */
|
||||
|
||||
b = body_list->first();
|
||||
|
||||
uint32_t body_island_count = 0;
|
||||
|
||||
while (b) {
|
||||
GodotBody2D *body = b->self();
|
||||
|
||||
if (body->get_island_step() != _step) {
|
||||
++body_island_count;
|
||||
if (body_islands.size() < body_island_count) {
|
||||
body_islands.resize(body_island_count);
|
||||
}
|
||||
LocalVector<GodotBody2D *> &body_island = body_islands[body_island_count - 1];
|
||||
body_island.clear();
|
||||
body_island.reserve(BODY_ISLAND_SIZE_RESERVE);
|
||||
|
||||
++island_count;
|
||||
if (constraint_islands.size() < island_count) {
|
||||
constraint_islands.resize(island_count);
|
||||
}
|
||||
LocalVector<GodotConstraint2D *> &constraint_island = constraint_islands[island_count - 1];
|
||||
constraint_island.clear();
|
||||
constraint_island.reserve(ISLAND_SIZE_RESERVE);
|
||||
|
||||
_populate_island(body, body_island, constraint_island);
|
||||
|
||||
if (body_island.is_empty()) {
|
||||
--body_island_count;
|
||||
}
|
||||
|
||||
if (constraint_island.is_empty()) {
|
||||
--island_count;
|
||||
}
|
||||
}
|
||||
b = b->next();
|
||||
}
|
||||
|
||||
p_space->set_island_count((int)island_count);
|
||||
|
||||
{ //profile
|
||||
profile_endtime = OS::get_singleton()->get_ticks_usec();
|
||||
p_space->set_elapsed_time(GodotSpace2D::ELAPSED_TIME_GENERATE_ISLANDS, profile_endtime - profile_begtime);
|
||||
profile_begtime = profile_endtime;
|
||||
}
|
||||
|
||||
/* SETUP CONSTRAINTS / PROCESS COLLISIONS */
|
||||
|
||||
uint32_t total_constraint_count = all_constraints.size();
|
||||
WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep2D::_setup_constraint, nullptr, total_constraint_count, -1, true, SNAME("Physics2DConstraintSetup"));
|
||||
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
|
||||
|
||||
{ //profile
|
||||
profile_endtime = OS::get_singleton()->get_ticks_usec();
|
||||
p_space->set_elapsed_time(GodotSpace2D::ELAPSED_TIME_SETUP_CONSTRAINTS, profile_endtime - profile_begtime);
|
||||
profile_begtime = profile_endtime;
|
||||
}
|
||||
|
||||
/* PRE-SOLVE CONSTRAINT ISLANDS */
|
||||
|
||||
// WARNING: This doesn't run on threads, because it involves thread-unsafe processing.
|
||||
for (uint32_t island_index = 0; island_index < island_count; ++island_index) {
|
||||
_pre_solve_island(constraint_islands[island_index]);
|
||||
}
|
||||
|
||||
/* SOLVE CONSTRAINT ISLANDS */
|
||||
|
||||
// WARNING: `_solve_island` modifies the constraint islands for optimization purpose,
|
||||
// their content is not reliable after these calls and shouldn't be used anymore.
|
||||
group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &GodotStep2D::_solve_island, nullptr, island_count, -1, true, SNAME("Physics2DConstraintSolveIslands"));
|
||||
WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
|
||||
|
||||
{ //profile
|
||||
profile_endtime = OS::get_singleton()->get_ticks_usec();
|
||||
p_space->set_elapsed_time(GodotSpace2D::ELAPSED_TIME_SOLVE_CONSTRAINTS, profile_endtime - profile_begtime);
|
||||
profile_begtime = profile_endtime;
|
||||
}
|
||||
|
||||
/* INTEGRATE VELOCITIES */
|
||||
|
||||
b = body_list->first();
|
||||
while (b) {
|
||||
const SelfList<GodotBody2D> *n = b->next();
|
||||
b->self()->integrate_velocities(p_delta);
|
||||
b = n; // in case it shuts itself down
|
||||
}
|
||||
|
||||
/* SLEEP / WAKE UP ISLANDS */
|
||||
|
||||
for (uint32_t island_index = 0; island_index < body_island_count; ++island_index) {
|
||||
_check_suspend(body_islands[island_index]);
|
||||
}
|
||||
|
||||
{ //profile
|
||||
profile_endtime = OS::get_singleton()->get_ticks_usec();
|
||||
p_space->set_elapsed_time(GodotSpace2D::ELAPSED_TIME_INTEGRATE_VELOCITIES, profile_endtime - profile_begtime);
|
||||
//profile_begtime=profile_endtime;
|
||||
}
|
||||
|
||||
all_constraints.clear();
|
||||
|
||||
p_space->unlock();
|
||||
_step++;
|
||||
}
|
||||
|
||||
GodotStep2D::GodotStep2D() {
|
||||
body_islands.reserve(BODY_ISLAND_COUNT_RESERVE);
|
||||
constraint_islands.reserve(ISLAND_COUNT_RESERVE);
|
||||
all_constraints.reserve(CONSTRAINT_COUNT_RESERVE);
|
||||
}
|
||||
|
||||
GodotStep2D::~GodotStep2D() {
|
||||
}
|
57
modules/godot_physics_2d/godot_step_2d.h
Normal file
57
modules/godot_physics_2d/godot_step_2d.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/**************************************************************************/
|
||||
/* godot_step_2d.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "godot_space_2d.h"
|
||||
|
||||
#include "core/templates/local_vector.h"
|
||||
|
||||
class GodotStep2D {
|
||||
uint64_t _step = 1;
|
||||
|
||||
int iterations = 0;
|
||||
real_t delta = 0.0;
|
||||
|
||||
LocalVector<LocalVector<GodotBody2D *>> body_islands;
|
||||
LocalVector<LocalVector<GodotConstraint2D *>> constraint_islands;
|
||||
LocalVector<GodotConstraint2D *> all_constraints;
|
||||
|
||||
void _populate_island(GodotBody2D *p_body, LocalVector<GodotBody2D *> &p_body_island, LocalVector<GodotConstraint2D *> &p_constraint_island);
|
||||
void _setup_constraint(uint32_t p_constraint_index, void *p_userdata = nullptr);
|
||||
void _pre_solve_island(LocalVector<GodotConstraint2D *> &p_constraint_island) const;
|
||||
void _solve_island(uint32_t p_island_index, void *p_userdata = nullptr) const;
|
||||
void _check_suspend(LocalVector<GodotBody2D *> &p_body_island) const;
|
||||
|
||||
public:
|
||||
void step(GodotSpace2D *p_space, real_t p_delta);
|
||||
GodotStep2D();
|
||||
~GodotStep2D();
|
||||
};
|
62
modules/godot_physics_2d/register_types.cpp
Normal file
62
modules/godot_physics_2d/register_types.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
/**************************************************************************/
|
||||
/* register_types.cpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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 "register_types.h"
|
||||
|
||||
#include "core/config/project_settings.h"
|
||||
#include "godot_physics_server_2d.h"
|
||||
#include "servers/physics_server_2d.h"
|
||||
#include "servers/physics_server_2d_wrap_mt.h"
|
||||
|
||||
static PhysicsServer2D *_createGodotPhysics2DCallback() {
|
||||
#ifdef THREADS_ENABLED
|
||||
bool using_threads = GLOBAL_GET("physics/2d/run_on_separate_thread");
|
||||
#else
|
||||
bool using_threads = false;
|
||||
#endif
|
||||
|
||||
PhysicsServer2D *physics_server_2d = memnew(GodotPhysicsServer2D(using_threads));
|
||||
|
||||
return memnew(PhysicsServer2DWrapMT(physics_server_2d, using_threads));
|
||||
}
|
||||
|
||||
void initialize_godot_physics_2d_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
return;
|
||||
}
|
||||
PhysicsServer2DManager::get_singleton()->register_server("GodotPhysics2D", callable_mp_static(_createGodotPhysics2DCallback));
|
||||
PhysicsServer2DManager::get_singleton()->set_default_server("GodotPhysics2D");
|
||||
}
|
||||
|
||||
void uninitialize_godot_physics_2d_module(ModuleInitializationLevel p_level) {
|
||||
if (p_level != MODULE_INITIALIZATION_LEVEL_SERVERS) {
|
||||
return;
|
||||
}
|
||||
}
|
36
modules/godot_physics_2d/register_types.h
Normal file
36
modules/godot_physics_2d/register_types.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/**************************************************************************/
|
||||
/* register_types.h */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* 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. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "modules/register_module_types.h"
|
||||
|
||||
void initialize_godot_physics_2d_module(ModuleInitializationLevel p_level);
|
||||
void uninitialize_godot_physics_2d_module(ModuleInitializationLevel p_level);
|
Reference in New Issue
Block a user