ODE stuff:
authorvortex <vortex@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 31 Dec 2011 09:53:16 +0000 (09:53 +0000)
committervortex <vortex@d7cf8633-e32d-0410-b094-e92efae38249>
Sat, 31 Dec 2011 09:53:16 +0000 (09:53 +0000)
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

dpdefs/dpextensions.qc
matrixlib.c
matrixlib.h
progs.h
prvm_cmds.c
prvm_offsets.h
world.c
world.h

index b340bae..c234012 100644 (file)
@@ -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
index f84e922..7d33463 100644 (file)
@@ -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]);
index ae83c1d..1b2b579 100644 (file)
@@ -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 62c71ea..c66ff4f 100644 (file)
--- 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
 {
index 217d897..f362d36 100644 (file)
@@ -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);
 }
index 03cac11..cc599a4 100644 (file)
@@ -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 e31a681..b23fe2c 100644 (file)
--- 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 122b9c7..e43ebd1 100644 (file)
--- 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