#include "quakedef.h"
#include "clvm_cmds.h"
+#include "cl_collision.h"
/*
if (world->areagrid_marknumber < 1)
world->areagrid_marknumber = 1;
// choose either the world box size, or a larger box to ensure the grid isn't too fine
- world->areagrid_size[0] = max(world->areagrid_maxs[0] - world->areagrid_mins[0], AREA_GRID * sv_areagrid_mingridsize.value);
- world->areagrid_size[1] = max(world->areagrid_maxs[1] - world->areagrid_mins[1], AREA_GRID * sv_areagrid_mingridsize.value);
- world->areagrid_size[2] = max(world->areagrid_maxs[2] - world->areagrid_mins[2], AREA_GRID * sv_areagrid_mingridsize.value);
+ world->areagrid_size[0] = max(world->maxs[0] - world->mins[0], AREA_GRID * sv_areagrid_mingridsize.value);
+ world->areagrid_size[1] = max(world->maxs[1] - world->mins[1], AREA_GRID * sv_areagrid_mingridsize.value);
+ world->areagrid_size[2] = max(world->maxs[2] - world->mins[2], AREA_GRID * sv_areagrid_mingridsize.value);
// figure out the corners of such a box, centered at the center of the world box
- world->areagrid_mins[0] = (world->areagrid_mins[0] + world->areagrid_maxs[0] - world->areagrid_size[0]) * 0.5f;
- world->areagrid_mins[1] = (world->areagrid_mins[1] + world->areagrid_maxs[1] - world->areagrid_size[1]) * 0.5f;
- world->areagrid_mins[2] = (world->areagrid_mins[2] + world->areagrid_maxs[2] - world->areagrid_size[2]) * 0.5f;
- world->areagrid_maxs[0] = (world->areagrid_mins[0] + world->areagrid_maxs[0] + world->areagrid_size[0]) * 0.5f;
- world->areagrid_maxs[1] = (world->areagrid_mins[1] + world->areagrid_maxs[1] + world->areagrid_size[1]) * 0.5f;
- world->areagrid_maxs[2] = (world->areagrid_mins[2] + world->areagrid_maxs[2] + world->areagrid_size[2]) * 0.5f;
+ world->areagrid_mins[0] = (world->mins[0] + world->maxs[0] - world->areagrid_size[0]) * 0.5f;
+ world->areagrid_mins[1] = (world->mins[1] + world->maxs[1] - world->areagrid_size[1]) * 0.5f;
+ world->areagrid_mins[2] = (world->mins[2] + world->maxs[2] - world->areagrid_size[2]) * 0.5f;
+ world->areagrid_maxs[0] = (world->mins[0] + world->maxs[0] + world->areagrid_size[0]) * 0.5f;
+ world->areagrid_maxs[1] = (world->mins[1] + world->maxs[1] + world->areagrid_size[1]) * 0.5f;
+ world->areagrid_maxs[2] = (world->mins[2] + world->maxs[2] + world->areagrid_size[2]) * 0.5f;
// now calculate the actual useful info from that
VectorNegate(world->areagrid_mins, world->areagrid_bias);
world->areagrid_scale[0] = AREA_GRID / world->areagrid_size[0];
// physics engine support
//============================================================================
-//#ifndef ODE_STATIC
-//#define ODE_DYNAMIC 1
-//#endif
+#ifndef ODE_STATIC
+# define ODE_DYNAMIC 1
+#endif
#if defined(ODE_STATIC) || defined(ODE_DYNAMIC)
#define USEODE 1
#endif
+// recent ODE trunk has dWorldStepFast1 removed
+//#define ODE_USE_STEPFAST
+
#ifdef USEODE
cvar_t physics_ode_quadtree_depth = {0, "physics_ode_quadtree_depth","5", "desired subdivision level of quadtree culling space"};
-cvar_t physics_ode_contactsurfacelayer = {0, "physics_ode_contactsurfacelayer","0", "allows objects to overlap this many units to reduce jitter"};
-cvar_t physics_ode_worldquickstep = {0, "physics_ode_worldquickstep","1", "use dWorldQuickStep rather than dWorldStepFast1 or dWorldStep"};
-cvar_t physics_ode_worldquickstep_iterations = {0, "physics_ode_worldquickstep_iterations","20", "parameter to dWorldQuickStep"};
-cvar_t physics_ode_worldstepfast = {0, "physics_ode_worldstepfast","0", "use dWorldStepFast1 rather than dWorldStep"};
-cvar_t physics_ode_worldstepfast_iterations = {0, "physics_ode_worldstepfast_iterations","20", "parameter to dWorldStepFast1"};
+cvar_t physics_ode_contactsurfacelayer = {0, "physics_ode_contactsurfacelayer","1", "allows objects to overlap this many units to reduce jitter"};
+cvar_t physics_ode_worldstep = {0, "physics_ode_worldstep","2", "step function to use, 0 - dWorldStep, 1 - dWorldStepFast1, 2 - dWorldQuickStep"};
+cvar_t physics_ode_worldstep_iterations = {0, "physics_ode_worldstep_iterations", "20", "parameter to dWorldQuickStep and dWorldStepFast1"};
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_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_iterationsperframe = {0, "physics_ode_iterationsperframe", "4", "divisor for time step, runs multiple physics steps per frame"};
-cvar_t physics_ode_movelimit = {0, "physics_ode_movelimit", "0.5", "clamp velocity if a single move would exceed this percentage of object thickness, to prevent flying through walls"};
+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_damping_linear = {0, "physics_ode_world_damping_linear", "0.005", "world linear damping scale (see ODE User Guide); use defaults when set to -1"};
+cvar_t physics_ode_world_damping_linear_threshold = {0, "physics_ode_world_damping_linear_threshold", "0.01", "world linear damping threshold (see ODE User Guide); use defaults when set to -1"};
+cvar_t physics_ode_world_damping_angular = {0, "physics_ode_world_damping_angular", "0.005", "world angular damping scale (see ODE User Guide); use defaults when set to -1"};
+cvar_t physics_ode_world_damping_angular_threshold = {0, "physics_ode_world_damping_angular_threshold", "0.01", "world angular damping threshold (see ODE User Guide); use defaults when set to -1"};
+cvar_t physics_ode_iterationsperframe = {0, "physics_ode_iterationsperframe", "1", "divisor for time step, runs multiple physics steps per frame"};
+cvar_t physics_ode_constantstep = {0, "physics_ode_constantstep", "1", "use constant step (sys_ticrate value) instead of variable step which tends to increase stability"};
+cvar_t physics_ode_autodisable = {0, "physics_ode_autodisable", "1", "automatic disabling of objects which dont move for long period of time, makes object stacking a lot faster"};
+cvar_t physics_ode_autodisable_steps = {0, "physics_ode_autodisable_steps", "10", "how many steps object should be dormant to be autodisabled"};
+cvar_t physics_ode_autodisable_time = {0, "physics_ode_autodisable_time", "0", "how many seconds object should be dormant to be autodisabled"};
+cvar_t physics_ode_autodisable_threshold_linear = {0, "physics_ode_autodisable_threshold_linear", "0.2", "body will be disabled if it's linear move below this value"};
+cvar_t physics_ode_autodisable_threshold_angular = {0, "physics_ode_autodisable_threshold_angular", "0.3", "body will be disabled if it's angular move below this value"};
+cvar_t physics_ode_autodisable_threshold_samples = {0, "physics_ode_autodisable_threshold_samples", "5", "average threshold with this number of samples"};
+cvar_t physics_ode_movelimit = {0, "physics_ode_movelimit", "0.5", "clamp velocity if a single move would exceed this percentage of object thickness, to prevent flying through walls, be aware that behavior depends of step type"};
cvar_t physics_ode_spinlimit = {0, "physics_ode_spinlimit", "10000", "reset spin velocity if it gets too large"};
+cvar_t physics_ode_trick_fixnan = {0, "physics_ode_trick_fixnan", "1", "engine trick that checks and fixes NaN velocity/origin/angles on objects, a value of 2 makes console prints on each fix"};
+cvar_t physics_ode_printstats = {0, "physics_ode_printstats", "0", "print ODE stats each frame"};
+cvar_t physics_ode = {0, "physics_ode", "0", "run ODE physics (VERY experimental and potentially buggy)"};
// LordHavoc: this large chunk of definitions comes from the ODE library
// include files.
//dReal (ODE_API *dWorldGetContactMaxCorrectingVel)(dWorldID);
void (ODE_API *dWorldSetContactSurfaceLayer)(dWorldID, dReal depth);
//dReal (ODE_API *dWorldGetContactSurfaceLayer)(dWorldID);
+#ifdef ODE_USE_STEPFAST
void (ODE_API *dWorldStepFast1)(dWorldID, dReal stepsize, int maxiterations);
+#endif
//void (ODE_API *dWorldSetAutoEnableDepthSF1)(dWorldID, int autoEnableDepth);
//int (ODE_API *dWorldGetAutoEnableDepthSF1)(dWorldID);
//dReal (ODE_API *dWorldGetAutoDisableLinearThreshold)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableLinearThreshold)(dWorldID, dReal linear_threshold);
+void (ODE_API *dWorldSetAutoDisableLinearThreshold)(dWorldID, dReal linear_threshold);
//dReal (ODE_API *dWorldGetAutoDisableAngularThreshold)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableAngularThreshold)(dWorldID, dReal angular_threshold);
+void (ODE_API *dWorldSetAutoDisableAngularThreshold)(dWorldID, dReal angular_threshold);
//dReal (ODE_API *dWorldGetAutoDisableLinearAverageThreshold)(dWorldID);
//void (ODE_API *dWorldSetAutoDisableLinearAverageThreshold)(dWorldID, dReal linear_average_threshold);
//dReal (ODE_API *dWorldGetAutoDisableAngularAverageThreshold)(dWorldID);
//void (ODE_API *dWorldSetAutoDisableAngularAverageThreshold)(dWorldID, dReal angular_average_threshold);
//int (ODE_API *dWorldGetAutoDisableAverageSamplesCount)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableAverageSamplesCount)(dWorldID, unsigned int average_samples_count );
+void (ODE_API *dWorldSetAutoDisableAverageSamplesCount)(dWorldID, unsigned int average_samples_count );
//int (ODE_API *dWorldGetAutoDisableSteps)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableSteps)(dWorldID, int steps);
+void (ODE_API *dWorldSetAutoDisableSteps)(dWorldID, int steps);
//dReal (ODE_API *dWorldGetAutoDisableTime)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableTime)(dWorldID, dReal time);
+void (ODE_API *dWorldSetAutoDisableTime)(dWorldID, dReal time);
//int (ODE_API *dWorldGetAutoDisableFlag)(dWorldID);
-//void (ODE_API *dWorldSetAutoDisableFlag)(dWorldID, int do_auto_disable);
+void (ODE_API *dWorldSetAutoDisableFlag)(dWorldID, int do_auto_disable);
//dReal (ODE_API *dWorldGetLinearDampingThreshold)(dWorldID w);
-//void (ODE_API *dWorldSetLinearDampingThreshold)(dWorldID w, dReal threshold);
+void (ODE_API *dWorldSetLinearDampingThreshold)(dWorldID w, dReal threshold);
//dReal (ODE_API *dWorldGetAngularDampingThreshold)(dWorldID w);
-//void (ODE_API *dWorldSetAngularDampingThreshold)(dWorldID w, dReal threshold);
+void (ODE_API *dWorldSetAngularDampingThreshold)(dWorldID w, dReal threshold);
//dReal (ODE_API *dWorldGetLinearDamping)(dWorldID w);
-//void (ODE_API *dWorldSetLinearDamping)(dWorldID w, dReal scale);
+void (ODE_API *dWorldSetLinearDamping)(dWorldID w, dReal scale);
//dReal (ODE_API *dWorldGetAngularDamping)(dWorldID w);
-//void (ODE_API *dWorldSetAngularDamping)(dWorldID w, dReal scale);
+void (ODE_API *dWorldSetAngularDamping)(dWorldID w, dReal scale);
//void (ODE_API *dWorldSetDamping)(dWorldID w, dReal linear_scale, dReal angular_scale);
//dReal (ODE_API *dWorldGetMaxAngularSpeed)(dWorldID w);
//void (ODE_API *dWorldSetMaxAngularSpeed)(dWorldID w, dReal max_speed);
//void (ODE_API *dBodyAddForce)(dBodyID, dReal fx, dReal fy, dReal fz);
//void (ODE_API *dBodyAddTorque)(dBodyID, dReal fx, dReal fy, dReal fz);
//void (ODE_API *dBodyAddRelForce)(dBodyID, dReal fx, dReal fy, dReal fz);
-//void (ODE_API *dBodyAddRelTorque)(dBodyID, dReal fx, dReal fy, dReal fz);
+void (ODE_API *dBodyAddRelTorque)(dBodyID, dReal fx, dReal fy, dReal fz);
//void (ODE_API *dBodyAddForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
-//void (ODE_API *dBodyAddForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
+void (ODE_API *dBodyAddForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
//void (ODE_API *dBodyAddRelForceAtPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
//void (ODE_API *dBodyAddRelForceAtRelPos)(dBodyID, dReal fx, dReal fy, dReal fz, dReal px, dReal py, dReal pz);
//const dReal * (ODE_API *dBodyGetForce)(dBodyID);
//void (ODE_API *dBodySetDynamic)(dBodyID);
//void (ODE_API *dBodySetKinematic)(dBodyID);
//int (ODE_API *dBodyIsKinematic)(dBodyID);
-//void (ODE_API *dBodyEnable)(dBodyID);
-//void (ODE_API *dBodyDisable)(dBodyID);
-//int (ODE_API *dBodyIsEnabled)(dBodyID);
+void (ODE_API *dBodyEnable)(dBodyID);
+void (ODE_API *dBodyDisable)(dBodyID);
+int (ODE_API *dBodyIsEnabled)(dBodyID);
void (ODE_API *dBodySetGravityMode)(dBodyID b, int mode);
int (ODE_API *dBodyGetGravityMode)(dBodyID b);
//void (*dBodySetMovedCallback)(dBodyID b, void(ODE_API *callback)(dBodyID));
// {"dWorldGetContactMaxCorrectingVel", (void **) &dWorldGetContactMaxCorrectingVel},
{"dWorldSetContactSurfaceLayer", (void **) &dWorldSetContactSurfaceLayer},
// {"dWorldGetContactSurfaceLayer", (void **) &dWorldGetContactSurfaceLayer},
+#ifdef ODE_USE_STEPFAST
{"dWorldStepFast1", (void **) &dWorldStepFast1},
+#endif
// {"dWorldSetAutoEnableDepthSF1", (void **) &dWorldSetAutoEnableDepthSF1},
// {"dWorldGetAutoEnableDepthSF1", (void **) &dWorldGetAutoEnableDepthSF1},
// {"dWorldGetAutoDisableLinearThreshold", (void **) &dWorldGetAutoDisableLinearThreshold},
-// {"dWorldSetAutoDisableLinearThreshold", (void **) &dWorldSetAutoDisableLinearThreshold},
+ {"dWorldSetAutoDisableLinearThreshold", (void **) &dWorldSetAutoDisableLinearThreshold},
// {"dWorldGetAutoDisableAngularThreshold", (void **) &dWorldGetAutoDisableAngularThreshold},
-// {"dWorldSetAutoDisableAngularThreshold", (void **) &dWorldSetAutoDisableAngularThreshold},
+ {"dWorldSetAutoDisableAngularThreshold", (void **) &dWorldSetAutoDisableAngularThreshold},
// {"dWorldGetAutoDisableLinearAverageThreshold", (void **) &dWorldGetAutoDisableLinearAverageThreshold},
// {"dWorldSetAutoDisableLinearAverageThreshold", (void **) &dWorldSetAutoDisableLinearAverageThreshold},
// {"dWorldGetAutoDisableAngularAverageThreshold", (void **) &dWorldGetAutoDisableAngularAverageThreshold},
// {"dWorldSetAutoDisableAngularAverageThreshold", (void **) &dWorldSetAutoDisableAngularAverageThreshold},
// {"dWorldGetAutoDisableAverageSamplesCount", (void **) &dWorldGetAutoDisableAverageSamplesCount},
-// {"dWorldSetAutoDisableAverageSamplesCount", (void **) &dWorldSetAutoDisableAverageSamplesCount},
+ {"dWorldSetAutoDisableAverageSamplesCount", (void **) &dWorldSetAutoDisableAverageSamplesCount},
// {"dWorldGetAutoDisableSteps", (void **) &dWorldGetAutoDisableSteps},
-// {"dWorldSetAutoDisableSteps", (void **) &dWorldSetAutoDisableSteps},
+ {"dWorldSetAutoDisableSteps", (void **) &dWorldSetAutoDisableSteps},
// {"dWorldGetAutoDisableTime", (void **) &dWorldGetAutoDisableTime},
-// {"dWorldSetAutoDisableTime", (void **) &dWorldSetAutoDisableTime},
+ {"dWorldSetAutoDisableTime", (void **) &dWorldSetAutoDisableTime},
// {"dWorldGetAutoDisableFlag", (void **) &dWorldGetAutoDisableFlag},
-// {"dWorldSetAutoDisableFlag", (void **) &dWorldSetAutoDisableFlag},
+ {"dWorldSetAutoDisableFlag", (void **) &dWorldSetAutoDisableFlag},
// {"dWorldGetLinearDampingThreshold", (void **) &dWorldGetLinearDampingThreshold},
-// {"dWorldSetLinearDampingThreshold", (void **) &dWorldSetLinearDampingThreshold},
+ {"dWorldSetLinearDampingThreshold", (void **) &dWorldSetLinearDampingThreshold},
// {"dWorldGetAngularDampingThreshold", (void **) &dWorldGetAngularDampingThreshold},
-// {"dWorldSetAngularDampingThreshold", (void **) &dWorldSetAngularDampingThreshold},
+ {"dWorldSetAngularDampingThreshold", (void **) &dWorldSetAngularDampingThreshold},
// {"dWorldGetLinearDamping", (void **) &dWorldGetLinearDamping},
-// {"dWorldSetLinearDamping", (void **) &dWorldSetLinearDamping},
+ {"dWorldSetLinearDamping", (void **) &dWorldSetLinearDamping},
// {"dWorldGetAngularDamping", (void **) &dWorldGetAngularDamping},
-// {"dWorldSetAngularDamping", (void **) &dWorldSetAngularDamping},
+ {"dWorldSetAngularDamping", (void **) &dWorldSetAngularDamping},
// {"dWorldSetDamping", (void **) &dWorldSetDamping},
// {"dWorldGetMaxAngularSpeed", (void **) &dWorldGetMaxAngularSpeed},
// {"dWorldSetMaxAngularSpeed", (void **) &dWorldSetMaxAngularSpeed},
// {"dBodyAddForce", (void **) &dBodyAddForce},
// {"dBodyAddTorque", (void **) &dBodyAddTorque},
// {"dBodyAddRelForce", (void **) &dBodyAddRelForce},
-// {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque},
+ {"dBodyAddRelTorque", (void **) &dBodyAddRelTorque},
// {"dBodyAddForceAtPos", (void **) &dBodyAddForceAtPos},
-// {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos},
+ {"dBodyAddForceAtRelPos", (void **) &dBodyAddForceAtRelPos},
// {"dBodyAddRelForceAtPos", (void **) &dBodyAddRelForceAtPos},
// {"dBodyAddRelForceAtRelPos", (void **) &dBodyAddRelForceAtRelPos},
// {"dBodyGetForce", (void **) &dBodyGetForce},
// {"dBodySetDynamic", (void **) &dBodySetDynamic},
// {"dBodySetKinematic", (void **) &dBodySetKinematic},
// {"dBodyIsKinematic", (void **) &dBodyIsKinematic},
-// {"dBodyEnable", (void **) &dBodyEnable},
-// {"dBodyDisable", (void **) &dBodyDisable},
-// {"dBodyIsEnabled", (void **) &dBodyIsEnabled},
+ {"dBodyEnable", (void **) &dBodyEnable},
+ {"dBodyDisable", (void **) &dBodyDisable},
+ {"dBodyIsEnabled", (void **) &dBodyIsEnabled},
{"dBodySetGravityMode", (void **) &dBodySetGravityMode},
{"dBodyGetGravityMode", (void **) &dBodyGetGravityMode},
// {"dBodySetMovedCallback", (void **) &dBodySetMovedCallback},
Cvar_RegisterVariable(&physics_ode_quadtree_depth);
Cvar_RegisterVariable(&physics_ode_contactsurfacelayer);
- Cvar_RegisterVariable(&physics_ode_worldquickstep);
- Cvar_RegisterVariable(&physics_ode_worldquickstep_iterations);
- Cvar_RegisterVariable(&physics_ode_worldstepfast);
- Cvar_RegisterVariable(&physics_ode_worldstepfast_iterations);
+ Cvar_RegisterVariable(&physics_ode_worldstep);
+ Cvar_RegisterVariable(&physics_ode_worldstep_iterations);
Cvar_RegisterVariable(&physics_ode_contact_mu);
Cvar_RegisterVariable(&physics_ode_contact_erp);
Cvar_RegisterVariable(&physics_ode_contact_cfm);
Cvar_RegisterVariable(&physics_ode_world_erp);
Cvar_RegisterVariable(&physics_ode_world_cfm);
+ Cvar_RegisterVariable(&physics_ode_world_damping);
+ Cvar_RegisterVariable(&physics_ode_world_damping_linear);
+ Cvar_RegisterVariable(&physics_ode_world_damping_linear_threshold);
+ Cvar_RegisterVariable(&physics_ode_world_damping_angular);
+ Cvar_RegisterVariable(&physics_ode_world_damping_angular_threshold);
Cvar_RegisterVariable(&physics_ode_iterationsperframe);
+ Cvar_RegisterVariable(&physics_ode_constantstep);
Cvar_RegisterVariable(&physics_ode_movelimit);
Cvar_RegisterVariable(&physics_ode_spinlimit);
+ Cvar_RegisterVariable(&physics_ode_trick_fixnan);
+ Cvar_RegisterVariable(&physics_ode_autodisable);
+ Cvar_RegisterVariable(&physics_ode_autodisable_steps);
+ Cvar_RegisterVariable(&physics_ode_autodisable_time);
+ Cvar_RegisterVariable(&physics_ode_autodisable_threshold_linear);
+ Cvar_RegisterVariable(&physics_ode_autodisable_threshold_angular);
+ Cvar_RegisterVariable(&physics_ode_autodisable_threshold_samples);
+ Cvar_RegisterVariable(&physics_ode_printstats);
+ Cvar_RegisterVariable(&physics_ode);
#ifdef ODE_DYNAMIC
// Load the DLL
# endif
{
# ifdef dSINGLE
- Con_Printf("ode library not compiled for single precision - incompatible! Not using ODE physics.\n");
+ Con_Printf("ODE library not compiled for single precision - incompatible! Not using ODE physics.\n");
# else
- Con_Printf("ode library not compiled for double precision - incompatible! Not using ODE physics.\n");
+ Con_Printf("ODE library not compiled for double precision - incompatible! Not using ODE physics.\n");
# endif
Sys_UnloadLibrary(&ode_dll);
ode_dll = NULL;
}
+ else
+ {
+# ifdef dSINGLE
+ Con_Printf("ODE library loaded with single precision.\n");
+# else
+ Con_Printf("ODE library loaded with double precision.\n");
+# endif
+ }
#endif
}
#endif
}
#ifdef USEODE
+static void World_Physics_UpdateODE(world_t *world)
+{
+ dWorldID odeworld;
+
+ odeworld = (dWorldID)world->physics.ode_world;
+
+ // ERP and CFM
+ if (physics_ode_world_erp.value >= 0)
+ dWorldSetERP(odeworld, physics_ode_world_erp.value);
+ if (physics_ode_world_cfm.value >= 0)
+ dWorldSetCFM(odeworld, physics_ode_world_cfm.value);
+ // Damping
+ if (physics_ode_world_damping.integer)
+ {
+ dWorldSetLinearDamping(odeworld, (physics_ode_world_damping_linear.value >= 0) ? (physics_ode_world_damping_linear.value * physics_ode_world_damping.value) : 0);
+ dWorldSetLinearDampingThreshold(odeworld, (physics_ode_world_damping_linear_threshold.value >= 0) ? (physics_ode_world_damping_linear_threshold.value * physics_ode_world_damping.value) : 0);
+ dWorldSetAngularDamping(odeworld, (physics_ode_world_damping_angular.value >= 0) ? (physics_ode_world_damping_angular.value * physics_ode_world_damping.value) : 0);
+ dWorldSetAngularDampingThreshold(odeworld, (physics_ode_world_damping_angular_threshold.value >= 0) ? (physics_ode_world_damping_angular_threshold.value * physics_ode_world_damping.value) : 0);
+ }
+ else
+ {
+ dWorldSetLinearDamping(odeworld, 0);
+ dWorldSetLinearDampingThreshold(odeworld, 0);
+ dWorldSetAngularDamping(odeworld, 0);
+ dWorldSetAngularDampingThreshold(odeworld, 0);
+ }
+ // Autodisable
+ dWorldSetAutoDisableFlag(odeworld, (physics_ode_autodisable.integer) ? 1 : 0);
+ if (physics_ode_autodisable.integer)
+ {
+ dWorldSetAutoDisableSteps(odeworld, bound(1, physics_ode_autodisable_steps.integer, 100));
+ dWorldSetAutoDisableTime(odeworld, physics_ode_autodisable_time.value);
+ dWorldSetAutoDisableAverageSamplesCount(odeworld, bound(1, physics_ode_autodisable_threshold_samples.integer, 100));
+ dWorldSetAutoDisableLinearThreshold(odeworld, physics_ode_autodisable_threshold_linear.value);
+ dWorldSetAutoDisableAngularThreshold(odeworld, physics_ode_autodisable_threshold_angular.value);
+ }
+}
+
static void World_Physics_EnableODE(world_t *world)
{
dVector3 center, extents;
world->physics.ode_world = dWorldCreate();
world->physics.ode_space = dQuadTreeSpaceCreate(NULL, center, extents, bound(1, physics_ode_quadtree_depth.integer, 10));
world->physics.ode_contactgroup = dJointGroupCreate(0);
- if(physics_ode_world_erp.value >= 0)
- dWorldSetERP(world->physics.ode_world, physics_ode_world_erp.value);
- if(physics_ode_world_cfm.value >= 0)
- dWorldSetCFM(world->physics.ode_world, physics_ode_world_cfm.value);
+
+ World_Physics_UpdateODE(world);
}
#endif
#ifdef USEODE
if (world->physics.ode)
{
- dWorldDestroy(world->physics.ode_world);
- dSpaceDestroy(world->physics.ode_space);
- dJointGroupDestroy(world->physics.ode_contactgroup);
+ dWorldDestroy((dWorldID)world->physics.ode_world);
+ dSpaceDestroy((dSpaceID)world->physics.ode_space);
+ dJointGroupDestroy((dJointGroupID)world->physics.ode_contactgroup);
world->physics.ode = false;
}
#endif
void World_Physics_RemoveFromEntity(world_t *world, prvm_edict_t *ed)
{
+ edict_odefunc_t *f, *nf;
+
// entity is not physics controlled, free any physics data
ed->priv.server->ode_physics = false;
#ifdef USEODE
if(ed->priv.server->ode_massbuf)
Mem_Free(ed->priv.server->ode_massbuf);
ed->priv.server->ode_massbuf = NULL;
+ // clear functions stack
+ for(f = ed->priv.server->ode_func; f; f = nf)
+ {
+ nf = f->next;
+ Mem_Free(f);
+ }
+ ed->priv.server->ode_func = NULL;
+}
+
+void World_Physics_ApplyCmd(prvm_edict_t *ed, edict_odefunc_t *f)
+{
+ dBodyID body = (dBodyID)ed->priv.server->ode_body;
+
+#ifdef USEODE
+ switch(f->type)
+ {
+ case ODEFUNC_ENABLE:
+ dBodyEnable(body);
+ break;
+ case ODEFUNC_DISABLE:
+ dBodyDisable(body);
+ break;
+ case ODEFUNC_RELFORCEATPOS:
+ dBodyEnable(body);
+ dBodyAddForceAtRelPos(body, f->v1[0], f->v1[1], f->v1[2], f->v2[0], f->v2[1], f->v2[2]);
+ break;
+ case ODEFUNC_RELTORQUE:
+ dBodyEnable(body);
+ dBodyAddRelTorque(body, f->v1[0], f->v1[1], f->v1[2]);
+ break;
+ default:
+ break;
+ }
+#endif
}
#ifdef USEODE
movetype = (int)val->_float;
if (movetype != MOVETYPE_PHYSICS)
{
- val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.jointtype);if (val) jointtype = (int)val->_float;
+ jointtype = 0;val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.jointtype);if (val) jointtype = (int)val->_float;
switch(jointtype)
{
// TODO feed back data from physics
VectorCopy(velocity, ed->priv.server->ode_velocity);
VectorCopy(angles, ed->priv.server->ode_angles);
VectorCopy(avelocity, ed->priv.server->ode_avelocity);
- ed->priv.server->ode_gravity = dBodyGetGravityMode(body);
+ ed->priv.server->ode_gravity = dBodyGetGravityMode(body) != 0;
if(!strcmp(prog->name, "server")) // FIXME some better way?
{
switch(jointtype)
{
case JOINTTYPE_POINT:
- j = dJointCreateBall(world->physics.ode_world, 0);
+ j = dJointCreateBall((dWorldID)world->physics.ode_world, 0);
break;
case JOINTTYPE_HINGE:
- j = dJointCreateHinge(world->physics.ode_world, 0);
+ j = dJointCreateHinge((dWorldID)world->physics.ode_world, 0);
break;
case JOINTTYPE_SLIDER:
- j = dJointCreateSlider(world->physics.ode_world, 0);
+ j = dJointCreateSlider((dWorldID)world->physics.ode_world, 0);
break;
case JOINTTYPE_UNIVERSAL:
- j = dJointCreateUniversal(world->physics.ode_world, 0);
+ j = dJointCreateUniversal((dWorldID)world->physics.ode_world, 0);
break;
case JOINTTYPE_HINGE2:
- j = dJointCreateHinge2(world->physics.ode_world, 0);
+ j = dJointCreateHinge2((dWorldID)world->physics.ode_world, 0);
break;
case JOINTTYPE_FIXED:
- j = dJointCreateFixed(world->physics.ode_world, 0);
+ j = dJointCreateFixed((dWorldID)world->physics.ode_world, 0);
break;
case 0:
default:
if(ed->priv.server->ode_joint)
{
//Con_Printf("deleted old joint %i\n", (int) (ed - prog->edicts));
- dJointAttach(ed->priv.server->ode_joint, 0, 0);
- dJointDestroy(ed->priv.server->ode_joint);
+ dJointAttach((dJointID)ed->priv.server->ode_joint, 0, 0);
+ dJointDestroy((dJointID)ed->priv.server->ode_joint);
}
ed->priv.server->ode_joint = (void *) j;
ed->priv.server->ode_joint_type = jointtype;
dBodyID body = (dBodyID)ed->priv.server->ode_body;
dMass mass;
dReal test;
+ const dReal *ovelocity, *ospinvelocity;
void *dataID;
dVector3 capsulerot[3];
dp_model_t *model;
vec_t scale = 1.0f;
vec_t spinlimit;
qboolean gravity;
+ edict_odefunc_t *func, *nextf;
+
#ifdef ODE_DYNAMIC
if (!ode_dll)
return;
#endif
+ VectorClear(entmins);
+ VectorClear(entmaxs);
val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.solid);if (val) solid = (int)val->_float;
val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.movetype);if (val) movetype = (int)val->_float;
val = PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.scale);if (val && val->_float) scale = val->_float;
mempool = cls.levelmempool;
else
mempool = NULL;
+ model = NULL;
switch(solid)
{
case SOLID_BSP:
if (!model)
{
Con_Printf("entity %i (classname %s) has no model\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string));
- break;
+ goto treatasbox;
}
// add an optimized mesh to the model containing only the SUPERCONTENTS_SOLID surfaces
if (!model->brush.collisionmesh)
if (!model->brush.collisionmesh || !model->brush.collisionmesh->numtriangles)
{
Con_Printf("entity %i (classname %s) has no geometry\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string));
- break;
+ goto treatasbox;
}
// ODE requires persistent mesh storage, so we need to copy out
// the data from the model because renderer restarts could free it
Matrix4x4_CreateTranslate(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);
// now create the geom
dataID = dGeomTriMeshDataCreate();
- dGeomTriMeshDataBuildSingle(dataID, (void*)ed->priv.server->ode_vertex3f, sizeof(float[3]), ed->priv.server->ode_numvertices, ed->priv.server->ode_element3i, ed->priv.server->ode_numtriangles*3, sizeof(int[3]));
- ed->priv.server->ode_geom = (void *)dCreateTriMesh(world->physics.ode_space, dataID, NULL, NULL, NULL);
+ dGeomTriMeshDataBuildSingle((dTriMeshDataID)dataID, (void*)ed->priv.server->ode_vertex3f, sizeof(float[3]), ed->priv.server->ode_numvertices, ed->priv.server->ode_element3i, ed->priv.server->ode_numtriangles*3, sizeof(int[3]));
+ ed->priv.server->ode_geom = (void *)dCreateTriMesh((dSpaceID)world->physics.ode_space, (dTriMeshDataID)dataID, NULL, NULL, NULL);
dMassSetBoxTotal(&mass, massval, geomsize[0], geomsize[1], geomsize[2]);
break;
case SOLID_BBOX:
case SOLID_SLIDEBOX:
case SOLID_CORPSE:
case SOLID_PHYSICS_BOX:
+treatasbox:
Matrix4x4_CreateTranslate(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);
- ed->priv.server->ode_geom = (void *)dCreateBox(world->physics.ode_space, geomsize[0], geomsize[1], geomsize[2]);
+ ed->priv.server->ode_geom = (void *)dCreateBox((dSpaceID)world->physics.ode_space, geomsize[0], geomsize[1], geomsize[2]);
dMassSetBoxTotal(&mass, massval, geomsize[0], geomsize[1], geomsize[2]);
break;
case SOLID_PHYSICS_SPHERE:
Matrix4x4_CreateTranslate(&ed->priv.server->ode_offsetmatrix, geomcenter[0], geomcenter[1], geomcenter[2]);
- ed->priv.server->ode_geom = (void *)dCreateSphere(world->physics.ode_space, geomsize[0] * 0.5f);
+ ed->priv.server->ode_geom = (void *)dCreateSphere((dSpaceID)world->physics.ode_space, geomsize[0] * 0.5f);
dMassSetSphereTotal(&mass, massval, geomsize[0] * 0.5f);
break;
case SOLID_PHYSICS_CAPSULE:
// 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)
- ed->priv.server->ode_geom = (void *)dCreateCapsule(world->physics.ode_space, radius, length);
+ ed->priv.server->ode_geom = (void *)dCreateCapsule((dSpaceID)world->physics.ode_space, radius, length);
dMassSetCapsuleTotal(&mass, massval, axisindex+1, radius, length);
break;
default:
Sys_Error("World_Physics_BodyFromEntity: unrecognized solid value %i was accepted by filter\n", solid);
+ // this goto only exists to prevent warnings from the compiler
+ // about uninitialized variables (mass), while allowing it to
+ // catch legitimate uninitialized variable warnings
+ goto treatasbox;
}
Matrix4x4_Invert_Simple(&ed->priv.server->ode_offsetimatrix, &ed->priv.server->ode_offsetmatrix);
ed->priv.server->ode_massbuf = Mem_Alloc(mempool, sizeof(mass));
}
if(ed->priv.server->ode_geom)
- dGeomSetData(ed->priv.server->ode_geom, (void*)ed);
+ dGeomSetData((dGeomID)ed->priv.server->ode_geom, (void*)ed);
if (movetype == MOVETYPE_PHYSICS && ed->priv.server->ode_geom)
{
if (ed->priv.server->ode_body == NULL)
{
- ed->priv.server->ode_body = (void *)(body = dBodyCreate(world->physics.ode_world));
- dGeomSetBody(ed->priv.server->ode_geom, body);
+ ed->priv.server->ode_body = (void *)(body = dBodyCreate((dWorldID)world->physics.ode_world));
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, body);
dBodySetData(body, (void*)ed);
dBodySetMass(body, (dMass *) ed->priv.server->ode_massbuf);
modified = true;
if (ed->priv.server->ode_body != NULL)
{
if(ed->priv.server->ode_geom)
- dGeomSetBody(ed->priv.server->ode_geom, 0);
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, 0);
dBodyDestroy((dBodyID) ed->priv.server->ode_body);
ed->priv.server->ode_body = NULL;
modified = true;
// we must prevent NANs...
- test = VectorLength2(origin) + VectorLength2(forward) + VectorLength2(left) + VectorLength2(up) + VectorLength2(velocity) + VectorLength2(spinvelocity);
- if (IS_NAN(test))
+ if (physics_ode_trick_fixnan.integer)
{
- modified = true;
- //Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .axis_forward = '%f %f %f' .axis_left = '%f %f %f' .axis_up = %f %f %f' .spinvelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], forward[0], forward[1], forward[2], left[0], left[1], left[2], up[0], up[1], up[2], spinvelocity[0], spinvelocity[1], spinvelocity[2]);
- Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .angles = '%f %f %f' .avelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], angles[0], angles[1], angles[2], avelocity[0], avelocity[1], avelocity[2]);
- test = VectorLength2(origin);
- if (IS_NAN(test))
- VectorClear(origin);
- test = VectorLength2(forward) * VectorLength2(left) * VectorLength2(up);
+ test = VectorLength2(origin) + VectorLength2(forward) + VectorLength2(left) + VectorLength2(up) + VectorLength2(velocity) + VectorLength2(spinvelocity);
if (IS_NAN(test))
{
- VectorSet(angles, 0, 0, 0);
- VectorSet(forward, 1, 0, 0);
- VectorSet(left, 0, 1, 0);
- VectorSet(up, 0, 0, 1);
- }
- test = VectorLength2(velocity);
- if (IS_NAN(test))
- VectorClear(velocity);
- test = VectorLength2(spinvelocity);
- if (IS_NAN(test))
- {
- VectorClear(avelocity);
- VectorClear(spinvelocity);
+ modified = true;
+ //Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .axis_forward = '%f %f %f' .axis_left = '%f %f %f' .axis_up = %f %f %f' .spinvelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], forward[0], forward[1], forward[2], left[0], left[1], left[2], up[0], up[1], up[2], spinvelocity[0], spinvelocity[1], spinvelocity[2]);
+ if (physics_ode_trick_fixnan.integer >= 2)
+ Con_Printf("Fixing NAN values on entity %i : .classname = \"%s\" .origin = '%f %f %f' .velocity = '%f %f %f' .angles = '%f %f %f' .avelocity = '%f %f %f'\n", PRVM_NUM_FOR_EDICT(ed), PRVM_GetString(PRVM_EDICTFIELDVALUE(ed, prog->fieldoffsets.classname)->string), origin[0], origin[1], origin[2], velocity[0], velocity[1], velocity[2], angles[0], angles[1], angles[2], avelocity[0], avelocity[1], avelocity[2]);
+ test = VectorLength2(origin);
+ if (IS_NAN(test))
+ VectorClear(origin);
+ test = VectorLength2(forward) * VectorLength2(left) * VectorLength2(up);
+ if (IS_NAN(test))
+ {
+ VectorSet(angles, 0, 0, 0);
+ VectorSet(forward, 1, 0, 0);
+ VectorSet(left, 0, 1, 0);
+ VectorSet(up, 0, 0, 1);
+ }
+ test = VectorLength2(velocity);
+ if (IS_NAN(test))
+ VectorClear(velocity);
+ test = VectorLength2(spinvelocity);
+ if (IS_NAN(test))
+ {
+ VectorClear(avelocity);
+ VectorClear(spinvelocity);
+ }
}
}
modified = true;
// store the qc values into the physics engine
- body = ed->priv.server->ode_body;
+ body = (dBodyID)ed->priv.server->ode_body;
if (modified && ed->priv.server->ode_geom)
{
dVector3 r[3];
{
if(movetype == MOVETYPE_PHYSICS)
{
- dGeomSetBody(ed->priv.server->ode_geom, body);
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, body);
dBodySetPosition(body, origin[0], origin[1], origin[2]);
dBodySetRotation(body, r[0]);
dBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);
}
else
{
- dGeomSetBody(ed->priv.server->ode_geom, body);
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, body);
dBodySetPosition(body, origin[0], origin[1], origin[2]);
dBodySetRotation(body, r[0]);
dBodySetLinearVel(body, velocity[0], velocity[1], velocity[2]);
dBodySetAngularVel(body, spinvelocity[0], spinvelocity[1], spinvelocity[2]);
dBodySetGravityMode(body, gravity);
- dGeomSetBody(ed->priv.server->ode_geom, 0);
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, 0);
}
}
else
{
// no body... then let's adjust the parameters of the geom directly
- dGeomSetBody(ed->priv.server->ode_geom, 0); // just in case we previously HAD a body (which should never happen)
- dGeomSetPosition(ed->priv.server->ode_geom, origin[0], origin[1], origin[2]);
- dGeomSetRotation(ed->priv.server->ode_geom, r[0]);
+ dGeomSetBody((dGeomID)ed->priv.server->ode_geom, 0); // just in case we previously HAD a body (which should never happen)
+ dGeomSetPosition((dGeomID)ed->priv.server->ode_geom, origin[0], origin[1], origin[2]);
+ dGeomSetRotation((dGeomID)ed->priv.server->ode_geom, r[0]);
}
}
if(body)
{
+
// limit movement speed to prevent missed collisions at high speed
- const dReal *ovelocity = dBodyGetLinearVel(body);
- const dReal *ospinvelocity = dBodyGetAngularVel(body);
+ ovelocity = dBodyGetLinearVel(body);
+ ospinvelocity = dBodyGetAngularVel(body);
movelimit = ed->priv.server->ode_movelimit * world->physics.ode_movelimit;
test = VectorLength2(ovelocity);
if (test > movelimit*movelimit)
{
dBodySetAngularVel(body, 0, 0, 0);
}
+
+ // apply functions and clear stack
+ for(func = ed->priv.server->ode_func; func; func = nextf)
+ {
+ nextf = func->next;
+ World_Physics_ApplyCmd(ed, func);
+ Mem_Free(func);
+ }
+ ed->priv.server->ode_func = NULL;
}
}
bouncefactor1 = bouncefactor2;
}
}
- dWorldGetGravity(world->physics.ode_world, grav);
+ dWorldGetGravity((dWorldID)world->physics.ode_world, grav);
bouncestop1 *= fabs(grav[2]);
// generate contact points between the two non-space geoms
contact[i].surface.soft_cfm = physics_ode_contact_cfm.value;
contact[i].surface.bounce = bouncefactor1;
contact[i].surface.bounce_vel = bouncestop1;
- c = dJointCreateContact(world->physics.ode_world, world->physics.ode_contactgroup, contact + i);
+ c = dJointCreateContact((dWorldID)world->physics.ode_world, (dJointGroupID)world->physics.ode_contactgroup, contact + i);
dJointAttach(c, b1, b2);
}
}
void World_Physics_Frame(world_t *world, double frametime, double gravity)
{
+ double tdelta, tdelta2, tdelta3, simulationtime, collisiontime;
+
+ tdelta = Sys_DoubleTime();
#ifdef USEODE
- if (world->physics.ode)
+ if (world->physics.ode && physics_ode.integer)
{
int i;
prvm_edict_t *ed;
world->physics.ode_iterations = bound(1, physics_ode_iterationsperframe.integer, 1000);
- world->physics.ode_step = frametime / world->physics.ode_iterations;
+ if (physics_ode_constantstep.integer)
+ world->physics.ode_step = sys_ticrate.value / world->physics.ode_iterations;
+ else
+ world->physics.ode_step = frametime / world->physics.ode_iterations;
world->physics.ode_movelimit = physics_ode_movelimit.value / world->physics.ode_step;
+ World_Physics_UpdateODE(world);
// copy physics properties from entities to physics engine
if (prog)
World_Physics_Frame_JointFromEntity(world, ed);
}
+ tdelta2 = Sys_DoubleTime();
+ collisiontime = 0;
for (i = 0;i < world->physics.ode_iterations;i++)
{
// set the gravity
- dWorldSetGravity(world->physics.ode_world, 0, 0, -gravity);
+ dWorldSetGravity((dWorldID)world->physics.ode_world, 0, 0, -gravity);
// set the tolerance for closeness of objects
- dWorldSetContactSurfaceLayer(world->physics.ode_world, max(0, physics_ode_contactsurfacelayer.value));
+ dWorldSetContactSurfaceLayer((dWorldID)world->physics.ode_world, max(0, physics_ode_contactsurfacelayer.value));
// run collisions for the current world state, creating JointGroup
- dSpaceCollide(world->physics.ode_space, (void *)world, nearCallback);
+ tdelta3 = Sys_DoubleTime();
+ dSpaceCollide((dSpaceID)world->physics.ode_space, (void *)world, nearCallback);
+ collisiontime += (Sys_DoubleTime() - tdelta3)*10000;
// run physics (move objects, calculate new velocities)
- if (physics_ode_worldquickstep.integer)
+ if (physics_ode_worldstep.integer == 2)
{
- dWorldSetQuickStepNumIterations(world->physics.ode_world, bound(1, physics_ode_worldquickstep_iterations.integer, 200));
- dWorldQuickStep(world->physics.ode_world, world->physics.ode_step);
+ dWorldSetQuickStepNumIterations((dWorldID)world->physics.ode_world, bound(1, physics_ode_worldstep_iterations.integer, 200));
+ dWorldQuickStep((dWorldID)world->physics.ode_world, world->physics.ode_step);
}
- else if (physics_ode_worldstepfast.integer)
- dWorldStepFast1(world->physics.ode_world, world->physics.ode_step, bound(1, physics_ode_worldstepfast_iterations.integer, 200));
+#ifdef ODE_USE_STEPFAST
+ else if (physics_ode_worldstep.integer == 1)
+ dWorldStepFast1((dWorldID)world->physics.ode_world, world->physics.ode_step, bound(1, physics_ode_worldstep_iterations.integer, 200));
+#endif
else
- dWorldStep(world->physics.ode_world, world->physics.ode_step);
+ dWorldStep((dWorldID)world->physics.ode_world, world->physics.ode_step);
// clear the JointGroup now that we're done with it
- dJointGroupEmpty(world->physics.ode_contactgroup);
+ dJointGroupEmpty((dJointGroupID)world->physics.ode_contactgroup);
}
+ simulationtime = (Sys_DoubleTime() - tdelta2)*10000;
- // copy physics properties from physics engine to entities
+ // copy physics properties from physics engine to entities and do some stats
if (prog)
+ {
for (i = 1, ed = prog->edicts + i;i < prog->num_edicts;i++, ed++)
if (!prog->edicts[i].priv.required->free)
World_Physics_Frame_BodyToEntity(world, ed);
+
+ // print stats
+ if (physics_ode_printstats.integer)
+ {
+ dBodyID body;
+
+ world->physics.ode_numobjects = 0;
+ world->physics.ode_activeovjects = 0;
+ for (i = 1, ed = prog->edicts + i;i < prog->num_edicts;i++, ed++)
+ {
+ if (prog->edicts[i].priv.required->free)
+ continue;
+ body = (dBodyID)prog->edicts[i].priv.server->ode_body;
+ if (!body)
+ continue;
+ world->physics.ode_numobjects++;
+ 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_DoubleTime() - tdelta)*10000, world->physics.ode_numobjects, world->physics.ode_activeovjects, (world->physics.ode_numobjects - world->physics.ode_activeovjects));
+ }
+ }
}
#endif
}