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
//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
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;
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);
}
#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
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;
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;
}
// 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);
+}
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);
"DP_SV_PING "
"DP_SV_PING_PACKETLOSS "
"DP_SV_PLAYERPHYSICS "
+"DP_PHYSICS_ODE "
"DP_SV_POINTPARTICLES "
"DP_SV_POINTSOUND "
"DP_SV_PRECACHEANYTIME "
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
#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"};
//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);
//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);
//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);
// {"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},
// {"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},
// {"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},
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
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
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;
vec_t scale = 1.0f;
vec_t spinlimit;
qboolean gravity;
+ edict_odefunc_t *func, *nextf;
+
#ifdef ODE_DYNAMIC
if (!ode_dll)
return;
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)
{
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;
}
}
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);
// 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);