public static TexturedPolygon newTexturedPolygon(Texture texture, Vector... vectors) {
    TexturedPolygon texturedPolygon = TexturedPolygonImpl.newInstance(texture, vectors);

    if (vectors.length >= 2) {
      if (texture instanceof ShadedTexture) {
        ShadedTexture shadedTexture = ShadedTexture.class.cast(texture);

        List<PointLight> pointLights = new ArrayList<>();

        Vector normal = texturedPolygon.getNormal();
        Vector location = null;

        double x = normal.getX();
        double y = normal.getY() + 500.0D;
        double z = normal.getZ();
        double distanceFalloff = 2500.0D;

        Color intensity = Color.white();

        location = Vector.newInstance(x, y, z);

        PointLight pointLight = PointLight.newInstance();
        pointLight.setLocation(location);
        pointLight.setIntensity(intensity);
        pointLight.setDistanceFalloff(distanceFalloff);

        pointLights.add(pointLight);

        Color ambientLightIntensity = Color.valueOf(0xFF333333);

        ShadedSurfaceTexture.createShadedSurfaceTexture(
            texturedPolygon,
            shadedTexture,
            pointLights,
            new ArrayList<Polygon>(),
            ambientLightIntensity);
      } else {
        Vector origin = vectors[0];
        Vector v = vectors[1].copy();
        Vector normal = texturedPolygon.getNormal();

        v.subtract(origin);

        Vector u = Vector.toCrossProduct(normal, v);

        BoundingBox boundingBox = texturedPolygon.getTextureBounds();
        boundingBox.setOrigin(origin);
        boundingBox.setU(u);
        boundingBox.setV(v);
      }
    }

    return texturedPolygon;
  }
  private double doGetIntersectionForTriangle(Ray ray, Vector a, Vector b, Vector c) {
    Vector direction = ray.getDirection();
    Vector edge1 = Vector.copyAndSubtract(b, a);
    Vector edge2 = Vector.copyAndSubtract(c, a);
    Vector p = Vector.toCrossProduct(direction, edge2);

    double determinant = edge1.getDotProduct(p);

    if (determinant == 0.0D) {
      return -1.0D;
    }

    double inverseDeterminant = 1.0D / determinant;

    Vector origin = Vector.newInstance(ray.getOrigin());
    Vector vector = Vector.copyAndSubtract(origin, a);

    double u = vector.getDotProduct(p) * inverseDeterminant;

    if (u < 0.0D || u > 1.0D) {
      return -1.0D;
    }

    Vector q = Vector.toCrossProduct(vector, edge1);

    double v = direction.getDotProduct(q) * inverseDeterminant;

    if (v < 0.0D || u + v > 1.0D) {
      return -1.0D;
    }

    double t = edge2.getDotProduct(q) * inverseDeterminant;

    if (t > EPSILON) {
      return t;
    } else {
      return -1.0D;
    }
  }
  public static Transform lookAt(final Point source, final Point target, final Vector up) {
    final double[][] m = new double[4][4];

    m[0][3] = source.getX();
    m[1][3] = source.getY();
    m[2][3] = source.getZ();
    m[3][3] = 1.0D;

    final Vector direction = Vector.copyAndSubtract(target, source).normalize();
    final Vector vector0 = Vector.copyAndNormalize(up);
    final Vector vector1 = Vector.toCrossProduct(vector0, direction);

    if (vector1.length() == 0.0D) {
      return newInstance();
    }

    final Vector vector2 = vector1.normalize();
    final Vector vector3 = Vector.toCrossProduct(direction, vector2);

    m[0][0] = vector2.getX();
    m[1][0] = vector2.getY();
    m[2][0] = vector2.getZ();
    m[3][0] = 0.0D;
    m[0][1] = vector3.getX();
    m[1][1] = vector3.getY();
    m[2][1] = vector3.getZ();
    m[3][1] = 0.0D;
    m[0][2] = direction.getX();
    m[1][2] = direction.getY();
    m[2][2] = direction.getZ();
    m[3][2] = 0.0D;

    final Matrix matrix = Matrix.newInstance(m);

    return newInstance(matrix.inverse(), matrix);
  }
  @Override
  public final Vector getNormal() {
    if (normal != null) {
      return normal;
    } else if (vectors.size() < 3) {
      return null;
    } else {
      Vector vector1 = vectors.get(0).copy();
      Vector vector2 = vectors.get(1);
      Vector vector3 = vectors.get(2).copy();
      Vector vector4;

      vector1.subtract(vector2);
      vector3.subtract(vector2);
      vector4 = Vector.toCrossProduct(vector3, vector1);
      vector4.normalize();

      return vector4;
    }
  }
  @Override
  public final BoundingBox getBounds() {
    BoundingBox boundingBox = BoundingBox.newInstance();

    double minimum = Double.MAX_VALUE;

    Vector u = Vector.newInstance(0.0D, 0.0D, 0.0D);
    Vector v;
    Vector d = Vector.newInstance(0.0D, 0.0D, 0.0D);

    int size = vectors.size();

    for (int i = 0; i < size; i++) {
      Vector vector1 = vectors.get((i + 1) % size);
      Vector vector2 = vectors.get(i);

      u.set(vector1);
      u.subtract(vector2);
      u.normalize();

      v = Vector.toCrossProduct(getNormal(), u);
      v.normalize();

      double uMaximum = 0.0D;
      double uMinimum = 0.0D;
      double vMaximum = 0.0D;
      double vMinimum = 0.0D;

      for (int j = 0; j < size; j++) {
        if (i != j) {
          Vector vector3 = vectors.get(j);

          d.set(vector3);
          d.subtract(vector2);

          double uLength = d.getDotProduct(u);
          double vLength = d.getDotProduct(v);

          uMaximum = Math.max(uLength, uMaximum);
          uMinimum = Math.min(uLength, uMinimum);
          vMaximum = Math.max(vLength, vMaximum);
          vMinimum = Math.min(vLength, vMinimum);
        }
      }

      double current = (uMaximum - uMinimum) * (vMaximum - vMinimum);

      if (current < minimum) {
        minimum = current;

        Vector origin = boundingBox.getOrigin();

        origin.set(vector2);

        d.set(u);
        d.multiply(uMinimum);

        origin.add(d);

        d.set(v);
        d.multiply(vMinimum);

        origin.add(d);

        boundingBox.getU().set(u);
        boundingBox.getV().set(v);
        boundingBox.setHeight(vMaximum - vMinimum);
        boundingBox.setWidth(uMaximum - uMinimum);
      }
    }

    return boundingBox;
  }