/* $Id: norm_tmp.h,v 1.2 1999/11/05 06:43:10 brianp Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.1
 * 
 * Copyright (C) 1999  Brian Paul   All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * New (3.1) transformation code written by Keith Whitwell.
 */


static void TAG(transform_normalize_normals)( const GLmatrix *mat,
					      GLfloat scale,
					      const GLvector3f *in,
					      const GLfloat *lengths,
					      const GLubyte mask[],
					      GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   GLfloat *m = mat->inv;
   GLfloat m0 = m[0],  m4 = m[4],  m8 = m[8];
   GLfloat m1 = m[1],  m5 = m[5],  m9 = m[9];
   GLfloat m2 = m[2],  m6 = m[6],  m10 = m[10];

   (void) mask;
   if (!lengths) {
      STRIDE_LOOP {
	 CULL_CHECK {
	    GLfloat tx, ty, tz;
	    {
	       const GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	       tx = ux * m0 + uy * m1 + uz * m2;
	       ty = ux * m4 + uy * m5 + uz * m6;
	       tz = ux * m8 + uy * m9 + uz * m10;
	    }
	    {
	       GLdouble len = tx*tx + ty*ty + tz*tz;
	       if (len > 1e-20) {
		  GLdouble scale = 1.0 / GL_SQRT(len);
		  out[i][0] = (GLfloat) (tx * scale);
		  out[i][1] = (GLfloat) (ty * scale);
		  out[i][2] = (GLfloat) (tz * scale);
	       }
	       else
	       {
		  out[i][0] = out[i][1] = out[i][2] = 0;
	       }
	    }
	 }
      }
   } else {
      
      /* scale has been snapped to 1.0 if it is close.
       */
      if (scale != 1.0) {
	 m0 *= scale,  m4 *= scale,  m8 *= scale;
	 m1 *= scale,  m5 *= scale,  m9 *= scale;
	 m2 *= scale,  m6 *= scale,  m10 *= scale;
      }

      STRIDE_LOOP {
	 CULL_CHECK {
	    GLfloat tx, ty, tz;
	    {
	       const GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	       tx = ux * m0 + uy * m1 + uz * m2;
	       ty = ux * m4 + uy * m5 + uz * m6;
	       tz = ux * m8 + uy * m9 + uz * m10;
	    }
	    {
	       GLfloat len = lengths[i];
	       out[i][0] = tx * len;
	       out[i][1] = ty * len;
	       out[i][2] = tz * len;
	    }
	 }
      }
   }
   dest->count = in->count;
}

static void TAG(transform_normalize_normals_no_rot)( const GLmatrix *mat,
						     GLfloat scale,
						     const GLvector3f *in,
						     const GLfloat *lengths,
						     const GLubyte mask[],
						     GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   GLfloat *m = mat->inv;
   GLfloat m0 = m[0];
   GLfloat m5 = m[5];
   GLfloat m10 = m[10];
   (void) mask;
   if (!lengths) {
      STRIDE_LOOP {
	 CULL_CHECK {
	    GLfloat tx, ty, tz;
	    {
	       const GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	       tx = ux * m0                    ;
	       ty =           uy * m5          ;
	       tz =                     uz * m10;
	    }
	    {
	       GLdouble len = tx*tx + ty*ty + tz*tz;
	       if (len > 1e-20) {
		  GLdouble scale = 1.0 / GL_SQRT(len);
		  out[i][0] = (GLfloat) (tx * scale);
		  out[i][1] = (GLfloat) (ty * scale);
		  out[i][2] = (GLfloat) (tz * scale);
	       }
	       else
	       {
		  out[i][0] = out[i][1] = out[i][2] = 0;
	       }
	    }
	 }
      }
   } else {
      /* scale has been snapped to 1.0 if it is close.
       */
      if (scale != 1.0) {
	 m0 *= scale; 
	 m5 *= scale;
	 m10 *= scale;
      }

      STRIDE_LOOP {
	 CULL_CHECK {
	    GLfloat tx, ty, tz;
	    {
	       const GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	       tx = ux * m0                    ;
	       ty =           uy * m5          ;
	       tz =                     uz * m10;
	    }
	    {
	       GLfloat len = lengths[i];
	       out[i][0] = tx * len;
	       out[i][1] = ty * len;
	       out[i][2] = tz * len;
	    }
	 }
      }
   }
   dest->count = in->count;
}


static void TAG(transform_rescale_normals_no_rot)( const GLmatrix *mat,
						   GLfloat scale,
						   const GLvector3f *in,
						   const GLfloat *lengths,
						   const GLubyte mask[],
						   GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   const GLfloat *m = mat->inv;
   GLfloat m0 = scale*m[0];
   GLfloat m5 = scale*m[5];
   GLfloat m10 = scale*m[10];
   (void) lengths;
   (void) mask;
   STRIDE_LOOP {
      CULL_CHECK {
	 GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	 out[i][0] = ux * m0;
	 out[i][1] =           uy * m5;
	 out[i][2] =                     uz * m10;
      }
   }
   dest->count = in->count;
}

