From: vortex Date: Sat, 31 Dec 2011 09:53:16 +0000 (+0000) Subject: ODE stuff: X-Git-Tag: xonotic-v0.6.0~86^2~6 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=9b11444784a52e3d91b41bda74aa0bfa5cdb6127 ODE stuff: 1) Cleaned up capsule/cylinder setup code. New geomtypes - axis-oriented cylinder (x, y, z) and capsule, the only difference from original cylinder/capsule is that leading axis is fixed for these types, allowing to set cylinder with length lesser than diameter. Axis oriented capsule is not that useful (as there is no capsule which height is lesser than radius, so it's going to fix radius to match), but could be useful is some cases. 2) GEOMTYPE_TRIMESH now supports .scale and .modelscale_vec (q3map2's per-axis scale). 3) New cvar physics_ode_contact_maxpoints (default 16, can be up to 32) to control default maximum number of contact points between two objects, and new .maxcontacts entity field to control maximum number of contacts it could have with other entities, setting it to low value (5 or 10) gains speed with large stacks of GEOMTYPE_TRIMESH, but makes collision more grude, use it on debris and such stuff. 4) At engine startup, print configuration (extensions string) ODE was built with. 5) physics_ode_constantstep is rewritten to do what actually it should do - it tries to run physics with constant time step, making more ODE iterations to match frametime. So it allows to run physics at 50 FPS, 100 fps no matter what rendering fps are, with some restrictions. physics_ode_iterationsperframe has no effect when using constantstep. 6) New GEOMTYPE_NONE that makes object to be entirely ignored by ODE. Also SOLID_NOT and SOLID_TRIGGER defaults to GEOMTYPE_NONE (if geomtype is 0). 7) Added basic forces in same manner as joints (entities with .forcetype), physics_addforce() and physics_addtorgue() is now deprecated as they cant work with multiple ODE iterations per frame. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@11626 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/dpdefs/dpextensions.qc b/dpdefs/dpextensions.qc index b340bae8..c2340125 100644 --- a/dpdefs/dpextensions.qc +++ b/dpdefs/dpextensions.qc @@ -1672,22 +1672,37 @@ const float SOLID_PHYSICS_CAPSULE = 34; const float SOLID_PHYSICS_TRIMESH = 35; const float SOLID_PHYSICS_CYLINDER = 36; //geometry types: -const float GEOMTYPE_NULL = 0; -const float GEOMTYPE_BOX = 1; -const float GEOMTYPE_SPHERE = 2; -const float GEOMTYPE_CAPSULE = 3; -const float GEOMTYPE_TRIMESH = 4; -const float GEOMTYPE_CYLINDER = 5; -//SOLID_BSP; +const float GEOMTYPE_NONE = -1; // entity will be entirely skipped by ODE +const float GEOMTYPE_SOLID = 0; // geometry type will be set based on .solid field +const float GEOMTYPE_BOX = 1; // entity bound box +const float GEOMTYPE_SPHERE = 2; // sphere with radius picked from x axis of entity bound box +const float GEOMTYPE_CAPSULE = 3; // with leading axis automatically determined from longest one, radius is picked as minimal of the rest 2 axes +const float GEOMTYPE_TRIMESH = 4; // triangle mesh +const float GEOMTYPE_CYLINDER = 5; // like capsule but not capped + // note that ODE's builtin cylinder support is experimental, somewhat bugged and unfinished (no cylinder-cylinder collision) + // to use properly working cylinder should build ODE with LIBCCD extension +const float GEOMTYPE_CAPSULE_X = 6; // capsule with fixed leading axis +const float GEOMTYPE_CAPSULE_Y = 7; +const float GEOMTYPE_CAPSULE_Z = 8; +const float GEOMTYPE_CYLINDER_X = 9; // cylinder with fixed leading axis +const float GEOMTYPE_CYLINDER_Y = 10; +const float GEOMTYPE_CYLINDER_Z = 11; //joint types: +const float JOINTTYPE_NONE = 0; 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; +//force types: +const float FORCETYPE_NONE = 0; +const float FORCETYPE_FORCE = 1; // applied at center of mass +const float FORCETYPE_FORCEATPOS = 2; +const float FORCETYPE_TORQUE = 3; // common joint properties: -// .entity aiment, enemy; // connected objects +// .entity aiment; // connected objects +// .entity enemy; // connected objects, forces // .vector movedir; // for a spring: // movedir_x = spring constant (force multiplier, must be > 0) @@ -1698,18 +1713,26 @@ const float JOINTTYPE_FIXED = -1; // movedir_y = -1 * max motor force to use // movedir_z = stop position (+/-), set to 0 for no stop // note that ODE does not support both in one anyway +// for a force: +// force vector to apply //field definitions: -.float geomtype; // see GEOMTYPE_*, a more correct way to set collision shape, allows to set SOLID_CORPSE and trimesh collisions -.float mass; // ODE mass, standart value is 1 -.vector massofs; // offsets a mass center out of object center, if not set a center of model bounds is used -.float friction; -.float bouncefactor; -.float bouncestop; -.float jointtype; +.float geomtype; // see GEOMTYPE_*, a more correct way to set collision shape, allows to set SOLID_CORPSE and trimesh collisions +.float maxcontacts; // maximum number of contacts to make for this object, lesser = faster (but setting it too low will could make object pass though walls), default is 16, maximum is 32 +.float mass; // ODE mass, standart value is 1 +.vector massofs; // offsets a mass center out of object center, if not set a center of model bounds is used +.float friction; // a friction of object, get multiplied by second objects's friction on contact +.float bouncefactor; +.float bouncestop; +.float jointtype; // type of joint +.float forcetype; // type of force +.float erp; // error restitution parameter, makes ODE solver attempt to fix errors in contacts, + // bringing together 2 joints or fixing object being stuch in other object, + // a value of 0.1 will fix slightly, a value of 1.0 attempts to fix whole error in one frame + // use with care as high values makes system unstable and likely to explode //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 torque) physics_addtorque = #542; // add relative torque +void(entity e, vector force, vector force_pos) physics_addforce = #541; // deprecated, apply a force from certain origin, length of force vector is power of force +void(entity e, vector torque) physics_addtorque = #542; // deprecated, add relative torque //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 is loaded and ready, also to enable physics set "physics_ode" cvar to 1 //note: this extension is highly experimental and may be unstable diff --git a/matrixlib.c b/matrixlib.c index f84e922d..7d33463a 100644 --- a/matrixlib.c +++ b/matrixlib.c @@ -1809,6 +1809,19 @@ void Matrix4x4_Scale (matrix4x4_t *out, double rotatescale, double originscale) #endif } +void Matrix4x4_OriginScale3 (matrix4x4_t *out, double x, double y, double z) +{ +#ifdef MATRIX4x4_OPENGLORIENTATION + out->m[3][0] *= x; + out->m[3][1] *= y; + out->m[3][2] *= z; +#else + out->m[0][3] *= x; + out->m[1][3] *= y; + out->m[2][3] *= z; +#endif +} + void Matrix4x4_Abs (matrix4x4_t *out) { out->m[0][0] = fabs(out->m[0][0]); diff --git a/matrixlib.h b/matrixlib.h index ae83c1d6..1b2b5790 100644 --- a/matrixlib.h +++ b/matrixlib.h @@ -166,6 +166,8 @@ void Matrix4x4_SetOrigin (matrix4x4_t *out, double x, double y, double z); void Matrix4x4_AdjustOrigin (matrix4x4_t *out, double x, double y, double z); // scales vectors of a matrix in place and allows you to scale origin as well void Matrix4x4_Scale (matrix4x4_t *out, double rotatescale, double originscale); +// scales origin of matrix by 3 axes +void Matrix4x4_OriginScale3 (matrix4x4_t *out, double x, double y, double z); // ensures each element of the 3x3 rotation matrix is facing in the + direction void Matrix4x4_Abs (matrix4x4_t *out); diff --git a/progs.h b/progs.h index 62c71ea8..c66ff4f2 100644 --- a/progs.h +++ b/progs.h @@ -25,23 +25,37 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define ENTITYGRIDAREAS 16 #define MAX_ENTITYCLUSTERS 16 +#define GEOMTYPE_NONE -1 +#define GEOMTYPE_SOLID 0 #define GEOMTYPE_BOX 1 #define GEOMTYPE_SPHERE 2 #define GEOMTYPE_CAPSULE 3 #define GEOMTYPE_TRIMESH 4 #define GEOMTYPE_CYLINDER 5 - -#define JOINTTYPE_POINT 1 -#define JOINTTYPE_HINGE 2 -#define JOINTTYPE_SLIDER 3 +#define GEOMTYPE_CAPSULE_X 6 +#define GEOMTYPE_CAPSULE_Y 7 +#define GEOMTYPE_CAPSULE_Z 8 +#define GEOMTYPE_CYLINDER_X 9 +#define GEOMTYPE_CYLINDER_Y 10 +#define GEOMTYPE_CYLINDER_Z 11 + +#define JOINTTYPE_NONE 0 +#define JOINTTYPE_POINT 1 +#define JOINTTYPE_HINGE 2 +#define JOINTTYPE_SLIDER 3 #define JOINTTYPE_UNIVERSAL 4 -#define JOINTTYPE_HINGE2 5 -#define JOINTTYPE_FIXED -1 - -#define ODEFUNC_ENABLE 1 -#define ODEFUNC_DISABLE 2 -#define ODEFUNC_RELFORCEATPOS 3 -#define ODEFUNC_RELTORQUE 4 +#define JOINTTYPE_HINGE2 5 +#define JOINTTYPE_FIXED -1 + +#define FORCETYPE_NONE 0 +#define FORCETYPE_FORCE 1 +#define FORCETYPE_FORCEATPOS 2 +#define FORCETYPE_TORQUE 3 + +#define ODEFUNC_ENABLE 1 +#define ODEFUNC_DISABLE 2 +#define ODEFUNC_FORCE 3 +#define ODEFUNC_TORQUE 4 typedef struct edict_odefunc_s { diff --git a/prvm_cmds.c b/prvm_cmds.c index 217d897d..f362d36c 100644 --- a/prvm_cmds.c +++ b/prvm_cmds.c @@ -6829,9 +6829,9 @@ void VM_physics_addforce(prvm_prog_t *prog) VM_Warning(prog, "VM_physics_addforce: entity is not MOVETYPE_PHYSICS!\n"); return; } - f.type = ODEFUNC_RELFORCEATPOS; + f.type = ODEFUNC_FORCE; VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); - VectorSubtract(PRVM_serveredictvector(ed, origin), PRVM_G_VECTOR(OFS_PARM2), f.v2); + VectorCopy(PRVM_G_VECTOR(OFS_PARM2), f.v2); VM_physics_ApplyCmd(ed, &f); } @@ -6855,7 +6855,7 @@ void VM_physics_addtorque(prvm_prog_t *prog) VM_Warning(prog, "VM_physics_addtorque: entity is not MOVETYPE_PHYSICS!\n"); return; } - f.type = ODEFUNC_RELTORQUE; + f.type = ODEFUNC_TORQUE; VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1); VM_physics_ApplyCmd(ed, &f); } diff --git a/prvm_offsets.h b/prvm_offsets.h index 03cac119..cc599a45 100644 --- a/prvm_offsets.h +++ b/prvm_offsets.h @@ -26,6 +26,7 @@ PRVM_DECLARE_clientfieldfloat(ideal_yaw) PRVM_DECLARE_clientfieldfloat(idealpitch) PRVM_DECLARE_clientfieldfloat(geomtype) PRVM_DECLARE_clientfieldfloat(jointtype) +PRVM_DECLARE_clientfieldfloat(forcetype) PRVM_DECLARE_clientfieldfloat(lerpfrac) PRVM_DECLARE_clientfieldfloat(lerpfrac3) PRVM_DECLARE_clientfieldfloat(lerpfrac4) @@ -41,6 +42,7 @@ PRVM_DECLARE_clientfieldfloat(pitch_speed) PRVM_DECLARE_clientfieldfloat(pmove_flags) PRVM_DECLARE_clientfieldfloat(renderflags) PRVM_DECLARE_clientfieldfloat(scale) +PRVM_DECLARE_clientfieldfloat(modelscale_vec) PRVM_DECLARE_clientfieldfloat(shadertime) PRVM_DECLARE_clientfieldfloat(skeletonindex) PRVM_DECLARE_clientfieldfloat(skin) @@ -307,6 +309,7 @@ PRVM_DECLARE_field(items) PRVM_DECLARE_field(items2) PRVM_DECLARE_field(geomtype) PRVM_DECLARE_field(jointtype) +PRVM_DECLARE_field(forcetype) PRVM_DECLARE_field(lerpfrac) PRVM_DECLARE_field(lerpfrac3) PRVM_DECLARE_field(lerpfrac4) @@ -354,6 +357,7 @@ PRVM_DECLARE_field(punchvector) PRVM_DECLARE_field(renderamt) PRVM_DECLARE_field(renderflags) PRVM_DECLARE_field(scale) +PRVM_DECLARE_field(modelscale_vec) PRVM_DECLARE_field(sendcomplexanimation) PRVM_DECLARE_field(shadertime) PRVM_DECLARE_field(size) @@ -669,6 +673,7 @@ PRVM_DECLARE_serverfieldfloat(items) PRVM_DECLARE_serverfieldfloat(items2) PRVM_DECLARE_serverfieldfloat(geomtype) PRVM_DECLARE_serverfieldfloat(jointtype) +PRVM_DECLARE_serverfieldfloat(forcetype) PRVM_DECLARE_serverfieldfloat(lerpfrac) PRVM_DECLARE_serverfieldfloat(lerpfrac3) PRVM_DECLARE_serverfieldfloat(lerpfrac4) @@ -692,6 +697,7 @@ PRVM_DECLARE_serverfieldfloat(pitch_speed) PRVM_DECLARE_serverfieldfloat(pmodel) PRVM_DECLARE_serverfieldfloat(renderamt) PRVM_DECLARE_serverfieldfloat(scale) +PRVM_DECLARE_serverfieldfloat(modelscale_vec) PRVM_DECLARE_serverfieldfloat(sendcomplexanimation) PRVM_DECLARE_serverfieldfloat(skeletonindex) PRVM_DECLARE_serverfieldfloat(skin) diff --git a/world.c b/world.c index e31a6818..b23fe2c9 100644 --- a/world.c +++ b/world.c @@ -342,6 +342,7 @@ cvar_t physics_ode_worldstep_iterations = {0, "physics_ode_worldstep_iterations" cvar_t physics_ode_contact_mu = {0, "physics_ode_contact_mu", "1", "contact solver mu parameter - friction pyramid approximation 1 (see ODE User Guide)"}; cvar_t physics_ode_contact_erp = {0, "physics_ode_contact_erp", "0.96", "contact solver erp parameter - Error Restitution Percent (see ODE User Guide)"}; cvar_t physics_ode_contact_cfm = {0, "physics_ode_contact_cfm", "0", "contact solver cfm parameter - Constraint Force Mixing (see ODE User Guide)"}; +cvar_t physics_ode_contact_maxpoints = {0, "physics_ode_contact_maxpoints", "16", "maximal number of contact points between 2 objects, higher = stable (and slower), can be up to 32"}; cvar_t physics_ode_world_erp = {0, "physics_ode_world_erp", "-1", "world solver erp parameter - Error Restitution Percent (see ODE User Guide); use defaults when set to -1"}; cvar_t physics_ode_world_cfm = {0, "physics_ode_world_cfm", "-1", "world solver cfm parameter - Constraint Force Mixing (see ODE User Guide); not touched when -1"}; cvar_t physics_ode_world_damping = {0, "physics_ode_world_damping", "1", "enabled damping scale (see ODE User Guide), this scales all damping values, be aware that behavior depends of step type"}; @@ -551,7 +552,7 @@ typedef void dNearCallback (void *data, dGeomID o1, dGeomID o2); #define dSAP_AXES_ZXY ((2)|(0<<2)|(1<<4)) #define dSAP_AXES_ZYX ((2)|(1<<2)|(0<<4)) -//const char* (ODE_API *dGetConfiguration)(void); +const char* (ODE_API *dGetConfiguration)(void); int (ODE_API *dCheckConfiguration)( const char* token ); int (ODE_API *dInitODE)(void); //int (ODE_API *dInitODE2)(unsigned int uiInitFlags); @@ -659,12 +660,12 @@ const dReal * (ODE_API *dBodyGetLinearVel)(dBodyID); const dReal * (ODE_API *dBodyGetAngularVel)(dBodyID); void (ODE_API *dBodySetMass)(dBodyID, const dMass *mass); //void (ODE_API *dBodyGetMass)(dBodyID, 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 *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 *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 *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 *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); @@ -1017,7 +1018,7 @@ dGeomID (ODE_API *dCreateTriMesh)(dSpaceID space, dTriMeshDataID Data, d static dllfunction_t odefuncs[] = { -// {"dGetConfiguration", (void **) &dGetConfiguration}, + {"dGetConfiguration", (void **) &dGetConfiguration}, {"dCheckConfiguration", (void **) &dCheckConfiguration}, {"dInitODE", (void **) &dInitODE}, // {"dInitODE2", (void **) &dInitODE2}, @@ -1124,12 +1125,12 @@ static dllfunction_t odefuncs[] = {"dBodyGetAngularVel", (void **) &dBodyGetAngularVel}, {"dBodySetMass", (void **) &dBodySetMass}, // {"dBodyGetMass", (void **) &dBodyGetMass}, -// {"dBodyAddForce", (void **) &dBodyAddForce}, -// {"dBodyAddTorque", (void **) &dBodyAddTorque}, + {"dBodyAddForce", (void **) &dBodyAddForce}, + {"dBodyAddTorque", (void **) &dBodyAddTorque}, // {"dBodyAddRelForce", (void **) &dBodyAddRelForce}, - {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque}, -// {"dBodyAddForceAtPos", (void **) &dBodyAddForceAtPos}, - {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos}, +// {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque}, + {"dBodyAddForceAtPos", (void **) &dBodyAddForceAtPos}, +// {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos}, // {"dBodyAddRelForceAtPos", (void **) &dBodyAddRelForceAtPos}, // {"dBodyAddRelForceAtRelPos", (void **) &dBodyAddRelForceAtRelPos}, // {"dBodyGetForce", (void **) &dBodyGetForce}, @@ -1492,6 +1493,7 @@ static void World_Physics_Init(void) Cvar_RegisterVariable(&physics_ode_contact_mu); Cvar_RegisterVariable(&physics_ode_contact_erp); Cvar_RegisterVariable(&physics_ode_contact_cfm); + Cvar_RegisterVariable(&physics_ode_contact_maxpoints); Cvar_RegisterVariable(&physics_ode_world_erp); Cvar_RegisterVariable(&physics_ode_world_cfm); Cvar_RegisterVariable(&physics_ode_world_damping); @@ -1543,6 +1545,7 @@ static void World_Physics_Init(void) # else Con_Printf("ODE library loaded with double precision.\n"); # endif + Con_Printf("ODE configuration list: %s\n", dGetConfiguration()); } #endif } @@ -1726,13 +1729,13 @@ void World_Physics_ApplyCmd(prvm_edict_t *ed, edict_odefunc_t *f) case ODEFUNC_DISABLE: dBodyDisable(body); break; - case ODEFUNC_RELFORCEATPOS: + case ODEFUNC_FORCE: dBodyEnable(body); - dBodyAddForceAtRelPos(body, f->v1[0], f->v1[1], f->v1[2], f->v2[0], f->v2[1], f->v2[2]); + dBodyAddForceAtPos(body, f->v1[0], f->v1[1], f->v1[2], f->v2[0], f->v2[1], f->v2[2]); break; - case ODEFUNC_RELTORQUE: + case ODEFUNC_TORQUE: dBodyEnable(body); - dBodyAddRelTorque(body, f->v1[0], f->v1[1], f->v1[2]); + dBodyAddTorque(body, f->v1[0], f->v1[1], f->v1[2]); break; default: break; @@ -1844,6 +1847,45 @@ static void World_Physics_Frame_BodyToEntity(world_t *world, prvm_edict_t *ed) } } +static void World_Physics_Frame_ForceFromEntity(world_t *world, prvm_edict_t *ed) +{ + prvm_prog_t *prog = world->prog; + int forcetype = 0, movetype = 0, enemy = 0; + vec3_t movedir, origin; + + movetype = (int)PRVM_gameedictfloat(ed, movetype); + forcetype = (int)PRVM_gameedictfloat(ed, forcetype); + if (movetype == MOVETYPE_PHYSICS) + forcetype = FORCETYPE_NONE; // can't have both + if (!forcetype) + return; + enemy = PRVM_gameedictedict(ed, enemy); + if (enemy <= 0 || enemy >= prog->num_edicts || prog->edicts[enemy].priv.required->free || prog->edicts[enemy].priv.server->ode_body == 0) + return; + VectorCopy(PRVM_gameedictvector(ed, movedir), movedir); + VectorCopy(PRVM_gameedictvector(ed, origin), origin); + dBodyEnable(prog->edicts[enemy].priv.server->ode_body); + switch(forcetype) + { + case FORCETYPE_FORCE: + if (movedir[0] || movedir[1] || movedir[2]) + dBodyAddForce(prog->edicts[enemy].priv.server->ode_body, movedir[0], movedir[1], movedir[2]); + break; + case FORCETYPE_FORCEATPOS: + if (movedir[0] || movedir[1] || movedir[2]) + dBodyAddForceAtPos(prog->edicts[enemy].priv.server->ode_body, movedir[0], movedir[1], movedir[2], origin[0], origin[1], origin[2]); + break; + case FORCETYPE_TORQUE: + if (movedir[0] || movedir[1] || movedir[2]) + dBodyAddTorque(prog->edicts[enemy].priv.server->ode_body, movedir[0], movedir[1], movedir[2]); + break; + case FORCETYPE_NONE: + default: + // bad force + break; + } +} + static void World_Physics_Frame_JointFromEntity(world_t *world, prvm_edict_t *ed) { prvm_prog_t *prog = world->prog; @@ -1855,12 +1897,13 @@ static void World_Physics_Frame_JointFromEntity(world_t *world, prvm_edict_t *ed int enemy = 0, aiment = 0; vec3_t origin, velocity, angles, forward, left, up, movedir; vec_t CFM, ERP, FMax, Stop, Vel; + + movetype = (int)PRVM_gameedictfloat(ed, movetype); + jointtype = (int)PRVM_gameedictfloat(ed, jointtype); VectorClear(origin); VectorClear(velocity); VectorClear(angles); VectorClear(movedir); - movetype = (int)PRVM_gameedictfloat(ed, movetype); - jointtype = (int)PRVM_gameedictfloat(ed, jointtype); enemy = PRVM_gameedictedict(ed, enemy); aiment = PRVM_gameedictedict(ed, aiment); VectorCopy(PRVM_gameedictvector(ed, origin), origin); @@ -1868,7 +1911,7 @@ static void World_Physics_Frame_JointFromEntity(world_t *world, prvm_edict_t *ed VectorCopy(PRVM_gameedictvector(ed, angles), angles); VectorCopy(PRVM_gameedictvector(ed, movedir), movedir); if(movetype == MOVETYPE_PHYSICS) - jointtype = 0; // can't have both + jointtype = JOINTTYPE_NONE; // can't have both if(enemy <= 0 || enemy >= prog->num_edicts || prog->edicts[enemy].priv.required->free || prog->edicts[enemy].priv.server->ode_body == 0) enemy = 0; if(aiment <= 0 || aiment >= prog->num_edicts || prog->edicts[aiment].priv.required->free || prog->edicts[aiment].priv.server->ode_body == 0) @@ -1926,7 +1969,7 @@ static void World_Physics_Frame_JointFromEntity(world_t *world, prvm_edict_t *ed case JOINTTYPE_FIXED: j = dJointCreateFixed((dWorldID)world->physics.ode_world, 0); break; - case 0: + case JOINTTYPE_NONE: default: // no joint j = 0; @@ -2036,7 +2079,6 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) dReal test; const dReal *ovelocity, *ospinvelocity; void *dataID; - dVector3 capsulerot[3]; dp_model_t *model; float *ov; int *oe; @@ -2070,6 +2112,7 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) vec_t scale = 1.0f; vec_t spinlimit; qboolean gravity; + vec3_t scalevec; edict_odefunc_t *func, *nextf; #ifdef ODE_DYNAMIC @@ -2091,6 +2134,8 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) // VorteX: keep support for deprecated solid fields to not break mods if (solid == SOLID_PHYSICS_TRIMESH || solid == SOLID_BSP) geomtype = GEOMTYPE_TRIMESH; + else if (solid == SOLID_NOT || solid == SOLID_TRIGGER) + geomtype = GEOMTYPE_NONE; else if (solid == SOLID_PHYSICS_SPHERE) geomtype = GEOMTYPE_SPHERE; else if (solid == SOLID_PHYSICS_CAPSULE) @@ -2123,7 +2168,7 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) massval = 1.0f; } } - else if (geomtype) + else if (geomtype && geomtype != GEOMTYPE_NONE) { VectorCopy(PRVM_gameedictvector(ed, mins), entmins); VectorCopy(PRVM_gameedictvector(ed, maxs), entmaxs); @@ -2187,6 +2232,13 @@ static void World_Physics_Frame_BodyFromEntity(world_t *world, prvm_edict_t *ed) { case GEOMTYPE_TRIMESH: ed->priv.server->ode_offsetmatrix = identitymatrix; + // honor scale, support q3map2's/radiant's modelscale_vec + VectorCopy(PRVM_gameedictvector(ed, modelscale_vec), scalevec); + if (scalevec[0] != 0.0 || scalevec[1] != 0.0 || scalevec[2] != 0.0) + Matrix4x4_OriginScale3(&ed->priv.server->ode_offsetmatrix, scalevec[0], scalevec[1],scalevec[2]); + else if (PRVM_gameedictfloat(ed, scale)) + Matrix4x4_OriginScale3(&ed->priv.server->ode_offsetmatrix, PRVM_gameedictfloat(ed, scale), PRVM_gameedictfloat(ed, scale), PRVM_gameedictfloat(ed, scale)); + // check model if (!model) { Con_Printf("entity %i (classname %s) has no model\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(prog, PRVM_gameedictstring(ed, classname))); @@ -2241,7 +2293,6 @@ treatasbox: dMassSetSphereTotal(&mass, massval, geomsize[0] * 0.5f); break; case GEOMTYPE_CAPSULE: - case GEOMTYPE_CYLINDER: axisindex = 0; if (geomsize[axisindex] < geomsize[1]) axisindex = 1; @@ -2251,28 +2302,122 @@ treatasbox: // axis, since ODE doesn't like this idea we have to create a // capsule which uses the standard orientation, and apply a // transform to it - memset(capsulerot, 0, sizeof(capsulerot)); if (axisindex == 0) + { Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + } else if (axisindex == 1) + { Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; + } else + { Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); - radius = geomsize[!axisindex] * 0.5f; // any other axis is the radius + radius = min(geomsize[0], geomsize[1]) * 0.5f; + } length = geomsize[axisindex] - radius*2; // because we want to support more than one axisindex, we have to // create a transform, and turn on its cleanup setting (which will // cause the child to be destroyed when it is destroyed) - if (geomtype == GEOMTYPE_CYLINDER) + ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCapsuleTotal(&mass, massval, axisindex+1, radius, length); + break; + case GEOMTYPE_CAPSULE_X: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + length = geomsize[0] - radius*2; + // check if length is not enough, reduce radius then + if (length <= 0) { - ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length); - dMassSetCapsuleTotal(&mass, massval, axisindex+1, radius, length); + radius -= (1 - length)*0.5; + length = 1; + } + ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCapsuleTotal(&mass, massval, 1, radius, length); + break; + case GEOMTYPE_CAPSULE_Y: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; + length = geomsize[1] - radius*2; + // check if length is not enough, reduce radius then + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; + } + ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCapsuleTotal(&mass, massval, 2, radius, length); + break; + case GEOMTYPE_CAPSULE_Z: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); + radius = min(geomsize[1], geomsize[0]) * 0.5f; + length = geomsize[2] - radius*2; + // check if length is not enough, reduce radius then + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; + } + ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCapsuleTotal(&mass, massval, 3, radius, length); + break; + case GEOMTYPE_CYLINDER: + axisindex = 0; + if (geomsize[axisindex] < geomsize[1]) + axisindex = 1; + if (geomsize[axisindex] < geomsize[2]) + axisindex = 2; + // the qc gives us 3 axis radius, the longest axis is the capsule + // axis, since ODE doesn't like this idea we have to create a + // capsule which uses the standard orientation, and apply a + // transform to it + if (axisindex == 0) + { + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + } + else if (axisindex == 1) + { + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; } else { - ed->priv.server->ode_geom = (void *)dCreateCylinder((dSpaceID)world->physics.ode_space, radius, length); - dMassSetCylinderTotal(&mass, massval, axisindex+1, radius, length); + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); + radius = min(geomsize[0], geomsize[1]) * 0.5f; + } + length = geomsize[axisindex]; + // check if length is not enough, reduce radius then + if (length <= 0) + { + radius -= (1 - length)*0.5; + length = 1; } + ed->priv.server->ode_geom = (void *)dCreateCylinder((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCylinderTotal(&mass, massval, axisindex+1, radius, length); + break; + case GEOMTYPE_CYLINDER_X: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1); + radius = min(geomsize[1], geomsize[2]) * 0.5f; + length = geomsize[0]; + ed->priv.server->ode_geom = (void *)dCreateCylinder((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCylinderTotal(&mass, massval, 1, radius, length); + break; + case GEOMTYPE_CYLINDER_Y: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 1); + radius = min(geomsize[0], geomsize[2]) * 0.5f; + length = geomsize[1]; + ed->priv.server->ode_geom = (void *)dCreateCylinder((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCylinderTotal(&mass, massval, 2, radius, length); + break; + case GEOMTYPE_CYLINDER_Z: + Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1); + radius = min(geomsize[0], geomsize[1]) * 0.5f; + length = geomsize[2]; + ed->priv.server->ode_geom = (void *)dCreateCylinder((dSpaceID)world->physics.ode_space, radius, length); + dMassSetCylinderTotal(&mass, massval, 3, radius, length); break; default: Sys_Error("World_Physics_BodyFromEntity: unrecognized geomtype value %i was accepted by filter\n", solid); @@ -2522,7 +2667,7 @@ treatasbox: } } -#define MAX_CONTACTS 16 +#define MAX_CONTACTS 32 static void nearCallback (void *data, dGeomID o1, dGeomID o2) { world_t *world = (world_t *)data; @@ -2621,8 +2766,17 @@ static void nearCallback (void *data, dGeomID o1, dGeomID o2) // select object that moves faster ang get it's erp erp = (VectorLength2(PRVM_gameedictvector(ed1, velocity)) > VectorLength2(PRVM_gameedictvector(ed2, velocity))) ? PRVM_gameedictfloat(ed1, erp) : PRVM_gameedictfloat(ed2, erp); + // get max contact points for this collision + numcontacts = (int)PRVM_gameedictfloat(ed1, maxcontacts); + if (!numcontacts) + numcontacts = physics_ode_contact_maxpoints.integer; + if (PRVM_gameedictfloat(ed2, maxcontacts)) + numcontacts = max(numcontacts, (int)PRVM_gameedictfloat(ed2, maxcontacts)); + else + numcontacts = max(numcontacts, physics_ode_contact_maxpoints.integer); + // generate contact points between the two non-space geoms - numcontacts = dCollide(o1, o2, MAX_CONTACTS, &(contact[0].geom), sizeof(contact[0])); + numcontacts = dCollide(o1, o2, min(MAX_CONTACTS, numcontacts), &(contact[0].geom), sizeof(contact[0])); // add these contact points to the simulation for (i = 0;i < numcontacts;i++) { @@ -2650,13 +2804,29 @@ void World_Physics_Frame(world_t *world, double frametime, double gravity) int i; prvm_edict_t *ed; - world->physics.ode_iterations = bound(1, physics_ode_iterationsperframe.integer, 1000); - if (physics_ode_constantstep.integer > 0 && physics_ode_constantstep.integer < 1) - world->physics.ode_step = physics_ode_constantstep.integer / world->physics.ode_iterations; - else if (physics_ode_constantstep.integer) - world->physics.ode_step = sys_ticrate.integer / world->physics.ode_iterations; - else + if (!physics_ode_constantstep.value) + { + world->physics.ode_iterations = bound(1, physics_ode_iterationsperframe.integer, 1000); world->physics.ode_step = frametime / world->physics.ode_iterations; + } + else + { + world->physics.ode_time += frametime; + // step size + if (physics_ode_constantstep.value > 0 && physics_ode_constantstep.value < 1) + world->physics.ode_step = physics_ode_constantstep.value; + else + world->physics.ode_step = sys_ticrate.value; + if (world->physics.ode_time > 0.2f) + world->physics.ode_time = world->physics.ode_step; + // set number of iterations to process + world->physics.ode_iterations = 0; + while(world->physics.ode_time >= world->physics.ode_step) + { + world->physics.ode_iterations++; + world->physics.ode_time -= world->physics.ode_step; + } + } world->physics.ode_movelimit = physics_ode_movelimit.value / world->physics.ode_step; World_Physics_UpdateODE(world); @@ -2680,18 +2850,23 @@ void World_Physics_Frame(world_t *world, double frametime, double gravity) dWorldSetGravity((dWorldID)world->physics.ode_world, 0, 0, -gravity * physics_ode_world_gravitymod.value); // set the tolerance for closeness of objects dWorldSetContactSurfaceLayer((dWorldID)world->physics.ode_world, max(0, physics_ode_contactsurfacelayer.value)); - // run collisions for the current world state, creating JointGroup tdelta3 = Sys_DirtyTime(); dSpaceCollide((dSpaceID)world->physics.ode_space, (void *)world, nearCallback); collisiontime += (Sys_DirtyTime() - tdelta3)*10000; - + // apply forces + if (prog) + { + int j; + for (j = 0, ed = prog->edicts + j;j < prog->num_edicts;j++, ed++) + if (!prog->edicts[j].priv.required->free) + World_Physics_Frame_ForceFromEntity(world, ed); + } // run physics (move objects, calculate new velocities) // be sure not to pass 0 as step time because that causes an ODE error dWorldSetQuickStepNumIterations((dWorldID)world->physics.ode_world, bound(1, physics_ode_worldstep_iterations.integer, 200)); if (world->physics.ode_step > 0) dWorldQuickStep((dWorldID)world->physics.ode_world, world->physics.ode_step); - // clear the JointGroup now that we're done with it dJointGroupEmpty((dJointGroupID)world->physics.ode_contactgroup); } @@ -2722,7 +2897,7 @@ void World_Physics_Frame(world_t *world, double frametime, double gravity) if (dBodyIsEnabled(body)) world->physics.ode_activeovjects++; } - Con_Printf("ODE Stats(%s): %3.01f (%3.01f collision) %3.01f total : %i objects %i active %i disabled\n", prog->name, simulationtime, collisiontime, (Sys_DirtyTime() - tdelta)*10000, world->physics.ode_numobjects, world->physics.ode_activeovjects, (world->physics.ode_numobjects - world->physics.ode_activeovjects)); + Con_Printf("ODE Stats(%s): %i iterations, %3.01f (%3.01f collision) %3.01f total : %i objects %i active %i disabled\n", prog->name, world->physics.ode_iterations, simulationtime, collisiontime, (Sys_DirtyTime() - tdelta)*10000, world->physics.ode_numobjects, world->physics.ode_activeovjects, (world->physics.ode_numobjects - world->physics.ode_activeovjects)); } } } diff --git a/world.h b/world.h index 122b9c70..e43ebd14 100644 --- a/world.h +++ b/world.h @@ -50,6 +50,8 @@ typedef struct world_physics_s int ode_iterations; // actual step (server frametime / ode_iterations) vec_t ode_step; + // time we need to simulate, for constantstep + vec_t ode_time; // stats int ode_numobjects; // total objects cound int ode_activeovjects; // active objects count