godot/servers/physics/space_sw.cpp
Rémi Verschelde d8223ffa75 Welcome in 2017, dear changelog reader!
That year should bring the long-awaited OpenGL ES 3.0 compatible renderer
with state-of-the-art rendering techniques tuned to work as low as middle
end handheld devices - without compromising with the possibilities given
for higher end desktop games of course. Great times ahead for the Godot
community and the gamers that will play our games!

(cherry picked from commit c7bc44d5ad)
2017-01-12 19:15:30 +01:00

749 lines
21 KiB
C++

/*************************************************************************/
/* space_sw.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* http://www.godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2017 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 "globals.h"
#include "space_sw.h"
#include "collision_solver_sw.h"
#include "physics_server_sw.h"
_FORCE_INLINE_ static bool _match_object_type_query(CollisionObjectSW *p_object, uint32_t p_layer_mask, uint32_t p_type_mask) {
if (p_object->get_type()==CollisionObjectSW::TYPE_AREA)
return p_type_mask&PhysicsDirectSpaceState::TYPE_MASK_AREA;
if ((p_object->get_layer_mask()&p_layer_mask)==0)
return false;
BodySW *body = static_cast<BodySW*>(p_object);
return (1<<body->get_mode())&p_type_mask;
}
bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3& p_from, const Vector3& p_to, RayResult &r_result, const Set<RID>& p_exclude, uint32_t p_layer_mask, uint32_t p_object_type_mask, bool p_pick_ray) {
ERR_FAIL_COND_V(space->locked,false);
Vector3 begin,end;
Vector3 normal;
begin=p_from;
end=p_to;
normal=(end-begin).normalized();
int amount = space->broadphase->cull_segment(begin,end,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
//todo, create another array tha references results, compute AABBs and check closest point to ray origin, sort, and stop evaluating results when beyond first collision
bool collided=false;
Vector3 res_point,res_normal;
int res_shape;
const CollisionObjectSW *res_obj;
real_t min_d=1e10;
for(int i=0;i<amount;i++) {
if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask))
continue;
if (p_pick_ray && !(static_cast<CollisionObjectSW*>(space->intersection_query_results[i])->is_ray_pickable()))
continue;
if (p_exclude.has( space->intersection_query_results[i]->get_self()))
continue;
const CollisionObjectSW *col_obj=space->intersection_query_results[i];
int shape_idx=space->intersection_query_subindex_results[i];
Transform inv_xform = col_obj->get_shape_inv_transform(shape_idx) * col_obj->get_inv_transform();
Vector3 local_from = inv_xform.xform(begin);
Vector3 local_to = inv_xform.xform(end);
const ShapeSW *shape = col_obj->get_shape(shape_idx);
Vector3 shape_point,shape_normal;
if (shape->intersect_segment(local_from,local_to,shape_point,shape_normal)) {
Transform xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
shape_point=xform.xform(shape_point);
real_t ld = normal.dot(shape_point);
if (ld<min_d) {
min_d=ld;
res_point=shape_point;
res_normal=inv_xform.basis.xform_inv(shape_normal).normalized();
res_shape=shape_idx;
res_obj=col_obj;
collided=true;
}
}
}
if (!collided)
return false;
r_result.collider_id=res_obj->get_instance_id();
if (r_result.collider_id!=0)
r_result.collider=ObjectDB::get_instance(r_result.collider_id);
else
r_result.collider=NULL;
r_result.normal=res_normal;
r_result.position=res_point;
r_result.rid=res_obj->get_self();
r_result.shape=res_shape;
return true;
}
int PhysicsDirectSpaceStateSW::intersect_shape(const RID& p_shape, const Transform& p_xform,float p_margin,ShapeResult *r_results,int p_result_max,const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) {
if (p_result_max<=0)
return 0;
ShapeSW *shape = static_cast<PhysicsServerSW*>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape,0);
AABB aabb = p_xform.xform(shape->get_aabb());
int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
int cc=0;
//Transform ai = p_xform.affine_inverse();
for(int i=0;i<amount;i++) {
if (cc>=p_result_max)
break;
if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask))
continue;
//area cant be picked by ray (default)
if (p_exclude.has( space->intersection_query_results[i]->get_self()))
continue;
const CollisionObjectSW *col_obj=space->intersection_query_results[i];
int shape_idx=space->intersection_query_subindex_results[i];
if (!CollisionSolverSW::solve_static(shape,p_xform,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), NULL,NULL,NULL,p_margin,0))
continue;
if (r_results) {
r_results[cc].collider_id=col_obj->get_instance_id();
if (r_results[cc].collider_id!=0)
r_results[cc].collider=ObjectDB::get_instance(r_results[cc].collider_id);
else
r_results[cc].collider=NULL;
r_results[cc].rid=col_obj->get_self();
r_results[cc].shape=shape_idx;
}
cc++;
}
return cc;
}
bool PhysicsDirectSpaceStateSW::cast_motion(const RID& p_shape, const Transform& p_xform,const Vector3& p_motion,float p_margin,float &p_closest_safe,float &p_closest_unsafe, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask,ShapeRestInfo *r_info) {
ShapeSW *shape = static_cast<PhysicsServerSW*>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape,false);
AABB aabb = p_xform.xform(shape->get_aabb());
aabb=aabb.merge(AABB(aabb.pos+p_motion,aabb.size)); //motion
aabb=aabb.grow(p_margin);
//if (p_motion!=Vector3())
// print_line(p_motion);
int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
float best_safe=1;
float best_unsafe=1;
Transform xform_inv = p_xform.affine_inverse();
MotionShapeSW mshape;
mshape.shape=shape;
mshape.motion=xform_inv.basis.xform(p_motion);
bool best_first=true;
Vector3 closest_A,closest_B;
for(int i=0;i<amount;i++) {
if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask))
continue;
if (p_exclude.has( space->intersection_query_results[i]->get_self()))
continue; //ignore excluded
const CollisionObjectSW *col_obj=space->intersection_query_results[i];
int shape_idx=space->intersection_query_subindex_results[i];
Vector3 point_A,point_B;
Vector3 sep_axis=p_motion.normalized();
Transform col_obj_xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
//test initial overlap, does it collide if going all the way?
if (CollisionSolverSW::solve_distance(&mshape,p_xform,col_obj->get_shape(shape_idx),col_obj_xform,point_A,point_B,aabb,&sep_axis)) {
//print_line("failed motion cast (no collision)");
continue;
}
//test initial overlap
#if 0
if (CollisionSolverSW::solve_static(shape,p_xform,col_obj->get_shape(shape_idx),col_obj_xform,NULL,NULL,&sep_axis)) {
print_line("failed initial cast (collision at begining)");
return false;
}
#else
sep_axis=p_motion.normalized();
if (!CollisionSolverSW::solve_distance(shape,p_xform,col_obj->get_shape(shape_idx),col_obj_xform,point_A,point_B,aabb,&sep_axis)) {
//print_line("failed motion cast (no collision)");
return false;
}
#endif
//just do kinematic solving
float low=0;
float hi=1;
Vector3 mnormal=p_motion.normalized();
for(int i=0;i<8;i++) { //steps should be customizable..
float ofs = (low+hi)*0.5;
Vector3 sep=mnormal; //important optimization for this to work fast enough
mshape.motion=xform_inv.basis.xform(p_motion*ofs);
Vector3 lA,lB;
bool collided = !CollisionSolverSW::solve_distance(&mshape,p_xform,col_obj->get_shape(shape_idx),col_obj_xform,lA,lB,aabb,&sep);
if (collided) {
//print_line(itos(i)+": "+rtos(ofs));
hi=ofs;
} else {
point_A=lA;
point_B=lB;
low=ofs;
}
}
if (low<best_safe) {
best_first=true; //force reset
best_safe=low;
best_unsafe=hi;
}
if (r_info && (best_first || (point_A.distance_squared_to(point_B) < closest_A.distance_squared_to(closest_B) && low<=best_safe))) {
closest_A=point_A;
closest_B=point_B;
r_info->collider_id=col_obj->get_instance_id();
r_info->rid=col_obj->get_self();
r_info->shape=shape_idx;
r_info->point=closest_B;
r_info->normal=(closest_A-closest_B).normalized();
best_first=false;
if (col_obj->get_type()==CollisionObjectSW::TYPE_BODY) {
const BodySW *body=static_cast<const BodySW*>(col_obj);
r_info->linear_velocity= body->get_linear_velocity() + (body->get_angular_velocity()).cross(body->get_transform().origin - closest_B);
}
}
}
p_closest_safe=best_safe;
p_closest_unsafe=best_unsafe;
return true;
}
bool PhysicsDirectSpaceStateSW::collide_shape(RID p_shape, const Transform& p_shape_xform,float p_margin,Vector3 *r_results,int p_result_max,int &r_result_count, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask){
if (p_result_max<=0)
return 0;
ShapeSW *shape = static_cast<PhysicsServerSW*>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape,0);
AABB aabb = p_shape_xform.xform(shape->get_aabb());
aabb=aabb.grow(p_margin);
int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
bool collided=false;
r_result_count=0;
PhysicsServerSW::CollCbkData cbk;
cbk.max=p_result_max;
cbk.amount=0;
cbk.ptr=r_results;
CollisionSolverSW::CallbackResult cbkres=NULL;
PhysicsServerSW::CollCbkData *cbkptr=NULL;
if (p_result_max>0) {
cbkptr=&cbk;
cbkres=PhysicsServerSW::_shape_col_cbk;
}
for(int i=0;i<amount;i++) {
if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask))
continue;
const CollisionObjectSW *col_obj=space->intersection_query_results[i];
int shape_idx=space->intersection_query_subindex_results[i];
if (p_exclude.has( col_obj->get_self() )) {
continue;
}
//print_line("AGAINST: "+itos(col_obj->get_self().get_id())+":"+itos(shape_idx));
//print_line("THE ABBB: "+(col_obj->get_transform() * col_obj->get_shape_transform(shape_idx)).xform(col_obj->get_shape(shape_idx)->get_aabb()));
if (CollisionSolverSW::solve_static(shape,p_shape_xform,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),cbkres,cbkptr,NULL,p_margin)) {
collided=true;
}
}
r_result_count=cbk.amount;
return collided;
}
struct _RestCallbackData {
const CollisionObjectSW *object;
const CollisionObjectSW *best_object;
int shape;
int best_shape;
Vector3 best_contact;
Vector3 best_normal;
float best_len;
};
static void _rest_cbk_result(const Vector3& p_point_A,const Vector3& p_point_B,void *p_userdata) {
_RestCallbackData *rd=(_RestCallbackData*)p_userdata;
Vector3 contact_rel = p_point_B - p_point_A;
float len = contact_rel.length();
if (len <= rd->best_len)
return;
rd->best_len=len;
rd->best_contact=p_point_B;
rd->best_normal=contact_rel/len;
rd->best_object=rd->object;
rd->best_shape=rd->shape;
}
bool PhysicsDirectSpaceStateSW::rest_info(RID p_shape, const Transform& p_shape_xform,float p_margin,ShapeRestInfo *r_info, const Set<RID>& p_exclude,uint32_t p_layer_mask,uint32_t p_object_type_mask) {
ShapeSW *shape = static_cast<PhysicsServerSW*>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape,0);
AABB aabb = p_shape_xform.xform(shape->get_aabb());
aabb=aabb.grow(p_margin);
int amount = space->broadphase->cull_aabb(aabb,space->intersection_query_results,SpaceSW::INTERSECTION_QUERY_MAX,space->intersection_query_subindex_results);
_RestCallbackData rcd;
rcd.best_len=0;
rcd.best_object=NULL;
rcd.best_shape=0;
for(int i=0;i<amount;i++) {
if (!_match_object_type_query(space->intersection_query_results[i],p_layer_mask,p_object_type_mask))
continue;
const CollisionObjectSW *col_obj=space->intersection_query_results[i];
int shape_idx=space->intersection_query_subindex_results[i];
if (p_exclude.has( col_obj->get_self() ))
continue;
rcd.object=col_obj;
rcd.shape=shape_idx;
bool sc = CollisionSolverSW::solve_static(shape,p_shape_xform,col_obj->get_shape(shape_idx),col_obj->get_transform() * col_obj->get_shape_transform(shape_idx),_rest_cbk_result,&rcd,NULL,p_margin);
if (!sc)
continue;
}
if (rcd.best_len==0)
return false;
r_info->collider_id=rcd.best_object->get_instance_id();
r_info->shape=rcd.best_shape;
r_info->normal=rcd.best_normal;
r_info->point=rcd.best_contact;
r_info->rid=rcd.best_object->get_self();
if (rcd.best_object->get_type()==CollisionObjectSW::TYPE_BODY) {
const BodySW *body = static_cast<const BodySW*>(rcd.best_object);
r_info->linear_velocity = body->get_linear_velocity() +
(body->get_angular_velocity()).cross(body->get_transform().origin-rcd.best_contact);// * mPos);
} else {
r_info->linear_velocity=Vector3();
}
return true;
}
PhysicsDirectSpaceStateSW::PhysicsDirectSpaceStateSW() {
space=NULL;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////
void* SpaceSW::_broadphase_pair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_self) {
CollisionObjectSW::Type type_A=A->get_type();
CollisionObjectSW::Type type_B=B->get_type();
if (type_A>type_B) {
SWAP(A,B);
SWAP(p_subindex_A,p_subindex_B);
SWAP(type_A,type_B);
}
SpaceSW *self = (SpaceSW*)p_self;
self->collision_pairs++;
if (type_A==CollisionObjectSW::TYPE_AREA) {
AreaSW *area=static_cast<AreaSW*>(A);
if (type_B==CollisionObjectSW::TYPE_AREA) {
AreaSW *area_b=static_cast<AreaSW*>(B);
Area2PairSW *area2_pair = memnew(Area2PairSW(area_b,p_subindex_B,area,p_subindex_A) );
return area2_pair;
} else {
BodySW *body=static_cast<BodySW*>(B);
AreaPairSW *area_pair = memnew(AreaPairSW(body,p_subindex_B,area,p_subindex_A) );
return area_pair;
}
} else {
BodyPairSW *b = memnew( BodyPairSW((BodySW*)A,p_subindex_A,(BodySW*)B,p_subindex_B) );
return b;
}
return NULL;
}
void SpaceSW::_broadphase_unpair(CollisionObjectSW *A,int p_subindex_A,CollisionObjectSW *B,int p_subindex_B,void *p_data,void *p_self) {
SpaceSW *self = (SpaceSW*)p_self;
self->collision_pairs--;
ConstraintSW *c = (ConstraintSW*)p_data;
memdelete(c);
}
const SelfList<BodySW>::List& SpaceSW::get_active_body_list() const {
return active_list;
}
void SpaceSW::body_add_to_active_list(SelfList<BodySW>* p_body) {
active_list.add(p_body);
}
void SpaceSW::body_remove_from_active_list(SelfList<BodySW>* p_body) {
active_list.remove(p_body);
}
void SpaceSW::body_add_to_inertia_update_list(SelfList<BodySW>* p_body) {
inertia_update_list.add(p_body);
}
void SpaceSW::body_remove_from_inertia_update_list(SelfList<BodySW>* p_body) {
inertia_update_list.remove(p_body);
}
BroadPhaseSW *SpaceSW::get_broadphase() {
return broadphase;
}
void SpaceSW::add_object(CollisionObjectSW *p_object) {
ERR_FAIL_COND( objects.has(p_object) );
objects.insert(p_object);
}
void SpaceSW::remove_object(CollisionObjectSW *p_object) {
ERR_FAIL_COND( !objects.has(p_object) );
objects.erase(p_object);
}
const Set<CollisionObjectSW*> &SpaceSW::get_objects() const {
return objects;
}
void SpaceSW::body_add_to_state_query_list(SelfList<BodySW>* p_body) {
state_query_list.add(p_body);
}
void SpaceSW::body_remove_from_state_query_list(SelfList<BodySW>* p_body) {
state_query_list.remove(p_body);
}
void SpaceSW::area_add_to_monitor_query_list(SelfList<AreaSW>* p_area) {
monitor_query_list.add(p_area);
}
void SpaceSW::area_remove_from_monitor_query_list(SelfList<AreaSW>* p_area) {
monitor_query_list.remove(p_area);
}
void SpaceSW::area_add_to_moved_list(SelfList<AreaSW>* p_area) {
area_moved_list.add(p_area);
}
void SpaceSW::area_remove_from_moved_list(SelfList<AreaSW>* p_area) {
area_moved_list.remove(p_area);
}
const SelfList<AreaSW>::List& SpaceSW::get_moved_area_list() const {
return area_moved_list;
}
void SpaceSW::call_queries() {
while(state_query_list.first()) {
BodySW * b = state_query_list.first()->self();
b->call_queries();
state_query_list.remove(state_query_list.first());
}
while(monitor_query_list.first()) {
AreaSW * a = monitor_query_list.first()->self();
a->call_queries();
monitor_query_list.remove(monitor_query_list.first());
}
}
void SpaceSW::setup() {
contact_debug_count=0;
while(inertia_update_list.first()) {
inertia_update_list.first()->self()->update_inertias();
inertia_update_list.remove(inertia_update_list.first());
}
}
void SpaceSW::update() {
broadphase->update();
}
void SpaceSW::set_param(PhysicsServer::SpaceParameter p_param, real_t p_value) {
switch(p_param) {
case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: contact_recycle_radius=p_value; break;
case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: contact_max_separation=p_value; break;
case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: contact_max_allowed_penetration=p_value; break;
case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: body_linear_velocity_sleep_threshold=p_value; break;
case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: body_angular_velocity_sleep_threshold=p_value; break;
case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: body_time_to_sleep=p_value; break;
case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: body_angular_velocity_damp_ratio=p_value; break;
case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: constraint_bias=p_value; break;
}
}
real_t SpaceSW::get_param(PhysicsServer::SpaceParameter p_param) const {
switch(p_param) {
case PhysicsServer::SPACE_PARAM_CONTACT_RECYCLE_RADIUS: return contact_recycle_radius;
case PhysicsServer::SPACE_PARAM_CONTACT_MAX_SEPARATION: return contact_max_separation;
case PhysicsServer::SPACE_PARAM_BODY_MAX_ALLOWED_PENETRATION: return contact_max_allowed_penetration;
case PhysicsServer::SPACE_PARAM_BODY_LINEAR_VELOCITY_SLEEP_TRESHOLD: return body_linear_velocity_sleep_threshold;
case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_SLEEP_TRESHOLD: return body_angular_velocity_sleep_threshold;
case PhysicsServer::SPACE_PARAM_BODY_TIME_TO_SLEEP: return body_time_to_sleep;
case PhysicsServer::SPACE_PARAM_BODY_ANGULAR_VELOCITY_DAMP_RATIO: return body_angular_velocity_damp_ratio;
case PhysicsServer::SPACE_PARAM_CONSTRAINT_DEFAULT_BIAS: return constraint_bias;
}
return 0;
}
void SpaceSW::lock() {
locked=true;
}
void SpaceSW::unlock() {
locked=false;
}
bool SpaceSW::is_locked() const {
return locked;
}
PhysicsDirectSpaceStateSW *SpaceSW::get_direct_state() {
return direct_access;
}
SpaceSW::SpaceSW() {
collision_pairs=0;
active_objects=0;
island_count=0;
contact_debug_count=0;
locked=false;
contact_recycle_radius=0.01;
contact_max_separation=0.05;
contact_max_allowed_penetration= 0.01;
constraint_bias = 0.01;
body_linear_velocity_sleep_threshold=GLOBAL_DEF("physics/sleep_threshold_linear",0.1);
body_angular_velocity_sleep_threshold=GLOBAL_DEF("physics/sleep_threshold_angular", (8.0 / 180.0 * Math_PI) );
body_time_to_sleep=GLOBAL_DEF("physics/time_before_sleep",0.5);
body_angular_velocity_damp_ratio=10;
broadphase = BroadPhaseSW::create_func();
broadphase->set_pair_callback(_broadphase_pair,this);
broadphase->set_unpair_callback(_broadphase_unpair,this);
area=NULL;
direct_access = memnew( PhysicsDirectSpaceStateSW );
direct_access->space=this;
for(int i=0;i<ELAPSED_TIME_MAX;i++)
elapsed_time[i]=0;
}
SpaceSW::~SpaceSW() {
memdelete(broadphase);
memdelete( direct_access );
}