static void TAG(transform_rescale_normals)( const GLmatrix *mat,
					    GLfloat scale,
					    const GLvector3f *in,
					    const GLfloat *lengths,
					    const GLubyte mask[],
					    GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   /* Since we are unlikely to have < 3 vertices in the buffer,
    * it makes sense to pre-multiply by scale.
    */
   const GLfloat *m = mat->inv;
   GLfloat m0 = scale*m[0],  m4 = scale*m[4],  m8 = scale*m[8];
   GLfloat m1 = scale*m[1],  m5 = scale*m[5],  m9 = scale*m[9];
   GLfloat m2 = scale*m[2],  m6 = scale*m[6],  m10 = scale*m[10];
   (void) lengths;
   (void) mask;
   STRIDE_LOOP {
      CULL_CHECK {
	 GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	 out[i][0] = ux * m0 + uy * m1 + uz * m2;
	 out[i][1] = ux * m4 + uy * m5 + uz * m6;
	 out[i][2] = ux * m8 + uy * m9 + uz * m10;
      }
   }
   dest->count = in->count;
}


static void TAG(transform_normals_no_rot)(const GLmatrix *mat,
					  GLfloat scale,
					  const GLvector3f *in,
					  const GLfloat *lengths,
					  const GLubyte mask[],
					  GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   const GLfloat *m = mat->inv;
   GLfloat m0 = m[0];
   GLfloat m5 = m[5];
   GLfloat m10 = m[10];
   (void) scale;
   (void) lengths;
   (void) mask;
   STRIDE_LOOP {
      CULL_CHECK {
	 GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	 out[i][0] = ux * m0;
	 out[i][1] =           uy * m5;
	 out[i][2] =                     uz * m10;
      }
   }
   dest->count = in->count;
}

static void TAG(transform_normals)( const GLmatrix *mat,
				    GLfloat scale,
				    const GLvector3f *in,
				    const GLfloat *lengths,
				    const GLubyte mask[],
				    GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   const GLfloat *m = mat->inv;
   GLfloat m0 = m[0],  m4 = m[4],  m8 = m[8];
   GLfloat m1 = m[1],  m5 = m[5],  m9 = m[9];
   GLfloat m2 = m[2],  m6 = m[6],  m10 = m[10];
   (void) scale;
   (void) lengths;
   (void) mask;
   STRIDE_LOOP {
      CULL_CHECK {
	 GLfloat ux = from[0],  uy = from[1],  uz = from[2];
	 out[i][0] = ux * m0 + uy * m1 + uz * m2;
	 out[i][1] = ux * m4 + uy * m5 + uz * m6;
	 out[i][2] = ux * m8 + uy * m9 + uz * m10;
      }
   }
   dest->count = in->count;
}


static void TAG(normalize_normals)( const GLmatrix *mat,
				    GLfloat scale,
				    const GLvector3f *in,
				    const GLfloat *lengths,
				    const GLubyte mask[],
				    GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   (void) mat;
   (void) mask;
   (void) scale;
   if (lengths) {
      STRIDE_LOOP {
	 CULL_CHECK {
	    const GLfloat x = from[0], y = from[1], z = from[2];
	    GLfloat invlen = lengths[i];
	    out[i][0] = x * invlen;
	    out[i][1] = y * invlen;
	    out[i][2] = z * invlen;
	 }	 
      }
   }
   else {
      STRIDE_LOOP {
	 CULL_CHECK {
	    const GLfloat x = from[0], y = from[1], z = from[2];
	    GLdouble len = x * x + y * y + z * z;
	    if (len > 1e-50) {
	       len = 1.0 / GL_SQRT(len);
	       out[i][0] = (GLfloat) (x * len);
	       out[i][1] = (GLfloat) (y * len);
	       out[i][2] = (GLfloat) (z * len);
	    }
	    else {
	       out[i][0] = x;
	       out[i][1] = y;
	       out[i][2] = z;
	    }
	 }	 
      }
   }
   dest->count = in->count;
}


static void TAG(rescale_normals)( const GLmatrix *mat,
				  GLfloat scale,
				  const GLvector3f *in,
				  const GLfloat *lengths,
				  const GLubyte mask[],
				  GLvector3f *dest )
{
   GLuint i;  
   const GLfloat *from = in->start;
   GLuint stride = in->stride;
   GLuint count = in->count;
   GLfloat (*out)[3] = (GLfloat (*)[3])dest->start;
   (void) mat;
   (void) lengths;
   (void) mask;

   STRIDE_LOOP {
      CULL_CHECK {
	 SCALE_SCALAR_3V( out[i], scale, from );
      }
   }
   dest->count = in->count;
}


static void TAG(init_c_norm_transform)( void )
{
   gl_normal_tab[NORM_TRANSFORM_NO_ROT][IDX] = 
      TAG(transform_normals_no_rot);

   gl_normal_tab[NORM_TRANSFORM_NO_ROT | NORM_RESCALE][IDX] = 
      TAG(transform_rescale_normals_no_rot);

   gl_normal_tab[NORM_TRANSFORM_NO_ROT | NORM_NORMALIZE][IDX] = 
      TAG(transform_normalize_normals_no_rot);

   gl_normal_tab[NORM_TRANSFORM][IDX] = 
      TAG(transform_normals);

   gl_normal_tab[NORM_TRANSFORM | NORM_RESCALE][IDX] = 
      TAG(transform_rescale_normals);

   gl_normal_tab[NORM_TRANSFORM | NORM_NORMALIZE][IDX] = 
      TAG(transform_normalize_normals);

   gl_normal_tab[NORM_RESCALE][IDX] = 
      TAG(rescale_normals);

   gl_normal_tab[NORM_NORMALIZE][IDX] = 
      TAG(normalize_normals);

}
