Advertisement
Earthcomputer

Roughly how physics constraints work

May 22nd, 2019
343
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 5.88 KB | None | 0 0
  1.     /*
  2.      * The idea of a constraint is entirely based around a cost function. A cost function takes in both the
  3.      * position (linear position) and orientation (angular position) of both entityA and entityB, and outputs
  4.      * a scalar, which is 0 if the configuration of the entities is valid (in the feasible region), and nonzero
  5.      * otherwise. We denote the cost function as C(xA, rA, xB, rB). For this comment, pA and pB will denote
  6.      * the anchor points (points where the joint is applied) of each entity in world coordinates. We will also
  7.      * lay out the components of each entity's position and orientation in a flattened 12D vector, the same
  8.      * for velocity.
  9.      *
  10.      * The cost function for 3D constraints is hard to visualize, but is sometimes easy in 2D. Imagine a fixed
  11.      * distance constraint between two points A and B on the xy-plane. Let C(xA, rA, xB, rB) = ||pB - pA|| - d
  12.      * where d is the target distance. If you plot pA and pB and the output of the cost function on a third
  13.      * axis, you get an upside-down cone shape, and the cost function is 0 in the feasible region. Take the
  14.      * time to visualize this. It should also be clear that the gradient of C points perpendicular to the
  15.      * feasible region, so in order to get back into the feasible region you must apply an impulse in the
  16.      * direction of this gradient, and how big an impulse (may be negative) depends on the cost. This is true
  17.      * for the cost functions of all constraints, in 3D too although you would need at least 4D to visualize.
  18.      *
  19.      * But we need to correct the velocities of the objects as well as the position, so the first step is
  20.      * differentiating the cost function with respect to time to get a velocity cost. By the chain rule:
  21.      * dC/dt = dC/dx * dx/dt. dx/dt is just velocity. dC/dx gets you a 12D row vector of all the partial
  22.      * derivatives of the cost function with respect to each component of x. This vector is called the
  23.      * jacobian. We will also change dC/dt to cDot because physics. So we get cDot = J * v. The jacobian,
  24.      * being the derivative of C with respect to position, is also the direction we must apply the impulse.
  25.      *
  26.      * The main difficulty with making a new type of constraint is that you have to hand-derive this jacobian.
  27.      * This step is specific to each constraint and each subclass has a comment like this one with the
  28.      * derivation. To derive the jacobian, you don't directly compute dC/dx, instead you directly compute
  29.      * dC/dt, then simply rearrange into the form cDot = J * v ("isolate the velocities"), then just read the
  30.      * jacobian by inspection.
  31.      *
  32.      * This is all you need to know to implement a constraint, so engineers stop reading. However we haven't
  33.      * done the maths yet to complete how the constraint actually works.
  34.      *
  35.      * Since the jacobian is the direction we apply the impulse, the force, f (a vector including linear forces
  36.      * and angular torques), is parallel to this vector, therefore a multiple of it:
  37.      * f = J(T) * lambda
  38.      * where (T) is the matrix transpose operation and lambda is a scalar.
  39.      *
  40.      * We also know that for a discrete physics time step:
  41.      * vFinal = vInitial + a * dt
  42.      * where a is an acceleration vector.
  43.      *
  44.      * By Newton's second law, for a constant mass matrix M:
  45.      * totalF = M * a
  46.      * a = M^-1 * totalF
  47.      * a = M^-1 * (fExt + fC)
  48.      * where totalF is the total force applied, fExt is the external forces and fC is the forces applied due to
  49.      * constraints. What is going to happen to these forces is that they are going to be added to momentum every
  50.      * time step, essentially numerical integration. Since integration is additive, we can integrate fExt and fC
  51.      * separately, and since we only care about fC, we get:
  52.      * aC = M^-1 * fC
  53.      * and since we're lazy we're going to be naughty and redefine a:
  54.      * a = M^-1 * fC
  55.      *
  56.      * The mass matrix is a 12x12 matrix which is mostly zeros. You get 4 3x3 matrices along the leading diagonal,
  57.      * containing mass (linear mass) and inertia (angular mass). Linear mass is just scalar so we insert a scalar
  58.      * multiple of the identity matrix. Inertia may not be so simple, it may be a tensor.
  59.      *
  60.      * Substituting, we get
  61.      * vFinal = vInitial + M^-1 * fC * dt
  62.      * Substituting again:
  63.      * vFinal = vInitial + M^-1 * J(T) * lambda * dt
  64.      * And again:
  65.      * cDotFinal = J * (vInitial + M^-1 * J(T) * lambda * dt)
  66.      * Rearrange:
  67.      * cDotFinal = J * vInitial   +   J * M^-1 * J(T) * lambda * dt
  68.      * J * M^-1 * J(T) * lambda * dt = cDotFinal - J * vInitial
  69.      *
  70.      * We also know that:
  71.      * f * dt = p
  72.      * where p is the impulse vector.
  73.      * So to describe impulse rather than force, we're going to be slightly naughty again and redefine lambda
  74.      * to describe that multiple for impulse rather than for force:
  75.      * J * M^-1 * J(T) * lambda = cDotFinal - J * vInitial
  76.      *
  77.      * Since we aim for C = 0, we also aim for cDotFinal = 0, so:
  78.      * J * M^-1 * J(T) * lambda = -J * vInitial
  79.      * Rearrange:
  80.      * lambda = (J * M^-1 * J(T))^-1 * -J * vInitial
  81.      *
  82.      * This J * M^-1 * J(T) term is so important for the calculations that we give it its own letter, K:
  83.      * lambda = K^-1 * -J * vInitial
  84.      * K^-1 is also called the "constraint mass"; K and therefore K^-1 are also scalars.
  85.      *
  86.      * We have lambda! We're almost there.
  87.      * J * vInitial = cDotInitial, so:
  88.      * lambda = K^-1 * -cDotInitial
  89.      * lambda = - K^-1 * cDotInitial
  90.      *
  91.      * And remember the definition of lambda: the scalar multiple of the jacobian, which was the direction of the
  92.      * impulse, to get the needed impulse to put cDotFinal to 0. So:
  93.      *
  94.      * impulse = J(T) * -(K^-1 * cDotInitial)
  95.      *
  96.      * And this is the formula you find in solveVelocityConstraints().
  97.      */
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement