public static Vector3f calculateNormal(Vector3f v1, Vector3f v2, Vector3f v3) {
		// if this method causes trouble, ask Joda or Sven

		// calculate facenormal from vertices
		Vector3f plA = new Vector3f(v1).sub(v2);
		Vector3f plB = new Vector3f(v2).sub(v3);
		Vector3f plC = new Vector3f(v3).sub(v1);

		// use some kind of "manhatten length"
		float lenA = manhattenLength(plA);
		float lenB = manhattenLength(plB);
		float lenC = manhattenLength(plC);

		Vector3f r1, r2;

		// chose the two best vectors.
		// the best vectors are the largest and the smallest.
		// they typically have the greatest angle between them.
		if (lenA < lenB) {
			// we know now: A B
			if (lenB < lenC) {
				// we know now: A B C
				r1 = plC;
				r2 = plA;
			} else if (lenC < lenA) {
				// we know now: C A B
				r1 = plB;
				r2 = plC;
			} else {
				// we know now: A C B
				r1 = plA;
				r2 = plB;
			}
		} else // we know now: B A
		if (lenA < lenC) {
			// we know now: B A C
			r1 = plB;
			r2 = plC;
		} else if (lenC < lenB) {
			// we know now: C B A
			r1 = plC;
			r2 = plA;
		} else {
			// we know now: B C A
			r1 = plA;
			r2 = plB;
		}

		if (r1.isZero() || r2.isZero()) {
			assert LOGGER.log(Level.FINE,
					"degenerated triangle: {0}, {1}, {2}", new Object[] { v1,
							v2, v3 });
			return null;
		}

		r1.normalize2();
		r2.normalize2();

		Vector3f normal = r1.cross(r2);

		if (normal.isZero()) {
			LOGGER.log(Level.WARNING,
					"unexpected zero normal, triangle was: {0}, {1}, {2}",
					new Object[] { v1, v2, v3 });
			return null;
		}

		if (normal.isInvalid()) {
			LOGGER.log(Level.WARNING,
					"normal is invalid, triangle was: {0}, {1}, {2}",
					new Object[] { v1, v2, v3 });
			return null;
		}

		// here we shouldn't hava a zero-vector or something like that
		normal.normalize2();

		// this should be quite sure now
		assert normal.isNormalized(Vector3f.TOLERANCE);

		return normal;
	}