public static ConvexSurface loadCylinder(int sides, double height, double radius) {
    ConvexSurface surface = new ConvexSurface();

    surface.faces = new Face[sides + 2];

    double twoPI = 2 * 3.1415926535897932384626433832795;

    double alpha, theta;

    for (int i = 0; i < sides; i++) {
      alpha = i * (twoPI / sides);
      theta = (i + 1) * (twoPI / sides);

      double[] face = {
        radius * Math.cos(alpha),
        -height / 2,
        radius * Math.sin(alpha),
        radius * Math.cos(alpha),
        height / 2,
        radius * Math.sin(alpha),
        radius * Math.cos(theta),
        height / 2,
        radius * Math.sin(theta),
        radius * Math.cos(theta),
        -height / 2,
        radius * Math.sin(theta),
      };

      surface.faces[i] = new Face(face, 4);
    }

    double[] top = new double[sides * 3];
    double[] bottom = new double[sides * 3];

    for (int i = 0; i < sides; i++) {
      alpha = i * (twoPI / sides);

      top[i * 3 + 0] = radius * Math.cos(alpha);
      top[i * 3 + 1] = height / 2;
      top[i * 3 + 2] = radius * Math.sin(alpha);

      bottom[i * 3 + 0] = radius * Math.cos(alpha);
      bottom[i * 3 + 1] = -height / 2;
      bottom[i * 3 + 2] = radius * Math.sin(alpha);
    }

    surface.faces[sides + 0] = new Face(top, sides);
    surface.faces[sides + 1] = new Face(bottom, sides);

    surface.SURFACE_TYPE = ConvexSurface.CYLINDER;
    surface.radius = (float) radius;
    surface.height = (float) height;

    surface.breakFaces();
    surface.loadEdgeIndexing();
    surface.loadPointIndexing();
    return surface;
  }
  public static ConvexSurface loadSphere(int slices, int stacks, double radius) {
    ConvexSurface surface = new ConvexSurface();

    surface.faces = new Face[slices * stacks];

    double twoPI = 2 * 3.1415926535897932384626433832795;

    double thetaA, phiA, thetaB, phiB;

    /*
    r = [0, infinity)
    theta = [0, 2*pi)
    phi = [0, pi]

    x = r*Math.cos(theta)*Math.sin(phi)
    y = r*Math.cos(phi)
    z = r*Math.sin(theta)*Math.sin(phi)
    */

    for (int i = 0; i < stacks; i++) {
      for (int j = 0; j < slices; j++) {
        thetaA = j * (twoPI / slices);
        thetaB = (j + 1) * (twoPI / slices);
        phiA = i * (twoPI / 2 / stacks);
        phiB = (i + 1) * (twoPI / 2 / stacks);

        double[] face = {
          radius * Math.cos(thetaA) * Math.sin(phiA),
          radius * Math.cos(phiA),
          radius * Math.sin(thetaA) * Math.sin(phiA),
          radius * Math.cos(thetaA) * Math.sin(phiB),
          radius * Math.cos(phiB),
          radius * Math.sin(thetaA) * Math.sin(phiB),
          radius * Math.cos(thetaB) * Math.sin(phiB),
          radius * Math.cos(phiB),
          radius * Math.sin(thetaB) * Math.sin(phiB),
          radius * Math.cos(thetaB) * Math.sin(phiA),
          radius * Math.cos(phiA),
          radius * Math.sin(thetaB) * Math.sin(phiA)
        };

        surface.faces[i * slices + j] = new Face(face, 4);
      }
    }

    surface.SURFACE_TYPE = ConvexSurface.SPHERE;
    surface.radius = (float) radius;

    surface.breakFaces();
    surface.loadEdgeIndexing();
    surface.loadPointIndexing();
    return surface;
  }