]> de.git.xonotic.org Git - voretournament/voretournament.git/blobdiff - misc/mediasource/netradiant-src/libs/mathlib/m4x4.c
Move the netradiant and fteqcc sources
[voretournament/voretournament.git] / misc / mediasource / netradiant-src / libs / mathlib / m4x4.c
diff --git a/misc/mediasource/netradiant-src/libs/mathlib/m4x4.c b/misc/mediasource/netradiant-src/libs/mathlib/m4x4.c
new file mode 100644 (file)
index 0000000..3b11461
--- /dev/null
@@ -0,0 +1,1878 @@
+/*
+Copyright (C) 2001-2006, William Joseph.
+All Rights Reserved.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#include "mathlib.h"
+
+const m4x4_t g_m4x4_identity = {
+  1, 0, 0, 0,
+  0, 1, 0, 0,
+  0, 0, 1, 0,
+  0, 0, 0, 1,
+};
+
+void m4x4_identity(m4x4_t matrix)
+{
+  matrix[1] = matrix[2] = matrix[3] =
+  matrix[4] = matrix[6] = matrix[7] =
+  matrix[8] = matrix[9] = matrix[11] =
+  matrix[12] = matrix[13] = matrix[14] = 0;
+
+  matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1;
+}
+
+m4x4Handedness_t m4x4_handedness(const m4x4_t matrix)
+{
+  vec3_t cross;
+  CrossProduct(matrix+0, matrix+4, cross);
+  return (DotProduct(matrix+8, cross) < 0) ? eLeftHanded : eRightHanded;
+}
+
+void m4x4_assign(m4x4_t matrix, const m4x4_t other)
+{
+  M4X4_COPY(matrix, other);
+}
+
+void m4x4_translation_for_vec3(m4x4_t matrix, const vec3_t translation)
+{
+  matrix[1] = matrix[2] = matrix[3] =
+  matrix[4] = matrix[6] = matrix[7] =
+  matrix[8] = matrix[9] = matrix[11] = 0;
+
+  matrix[0] = matrix[5] = matrix[10] = matrix[15] = 1;
+
+  matrix[12] = translation[0];
+  matrix[13] = translation[1];
+  matrix[14] = translation[2];
+}
+
+/*
+clockwise rotation around X, Y, Z, facing along axis
+ 1  0   0    cy 0  sy   cz  sz 0
+ 0  cx  sx   0  1  0   -sz  cz 0
+ 0 -sx  cx  -sy 0  cy   0   0  1
+
+rows of Z by cols of Y
+ cy*cz -sy*cz+sz -sy*sz+cz
+-sz*cy -sz*sy+cz 
+
+  .. or something like that..
+
+final rotation is Z * Y * X
+ cy*cz -sx*-sy*cz+cx*sz  cx*-sy*sz+sx*cz
+-cy*sz  sx*sy*sz+cx*cz  -cx*-sy*sz+sx*cz
+ sy    -sx*cy            cx*cy
+*/
+
+/* transposed
+|  cy.cz + 0.sz + sy.0            cy.-sz + 0 .cz +  sy.0          cy.0  + 0 .0  +   sy.1       |
+|  sx.sy.cz + cx.sz + -sx.cy.0    sx.sy.-sz + cx.cz + -sx.cy.0    sx.sy.0  + cx.0  + -sx.cy.1  |
+| -cx.sy.cz + sx.sz +  cx.cy.0   -cx.sy.-sz + sx.cz +  cx.cy.0   -cx.sy.0  + 0 .0  +  cx.cy.1  |
+*/
+void m4x4_rotation_for_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order)
+{
+  double cx, sx, cy, sy, cz, sz;
+    
+  cx = cos(DEG2RAD(euler[0]));
+  sx = sin(DEG2RAD(euler[0]));
+  cy = cos(DEG2RAD(euler[1]));
+  sy = sin(DEG2RAD(euler[1]));
+  cz = cos(DEG2RAD(euler[2]));
+  sz = sin(DEG2RAD(euler[2]));
+
+  switch(order)
+  {
+  case eXYZ:
+
+#if 1
+
+    {
+      matrix[0]  = (vec_t)(cy*cz);
+      matrix[1]  = (vec_t)(cy*sz);
+      matrix[2]  = (vec_t)-sy;
+      matrix[4]  = (vec_t)(sx*sy*cz + cx*-sz);
+      matrix[5]  = (vec_t)(sx*sy*sz + cx*cz);
+      matrix[6]  = (vec_t)(sx*cy);
+      matrix[8]  = (vec_t)(cx*sy*cz + sx*sz);
+      matrix[9]  = (vec_t)(cx*sy*sz + -sx*cz);
+      matrix[10] = (vec_t)(cx*cy);
+    }
+
+    matrix[12]  =  matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0;
+    matrix[15] =  1;
+
+#else
+
+    m4x4_identity(matrix);
+    matrix[5] =(vec_t) cx; matrix[6] =(vec_t) sx;
+    matrix[9] =(vec_t)-sx; matrix[10]=(vec_t) cx;
+
+    {
+      m4x4_t temp;
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy;
+      temp[8] =(vec_t) sy; temp[10]=(vec_t) cy;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cz; temp[1] =(vec_t) sz;
+      temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+    }
+#endif
+
+    break;
+
+  case eYZX:
+    m4x4_identity(matrix);
+    matrix[0] =(vec_t) cy; matrix[2] =(vec_t)-sy;
+    matrix[8] =(vec_t) sy; matrix[10]=(vec_t) cy;
+
+    {
+      m4x4_t temp;
+      m4x4_identity(temp);
+      temp[5] =(vec_t) cx; temp[6] =(vec_t) sx;
+      temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cz; temp[1] =(vec_t) sz;
+      temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+    }
+    break;
+
+  case eZXY:
+    m4x4_identity(matrix);
+    matrix[0] =(vec_t) cz; matrix[1] =(vec_t) sz;
+    matrix[4] =(vec_t)-sz; matrix[5] =(vec_t) cz;
+
+    {
+      m4x4_t temp;
+      m4x4_identity(temp);
+      temp[5] =(vec_t) cx; temp[6] =(vec_t) sx;
+      temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy;
+      temp[8] =(vec_t) sy; temp[10]=(vec_t) cy;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+    }
+    break;
+
+  case eXZY:
+    m4x4_identity(matrix);
+    matrix[5] =(vec_t) cx; matrix[6] =(vec_t) sx;
+    matrix[9] =(vec_t)-sx; matrix[10]=(vec_t) cx;
+
+    {
+      m4x4_t temp;
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cz; temp[1] =(vec_t) sz;
+      temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+      m4x4_identity(temp);
+      temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy;
+      temp[8] =(vec_t) sy; temp[10]=(vec_t) cy;
+      m4x4_premultiply_by_m4x4(matrix, temp);
+    }
+    break;
+
+  case eYXZ:
+
+/* transposed
+|  cy.cz + sx.sy.-sz + -cx.sy.0   0.cz + cx.-sz + sx.0   sy.cz + -sx.cy.-sz + cx.cy.0 |
+|  cy.sz + sx.sy.cz + -cx.sy.0    0.sz + cx.cz + sx.0    sy.sz + -sx.cy.cz + cx.cy.0  |
+|  cy.0 + sx.sy.0 + -cx.sy.1      0.0 + cx.0 + sx.1      sy.0 + -sx.cy.0 + cx.cy.1    |
+*/
+
+#if 1
+
+  {
+    matrix[0]  = (vec_t)(cy*cz + sx*sy*-sz);
+    matrix[1]  = (vec_t)(cy*sz + sx*sy*cz);
+    matrix[2]  = (vec_t)(-cx*sy);
+    matrix[4]  = (vec_t)(cx*-sz);
+    matrix[5]  = (vec_t)(cx*cz);
+    matrix[6]  = (vec_t)(sx);
+    matrix[8]  = (vec_t)(sy*cz + -sx*cy*-sz);
+    matrix[9]  = (vec_t)(sy*sz + -sx*cy*cz);
+    matrix[10] = (vec_t)(cx*cy);
+  }
+
+  matrix[12]  =  matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0;
+  matrix[15] =  1;
+
+#else
+
+  m4x4_identity(matrix);
+  matrix[0] =(vec_t) cy; matrix[2] =(vec_t)-sy;
+  matrix[8] =(vec_t) sy; matrix[10]=(vec_t) cy;
+
+  {
+    m4x4_t temp;
+    m4x4_identity(temp);
+    temp[5] =(vec_t) cx; temp[6] =(vec_t) sx;
+    temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx;
+    m4x4_premultiply_by_m4x4(matrix, temp);
+    m4x4_identity(temp);
+    temp[0] =(vec_t) cz; temp[1] =(vec_t) sz;
+    temp[4] =(vec_t)-sz; temp[5] =(vec_t) cz;
+    m4x4_premultiply_by_m4x4(matrix, temp);
+  }
+#endif
+  break;
+
+  case eZYX:
+#if 1
+
+  {
+    matrix[0]  = (vec_t)(cy*cz);
+    matrix[4]  = (vec_t)(cy*-sz);
+    matrix[8]  = (vec_t)sy;
+    matrix[1]  = (vec_t)(sx*sy*cz + cx*sz);
+    matrix[5]  = (vec_t)(sx*sy*-sz + cx*cz);
+    matrix[9]  = (vec_t)(-sx*cy);
+    matrix[2]  = (vec_t)(cx*-sy*cz + sx*sz);
+    matrix[6]  = (vec_t)(cx*-sy*-sz + sx*cz);
+    matrix[10] = (vec_t)(cx*cy);
+  }
+
+  matrix[12]  =  matrix[13] = matrix[14] = matrix[3] = matrix[7] = matrix[11] = 0;
+  matrix[15] =  1;
+
+#else
+
+  m4x4_identity(matrix);
+  matrix[0] =(vec_t) cz; matrix[1] =(vec_t) sz;
+  matrix[4] =(vec_t)-sz; matrix[5] =(vec_t) cz;
+  {
+    m4x4_t temp;
+    m4x4_identity(temp);
+    temp[0] =(vec_t) cy; temp[2] =(vec_t)-sy;
+    temp[8] =(vec_t) sy; temp[10]=(vec_t) cy;
+    m4x4_premultiply_by_m4x4(matrix, temp);
+    m4x4_identity(temp);
+    temp[5] =(vec_t) cx; temp[6] =(vec_t) sx;
+    temp[9] =(vec_t)-sx; temp[10]=(vec_t) cx;
+    m4x4_premultiply_by_m4x4(matrix, temp);
+  }
+
+#endif
+  break;
+
+  }
+}
+
+void m4x4_scale_for_vec3(m4x4_t matrix, const vec3_t scale)
+{
+  matrix[1] = matrix[2] = matrix[3] =
+  matrix[4] = matrix[6] = matrix[7] =
+  matrix[8] = matrix[9] = matrix[11] =
+  matrix[12] = matrix[13] = matrix[14] = 0;
+
+  matrix[15] = 1;
+
+  matrix[0] = scale[0];
+  matrix[5] = scale[1];
+  matrix[10] = scale[2];
+}
+
+void m4x4_rotation_for_quat(m4x4_t matrix, const vec4_t quat)
+{
+#if 0
+  const double xx = quat[0] * quat[0];
+  const double xy = quat[0] * quat[1];
+  const double xz = quat[0] * quat[2];
+  const double xw = quat[0] * quat[3];
+
+  const double yy = quat[1] * quat[1];
+  const double yz = quat[1] * quat[2];
+  const double yw = quat[1] * quat[3];
+
+  const double zz = quat[2] * quat[2];
+  const double zw = quat[2] * quat[3];
+
+  matrix[0]  = 1 - 2 * ( yy + zz );
+  matrix[4]  =     2 * ( xy - zw );
+  matrix[8]  =     2 * ( xz + yw );
+
+  matrix[1]  =     2 * ( xy + zw );
+  matrix[5]  = 1 - 2 * ( xx + zz );
+  matrix[9]  =     2 * ( yz - xw );
+
+  matrix[2]  =     2 * ( xz - yw );
+  matrix[6]  =     2 * ( yz + xw );
+  matrix[10] = 1 - 2 * ( xx + yy );
+#else
+  const double x2 = quat[0] + quat[0];
+  const double y2 = quat[1] + quat[1]; 
+  const double z2 = quat[2] + quat[2];
+  const double xx = quat[0] * x2;
+  const double xy = quat[0] * y2;
+  const double xz = quat[0] * z2;
+  const double yy = quat[1] * y2;
+  const double yz = quat[1] * z2;
+  const double zz = quat[2] * z2;
+  const double wx = quat[3] * x2;
+  const double wy = quat[3] * y2;
+  const double wz = quat[3] * z2;
+
+  matrix[0] = (vec_t)( 1.0 - (yy + zz) );
+  matrix[4] = (vec_t)(xy - wz);
+  matrix[8] = (vec_t)(xz + wy);
+
+  matrix[1] = (vec_t)(xy + wz);
+  matrix[5] = (vec_t)( 1.0 - (xx + zz) );
+  matrix[9] = (vec_t)(yz - wx);
+
+  matrix[2] = (vec_t)(xz - wy);
+  matrix[6] = (vec_t)(yz + wx);
+  matrix[10] = (vec_t)( 1.0 - (xx + yy) );
+#endif
+
+  matrix[3]  = matrix[7] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0;
+  matrix[15] = 1;
+}
+
+void m4x4_rotation_for_axisangle(m4x4_t matrix, const vec3_t axis, double angle)
+{
+  vec4_t quat;
+  quat_for_axisangle(quat, axis, angle);
+  m4x4_rotation_for_quat(matrix, quat);
+}
+
+void m4x4_frustum(m4x4_t matrix,
+                     vec_t left, vec_t right,
+                     vec_t bottom, vec_t top,
+                     vec_t nearval, vec_t farval)
+{
+   matrix[0] = (vec_t)( (2*nearval) / (right-left) );
+   matrix[1] = 0;
+   matrix[2] = 0;
+   matrix[3] = 0;
+
+   matrix[4] = 0;
+   matrix[5] = (vec_t)( (2*nearval) / (top-bottom) );
+   matrix[6] = 0;
+   matrix[7] = 0;
+
+   matrix[8] = (vec_t)( (right+left) / (right-left) );
+   matrix[9] = (vec_t)( (top+bottom) / (top-bottom) );
+   matrix[10] = (vec_t)( -(farval+nearval) / (farval-nearval) );
+   matrix[11] =-1;
+
+   matrix[12] = 0;
+   matrix[13] = 0;
+   matrix[14] = (vec_t)( -(2*farval*nearval) / (farval-nearval) );
+   matrix[15] = 0;
+}
+
+
+void m4x4_get_translation_vec3(const m4x4_t matrix, vec3_t translation)
+{
+  translation[0] = matrix[12];
+       translation[1] = matrix[13];
+       translation[2] = matrix[14];
+}
+
+void m4x4_get_rotation_vec3(const m4x4_t matrix, vec3_t euler, eulerOrder_t order)
+{
+  double a, ca;
+
+  switch(order)
+  {
+  case eXYZ:
+    a = asin(-matrix[2]);
+    ca = cos(a);
+    euler[1] = (vec_t)RAD2DEG(a);  /* Calculate Y-axis angle */    
+
+    if (fabs(ca) > 0.005) /* Gimbal lock? */
+    {
+      /* No, so get Z-axis angle */
+      euler[2] = (vec_t)RAD2DEG(atan2(matrix[1] / ca, matrix[0]/ ca));
+
+      /* Get X-axis angle */
+      euler[0] = (vec_t)RAD2DEG(atan2(matrix[6] / ca, matrix[10] / ca));
+    }
+    else /* Gimbal lock has occurred */
+    {
+      /* Set Z-axis angle to zero */
+      euler[2]  = 0;
+
+      /* And calculate X-axis angle */
+      euler[0] = (vec_t)RAD2DEG(atan2(-matrix[9], matrix[5]));
+    }
+    break;
+  case eYZX:
+    /* NOT IMPLEMENTED */
+    break;
+  case eZXY:
+    /* NOT IMPLEMENTED */
+    break;
+  case eXZY:
+    /* NOT IMPLEMENTED */
+    break;
+  case eYXZ:
+    a = asin(matrix[6]);
+    ca = cos(a);
+    euler[0] = (vec_t)RAD2DEG(a);  /* Calculate X-axis angle */    
+
+    if (fabs(ca) > 0.005) /* Gimbal lock? */
+    {
+      /* No, so get Y-axis angle */
+      euler[1] = (vec_t)RAD2DEG(atan2(-matrix[2] / ca, matrix[10]/ ca));
+
+      /* Get Z-axis angle */
+      euler[2] = (vec_t)RAD2DEG(atan2(-matrix[4] / ca, matrix[5] / ca));
+    }
+    else /* Gimbal lock has occurred */
+    {
+      /* Set Z-axis angle to zero */
+      euler[2]  = 0;
+
+      /* And calculate Y-axis angle */
+      euler[1] = (vec_t)RAD2DEG(atan2(matrix[8], matrix[0]));
+    }
+    break;
+  case eZYX:
+    a = asin(matrix[8]);
+    ca = cos(a);
+    euler[1] = (vec_t)RAD2DEG(a);  /* Calculate Y-axis angle */    
+
+    if (fabs(ca) > 0.005) /* Gimbal lock? */
+    {
+      /* No, so get X-axis angle */
+      euler[0] = (vec_t)RAD2DEG(atan2(-matrix[9] / ca, matrix[10]/ ca));
+
+      /* Get Z-axis angle */
+      euler[2] = (vec_t)RAD2DEG(atan2(-matrix[4] / ca, matrix[0] / ca));
+    }
+    else /* Gimbal lock has occurred */
+    {
+      /* Set X-axis angle to zero */
+      euler[0]  = 0;
+
+      /* And calculate Z-axis angle */
+      euler[2] = (vec_t)RAD2DEG(atan2(matrix[1], matrix[5]));
+    }
+    break;
+  }
+
+  /* return only positive angles in [0,360] */
+  if (euler[0] < 0) euler[0] += 360;
+  if (euler[1] < 0) euler[1] += 360;
+  if (euler[2] < 0) euler[2] += 360;
+}
+
+void m4x4_get_scale_vec3(const m4x4_t matrix, vec3_t scale)
+{
+  scale[0] = VectorLength(matrix+0);
+  scale[1] = VectorLength(matrix+4);
+  scale[2] = VectorLength(matrix+8);
+}
+
+void m4x4_get_transform_vec3(const m4x4_t matrix, vec3_t translation, vec3_t euler, eulerOrder_t order, vec3_t scale)
+{
+  m4x4_t normalised;
+  m4x4_assign(normalised, matrix);
+  scale[0] = VectorNormalize(normalised+0, normalised+0);
+  scale[1] = VectorNormalize(normalised+4, normalised+4);
+  scale[2] = VectorNormalize(normalised+8, normalised+8);
+  if(m4x4_handedness(normalised) == eLeftHanded)
+  {
+    VectorNegate(normalised+0, normalised+0);
+    VectorNegate(normalised+4, normalised+4);
+    VectorNegate(normalised+8, normalised+8);
+    scale[0] = -scale[0];
+    scale[1] = -scale[1];
+    scale[2] = -scale[2];
+  }
+  m4x4_get_rotation_vec3(normalised, euler, order);
+  m4x4_get_translation_vec3(matrix, translation);
+}
+
+void m4x4_translate_by_vec3(m4x4_t matrix, const vec3_t translation)
+{
+  m4x4_t temp;
+  m4x4_translation_for_vec3(temp, translation);
+  m4x4_multiply_by_m4x4(matrix, temp);
+}
+
+void m4x4_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order)
+{
+  m4x4_t temp;
+  m4x4_rotation_for_vec3(temp, euler, order);
+  m4x4_multiply_by_m4x4(matrix, temp);
+}
+
+void m4x4_scale_by_vec3(m4x4_t matrix, const vec3_t scale)
+{
+  m4x4_t temp;
+  m4x4_scale_for_vec3(temp, scale);
+  m4x4_multiply_by_m4x4(matrix, temp);
+}
+
+void m4x4_rotate_by_quat(m4x4_t matrix, const vec4_t rotation)
+{
+  m4x4_t temp;
+  m4x4_rotation_for_quat(temp, rotation);
+  m4x4_multiply_by_m4x4(matrix, temp);
+}
+
+void m4x4_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle)
+{
+  m4x4_t temp;
+  m4x4_rotation_for_axisangle(temp, axis, angle);
+  m4x4_multiply_by_m4x4(matrix, temp);
+}
+
+void m4x4_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale)
+{
+  m4x4_translate_by_vec3(matrix, translation);
+  m4x4_rotate_by_vec3(matrix, euler, order);
+  m4x4_scale_by_vec3(matrix, scale);
+}
+
+void m4x4_pivoted_rotate_by_vec3(m4x4_t matrix, const vec3_t euler, eulerOrder_t order, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+  VectorNegate(pivotpoint, vec3_temp);
+
+  m4x4_translate_by_vec3(matrix, pivotpoint);
+  m4x4_rotate_by_vec3(matrix, euler, order);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+void m4x4_pivoted_scale_by_vec3(m4x4_t matrix, const vec3_t scale, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+  VectorNegate(pivotpoint, vec3_temp);
+
+  m4x4_translate_by_vec3(matrix, pivotpoint);
+  m4x4_scale_by_vec3(matrix, scale);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+void m4x4_pivoted_transform_by_vec3(m4x4_t matrix, const vec3_t translation, const vec3_t euler, eulerOrder_t order, const vec3_t scale, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+
+  VectorAdd(pivotpoint, translation, vec3_temp);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+  m4x4_rotate_by_vec3(matrix, euler, order);
+  m4x4_scale_by_vec3(matrix, scale);
+  VectorNegate(pivotpoint, vec3_temp);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+void m4x4_pivoted_transform_by_rotation(m4x4_t matrix, const vec3_t translation, const m4x4_t rotation, const vec3_t scale, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+
+  VectorAdd(pivotpoint, translation, vec3_temp);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+  m4x4_multiply_by_m4x4(matrix, rotation);
+  m4x4_scale_by_vec3(matrix, scale);
+  VectorNegate(pivotpoint, vec3_temp);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+void m4x4_pivoted_rotate_by_quat(m4x4_t matrix, const vec4_t rotation, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+  VectorNegate(pivotpoint, vec3_temp);
+
+  m4x4_translate_by_vec3(matrix, pivotpoint);
+  m4x4_rotate_by_quat(matrix, rotation);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+void m4x4_pivoted_rotate_by_axisangle(m4x4_t matrix, const vec3_t axis, double angle, const vec3_t pivotpoint)
+{
+  vec3_t vec3_temp;
+  VectorNegate(pivotpoint, vec3_temp);
+
+  m4x4_translate_by_vec3(matrix, pivotpoint);
+  m4x4_rotate_by_axisangle(matrix, axis, angle);
+  m4x4_translate_by_vec3(matrix, vec3_temp);
+}
+
+/*
+A = A.B
+
+A0 = B0 * A0 + B1 * A4 + B2 * A8 + B3 * A12
+A4 = B4 * A0 + B5 * A4 + B6 * A8 + B7 * A12
+A8 = B8 * A0 + B9 * A4 + B10* A8 + B11* A12
+A12= B12* A0 + B13* A4 + B14* A8 + B15* A12
+
+A1 = B0 * A1 + B1 * A5 + B2 * A9 + B3 * A13
+A5 = B4 * A1 + B5 * A5 + B6 * A9 + B7 * A13
+A9 = B8 * A1 + B9 * A5 + B10* A9 + B11* A13
+A13= B12* A1 + B13* A5 + B14* A9 + B15* A13
+
+A2 = B0 * A2 + B1 * A6 + B2 * A10+ B3 * A14
+A6 = B4 * A2 + B5 * A6 + B6 * A10+ B7 * A14
+A10= B8 * A2 + B9 * A6 + B10* A10+ B11* A14
+A14= B12* A2 + B13* A6 + B14* A10+ B15* A14
+
+A3 = B0 * A3 + B1 * A7 + B2 * A11+ B3 * A15
+A7 = B4 * A3 + B5 * A7 + B6 * A11+ B7 * A15
+A11= B8 * A3 + B9 * A7 + B10* A11+ B11* A15
+A15= B12* A3 + B13* A7 + B14* A11+ B15* A15
+*/
+
+void m4x4_multiply_by_m4x4(m4x4_t dst, const m4x4_t src)
+{
+       vec_t dst0, dst1, dst2, dst3;
+
+#if 1
+
+  dst0 = src[0] * dst[0] + src[1] * dst[4] + src[2] * dst[8] + src[3] * dst[12];
+  dst1 = src[4] * dst[0] + src[5] * dst[4] + src[6] * dst[8] + src[7] * dst[12];
+  dst2 = src[8] * dst[0] + src[9] * dst[4] + src[10]* dst[8] + src[11]* dst[12];
+  dst3 = src[12]* dst[0] + src[13]* dst[4] + src[14]* dst[8] + src[15]* dst[12];
+  dst[0] = dst0; dst[4] = dst1; dst[8] = dst2; dst[12]= dst3;
+
+  dst0 = src[0] * dst[1] + src[1] * dst[5] + src[2] * dst[9] + src[3] * dst[13];
+  dst1 = src[4] * dst[1] + src[5] * dst[5] + src[6] * dst[9] + src[7] * dst[13];
+  dst2 = src[8] * dst[1] + src[9] * dst[5] + src[10]* dst[9] + src[11]* dst[13];
+  dst3 = src[12]* dst[1] + src[13]* dst[5] + src[14]* dst[9] + src[15]* dst[13];
+  dst[1] = dst0; dst[5] = dst1; dst[9] = dst2; dst[13]= dst3;
+
+  dst0 = src[0] * dst[2] + src[1] * dst[6] + src[2] * dst[10]+ src[3] * dst[14];
+  dst1 = src[4] * dst[2] + src[5] * dst[6] + src[6] * dst[10]+ src[7] * dst[14];
+  dst2 = src[8] * dst[2] + src[9] * dst[6] + src[10]* dst[10]+ src[11]* dst[14];
+  dst3 = src[12]* dst[2] + src[13]* dst[6] + src[14]* dst[10]+ src[15]* dst[14];
+  dst[2] = dst0; dst[6] = dst1; dst[10]= dst2; dst[14]= dst3;
+
+  dst0 = src[0] * dst[3] + src[1] * dst[7] + src[2] * dst[11]+ src[3] * dst[15];
+  dst1 = src[4] * dst[3] + src[5] * dst[7] + src[6] * dst[11]+ src[7] * dst[15];
+  dst2 = src[8] * dst[3] + src[9] * dst[7] + src[10]* dst[11]+ src[11]* dst[15];
+  dst3 = src[12]* dst[3] + src[13]* dst[7] + src[14]* dst[11]+ src[15]* dst[15];
+  dst[3] = dst0; dst[7] = dst1; dst[11]= dst2; dst[15]= dst3;
+
+#else
+
+  vec_t * p = dst;
+       for(int i=0;i<4;i++)
+       {
+               dst1 =  src[0]  * p[0];
+               dst1 += src[1]  * p[4];
+               dst1 += src[2]  * p[8];
+               dst1 += src[3]  * p[12];
+               dst2 =  src[4]  * p[0];
+               dst2 += src[5]  * p[4];
+               dst2 += src[6]  * p[8];
+               dst2 += src[7]  * p[12];
+               dst3 =  src[8]  * p[0];
+               dst3 += src[9]  * p[4];
+               dst3 += src[10] * p[8];
+               dst3 += src[11] * p[12];
+               dst4 =  src[12] * p[0];
+               dst4 += src[13] * p[4];
+               dst4 += src[14] * p[8];
+               dst4 += src[15] * p[12];
+
+               p[0] = dst1;
+               p[4] = dst2;
+               p[8] = dst3;
+               p[12] = dst4;
+    p++;
+       }
+
+#endif
+}
+
+/*
+A = B.A
+
+A0 = A0 * B0 + A1 * B4 + A2 * B8 + A3 * B12
+A1 = A0 * B1 + A1 * B5 + A2 * B9 + A3 * B13
+A2 = A0 * B2 + A1 * B6 + A2 * B10+ A3 * B14
+A3 = A0 * B3 + A1 * B7 + A2 * B11+ A3 * B15
+
+A4 = A4 * B0 + A5 * B4 + A6 * B8 + A7 * B12
+A5 = A4 * B1 + A5 * B5 + A6 * B9 + A7 * B13
+A6 = A4 * B2 + A5 * B6 + A6 * B10+ A7 * B14
+A7 = A4 * B3 + A5 * B7 + A6 * B11+ A7 * B15
+
+A8 = A8 * B0 + A9 * B4 + A10* B8 + A11* B12
+A9 = A8 * B1 + A9 * B5 + A10* B9 + A11* B13
+A10= A8 * B2 + A9 * B6 + A10* B10+ A11* B14
+A11= A8 * B3 + A9 * B7 + A10* B11+ A11* B15
+
+A12= A12* B0 + A13* B4 + A14* B8 + A15* B12
+A13= A12* B1 + A13* B5 + A14* B9 + A15* B13
+A14= A12* B2 + A13* B6 + A14* B10+ A15* B14
+A15= A12* B3 + A13* B7 + A14* B11+ A15* B15
+*/
+
+void m4x4_premultiply_by_m4x4(m4x4_t dst, const m4x4_t src)
+{
+       vec_t dst0, dst1, dst2, dst3;
+
+#if 1
+
+  dst0 = dst[0] * src[0] + dst[1] * src[4] + dst[2] * src[8] + dst[3] * src[12];
+  dst1 = dst[0] * src[1] + dst[1] * src[5] + dst[2] * src[9] + dst[3] * src[13];
+  dst2 = dst[0] * src[2] + dst[1] * src[6] + dst[2] * src[10]+ dst[3] * src[14];
+  dst3 = dst[0] * src[3] + dst[1] * src[7] + dst[2] * src[11]+ dst[3] * src[15];
+  dst[0] = dst0; dst[1] = dst1; dst[2] = dst2; dst[3]= dst3;
+
+  dst0 = dst[4] * src[0] + dst[5] * src[4] + dst[6] * src[8] + dst[7] * src[12];
+  dst1 = dst[4] * src[1] + dst[5] * src[5] + dst[6] * src[9] + dst[7] * src[13];
+  dst2 = dst[4] * src[2] + dst[5] * src[6] + dst[6] * src[10]+ dst[7] * src[14];
+  dst3 = dst[4] * src[3] + dst[5] * src[7] + dst[6] * src[11]+ dst[7] * src[15];
+  dst[4] = dst0; dst[5] = dst1; dst[6] = dst2; dst[7]= dst3;
+
+  dst0 = dst[8] * src[0] + dst[9] * src[4] + dst[10]* src[8] + dst[11]* src[12];
+  dst1 = dst[8] * src[1] + dst[9] * src[5] + dst[10]* src[9] + dst[11]* src[13];
+  dst2 = dst[8] * src[2] + dst[9] * src[6] + dst[10]* src[10]+ dst[11]* src[14];
+  dst3 = dst[8] * src[3] + dst[9] * src[7] + dst[10]* src[11]+ dst[11]* src[15];
+  dst[8] = dst0; dst[9] = dst1; dst[10] = dst2; dst[11]= dst3;
+
+  dst0 = dst[12]* src[0] + dst[13]* src[4] + dst[14]* src[8] + dst[15]* src[12];
+  dst1 = dst[12]* src[1] + dst[13]* src[5] + dst[14]* src[9] + dst[15]* src[13];
+  dst2 = dst[12]* src[2] + dst[13]* src[6] + dst[14]* src[10]+ dst[15]* src[14];
+  dst3 = dst[12]* src[3] + dst[13]* src[7] + dst[14]* src[11]+ dst[15]* src[15];
+  dst[12] = dst0; dst[13] = dst1; dst[14] = dst2; dst[15]= dst3;
+
+#else
+
+  vec_t* p = dst;
+       for(int i=0;i<4;i++)
+       {
+               dst1 =  src[0]  * p[0];
+               dst2 =  src[1]  * p[0];
+               dst3 =  src[2]  * p[0];
+               dst4 =  src[3]  * p[0];
+               dst1 += src[4]  * p[1];
+               dst2 += src[5]  * p[1];
+               dst3 += src[6]  * p[1];
+               dst4 += src[7]  * p[1];
+               dst1 += src[8]  * p[2];
+               dst2 += src[9]  * p[2];
+               dst4 += src[11] * p[2];
+               dst3 += src[10] * p[2];
+               dst1 += src[12] * p[3];
+               dst2 += src[13] * p[3];
+               dst3 += src[14] * p[3];
+               dst4 += src[15] * p[3];
+
+               *p++ = dst1;
+               *p++ = dst2;
+               *p++ = dst3;
+               *p++ = dst4;
+       }
+
+#endif
+}
+
+void m4x4_orthogonal_multiply_by_m4x4(m4x4_t dst, const m4x4_t src)
+{
+       vec_t dst0, dst1, dst2, dst3;
+
+  dst0 = src[0] * dst[0] + src[1] * dst[4] + src[2] * dst[8];
+  dst1 = src[4] * dst[0] + src[5] * dst[4] + src[6] * dst[8];
+  dst2 = src[8] * dst[0] + src[9] * dst[4] + src[10]* dst[8];
+  dst3 = src[12]* dst[0] + src[13]* dst[4] + src[14]* dst[8] + dst[12];
+  dst[0] = dst0; dst[4] = dst1; dst[8] = dst2; dst[12]= dst3;
+
+  dst0 = src[0] * dst[1] + src[1] * dst[5] + src[2] * dst[9];
+  dst1 = src[4] * dst[1] + src[5] * dst[5] + src[6] * dst[9];
+  dst2 = src[8] * dst[1] + src[9] * dst[5] + src[10]* dst[9];
+  dst3 = src[12]* dst[1] + src[13]* dst[5] + src[14]* dst[9] + dst[13];
+  dst[1] = dst0; dst[5] = dst1; dst[9] = dst2; dst[13]= dst3;
+
+  dst0 = src[0] * dst[2] + src[1] * dst[6] + src[2] * dst[10];
+  dst1 = src[4] * dst[2] + src[5] * dst[6] + src[6] * dst[10];
+  dst2 = src[8] * dst[2] + src[9] * dst[6] + src[10]* dst[10];
+  dst3 = src[12]* dst[2] + src[13]* dst[6] + src[14]* dst[10]+ dst[14];
+  dst[2] = dst0; dst[6] = dst1; dst[10]= dst2; dst[14]= dst3;
+}
+
+void m4x4_orthogonal_premultiply_by_m4x4(m4x4_t dst, const m4x4_t src)
+{
+       vec_t dst0, dst1, dst2;
+
+  dst0 = dst[0] * src[0] + dst[1] * src[4] + dst[2] * src[8];
+  dst1 = dst[0] * src[1] + dst[1] * src[5] + dst[2] * src[9];
+  dst2 = dst[0] * src[2] + dst[1] * src[6] + dst[2] * src[10];
+  dst[0] = dst0; dst[1] = dst1; dst[2] = dst2;
+
+  dst0 = dst[4] * src[0] + dst[5] * src[4] + dst[6] * src[8];
+  dst1 = dst[4] * src[1] + dst[5] * src[5] + dst[6] * src[9];
+  dst2 = dst[4] * src[2] + dst[5] * src[6] + dst[6] * src[10];
+  dst[4] = dst0; dst[5] = dst1; dst[6] = dst2;
+
+  dst0 = dst[8] * src[0] + dst[9] * src[4] + dst[10]* src[8];
+  dst1 = dst[8] * src[1] + dst[9] * src[5] + dst[10]* src[9];
+  dst2 = dst[8] * src[2] + dst[9] * src[6] + dst[10]* src[10];
+  dst[8] = dst0; dst[9] = dst1; dst[10] = dst2;
+
+  dst0 = dst[12]* src[0] + dst[13]* src[4] + dst[14]* src[8] + dst[15]* src[12];
+  dst1 = dst[12]* src[1] + dst[13]* src[5] + dst[14]* src[9] + dst[15]* src[13];
+  dst2 = dst[12]* src[2] + dst[13]* src[6] + dst[14]* src[10]+ dst[15]* src[14];
+  dst[12] = dst0; dst[13] = dst1; dst[14] = dst2;
+}
+
+void m4x4_transform_point(const m4x4_t matrix, vec3_t point)
+{
+  float out1, out2, out3;
+
+       out1 =  matrix[0]  * point[0] + matrix[4]  * point[1] + matrix[8]  * point[2] + matrix[12];
+       out2 =  matrix[1]  * point[0] + matrix[5]  * point[1] + matrix[9]  * point[2] + matrix[13];
+       out3 =  matrix[2]  * point[0] + matrix[6]  * point[1] + matrix[10] * point[2] + matrix[14];
+
+       point[0] = out1;
+       point[1] = out2;
+       point[2] = out3;
+}
+
+void m4x4_transform_normal(const m4x4_t matrix, vec3_t normal)
+{
+  float out1, out2, out3;
+
+       out1 =  matrix[0]  * normal[0] + matrix[4]  * normal[1] + matrix[8]  * normal[2];
+       out2 =  matrix[1]  * normal[0] + matrix[5]  * normal[1] + matrix[9]  * normal[2];
+       out3 =  matrix[2]  * normal[0] + matrix[6]  * normal[1] + matrix[10] * normal[2];
+
+       normal[0] = out1;
+       normal[1] = out2;
+       normal[2] = out3;
+}
+
+void m4x4_transform_vec4(const m4x4_t matrix, vec4_t vector)
+{
+  float out1, out2, out3, out4;
+
+       out1 =  matrix[0]  * vector[0] + matrix[4]  * vector[1] + matrix[8]  * vector[2] + matrix[12] * vector[3];
+       out2 =  matrix[1]  * vector[0] + matrix[5]  * vector[1] + matrix[9]  * vector[2] + matrix[13] * vector[3];
+       out3 =  matrix[2]  * vector[0] + matrix[6]  * vector[1] + matrix[10] * vector[2] + matrix[14] * vector[3];
+       out4 =  matrix[3]  * vector[0] + matrix[7]  * vector[1] + matrix[11] * vector[2] + matrix[15] * vector[3];
+
+       vector[0] = out1;
+       vector[1] = out2;
+       vector[2] = out3;
+  vector[3] = out4;
+}
+
+#define CLIP_X_LT_W(p) ((p)[0] < (p)[3])
+#define CLIP_X_GT_W(p) ((p)[0] > -(p)[3])
+#define CLIP_Y_LT_W(p) ((p)[1] < (p)[3])
+#define CLIP_Y_GT_W(p) ((p)[1] > -(p)[3])
+#define CLIP_Z_LT_W(p) ((p)[2] < (p)[3])
+#define CLIP_Z_GT_W(p) ((p)[2] > -(p)[3])
+
+clipmask_t homogenous_clip_point(const vec4_t clipped)
+{
+  clipmask_t result = CLIP_FAIL;
+  if(CLIP_X_LT_W(clipped)) result &= ~CLIP_LT_X; // X < W
+  if(CLIP_X_GT_W(clipped)) result &= ~CLIP_GT_X; // X > -W
+  if(CLIP_Y_LT_W(clipped)) result &= ~CLIP_LT_Y; // Y < W
+  if(CLIP_Y_GT_W(clipped)) result &= ~CLIP_GT_Y; // Y > -W
+  if(CLIP_Z_LT_W(clipped)) result &= ~CLIP_LT_Z; // Z < W
+  if(CLIP_Z_GT_W(clipped)) result &= ~CLIP_GT_Z; // Z > -W
+  return result;
+}
+
+clipmask_t m4x4_clip_point(const m4x4_t matrix, const vec3_t point, vec4_t clipped)
+{
+  clipped[0] = point[0];
+  clipped[1] = point[1];
+  clipped[2] = point[2];
+  clipped[3] = 1;
+  m4x4_transform_vec4(matrix, clipped);
+  return homogenous_clip_point(clipped);
+}
+
+
+unsigned int homogenous_clip_triangle(vec4_t clipped[9])
+{
+  vec4_t buffer[9];
+  unsigned int rcount = 3;
+  unsigned int wcount = 0;
+  vec_t const* rptr = clipped[0];
+  vec_t* wptr = buffer[0];
+  const vec_t* p0;
+  const vec_t* p1;
+  unsigned char b0, b1;
+
+  unsigned int i;
+  double scale;
+
+  p0 = rptr;
+  b0 = CLIP_X_LT_W(p0);
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_X_LT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[0] - p0[3]) / (wptr[3] - wptr[0]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  rcount = wcount;
+  wcount = 0;
+  rptr = buffer[0];
+  wptr = clipped[0];
+  p0 = rptr;
+  b0 = CLIP_X_GT_W(p0);
+
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_X_GT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[0] + p0[3]) / (-wptr[3] - wptr[0]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  rcount = wcount;
+  wcount = 0;
+  rptr = clipped[0];
+  wptr = buffer[0];
+  p0 = rptr;
+  b0 = CLIP_Y_LT_W(p0);
+
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_Y_LT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[1] - p0[3]) / (wptr[3] - wptr[1]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  rcount = wcount;
+  wcount = 0;
+  rptr = buffer[0];
+  wptr = clipped[0];
+  p0 = rptr;
+  b0 = CLIP_Y_GT_W(p0);
+
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_Y_GT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[1] + p0[3]) / (-wptr[3] - wptr[1]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  rcount = wcount;
+  wcount = 0;
+  rptr = clipped[0];
+  wptr = buffer[0];
+  p0 = rptr;
+  b0 = CLIP_Z_LT_W(p0);
+
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_Z_LT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[2] - p0[3]) / (wptr[3] - wptr[2]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  rcount = wcount;
+  wcount = 0;
+  rptr = buffer[0];
+  wptr = clipped[0];
+  p0 = rptr;
+  b0 = CLIP_Z_GT_W(p0);
+
+  for(i=0; i<rcount; ++i)
+  {
+    p1 = (i+1 != rcount) ? p0 + 4 : rptr;
+    b1 = CLIP_Z_GT_W(p1);
+    if(b0 ^ b1)
+    {
+                       wptr[0] = p1[0] - p0[0];
+                       wptr[1] = p1[1] - p0[1];
+                       wptr[2] = p1[2] - p0[2];
+                       wptr[3] = p1[3] - p0[3];
+
+      scale = (p0[2] + p0[3]) / (-wptr[3] - wptr[2]);
+
+                       wptr[0] = (vec_t)(p0[0] + scale*(wptr[0]));
+                       wptr[1] = (vec_t)(p0[1] + scale*(wptr[1]));
+                       wptr[2] = (vec_t)(p0[2] + scale*(wptr[2]));
+                       wptr[3] = (vec_t)(p0[3] + scale*(wptr[3]));
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    if(b1)
+    {
+      wptr[0] = p1[0];
+      wptr[1] = p1[1];
+      wptr[2] = p1[2];
+      wptr[3] = p1[3];
+
+      wptr += 4;
+      ++wcount;
+    }
+
+    p0 = p1;
+    b0 = b1;
+  }
+
+  return wcount;
+}
+
+unsigned int m4x4_clip_triangle(const m4x4_t matrix, const vec3_t p0, const vec3_t p1, const vec3_t p2, vec4_t clipped[9])
+{
+  clipped[0][0] = p0[0];
+  clipped[0][1] = p0[1];
+  clipped[0][2] = p0[2];
+  clipped[0][3] = 1;
+  clipped[1][0] = p1[0];
+  clipped[1][1] = p1[1];
+  clipped[1][2] = p1[2];
+  clipped[1][3] = 1;
+  clipped[2][0] = p2[0];
+  clipped[2][1] = p2[1];
+  clipped[2][2] = p2[2];
+  clipped[2][3] = 1;
+
+  m4x4_transform_vec4(matrix, clipped[0]);
+  m4x4_transform_vec4(matrix, clipped[1]);
+  m4x4_transform_vec4(matrix, clipped[2]);
+
+  return homogenous_clip_triangle(clipped);
+}
+
+unsigned int homogenous_clip_line(vec4_t clipped[2])
+{
+  vec4_t clip;
+  double scale;
+  const vec_t* const p0 = clipped[0];
+  const vec_t* const p1 = clipped[1];
+
+  // early out
+  {
+    clipmask_t mask0 = homogenous_clip_point(clipped[0]);
+    clipmask_t mask1 = homogenous_clip_point(clipped[1]);
+
+    if((mask0 | mask1) == CLIP_PASS) // both points passed all planes
+      return 2;
+
+    if(mask0 & mask1) // both points failed any one plane
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_X_LT_W(p0);
+    if(index ^ CLIP_X_LT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[0] - p0[3]) / (clip[3] - clip[0]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_X_GT_W(p0);
+    if(index ^ CLIP_X_GT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[0] + p0[3]) / (-clip[3] - clip[0]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_Y_LT_W(p0);
+    if(index ^ CLIP_Y_LT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[1] - p0[3]) / (clip[3] - clip[1]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_Y_GT_W(p0);
+    if(index ^ CLIP_Y_GT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[1] + p0[3]) / (-clip[3] - clip[1]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_Z_LT_W(p0);
+    if(index ^ CLIP_Z_LT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[2] - p0[3]) / (clip[3] - clip[2]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  {
+    const unsigned int index = CLIP_Z_GT_W(p0);
+    if(index ^ CLIP_Z_GT_W(p1))
+    {
+                 clip[0] = p1[0] - p0[0];
+                 clip[1] = p1[1] - p0[1];
+                 clip[2] = p1[2] - p0[2];
+                 clip[3] = p1[3] - p0[3];
+
+      scale = (p0[2] + p0[3]) / (-clip[3] - clip[2]);
+
+                 clip[0] = (vec_t)(p0[0] + scale*(clip[0]));
+                 clip[1] = (vec_t)(p0[1] + scale*(clip[1]));
+                 clip[2] = (vec_t)(p0[2] + scale*(clip[2]));
+                 clip[3] = (vec_t)(p0[3] + scale*(clip[3]));
+
+      clipped[index][0] = clip[0];
+      clipped[index][1] = clip[1];
+      clipped[index][2] = clip[2];
+      clipped[index][3] = clip[3];
+    }
+    else if(index == 0)
+      return 0;
+  }
+
+  return 2;
+}
+
+unsigned int m4x4_clip_line(const m4x4_t matrix, const vec3_t p0, const vec3_t p1, vec4_t clipped[2])
+{
+  clipped[0][0] = p0[0];
+  clipped[0][1] = p0[1];
+  clipped[0][2] = p0[2];
+  clipped[0][3] = 1;
+  clipped[1][0] = p1[0];
+  clipped[1][1] = p1[1];
+  clipped[1][2] = p1[2];
+  clipped[1][3] = 1;
+
+  m4x4_transform_vec4(matrix, clipped[0]);
+  m4x4_transform_vec4(matrix, clipped[1]);
+
+  return homogenous_clip_line(clipped);
+}
+
+void m4x4_transpose(m4x4_t matrix)
+{
+       int i, j;
+       float temp, *p1, *p2;
+
+  for (i=1; i<4; i++) {
+    for (j=0; j<i; j++) {
+      p1 = matrix+(j*4+i);
+      p2 = matrix+(i*4+j);
+      temp = *p1;
+      *p1=*p2;
+      *p2=temp;
+    }
+  }
+}
+
+/* adapted from Graphics Gems 2
+ invert a 3d matrix (4x3) */
+int m4x4_orthogonal_invert(m4x4_t matrix)
+{
+  m4x4_t temp;
+  vec_t* src = temp;
+
+  m4x4_assign(src, matrix);
+  
+  /* Calculate the determinant of upper left 3x3 submatrix and
+  * determine if the matrix is singular.
+  */
+  {
+#if 0
+  float pos = 0.0f;
+  float neg = 0.0f;
+  float det = src[0] * src[5] * src[10];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = src[1] * src[6] * src[8];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = src[2] * src[4] * src[9];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = -src[2] * src[5] * src[8];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = -src[1] * src[4] * src[10];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = -src[0] * src[6] * src[9];
+  if (det >= 0.0) pos += det; else neg += det;
+  
+  det = pos + neg;
+#elif 0
+  float det
+    = (src[0] * src[5] * src[10])
+    + (src[1] * src[6] * src[8])
+    + (src[2] * src[4] * src[9])
+    - (src[2] * src[5] * src[8])
+    - (src[1] * src[4] * src[10])  
+    - (src[0] * src[6] * src[9]);
+#else
+  float det
+    = src[0] * ( src[5]*src[10] - src[9]*src[6] )
+    - src[1] * ( src[4]*src[10] - src[8]*src[6] )
+    + src[2] * ( src[4]*src[9] - src[8]*src[5] );
+
+#endif
+  
+  if (det*det < 1e-25)
+    return 1;
+  
+  det = 1.0f / det;
+  matrix[0] = (  (src[5]*src[10]- src[6]*src[9] )*det);
+  matrix[1] = (- (src[1]*src[10]- src[2]*src[9] )*det);
+  matrix[2] = (  (src[1]*src[6] - src[2]*src[5] )*det);
+  matrix[4] = (- (src[4]*src[10]- src[6]*src[8] )*det);
+  matrix[5] = (  (src[0]*src[10]- src[2]*src[8] )*det);
+  matrix[6] = (- (src[0]*src[6] - src[2]*src[4] )*det);
+  matrix[8] = (  (src[4]*src[9] - src[5]*src[8] )*det);
+  matrix[9] = (- (src[0]*src[9] - src[1]*src[8] )*det);
+  matrix[10]= (  (src[0]*src[5] - src[1]*src[4] )*det);
+  }
+  
+  /* Do the translation part */
+  matrix[12] = - (src[12] * matrix[0] +
+    src[13] * matrix[4] +
+    src[14] * matrix[8]);
+  matrix[13] = - (src[12] * matrix[1] +
+    src[13] * matrix[5] +
+    src[14] * matrix[9]);
+  matrix[14] = - (src[12] * matrix[2] +
+    src[13] * matrix[6] +
+    src[14] * matrix[10]);
+  
+  return 0;
+}
+
+void quat_identity(vec4_t quat)
+{
+  quat[0] = quat[1] = quat[2] = 0;
+  quat[3] = 1;
+}
+
+void quat_multiply_by_quat(vec4_t quat, const vec4_t other)
+{
+  const vec_t x = quat[3]*other[0] + quat[0]*other[3] + quat[1]*other[2] - quat[2]*other[1];
+  const vec_t y = quat[3]*other[1] + quat[1]*other[3] + quat[2]*other[0] - quat[0]*other[2];
+  const vec_t z = quat[3]*other[2] + quat[2]*other[3] + quat[0]*other[1] - quat[1]*other[0];
+  const vec_t w = quat[3]*other[3] - quat[0]*other[0] - quat[1]*other[1] - quat[2]*other[2];
+  quat[0] = x;
+  quat[1] = y;
+  quat[2] = z;
+  quat[3] = w;
+}
+
+void quat_conjugate(vec4_t quat)
+{
+  VectorNegate(quat, quat);
+}
+
+//! quaternion from two unit vectors
+void quat_for_unit_vectors(vec4_t quat, const vec3_t from, const vec3_t to)
+{
+  CrossProduct(from, to, quat);
+  quat[3] = DotProduct(from, to);
+}
+
+void quat_normalise(vec4_t quat)
+{
+  const vec_t n = 1 / ( quat[0] * quat[0] +  quat[1] * quat[1] +  quat[2] * quat[2] +  quat[3] *  quat[3] );
+  quat[0] *= n;
+  quat[1] *= n;
+  quat[2] *= n;
+  quat[3] *= n;
+}
+
+void quat_for_axisangle(vec4_t quat, const vec3_t axis, double angle)
+{
+  angle *= 0.5;
+
+  quat[3] = (float)sin(angle);
+
+  quat[0] = axis[0] * quat[3];
+  quat[1] = axis[1] * quat[3];
+  quat[2] = axis[2] * quat[3];
+  quat[3] = (float)cos(angle);
+}
+
+void m3x3_multiply_by_m3x3(m3x3_t matrix, const m3x3_t matrix_src)
+{
+  float *pDest = matrix;
+       float out1, out2, out3;
+  int i;
+
+       for(i=0;i<3;i++)
+       {
+               out1 =  matrix_src[0] * pDest[0];
+               out1 += matrix_src[1] * pDest[3];
+               out1 += matrix_src[2] * pDest[6];
+               out2 =  matrix_src[3] * pDest[0];
+               out2 += matrix_src[4] * pDest[3];
+               out2 += matrix_src[5] * pDest[6];
+               out3 =  matrix_src[6] * pDest[0];
+               out3 += matrix_src[7] * pDest[3];
+               out3 += matrix_src[8] * pDest[6];
+
+               pDest[0] = out1;
+               pDest[3] = out2;
+               pDest[6] = out3;
+
+    pDest++;
+       }
+}
+
+void m3x3_transform_vec3(const m3x3_t matrix, vec3_t vector)
+{
+  float out1, out2, out3;
+
+       out1 =  matrix[0]  * vector[0];
+       out1 += matrix[3]  * vector[1];
+       out1 += matrix[6]  * vector[2];
+       out2 =  matrix[1]  * vector[0];
+       out2 += matrix[4]  * vector[1];
+       out2 += matrix[7]  * vector[2];
+       out3 =  matrix[2]  * vector[0];
+       out3 += matrix[5]  * vector[1];
+       out3 += matrix[8] * vector[2];
+
+       vector[0] = out1;
+       vector[1] = out2;
+       vector[2] = out3;
+}
+
+float m3_det( m3x3_t mat )
+{
+  float det;
+  
+  det = mat[0] * ( mat[4]*mat[8] - mat[7]*mat[5] )
+    - mat[1] * ( mat[3]*mat[8] - mat[6]*mat[5] )
+    + mat[2] * ( mat[3]*mat[7] - mat[6]*mat[4] );
+  
+  return( det );
+}
+
+int m3_inverse( m3x3_t mr, m3x3_t ma )
+{
+  float det = m3_det( ma );
+  if (det == 0 )
+  {
+    return 1;
+  }
+
+  
+  mr[0] =    ma[4]*ma[8] - ma[5]*ma[7]   / det;
+  mr[1] = -( ma[1]*ma[8] - ma[7]*ma[2] ) / det;
+  mr[2] =    ma[1]*ma[5] - ma[4]*ma[2]   / det;
+  
+  mr[3] = -( ma[3]*ma[8] - ma[5]*ma[6] ) / det;
+  mr[4] =    ma[0]*ma[8] - ma[6]*ma[2]   / det;
+  mr[5] = -( ma[0]*ma[5] - ma[3]*ma[2] ) / det;
+  
+  mr[6] =    ma[3]*ma[7] - ma[6]*ma[4]   / det;
+  mr[7] = -( ma[0]*ma[7] - ma[6]*ma[1] ) / det;
+  mr[8] =    ma[0]*ma[4] - ma[1]*ma[3]   / det;
+
+  return 0;
+}
+
+void m4_submat( m4x4_t mr, m3x3_t mb, int i, int j )
+{
+  int ti, tj, idst, jdst;
+  
+  for ( ti = 0; ti < 4; ti++ )
+  {
+    if ( ti < i )
+      idst = ti;
+    else if ( ti > i )
+      idst = ti-1;
+    else
+      continue;
+      
+      for ( tj = 0; tj < 4; tj++ )
+      {
+        if ( tj < j )
+          jdst = tj;
+        else if ( tj > j )
+          jdst = tj-1;
+        else
+          continue;
+          
+        mb[idst*3 + jdst] = mr[ti*4 + tj ];
+      }
+  }
+}
+
+float m4_det( m4x4_t mr )
+{
+  float  det, result = 0, i = 1;
+  m3x3_t msub3;
+  int     n;
+  
+  for ( n = 0; n < 4; n++, i *= -1 )
+  {
+    m4_submat( mr, msub3, 0, n );
+    
+    det     = m3_det( msub3 );
+    result += mr[n] * det * i;
+  }
+  
+  return result;
+}
+
+int m4x4_invert(m4x4_t matrix)
+{
+  float  mdet = m4_det( matrix );
+  m3x3_t mtemp;
+  int     i, j, sign;
+  m4x4_t m4x4_temp;
+  
+#if 0
+  if ( fabs( mdet ) < 0.0000000001 )
+    return 1;
+#endif
+
+  m4x4_assign(m4x4_temp, matrix);
+  
+  for ( i = 0; i < 4; i++ )
+    for ( j = 0; j < 4; j++ )
+    {
+      sign = 1 - ( (i +j) % 2 ) * 2;
+      
+      m4_submat( m4x4_temp, mtemp, i, j );
+      
+      matrix[i+j*4] = ( m3_det( mtemp ) * sign ) / mdet; /*  FIXME: try using * inverse det and see if speed/accuracy are good enough */
+    }
+    
+  return 0;
+}
+#if 0
+void m4x4_solve_ge(m4x4_t matrix, vec4_t x)
+{
+  int indx[4];
+  int c,r;
+  int i;
+  int best;
+  float scale[4];
+  float f, pivot;
+  float aug[4];
+  float recip, ratio;
+  float* p;
+
+  for(r=0; r<4; r++)
+  {
+    aug[r] = 0;
+    indx[r] = r;
+  }
+
+  for (r=0; r<4; r++)
+  {
+    scale[r] = 0;
+    for (c=0; c<4; c++, p++)
+    {
+      if (fabs(*p) > scale[r])
+      {
+        scale[r] = (float)fabs(*p);
+      }
+    }
+  }
+
+  for (c=0; c<3; c++)
+  {
+    pivot = 0;
+    for (r=c; r<4; r++)
+    {
+      f = (float)fabs(matrix[(indx[r]<<2)+c]) / scale[indx[r]];
+      if (f > pivot)
+      {
+        pivot = f;
+        best = r;
+      }
+    }
+
+    i = indx[c];
+    indx[c] = indx[best];
+    indx[best] = i;
+
+    recip = 1 / matrix[(indx[c]<<2)+c];
+
+    for (r=c+1; r<4; r++)
+    {
+      p = matrix + (indx[r]<<2);
+      ratio = p[c] * recip;
+
+      for (i=c+1; i<4; i++)
+        p[i] -= ratio * matrix[(indx[c]<<2)+i];
+      aug[indx[r]] -= ratio * aug[indx[c]];
+    }
+  }
+
+  x[indx[3]] = aug[indx[3]] / matrix[(indx[3]<<2)+3];
+  for(r=2; r>=0; r--)
+  {
+    f = aug[indx[r]];
+    p = matrix + (indx[r]<<2);
+    recip = 1 / p[r];
+    for(c=(r+1); c<4; c++)
+    {
+      f -= (p[c] * x[indx[c]]);
+    }
+    x[indx[r]] = f * recip;
+  }
+}
+#endif
+
+#define N 3
+
+int matrix_solve_ge(vec_t* matrix, vec_t* aug, vec3_t x)
+{
+  int indx[N];
+  int c,r;
+  int i;
+  int best;
+  float scale[N];
+  float f, pivot;
+  float ratio;
+  float* p;
+
+  for(r=0; r<N; r++)
+  {
+    indx[r] = r;
+  }
+
+  for (r=0; r<N; r++)
+  {
+    p = matrix+r;
+    scale[r] = 0;
+    for (c=0; c<N; c++, p++)
+    {
+      if (fabs(*p) > scale[r])
+      {
+        scale[r] = (float)fabs(*p);
+      }
+    }
+  }
+
+  for (c=0; c<N; c++)
+  {
+    pivot = 0;
+    best = -1;
+    for (r=c; r<N; r++)
+    {
+      f = (float)fabs(matrix[(indx[r]*N)+c]) / scale[indx[r]];
+      if (f > pivot)
+      {
+        pivot = f;
+        best = r;
+      }
+    }
+
+    if(best == -1) return 1;
+
+    i = indx[c];
+    indx[c] = indx[best];
+    indx[best] = i;
+
+    for (r=c+1; r<N; r++)
+    {
+      p = matrix + (indx[r]*N);
+      ratio = p[c] / matrix[(indx[c]*N)+c];
+
+      for (i=c+1; i<N; i++) p[i] -= ratio * matrix[(indx[c]*N)+i];
+      aug[indx[r]] -= ratio * aug[indx[c]];
+    }
+  }
+
+  x[N-1] = aug[indx[N-1]] / matrix[(indx[N-1]*N)+N-1];
+  for(r=1; r>=0; r--)
+  {
+    f = aug[indx[r]];
+    p = matrix + (indx[r]*N);
+    for(c=(r+1); c<N; c++) f -= (p[c] * x[c]);
+    x[r] = f / p[r];
+  }
+  return 0;
+}
+
+#ifdef YOU_WANT_IT_TO_BORK
+ /* Gaussian elimination */
+  for(i=0;i<4;i++)
+  {
+    for(j=(i+1);j<4;j++)
+    {
+      ratio = matrix[j][i] / matrix[i][i];
+      for(count=i;count<n;count++) {
+        matrix[j][count] -= (ratio * matrix[i][count]);
+      }
+      b[j] -= (ratio * b[i]);
+    }
+  }
+
+  /* Back substitution */
+  x[n-1] = b[n-1] / matrix[n-1][n-1];
+  for(i=(n-2);i>=0;i--)
+  {
+    temp = b[i];
+    for(j=(i+1);j<n;j++)
+    {
+      temp -= (matrix[i][j] * x[j]);
+    }
+    x[i] = temp / matrix[i][i];
+  }
+#endif
+
+int plane_intersect_planes(const vec4_t plane1, const vec4_t plane2, const vec4_t plane3, vec3_t intersection)
+{
+  m3x3_t planes;
+  vec3_t b;
+  VectorCopy(plane1, planes+0);
+  b[0] = plane1[3];
+  VectorCopy(plane2, planes+3);
+  b[1] = plane2[3];
+  VectorCopy(plane3, planes+6);
+  b[2] = plane3[3];
+
+  return matrix_solve_ge(planes, b, intersection);
+}