]> git.xonotic.org Git - xonotic/darkplaces.git/commitdiff
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 b340bae85260d5686a769c3774946b381720f3cf..c2340125bb6c465068abb9e5371933f550fde4fc 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 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:
 //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;
 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:
 // 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)
 // .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
 //     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:
 //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
 //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
 //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 f84e922d506dce80ac34ed6e69f081b59d1b56eb..7d33463adfde275495e633bf3c5c331a15c0361e 100644 (file)
@@ -1809,6 +1809,19 @@ void Matrix4x4_Scale (matrix4x4_t *out, double rotatescale, double originscale)
 #endif
 }
 
 #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]);
 void Matrix4x4_Abs (matrix4x4_t *out)
 {
     out->m[0][0] = fabs(out->m[0][0]);
index ae83c1d68901cab73bf8642990b7ebb8bde2f7cf..1b2b57908504dce11eb3e9870960c9b36939b693 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);
 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);
 
 // 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 62c71ea89972d8aa9a8e3507c0af22a52b61e200..c66ff4f28f1514363b48d599e9aeed6aed0e216a 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 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        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_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
 {
 
 typedef struct edict_odefunc_s
 {
index 217d897d8464e8155c319d442d57199d474a07bc..f362d36cd9ab0beb2f560204481f0091f4b39552 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;
        }
                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);
        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);
 }
 
        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;
        }
                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);
 }
        VectorCopy(PRVM_G_VECTOR(OFS_PARM1), f.v1);
        VM_physics_ApplyCmd(ed, &f);
 }
index 03cac1195e91c86868978ae14c60bb19356b41f0..cc599a458a6eb4f4aadf28957277e0b17bc40a02 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(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)
 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(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)
 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(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)
 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(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)
 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(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)
 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(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)
 PRVM_DECLARE_serverfieldfloat(sendcomplexanimation)
 PRVM_DECLARE_serverfieldfloat(skeletonindex)
 PRVM_DECLARE_serverfieldfloat(skin)
diff --git a/world.c b/world.c
index e31a6818ee83085530e7c1602c0ceabbdca9cbc9..b23fe2c924c4de7c782c7b7fe491201294d165ce 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_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"};
 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))
 
 #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);
 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);
 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 *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);
 //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[] =
 {
 
 static dllfunction_t odefuncs[] =
 {
-//     {"dGetConfiguration",                                                   (void **) &dGetConfiguration},
+       {"dGetConfiguration",                                                   (void **) &dGetConfiguration},
        {"dCheckConfiguration",                                                 (void **) &dCheckConfiguration},
        {"dInitODE",                                                                    (void **) &dInitODE},
 //     {"dInitODE2",                                                                   (void **) &dInitODE2},
        {"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},
        {"dBodyGetAngularVel",                                                  (void **) &dBodyGetAngularVel},
        {"dBodySetMass",                                                                (void **) &dBodySetMass},
 //     {"dBodyGetMass",                                                                (void **) &dBodyGetMass},
-//     {"dBodyAddForce",                                                               (void **) &dBodyAddForce},
-//     {"dBodyAddTorque",                                                              (void **) &dBodyAddTorque},
+       {"dBodyAddForce",                                                               (void **) &dBodyAddForce},
+       {"dBodyAddTorque",                                                              (void **) &dBodyAddTorque},
 //     {"dBodyAddRelForce",                                                    (void **) &dBodyAddRelForce},
 //     {"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},
 //     {"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_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);
        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
 # else
                        Con_Printf("ODE library loaded with double precision.\n");
 # endif
+                       Con_Printf("ODE configuration list: %s\n", dGetConfiguration());
                }
 #endif
        }
                }
 #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_DISABLE:
                dBodyDisable(body);
                break;
-       case ODEFUNC_RELFORCEATPOS:
+       case ODEFUNC_FORCE:
                dBodyEnable(body);
                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;
                break;
-       case ODEFUNC_RELTORQUE:
+       case ODEFUNC_TORQUE:
                dBodyEnable(body);
                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;
                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;
 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;
        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);
        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);
        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)
        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)
        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 JOINTTYPE_FIXED:
                        j = dJointCreateFixed((dWorldID)world->physics.ode_world, 0);
                        break;
-               case 0:
+               case JOINTTYPE_NONE:
                default:
                        // no joint
                        j = 0;
                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;
        dReal test;
        const dReal *ovelocity, *ospinvelocity;
        void *dataID;
-       dVector3 capsulerot[3];
        dp_model_t *model;
        float *ov;
        int *oe;
        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;
        vec_t scale = 1.0f;
        vec_t spinlimit;
        qboolean gravity;
+       vec3_t scalevec;
        edict_odefunc_t *func, *nextf;
 
 #ifdef ODE_DYNAMIC
        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;
                // 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)
                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;
                }
        }
                        massval = 1.0f;
                }
        }
-       else if (geomtype)
+       else if (geomtype && geomtype != GEOMTYPE_NONE)
        {
                VectorCopy(PRVM_gameedictvector(ed, mins), entmins);
                VectorCopy(PRVM_gameedictvector(ed, maxs), entmaxs);
        {
                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;
                {
                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)));
                        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:
                        dMassSetSphereTotal(&mass, massval, geomsize[0] * 0.5f);
                        break;
                case GEOMTYPE_CAPSULE:
-               case GEOMTYPE_CYLINDER:
                        axisindex = 0;
                        if (geomsize[axisindex] < geomsize[1])
                                axisindex = 1;
                        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
                        // 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)
                        if (axisindex == 0)
+                       {
                                Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 90, 1);
                                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)
                        else if (axisindex == 1)
+                       {
                                Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 90, 0, 0, 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
                        else
+                       {
                                Matrix4x4_CreateFromQuakeEntity(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2], 0, 0, 0, 1);
                                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)
                        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
                        {
                        }
                        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);
                        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;
 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);
 
        // 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
        // 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++)
        {
        // 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;
 
                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;
                        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);
 
                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));
                        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;
                        // 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);
                        // 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);
                }
                        // 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++;
                                }
                                        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 122b9c708a635d959f66780fd8bf6857ad31c612..e43ebd142c6ffc99f7fcfa01a5badb0baa86966d 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;
        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
        // stats
        int ode_numobjects; // total objects cound
        int ode_activeovjects; // active objects count