From: vortex Date: Thu, 16 Dec 2010 17:49:24 +0000 (+0000) Subject: ODE improvements: X-Git-Tag: xonotic-v0.5.0~438^2~174 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=8b68ee05515266fdb1b20b06b0c4c04646fdc397;p=xonotic%2Fdarkplaces.git ODE improvements: - physics_enable builtin (enable/disable objects) - physics_addforce (add force to certain point in object) - physics_addtorgue (add relative torgue) - extension documented as "DP_PHYSICS_ODE", added additional check if lib is enabled in checextension() - #ifdef'ed usage of dWorldStepFast1 and make it off by default as it's been removed from ODE trunk - added "collision" mesh support, if there is some mesh with "collision" texture, all other meshes are ignored and that mesh is used. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@10667 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/clvm_cmds.c b/clvm_cmds.c index c95864ba..a30ce3c0 100644 --- a/clvm_cmds.c +++ b/clvm_cmds.c @@ -4544,9 +4544,9 @@ NULL, // #536 NULL, // #537 NULL, // #538 NULL, // #539 -NULL, // #540 -NULL, // #541 -NULL, // #542 +VM_physics_enable, // #540 void(entity e, float physics_enabled) physics_enable = #540; (DP_PHYSICS_ODE) +VM_physics_addforce, // #541 void(entity e, vector force, vector relative_ofs) physics_addforce = #541; (DP_PHYSICS_ODE) +VM_physics_addtorgue, // #542 void(entity e, vector torgue) physics_addtorgue = #542; (DP_PHYSICS_ODE) NULL, // #543 NULL, // #544 NULL, // #545 diff --git a/dpdefs/dpextensions.qc b/dpdefs/dpextensions.qc index afe67c9b..06361ae6 100644 --- a/dpdefs/dpextensions.qc +++ b/dpdefs/dpextensions.qc @@ -1572,6 +1572,38 @@ void(float effectnum, vector org, vector vel, float howmany) pointparticles = #3 //description: //.movement vector contains the movement input from the player, allowing QC to do as it wishs with the input, and SV_PlayerPhysics will completely replace the player physics if present (works for all MOVETYPE's), see darkplaces mod source for example of this function (in playermovement.qc, adds HalfLife ladders support, as well as acceleration/deceleration while airborn (rather than the quake sudden-stop while airborn), and simplifies the physics a bit) +//DP_PHYSICS_ODE +//idea: LordHavoc +//darkplaces implementation: LordHavoc +//globals: +//new movetypes: +const float MOVETYPE_PHYSICS = 32; // need to be set before any physics_* builtins applied +//new solid types: +const float SOLID_PHYSICS_BOX = 32; +const float SOLID_PHYSICS_SPHERE = 33; +const float SOLID_PHYSICS_CAPSULE = 34; +//SOLID_BSP; +//joint types: +const float JOINTTYPE_POINT = 1; +const float JOINTTYPE_HINGE = 2; +const float JOINTTYPE_SLIDER = 3; +const float JOINTTYPE_UNIVERSAL = 4; +const float JOINTTYPE_HINGE2 = 5; +const float JOINTTYPE_FIXED = -1; +//field definitions: +.float mass; // ODE mass, standart value is 1 +.float bouncefactor; +.float bouncestop; +.float jointtype; +//builtin definitions: +void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object +void(entity e, vector force, vector force_pos) physics_addforce = #541; // apply a force from certain origin, length of force vector is power of force +void(entity e, vector torgue) physics_addtorgue = #542; // add relative torgue +//description: provides Open Dynamics Engine support, requires extenal dll to be present or engine compiled with statical link option +//be sure to checkextension for it to know if library i loaded and ready, also to enable physics set "physice_ode" cvar to 1 +//note: this extension is highly experimental and may be unstable +//note: use SOLID_BSP on entities to get a trimesh collision models on them + //DP_SV_PRINT //idea: id Software (QuakeWorld Server) //darkplaces implementation: Black, LordHavoc diff --git a/model_shared.c b/model_shared.c index 74af7c59..771311e9 100644 --- a/model_shared.c +++ b/model_shared.c @@ -1364,9 +1364,10 @@ void Mod_ShadowMesh_Free(shadowmesh_t *mesh) void Mod_CreateCollisionMesh(dp_model_t *mod) { - int k; - int numcollisionmeshtriangles; - const msurface_t *surface; + int k, numcollisionmeshtriangles; + qboolean usesinglecollisionmesh = false; + const msurface_t *surface = NULL; + mempool_t *mempool = mod->mempool; if (!mempool && mod->brush.parentmodel) mempool = mod->brush.parentmodel->mempool; @@ -1376,17 +1377,28 @@ void Mod_CreateCollisionMesh(dp_model_t *mod) for (k = 0;k < mod->nummodelsurfaces;k++) { surface = mod->data_surfaces + mod->firstmodelsurface + k; + if (!strcmp(surface->texture->name, "collision")) // found collision mesh + { + usesinglecollisionmesh = true; + numcollisionmeshtriangles = surface->num_triangles; + break; + } if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID)) continue; numcollisionmeshtriangles += surface->num_triangles; } mod->brush.collisionmesh = Mod_ShadowMesh_Begin(mempool, numcollisionmeshtriangles * 3, numcollisionmeshtriangles, NULL, NULL, NULL, false, false, true); - for (k = 0;k < mod->nummodelsurfaces;k++) - { - surface = mod->data_surfaces + mod->firstmodelsurface + k; - if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID)) - continue; + if (usesinglecollisionmesh) Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle)); + else + { + for (k = 0;k < mod->nummodelsurfaces;k++) + { + surface = mod->data_surfaces + mod->firstmodelsurface + k; + if (!(surface->texture->supercontents & SUPERCONTENTS_SOLID)) + continue; + Mod_ShadowMesh_AddMesh(mempool, mod->brush.collisionmesh, NULL, NULL, NULL, mod->surfmesh.data_vertex3f, NULL, NULL, NULL, NULL, surface->num_triangles, (mod->surfmesh.data_element3i + 3 * surface->num_firsttriangle)); + } } mod->brush.collisionmesh = Mod_ShadowMesh_Finish(mempool, mod->brush.collisionmesh, false, true, false); } diff --git a/progs.h b/progs.h index fedd19e5..b7208e5f 100644 --- a/progs.h +++ b/progs.h @@ -32,6 +32,19 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define JOINTTYPE_HINGE2 5 #define JOINTTYPE_FIXED -1 +#define ODEFUNC_ENABLE 1 +#define ODEFUNC_DISABLE 2 +#define ODEFUNC_RELFORCEATPOS 3 +#define ODEFUNC_RELTORGUE 4 + +typedef struct edict_odefunc_s +{ + int type; + vec3_t v1; + vec3_t v2; + struct edict_odefunc_s *next; +}edict_odefunc_t; + typedef struct edict_engineprivate_s { // true if this edict is unused @@ -89,6 +102,7 @@ typedef struct edict_engineprivate_s int *ode_element3i; int ode_numvertices; int ode_numtriangles; + edict_odefunc_t *ode_func; vec3_t ode_mins; vec3_t ode_maxs; vec_t ode_mass; diff --git a/prvm_cmds.c b/prvm_cmds.c index a1aef535..5755ef7c 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -272,7 +272,18 @@ static qboolean checkextension(const char *name) while (*e && *e != ' ') e++; if ((e - start) == len && !strncasecmp(start, name, len)) + { + // special sheck for ODE + if (!strncasecmp("DP_PHYSICS_ODE", name, 14)) + { +#ifdef USEODE + return ode_dll ? true : false; +#else + return false; +#endif + } return true; + } } return false; } @@ -6865,3 +6876,103 @@ void VM_getsurfacetriangle(void) // FIXME: implement rotation/scaling VectorMA(&(model->surfmesh.data_element3i + 3 * surface->num_firsttriangle)[trinum * 3], surface->num_firstvertex, d, PRVM_G_VECTOR(OFS_RETURN)); } + +// +// physics builtins +// + +void World_Physics_ApplyCmd(prvm_edict_t *ed, edict_odefunc_t *f); + +#define VM_physics_ApplyCmd(ed,f) if (!ed->priv.server->ode_body) VM_physics_newstackfunction(ed, f); else World_Physics_ApplyCmd(ed, f) + +edict_odefunc_t *VM_physics_newstackfunction(prvm_edict_t *ed, edict_odefunc_t *f) +{ + edict_odefunc_t *newfunc, *func; + + newfunc = (edict_odefunc_t *)Mem_Alloc(prog->progs_mempool, sizeof(edict_odefunc_t)); + memcpy(newfunc, f, sizeof(edict_odefunc_t)); + newfunc->next = NULL; + if (!ed->priv.server->ode_func) + ed->priv.server->ode_func = newfunc; + else + { + for (func = ed->priv.server->ode_func; func->next; func = func->next); + func->next = newfunc; + } + return newfunc; +} + +// void(entity e, float physics_enabled) physics_enable = #; +void VM_physics_enable(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(2, VM_physics_enable); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_enable: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (ed->fields.server->movetype != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_enable: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = PRVM_G_FLOAT(OFS_PARM1) == 0 ? ODEFUNC_DISABLE : ODEFUNC_ENABLE; + VM_physics_ApplyCmd(ed, &f); +} + +// void(entity e, vector force, vector relative_ofs) physics_addforce = #; +void VM_physics_addforce(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(3, VM_physics_addforce); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_addforce: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (ed->fields.server->movetype != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = ODEFUNC_RELFORCEATPOS; + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); + VectorSubtract(ed->fields.server->origin, PRVM_G_VECTOR(OFS_PARM2), f.v2); + VM_physics_ApplyCmd(ed, &f); +} + +// void(entity e, vector torgue) physics_addtorgue = #; +void VM_physics_addtorgue(void) +{ + prvm_edict_t *ed; + edict_odefunc_t f; + + VM_SAFEPARMCOUNT(2, VM_physics_addtorgue); + ed = PRVM_G_EDICT(OFS_PARM0); + if (!ed) + { + if (developer.integer > 0) + VM_Warning("VM_physics_addtorgue: null entity!\n"); + return; + } + // entity should have MOVETYPE_PHYSICS already set, this can damage memory (making leaked allocation) so warn about this even if non-developer + if (ed->fields.server->movetype != MOVETYPE_PHYSICS) + { + VM_Warning("VM_physics_addtorgue: entity is not MOVETYPE_PHYSICS!\n"); + return; + } + f.type = ODEFUNC_RELTORGUE; + VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); + VM_physics_ApplyCmd(ed, &f); +} diff --git a/prvm_cmds.h b/prvm_cmds.h index d47aa613..6ca09b19 100644 --- a/prvm_cmds.h +++ b/prvm_cmds.h @@ -473,3 +473,8 @@ void VM_getsurfacenearpoint(void); void VM_getsurfaceclippedpoint(void); void VM_getsurfacenumtriangles(void); void VM_getsurfacetriangle(void); + +// physics builtins +void VM_physics_enable(void); +void VM_physics_addforce(void); +void VM_physics_addtorgue(void); diff --git a/svvm_cmds.c b/svvm_cmds.c index 890dee78..d9bc0aed 100644 --- a/svvm_cmds.c +++ b/svvm_cmds.c @@ -167,6 +167,7 @@ const char *vm_sv_extensions = "DP_SV_PING " "DP_SV_PING_PACKETLOSS " "DP_SV_PLAYERPHYSICS " +"DP_PHYSICS_ODE " "DP_SV_POINTPARTICLES " "DP_SV_POINTSOUND " "DP_SV_PRECACHEANYTIME " @@ -3679,9 +3680,9 @@ NULL, // #536 NULL, // #537 NULL, // #538 NULL, // #539 -NULL, // #540 -NULL, // #541 -NULL, // #542 +VM_physics_enable, // #540 void(entity e, float physics_enabled) physics_enable = #540; (DP_PHYSICS_ODE) +VM_physics_addforce, // #541 void(entity e, vector force, vector relative_ofs) physics_addforce = #541; (DP_PHYSICS_ODE) +VM_physics_addtorgue, // #542 void(entity e, vector torgue) physics_addtorgue = #542; (DP_PHYSICS_ODE) NULL, // #543 NULL, // #544 NULL, // #545 diff --git a/world.c b/world.c index b54dbc81..90281957 100644 --- a/world.c +++ b/world.c @@ -323,6 +323,9 @@ void World_LinkEdict(world_t *world, prvm_edict_t *ent, const vec3_t mins, const #define USEODE 1 #endif +// recent ODE trunk has dWorldStepFast1 removed +//#define ODE_USE_STEPFAST + #ifdef USEODE cvar_t physics_ode_quadtree_depth = {0, "physics_ode_quadtree_depth","5", "desired subdivision level of quadtree culling space"}; cvar_t physics_ode_contactsurfacelayer = {0, "physics_ode_contactsurfacelayer","1", "allows objects to overlap this many units to reduce jitter"}; @@ -584,7 +587,9 @@ void (ODE_API *dWorldSetQuickStepNumIterations)(dWorldID, int num); //dReal (ODE_API *dWorldGetContactMaxCorrectingVel)(dWorldID); void (ODE_API *dWorldSetContactSurfaceLayer)(dWorldID, dReal depth); //dReal (ODE_API *dWorldGetContactSurfaceLayer)(dWorldID); +#ifdef ODE_USE_STEPFAST void (ODE_API *dWorldStepFast1)(dWorldID, dReal stepsize, int maxiterations); +#endif //void (ODE_API *dWorldSetAutoEnableDepthSF1)(dWorldID, int autoEnableDepth); //int (ODE_API *dWorldGetAutoEnableDepthSF1)(dWorldID); //dReal (ODE_API *dWorldGetAutoDisableLinearThreshold)(dWorldID); @@ -650,9 +655,9 @@ void (ODE_API *dBodySetMass)(dBodyID, const dMass *mass); //void (ODE_API *dBodyAddForce)(dBodyID, dReal fx, dReal fy, dReal fz); //void (ODE_API *dBodyAddTorque)(dBodyID, dReal fx, dReal fy, dReal fz); //void (ODE_API *dBodyAddRelForce)(dBodyID, dReal fx, dReal fy, dReal fz); -//void (ODE_API *dBodyAddRelTorque)(dBodyID, dReal fx, dReal fy, dReal fz); +void (ODE_API *dBodyAddRelTorque)(dBodyID, dReal fx, dReal fy, dReal fz); //void (ODE_API *dBodyAddForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz); -//void (ODE_API *dBodyAddForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz); +void (ODE_API *dBodyAddForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz); //void (ODE_API *dBodyAddRelForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz); //void (ODE_API *dBodyAddRelForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz); //const dReal * (ODE_API *dBodyGetForce)(dBodyID); @@ -674,8 +679,8 @@ dJointID (ODE_API *dBodyGetJoint)(dBodyID, int index); //void (ODE_API *dBodySetDynamic)(dBodyID); //void (ODE_API *dBodySetKinematic)(dBodyID); //int (ODE_API *dBodyIsKinematic)(dBodyID); -//void (ODE_API *dBodyEnable)(dBodyID); -//void (ODE_API *dBodyDisable)(dBodyID); +void (ODE_API *dBodyEnable)(dBodyID); +void (ODE_API *dBodyDisable)(dBodyID); int (ODE_API *dBodyIsEnabled)(dBodyID); void (ODE_API *dBodySetGravityMode)(dBodyID b, int mode); int (ODE_API *dBodyGetGravityMode)(dBodyID b); @@ -1049,7 +1054,9 @@ static dllfunction_t odefuncs[] = // {"dWorldGetContactMaxCorrectingVel", (void **) &dWorldGetContactMaxCorrectingVel}, {"dWorldSetContactSurfaceLayer", (void **) &dWorldSetContactSurfaceLayer}, // {"dWorldGetContactSurfaceLayer", (void **) &dWorldGetContactSurfaceLayer}, +#ifdef ODE_USE_STEPFAST {"dWorldStepFast1", (void **) &dWorldStepFast1}, +#endif // {"dWorldSetAutoEnableDepthSF1", (void **) &dWorldSetAutoEnableDepthSF1}, // {"dWorldGetAutoEnableDepthSF1", (void **) &dWorldGetAutoEnableDepthSF1}, // {"dWorldGetAutoDisableLinearThreshold", (void **) &dWorldGetAutoDisableLinearThreshold}, @@ -1115,9 +1122,9 @@ static dllfunction_t odefuncs[] = // {"dBodyAddForce", (void **) &dBodyAddForce}, // {"dBodyAddTorque", (void **) &dBodyAddTorque}, // {"dBodyAddRelForce", (void **) &dBodyAddRelForce}, -// {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque}, + {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque}, // {"dBodyAddForceAtPos", (void **) &dBodyAddForceAtPos}, -// {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos}, + {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos}, // {"dBodyAddRelForceAtPos", (void **) &dBodyAddRelForceAtPos}, // {"dBodyAddRelForceAtRelPos", (void **) &dBodyAddRelForceAtRelPos}, // {"dBodyGetForce", (void **) &dBodyGetForce}, @@ -1139,8 +1146,8 @@ static dllfunction_t odefuncs[] = // {"dBodySetDynamic", (void **) &dBodySetDynamic}, // {"dBodySetKinematic", (void **) &dBodySetKinematic}, // {"dBodyIsKinematic", (void **) &dBodyIsKinematic}, -// {"dBodyEnable", (void **) &dBodyEnable}, -// {"dBodyDisable", (void **) &dBodyDisable}, + {"dBodyEnable", (void **) &dBodyEnable}, + {"dBodyDisable", (void **) &dBodyDisable}, {"dBodyIsEnabled", (void **) &dBodyIsEnabled}, {"dBodySetGravityMode", (void **) &dBodySetGravityMode}, {"dBodyGetGravityMode", (void **) &dBodyGetGravityMode}, @@ -1646,6 +1653,8 @@ void World_Physics_RemoveJointFromEntity(world_t *world, prvm_edict_t *ed) void World_Physics_RemoveFromEntity(world_t *world, prvm_edict_t *ed) { + edict_odefunc_t *f, *nf; + // entity is not physics controlled, free any physics data ed->priv.server->ode_physics = false; #ifdef USEODE @@ -1690,6 +1699,40 @@ void World_Physics_RemoveFromEntity(world_t *world, prvm_edict_t *ed) if(ed->priv.server->ode_massbuf) Mem_Free(ed->priv.server->ode_massbuf); ed->priv.server->ode_massbuf = NULL; + // clear functions stack + for(f = ed->priv.server->ode_func; f; f = nf) + { + nf = f->next; + Mem_Free(f); + } + ed->priv.server->ode_func = NULL; +} + +void World_Physics_ApplyCmd(prvm_edict_t *ed, edict_odefunc_t *f) +{ + dBodyID body = (dBodyID)ed->priv.server->ode_body; + +#ifdef USEODE + switch(f->type) + { + case ODEFUNC_ENABLE: + dBodyEnable(body); + break; + case ODEFUNC_DISABLE: + dBodyDisable(body); + break; + case ODEFUNC_RELFORCEATPOS: + dBodyEnable(body); + dBodyAddForceAtRelPos(body, f->v1[0], f->v1[1], f->v1[2], f->v2[0], f->v2[1], f->v2[2]); + break; + case ODEFUNC_RELTORGUE: + dBodyEnable(body); + dBodyAddRelTorque(body, f->v1[0], f->v1[1], f->v1[2]); + break; + default: + break; + } +#endif } #ifdef USEODE @@ -1986,6 +2029,7 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) dBodyID body = (dBodyID)ed->priv.server->ode_body; dMass mass; dReal test; + const dReal *ovelocity, *ospinvelocity; void *dataID; dVector3 capsulerot[3]; dp_model_t *model; @@ -2022,6 +2066,8 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) vec_t scale = 1.0f; vec_t spinlimit; qboolean gravity; + edict_odefunc_t *func, *nextf; + #ifdef ODE_DYNAMIC if (!ode_dll) return; @@ -2414,9 +2460,10 @@ treatasbox: if(body) { + // limit movement speed to prevent missed collisions at high speed - const dReal *ovelocity = dBodyGetLinearVel(body); - const dReal *ospinvelocity = dBodyGetAngularVel(body); + ovelocity = dBodyGetLinearVel(body); + ospinvelocity = dBodyGetAngularVel(body); movelimit = ed->priv.server->ode_movelimit * world->physics.ode_movelimit; test = VectorLength2(ovelocity); if (test > movelimit*movelimit) @@ -2437,6 +2484,15 @@ treatasbox: { dBodySetAngularVel(body, 0, 0, 0); } + + // apply functions and clear stack + for(func = ed->priv.server->ode_func; func; func = nextf) + { + nextf = func->next; + World_Physics_ApplyCmd(ed, func); + Mem_Free(func); + } + ed->priv.server->ode_func = NULL; } } @@ -2608,8 +2664,10 @@ void World_Physics_Frame(world_t *world, double frametime, double gravity) dWorldSetQuickStepNumIterations((dWorldID)world->physics.ode_world, bound(1, physics_ode_worldstep_iterations.integer, 200)); dWorldQuickStep((dWorldID)world->physics.ode_world, world->physics.ode_step); } +#ifdef ODE_USE_STEPFAST else if (physics_ode_worldstep.integer == 1) dWorldStepFast1((dWorldID)world->physics.ode_world, world->physics.ode_step, bound(1, physics_ode_worldstep_iterations.integer, 200)); +#endif else dWorldStep((dWorldID)world->physics.ode_world, world->physics.ode_step); diff --git a/world.h b/world.h index fbf9e835..334420fb 100644 --- a/world.h +++ b/world.h @@ -118,9 +118,13 @@ void World_End(world_t *world); // this is called by SV_Physics void World_Physics_Frame(world_t *world, double frametime, double gravity); +// change physics properties of entity +struct prvm_edict_s; +struct edict_odefunc_s; +//void World_Physics_ApplyCmd(prvm_edict_s *ed, edict_odefunc_s *f); + // remove physics data from entity // this is called by entity removal -struct prvm_edict_s; void World_Physics_RemoveFromEntity(world_t *world, struct prvm_edict_s *ed); void World_Physics_RemoveJointFromEntity(world_t *world, struct prvm_edict_s *ed);