DomNomNom_PixelJam2013/libraries/toxiclibscore/examples/mesh/TerrainSteering/Car.pde
class Car extends Vec2D {

  Vec3D currNormal = Vec3D.Y_AXIS.copy();
  Vec3D pos;
  IsectData3D isec;

  float currTheta;
  float targetTheta;
  float targetSpeed;
  float speed;

  public Car(float x, float y) {
    super(x, y);
    pos = new Vec3D(0,500,0);
  }

  public void accelerate(float a) {
    targetSpeed += a;
    targetSpeed = MathUtils.clip(targetSpeed, -20, 20);
  }

  public void draw() {
    // create an axis aligned box and convert to mesh
    TriangleMesh box = (TriangleMesh)new AABB(new Vec3D(), new Vec3D(20, 10, 10)).toMesh();
    // align to terrain normal
    box.pointTowards(currNormal);
    // rotate into direction of movement
    box.rotateAroundAxis(currNormal, currTheta);
    // move to correct position
    box.translate(pos);
    fill(255, 0, 0);
    // and draw
    gfx.mesh(box);
  }

  public void steer(float t) {
    targetTheta += t;
  }

  public void update() {
    // slowly decay target speed
    targetSpeed *= 0.992f;
    // interpolate steering & speed
    currTheta += (targetTheta - currTheta) * 0.1f;
    speed += (targetSpeed - speed) * 0.1f;
    // update position
    addSelf(Vec2D.fromTheta(currTheta).scaleSelf(speed));
    // constrain position to terrain size in XZ plane
    AABB b = mesh.getBoundingBox();
    constrain(new Rect(b.getMin().to2DXZ(), b.getMax().to2DXZ()).scale(0.99f));
    // compute intersection point on terrain surface
    isec = terrain.intersectAtPoint(x, y);
    if (isec.isIntersection) {
      // smoothly update normal
      currNormal.interpolateToSelf(isec.normal, 0.25f);
      // move bot slightly above terrain
      Vec3D newPos = isec.pos.add(0, 10, 0);
      pos.interpolateToSelf(newPos, 0.25f);
    }
  }
}

