• VS2012下基于Glut 矩阵变换示例程序:


    也可以使用我们自己的矩阵运算来实现OpenGL下的glTranslatef相应的旋转变换。需要注意的是OpenGL下的矩阵是列优先存储的。


    示例通过矩阵运算使得圆柱或者甜圈自动绕Y轴旋转,可以单击鼠标右键来弹出菜单选择是否显示坐标轴、正视图或者是透视图、是否打印变换矩阵、显示圆柱还是甜圈。程序用到math3d中的矩阵相关函数。由于绘制的坐标轴并未参加矩阵变换,在运行过程中会发现坐标轴并不会在定时器作用下不断旋转。




    源代码:

    GlutTransformDemo

    // GlutTransformDemo.cpp : 定义控制台应用程序的入口点。
    //
    
    
    #include "stdafx.h"
    #include <gl/glut.h>
    #include <math.h>
    #include "math3d.h"
    //圆周率宏
    #define GL_PI 3.1415f
    //获取屏幕的宽度
    GLint SCREEN_WIDTH=0;
    GLint SCREEN_HEIGHT=0;
    //设置程序的窗口大小
    GLint windowWidth=400;
    GLint windowHeight=300;
    //绕x轴旋转角度
    GLfloat xRotAngle=0.0f;
    //绕y轴旋转角度
    GLfloat yRotAngle=0.0f;
    //受支持的点大小范围
    GLfloat sizes[2];
    //受支持的点大小增量
    GLfloat step;
    //最大的投影矩阵堆栈深度
    GLint iMaxProjectionStackDepth;
    //最大的模型视图矩阵堆栈深度
    GLint iMaxModeviewStackDepth;
    //最大的纹理矩阵堆栈深度
    GLint iMaxTextureStackDepth;
    
    GLint iCoordinateaxis=2;//是否显示坐标轴
    GLint iProjectionMode=1;//投影模式
    GLint iPrintMatrix=1;//是否打印变换矩阵
    GLint iCylinder=1;//显示圆柱还是甜圈
    void changSize(GLint w,GLint h);
    
    void DrawTorus(M3DMatrix44f mTransform){  
        // 大圆只存在于 xy 平面,  
        // 小圆存在于 xyz 空间中,  
        // 其圆心是大圆圆周上的点。  
        // 小圆环大圆半径方向为起始旋转一周形成的。  
        // 由于 z 轴垂直于 xy 平面,  
        // 又因为大圆的半径位于 xy 平面,  
        // 因此,z 轴垂直于大圆的半径(垂直于面,垂直于线),  
        // 因此,z 轴与大圆的半径方向是正交的。  
        // 小圆位于 z 轴与大圆半径方向形成的平面,  
        // 后面计算具体点的位置是基于上面的描述。  
          
        // 大圆半径  
        GLfloat majorRadius = 55.0f;  
        // 小圆半径  
        GLfloat minorRadius = 15.0f;  
        // 大圆圆周被切分的点数  
        GLint   numMajor = 50;  
        // 小圆圆周被切分的点数  
        GLint   numMinor = 20;  
        M3DVector3f objectVertex;         // Vertex in object/eye space  
        M3DVector3f transformedVertex;    // New Transformed vertex  
        // 每个点对应的弧度数  
        double majorStep = 2.0f*M3D_PI / numMajor;  
        double minorStep = 2.0f*M3D_PI / numMinor;  
        int i, j;  
          
        // 对于大圆上的点进行迭代  
        for (i=0; i<numMajor; ++i)   
            {  
            // 第一个点对应的弧度  
            double a0 = i * majorStep;  
            // 第二个点对应的弧度  
            double a1 = a0 + majorStep;  
            // 第一个点在 x 与 y 轴上的单位长度  
            GLfloat x0 = (GLfloat) cos(a0);  
            GLfloat y0 = (GLfloat) sin(a0);  
            // 第二个点在 x 与 y 轴上的单位长度  
            GLfloat x1 = (GLfloat) cos(a1);  
            GLfloat y1 = (GLfloat) sin(a1);  
      
            glBegin(GL_TRIANGLE_STRIP);  
            // 对小圆上的点进行迭代  
            for (j=0; j<=numMinor; ++j)   
                {  
                // 小圆上点对应的弧度  
                double b = j * minorStep;  
                // 小圆上点在半径方向的单位长度  
                GLfloat c = (GLfloat) cos(b);  
                // 小圆上点,在xy 平面的分量长度  
                GLfloat r = minorRadius * c + majorRadius;  
                // 小圆上点在 z 轴上的长度  
                GLfloat z = minorRadius * (GLfloat) sin(b);  
                  
                // 小圆上点坐标确认的过程:将该点分为在 z 轴 与 大圆半径方向,由于大圆半径只存在于 xy 平面,就相对容易求到 x , y 坐标。  
                  
                // First point  
                objectVertex[0] = x0*r;// 小圆上点对应的 x 坐标  
                objectVertex[1] = y0*r;// 小圆上点对应的 y 坐标  
                objectVertex[2] = z; // 小圆上点对应的 z 坐标  
                m3dTransformVector3(transformedVertex, objectVertex, mTransform);  
                glVertex3fv(transformedVertex);  
      
                // Second point  
                objectVertex[0] = x1*r;  
                objectVertex[1] = y1*r;  
                objectVertex[2] = z;  
                m3dTransformVector3(transformedVertex, objectVertex, mTransform);  
                glVertex3fv(transformedVertex);  
                }  
            glEnd();  
            }  
    }  
    
    void DrawCylinder(M3DMatrix44f mTransform){  
        // 大圆半径  
        GLfloat majorRadius = 55.0f;  
        // 大圆圆周被切分的点数  
        GLint   numMajor = 100;  
    
        M3DVector3f objectVertex;         // Vertex in object/eye space  
        M3DVector3f transformedVertex;    // New Transformed vertex  
        // 每个点对应的弧度数  
        double majorStep = 2.0f*M3D_PI / numMajor;  
      
        glBegin(GL_TRIANGLE_STRIP);  
        // 对于大圆上的点进行迭代  
        for (int i=0; i<=numMajor; ++i)   
            {  
    
            // 第一个点对应的弧度  
            double a0 = i * majorStep;  
            // 第二个点对应的弧度  
            double a1 = a0 - majorStep;  
            // 第一个点在 x 与 y 轴上的单位长度  
            GLfloat x0 = (GLfloat) cos(a0);  
            GLfloat y0 = (GLfloat) sin(a0);  
            // 第二个点在 x 与 y 轴上的单位长度  
            GLfloat x1 = (GLfloat) cos(a1);  
            GLfloat y1 = (GLfloat) sin(a1);  
      
    		// First point  
    		objectVertex[0] = x0*majorRadius;// 小圆上点对应的 x 坐标  
    		objectVertex[1] = y0*majorRadius;// 小圆上点对应的 y 坐标  
    		objectVertex[2] = 50.0f; // 小圆上点对应的 z 坐标  
    		m3dTransformVector3(transformedVertex, objectVertex, mTransform);  
    		glVertex3fv(transformedVertex);  
    
    		// Second point  
    		objectVertex[0] = x1*majorRadius;  
    		objectVertex[1] = y1*majorRadius;  
    		objectVertex[2] = -50.0f;  
    		m3dTransformVector3(transformedVertex, objectVertex, mTransform);  
    		glVertex3fv(transformedVertex); 
    		}
            glEnd();  
    }  
    
    //菜单回调函数
    void processMenu(int value){
    	switch(value){
    		case 1:
    			iCoordinateaxis=1;
    			break;
    		case 2:
    			iCoordinateaxis=2;
    			break;
    		case 3:
    			iProjectionMode=1;
    			//强制调用窗口大小变化回调函数,更改投影模式为正交投影
    			changSize(glutGet(GLUT_WINDOW_WIDTH),glutGet(GLUT_WINDOW_HEIGHT));
    			break;
    		case 4:
    			iProjectionMode=2;
    			//强制调用窗口大小变化回调函数,更改投影模式为透视投影
    			changSize(glutGet(GLUT_WINDOW_WIDTH),glutGet(GLUT_WINDOW_HEIGHT));
    			break;
    		case 5:
    			iPrintMatrix=1;
    			break;
    		case 6:
    			iPrintMatrix=2;
    			break;
    		case 7:
    			iCylinder=1;
    			break;
    		case 8:
    			iCylinder=2;
    			break;
    		default:
    			break;
    	}
    	//重新绘制
    	glutPostRedisplay();
    }
    
    //显示回调函数
    void renderScreen(void){
        M3DMatrix44f   transformationMatrix;   // Storeage for rotation matrix
        static GLfloat yRot = 0.0f;         // Rotation angle for animation
        yRot += 0.5f;
    	//将窗口颜色清理为黑色
    	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        //把整个窗口清理为当前清理颜色:黑色;清除深度缓冲区。
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
        //将当前Matrix状态入栈
        glPushMatrix();
    	if(2==iProjectionMode)
    		glTranslatef(0.0f, 0.0f, -250.0f);	//透视投影为便于观察整个坐标系往内移动250个单位
        //坐标系绕x轴旋转xRotAngle
        glRotatef(xRotAngle,1.0f,0.0f,0.0f);
        //坐标系绕y轴旋转yRotAngle
        glRotatef(yRotAngle,0.0f,1.0f,0.0f);
    	//进行平滑处理 
    	glEnable(GL_POINT_SMOOTH);
    	glHint(GL_POINT_SMOOTH,GL_NICEST);
    	glEnable(GL_LINE_SMOOTH);
    	glHint(GL_LINE_SMOOTH,GL_NICEST);
    	glEnable(GL_BLEND);
    	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    
    	if(1==iCoordinateaxis){
    		glColor3f(1.0f,1.0f,1.0f);
    		glBegin(GL_LINES);
    			glVertex3f(-90.0f,00.0f,0.0f);
    			glVertex3f(90.0f,0.0f,0.0f);
    			glVertex3f(0.0f,-90.0f,0.0f);
    			glVertex3f(0.0f,90.0f,0.0f);
    			glVertex3f(0.0f,0.0f,-90.0f);
    			glVertex3f(0.0f,0.0f,90.0f);
    		glEnd();
    
    		glPushMatrix();
    		glTranslatef(90.0f,0.0f,0.0f);
    		glRotatef(90.0f,0.0f,1.0f,0.0f);
    		glutSolidCone(3,6,10,10);
    		glPopMatrix();
    
    		glPushMatrix();
    		glTranslatef(0.0f,90.0f,0.0f);
    		glRotatef(-90.0f,1.0f,0.0f,0.0f);
    		glutSolidCone(3,6,10,10);
    		glPopMatrix();
    
    		glPushMatrix();
    		glTranslatef(0.0f,0.0f,90.0f);
    		glRotatef(70.0f,0.0f,0.0f,1.0f);
    		glutSolidCone(3,6,10,10);
    		glPopMatrix();
    	}
    	glColor3f(0.5f,0.5f,1.0f);
    	memset(transformationMatrix,0,sizeof(transformationMatrix));
    	//打印变换矩阵
    	if(2==iPrintMatrix){
    		printf("--------------------------------------
    ");
    		for(int i=0;i<4;i++){
    			for(int j=0;j<4;j++){
    				printf("%9.6f ",transformationMatrix[4*j+i]);
    			}
    			printf("
    ");
    		}
    	}
        m3dRotationMatrix44(transformationMatrix, m3dDegToRad(yRot), 0.0f, 1.0f, 0.0f);
    	//transformationMatrix[12]、transformationMatrix[13] = 0.0f、transformationMatrix[14] = 0.0f是平移参数,分别代表x、y、 z轴的偏移参数。
    	//transformationMatrix[15]代表缩放为原来的1/transformationMatrix[15]
    	transformationMatrix[12] = 0.0f;
    	transformationMatrix[13] = 0.0f;
    	transformationMatrix[14] = 0.0f;     
    	transformationMatrix[15] = 2.0f; 
    	//打印变换矩阵
    	if(2==iPrintMatrix){
    		printf("--------------------------------------
    ");
    		for(int i=0;i<4;i++){
    			for(int j=0;j<4;j++){
    				printf("%9.6f ",transformationMatrix[4*j+i]);
    			}
    			printf("
    ");
    		}
    	}
        
    	if(1==iCylinder)
    		DrawCylinder(transformationMatrix);
    	else
    		DrawTorus(transformationMatrix);
    
        //恢复压入栈的Matrix
        glPopMatrix();
        //交换两个缓冲区的指针
        glutSwapBuffers();
    }
    
    //设置Redering State 
    void setupRederingState(void){
        glEnable(GL_DEPTH_TEST);	//使能深度测试
        glFrontFace(GL_CCW);		//多边形逆时针方向为正面
        //glEnable(GL_CULL_FACE);		//不显示背面
    	glPolygonMode(GL_FRONT_AND_BACK,GL_LINE);//背面正面均使用线填充
    
        //设置清理颜色为黑色
        glClearColor(0.0f,0.0,0.0,1.0f);
        //设置绘画颜色为绿色
        glColor3f(1.0f,1.0f,0.0f);
    	//使能深度测试
    	glEnable(GL_DEPTH_TEST);
    	//获取受支持的点大小范围
    	glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
    	//获取受支持的点大小增量
    	glGetFloatv(GL_POINT_SIZE_GRANULARITY,&step);
    	//获取最大的投影矩阵堆栈深度
    	glGetIntegerv( GL_MAX_PROJECTION_STACK_DEPTH,&iMaxProjectionStackDepth);
    	//获取最大的模型视图矩阵堆栈深度
    	glGetIntegerv( GL_MAX_MODELVIEW_STACK_DEPTH,&iMaxModeviewStackDepth);
    	//获取最大的纹理矩阵堆栈深度
    	glGetIntegerv( GL_MAX_TEXTURE_STACK_DEPTH,&iMaxTextureStackDepth);
    	printf("point size range:%f-%f
    ",sizes[0],sizes[1]);
    	printf("point step:%f
    ",step);
    	printf("iMaxProjectionStackDepth=%d
    ",iMaxProjectionStackDepth);
    	printf("iMaxModeviewStackDepth=%d
    ",iMaxModeviewStackDepth);
    	printf("iMaxTextureStackDepth=%d
    ",iMaxTextureStackDepth);
    }
    
    //窗口大小变化回调函数
    void changSize(GLint w,GLint h){
        //横宽比率
        GLfloat ratio;
        //设置坐标系为x(-100.0f,100.0f)、y(-100.0f,100.0f)、z(-100.0f,100.0f)
        GLfloat coordinatesize=100.0f;
        //窗口宽高为零直接返回
        if((w==0)||(h==0))
            return;
        //设置视口和窗口大小一致
        glViewport(0,0,w,h);
        //对投影矩阵应用随后的矩阵操作
        glMatrixMode(GL_PROJECTION);
        //重置当前指定的矩阵为单位矩阵 
        glLoadIdentity();
        ratio=(GLfloat)w/(GLfloat)h;
        //正交投影
    	if(1==iProjectionMode){
    		printf("glOrtho
    ");
    		if(w<h)
    			glOrtho(-coordinatesize,coordinatesize,-coordinatesize/ratio,coordinatesize/ratio,-coordinatesize,coordinatesize);
    		else
    			glOrtho(-coordinatesize*ratio,coordinatesize*ratio,-coordinatesize,coordinatesize,-coordinatesize,coordinatesize);
    		//当前矩阵设置为模型视图矩阵
    		glMatrixMode(GL_MODELVIEW);
    		//重置当前指定的矩阵为单位矩阵 
    		glLoadIdentity();
    	}
    	else{
    		printf("gluPerspective
    ");
    		gluPerspective(45,ratio,10.0f,500.0f);
    		//当前矩阵设置为模型视图矩阵
    		glMatrixMode(GL_MODELVIEW);
    		//重置当前指定的矩阵为单位矩阵 
    		glLoadIdentity();
    	}
    
    }
    
    //按键输入处理回调函数
    void specialKey(int key,int x,int y){
    
        if(key==GLUT_KEY_UP){
            xRotAngle-=5.0f;
        }
        else if(key==GLUT_KEY_DOWN){
            xRotAngle+=5.0f;
        }
        else if(key==GLUT_KEY_LEFT){
            yRotAngle-=5.0f;
        }
        else if(key==GLUT_KEY_RIGHT){
            yRotAngle+=5.0f;
        }
        //重新绘制
        glutPostRedisplay();
    }
    
    void timerFunc(int value)
    {
    	glutPostRedisplay();
    	glutTimerFunc(10, timerFunc, 1);
    }
    
    int main(int argc, char* argv[])
    {
    	//菜单
    	GLint iMainMenu;
    	GLint iCoordinateaxisMenu;
    	GLint iOrthoOrPerspectMenu;
    	GLint iPrintmatrix;
    	GLint iCylinderOrTorus;
    	//初始化glut 
        glutInit(&argc,argv);
        //使用双缓冲区、深度缓冲区。
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH);
        //获取系统的宽像素
        SCREEN_WIDTH=glutGet(GLUT_SCREEN_WIDTH);
        //获取系统的高像素
        SCREEN_HEIGHT=glutGet(GLUT_SCREEN_HEIGHT);
    	//创建窗口,窗口名字为OpenGL Transform Demo
        glutCreateWindow("OpenGL Transform Demo");
        //设置窗口大小
        glutReshapeWindow(windowWidth,windowHeight);
        //窗口居中显示
        glutPositionWindow((SCREEN_WIDTH-windowWidth)/2,(SCREEN_HEIGHT-windowHeight)/2);
        //窗口大小变化时的处理函数
        glutReshapeFunc(changSize);
        //设置显示回调函数 
        glutDisplayFunc(renderScreen);
        //设置按键输入处理回调函数
        glutSpecialFunc(specialKey);
    	//菜单回调函数
    	iCoordinateaxisMenu=glutCreateMenu(processMenu);
    	//添加菜单
    	glutAddMenuEntry("Display coordinate axis",1);
    	glutAddMenuEntry("Don't dispaly coordinate axis",2);
    	iOrthoOrPerspectMenu=glutCreateMenu(processMenu);
    	glutAddMenuEntry("Ortho",3);
    	glutAddMenuEntry("Perspect",4);
    	iPrintmatrix=glutCreateMenu(processMenu);
    	glutAddMenuEntry("Don't print Matrix",5);
    	glutAddMenuEntry("Print Matrix",6);
    	iCylinderOrTorus=glutCreateMenu(processMenu);
    	glutAddMenuEntry("Cylinder",7);
    	glutAddMenuEntry("Torus",8);
    	iMainMenu=glutCreateMenu(processMenu);
    	glutAddSubMenu("Whether Display coordinate axis",iCoordinateaxisMenu);
    	glutAddSubMenu("Ortho Or Perspect",iOrthoOrPerspectMenu);
    	glutAddSubMenu("Whether Print Matrix",iPrintmatrix);
    	glutAddSubMenu("Cylinder or torus",iCylinderOrTorus);
    	//将菜单榜定到鼠标右键上
    	glutAttachMenu(GLUT_RIGHT_BUTTON);
    	glutTimerFunc(10,timerFunc, 1);
        //设置全局渲染参数
        setupRederingState();
        glutMainLoop();
    	
        return 0;
    }


    math3d.h

    // Math3d.h
    // Header file for the Math3d library. The C-Runtime has math.h, this file and the
    // accompanying math.c are meant to suppliment math.h by adding geometry/math routines
    // useful for graphics, simulation, and physics applications (3D stuff).
    // Richard S. Wright Jr.
    #ifndef _MATH3D_LIBRARY__
    #define _MATH3D_LIBRARY__
    
    #include <math.h>
    #include <memory.h>
    
    ///////////////////////////////////////////////////////////////////////////////
    // Data structures and containers
    // Much thought went into how these are declared. Many libraries declare these
    // as structures with x, y, z data members. However structure alignment issues
    // could limit the portability of code based on such structures, or the binary
    // compatibility of data files (more likely) that contain such structures across
    // compilers/platforms. Arrays are always tightly packed, and are more efficient 
    // for moving blocks of data around (usually).
    typedef float	M3DVector3f[3];		// Vector of three floats (x, y, z)
    typedef double	M3DVector3d[3];		// Vector of three doubles (x, y, z)
    
    typedef float M3DVector4f[4];		// Lesser used... Do we really need these?
    typedef double M3DVector4d[4];		// Yes, occasionaly
    
    typedef float M3DVector2f[2];		// 3D points = 3D Vectors, but we need a 
    typedef double M3DVector2d[2];		// 2D representations sometimes... (x,y) order
    
    
    
    // 3x3 matrix - column major. X vector is 0, 1, 2, etc.
    //		0	3	6	
    //		1	4	7
    //		2	5	8
    typedef float M3DMatrix33f[9];		// A 3 x 3 matrix, column major (floats) - OpenGL Style
    typedef double M3DMatrix33d[9];		// A 3 x 3 matrix, column major (doubles) - OpenGL Style
    
    
    // 4x4 matrix - column major. X vector is 0, 1, 2, etc.
    //	0	4	8	12
    //	1	5	9	13
    //	2	6	10	14
    //	3	7	11	15
    typedef float M3DMatrix44f[16];		// A 4 X 4 matrix, column major (floats) - OpenGL style
    typedef double M3DMatrix44d[16];	// A 4 x 4 matrix, column major (doubles) - OpenGL style
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Useful constants
    #define M3D_PI (3.14159265358979323846)
    #define M3D_2PI (2.0 * M3D_PI)
    #define M3D_PI_DIV_180 (0.017453292519943296)
    #define M3D_INV_PI_DIV_180 (57.2957795130823229)
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Useful shortcuts and macros
    // Radians are king... but we need a way to swap back and forth
    #define m3dDegToRad(x)	((x)*M3D_PI_DIV_180)
    #define m3dRadToDeg(x)	((x)*M3D_INV_PI_DIV_180)
    
    // Hour angles
    #define m3dHrToDeg(x)	((x) * (1.0 / 15.0))
    #define m3dHrToRad(x)	m3dDegToRad(m3dHrToDeg(x))
    
    #define m3dDegToHr(x)	((x) * 15.0))
    #define m3dRadToHr(x)	m3dDegToHr(m3dRadToDeg(x))
    
    
    // Returns the same number if it is a power of
    // two. Returns a larger integer if it is not a 
    // power of two. The larger integer is the next
    // highest power of two.
    inline unsigned int m3dIsPOW2(unsigned int iValue)
        {
        unsigned int nPow2 = 1;
        
        while(iValue > nPow2)
            nPow2 = (nPow2 << 1);
        
        return nPow2;
        }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Inline accessor functions for people who just can't count to 3 - Vectors
    #define	m3dGetVectorX(v) (v[0])
    #define m3dGetVectorY(v) (v[1])
    #define m3dGetVectorZ(v) (v[2])
    #define m3dGetVectorW(v) (v[3])
    
    #define m3dSetVectorX(v, x)	((v)[0] = (x))
    #define m3dSetVectorY(v, y)	((v)[1] = (y))
    #define m3dSetVectorZ(v, z)	((v)[2] = (z))
    #define m3dSetVectorW(v, w)	((v)[3] = (w))
    
    ///////////////////////////////////////////////////////////////////////////////
    // Inline vector functions
    // Load Vector with (x, y, z, w).
    inline void m3dLoadVector2(M3DVector2f v, float x, float y)
        { v[0] = x; v[1] = y; }
    inline void m3dLoadVector2(M3DVector2d v, float x, float y)
        { v[0] = x; v[1] = y; }
    inline void m3dLoadVector3(M3DVector3f v, float x, float y, float z) 
    	{ v[0] = x; v[1] = y; v[2] = z; }
    inline void m3dLoadVector3(M3DVector3d v, double x, double y, double z)
    	{ v[0] = x; v[1] = y; v[2] = z; }
    inline void m3dLoadVector4(M3DVector4f v, float x, float y, float z, float w) 
    	{ v[0] = x; v[1] = y; v[2] = z; v[3] = w;}
    inline void m3dLoadVector4(M3DVector4d v, double x, double y, double z, double w)
    	{ v[0] = x; v[1] = y; v[2] = z; v[3] = w;}
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // Copy vector src into vector dst
    inline void	m3dCopyVector2(M3DVector2f dst, const M3DVector2f src) { memcpy(dst, src, sizeof(M3DVector2f)); }
    inline void	m3dCopyVector2(M3DVector2d dst, const M3DVector2d src) { memcpy(dst, src, sizeof(M3DVector2d)); }
    
    inline void	m3dCopyVector3(M3DVector3f dst, const M3DVector3f src) { memcpy(dst, src, sizeof(M3DVector3f)); }
    inline void	m3dCopyVector3(M3DVector3d dst, const M3DVector3d src) { memcpy(dst, src, sizeof(M3DVector3d)); }
    
    inline void	m3dCopyVector4(M3DVector4f dst, const M3DVector4f src) { memcpy(dst, src, sizeof(M3DVector4f)); }
    inline void	m3dCopyVector4(M3DVector4d dst, const M3DVector4d src) { memcpy(dst, src, sizeof(M3DVector4d)); }
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // Add Vectors (r, a, b) r = a + b
    inline void m3dAddVectors2(M3DVector2f r, const M3DVector2f a, const M3DVector2f b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1];  }
    inline void m3dAddVectors2(M3DVector2d r, const M3DVector2d a, const M3DVector2d b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1];  }
    
    inline void m3dAddVectors3(M3DVector3f r, const M3DVector3f a, const M3DVector3f b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; }
    inline void m3dAddVectors3(M3DVector3d r, const M3DVector3d a, const M3DVector3d b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; }
    
    inline void m3dAddVectors4(M3DVector4f r, const M3DVector4f a, const M3DVector4f b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; r[3] = a[3] + b[3]; }
    inline void m3dAddVectors4(M3DVector4d r, const M3DVector4d a, const M3DVector4d b)
    	{ r[0] = a[0] + b[0];	r[1] = a[1] + b[1]; r[2] = a[2] + b[2]; r[3] = a[3] + b[3]; }
    
    ////////////////////////////////////////////////////////////////////////////////
    // Subtract Vectors (r, a, b) r = a - b
    inline void m3dSubtractVectors2(M3DVector2f r, const M3DVector2f a, const M3DVector2f b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1];  }
    inline void m3dSubtractVectors2(M3DVector2d r, const M3DVector2d a, const M3DVector2d b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; }
    
    inline void m3dSubtractVectors3(M3DVector3f r, const M3DVector3f a, const M3DVector3f b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; }
    inline void m3dSubtractVectors3(M3DVector3d r, const M3DVector3d a, const M3DVector3d b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; }
    
    inline void m3dSubtractVectors4(M3DVector4f r, const M3DVector4f a, const M3DVector4f b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; r[3] = a[3] - b[3]; }
    inline void m3dSubtractVectors4(M3DVector4d r, const M3DVector4d a, const M3DVector4d b)
    	{ r[0] = a[0] - b[0]; r[1] = a[1] - b[1]; r[2] = a[2] - b[2]; r[3] = a[3] - b[3]; }
    
    
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // Scale Vectors (in place)
    inline void m3dScaleVector2(M3DVector2f v, float scale) 
    	{ v[0] *= scale; v[1] *= scale; }
    inline void m3dScaleVector2(M3DVector2d v, double scale) 
    	{ v[0] *= scale; v[1] *= scale; }
    
    inline void m3dScaleVector3(M3DVector3f v, float scale) 
    	{ v[0] *= scale; v[1] *= scale; v[2] *= scale; }
    inline void m3dScaleVector3(M3DVector3d v, double scale) 
    	{ v[0] *= scale; v[1] *= scale; v[2] *= scale; }
    
    inline void m3dScaleVector4(M3DVector4f v, float scale) 
    	{ v[0] *= scale; v[1] *= scale; v[2] *= scale; v[3] *= scale; }
    inline void m3dScaleVector4(M3DVector4d v, double scale) 
    	{ v[0] *= scale; v[1] *= scale; v[2] *= scale; v[3] *= scale; }
    
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Cross Product
    // u x v = result
    // We only need one version for floats, and one version for doubles. A 3 component
    // vector fits in a 4 component vector. If  M3DVector4d or M3DVector4f are passed
    // we will be OK because 4th component is not used.
    inline void m3dCrossProduct(M3DVector3f result, const M3DVector3f u, const M3DVector3f v)
    	{
    	result[0] = u[1]*v[2] - v[1]*u[2];
    	result[1] = -u[0]*v[2] + v[0]*u[2];
    	result[2] = u[0]*v[1] - v[0]*u[1];
    	}
    
    inline void m3dCrossProduct(M3DVector3d result, const M3DVector3d u, const M3DVector3d v)
    	{
    	result[0] = u[1]*v[2] - v[1]*u[2];
    	result[1] = -u[0]*v[2] + v[0]*u[2];
    	result[2] = u[0]*v[1] - v[0]*u[1];
    	}
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Dot Product, only for three component vectors
    // return u dot v
    inline float m3dDotProduct(const M3DVector3f u, const M3DVector3f v)
    	{ return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; }
    
    inline double m3dDotProduct(const M3DVector3d u, const M3DVector3d v)
    	{ return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; }
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Angle between vectors, only for three component vectors. Angle is in radians...
    inline float m3dGetAngleBetweenVectors(const M3DVector3f u, const M3DVector3f v)
        {
        float dTemp = m3dDotProduct(u, v);
        return float(acos(double(dTemp)));
        }
    
    inline double m3dGetAngleBetweenVectors(const M3DVector3d u, const M3DVector3d v)
        {
        double dTemp = m3dDotProduct(u, v);
        return acos(dTemp);
        }
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Get Square of a vectors length
    // Only for three component vectors
    inline float m3dGetVectorLengthSquared(const M3DVector3f u)
    	{ return (u[0] * u[0]) + (u[1] * u[1]) + (u[2] * u[2]); }
    
    inline double m3dGetVectorLengthSquared(const M3DVector3d u)
    	{ return (u[0] * u[0]) + (u[1] * u[1]) + (u[2] * u[2]); }
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Get lenght of vector
    // Only for three component vectors.
    inline float m3dGetVectorLength(const M3DVector3f u)
    	{ return float(sqrt(double(m3dGetVectorLengthSquared(u)))); }
    
    inline double m3dGetVectorLength(const M3DVector3d u)
    	{ return sqrt(m3dGetVectorLengthSquared(u)); }
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Normalize a vector
    // Scale a vector to unit length. Easy, just scale the vector by it's length
    inline void m3dNormalizeVector(M3DVector3f u)
    	{ m3dScaleVector3(u, 1.0f / m3dGetVectorLength(u)); }
    
    inline void m3dNormalizeVector(M3DVector3d u)
    	{ m3dScaleVector3(u, 1.0 / m3dGetVectorLength(u)); }
    
    
    //////////////////////////////////////////////////////////////////////////////////////
    // Get the distance between two points. The distance between two points is just
    // the magnitude of the difference between two vectors
    // Located in math.cpp
    float m3dGetDistanceSquared(const M3DVector3f u, const M3DVector3f v);
    double m3dGetDistanceSquared(const M3DVector3d u, const M3DVector3d v);
    
    inline double m3dGetDistance(const M3DVector3d u, const M3DVector3d v)
    { return sqrt(m3dGetDistanceSquared(u, v)); }
    
    inline float m3dGetDistance(const M3DVector3f u, const M3DVector3f v)
    { return float(sqrt(m3dGetDistanceSquared(u, v))); }
    
    inline float m3dGetMagnitudeSquared(const M3DVector3f u) { return u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; }
    inline double m3dGetMagnitudeSquared(const M3DVector3d u) { return u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; }
    
    inline float m3dGetMagnitude(const M3DVector3f u) { return float(sqrt(m3dGetMagnitudeSquared(u))); }
    inline double m3dGetMagnitude(const M3DVector3d u) { return sqrt(m3dGetMagnitudeSquared(u)); }
    
    	
    
    //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Matrix functions
    // Both floating point and double precision 3x3 and 4x4 matricies are supported.
    // No support is included for arbitrarily dimensioned matricies on purpose, since
    // the 3x3 and 4x4 matrix routines are the most common for the purposes of this
    // library. Matrices are column major, like OpenGL matrices.
    // Unlike the vector functions, some of these are going to have to not be inlined,
    // although many will be.
    
    
    // Copy Matrix
    // Brain-dead memcpy
    inline void m3dCopyMatrix33(M3DMatrix33f dst, const M3DMatrix33f src)
    	{ memcpy(dst, src, sizeof(M3DMatrix33f)); }
    
    inline void m3dCopyMatrix33(M3DMatrix33d dst, const M3DMatrix33d src)
    	{ memcpy(dst, src, sizeof(M3DMatrix33d)); }
    
    inline void m3dCopyMatrix44(M3DMatrix44f dst, const M3DMatrix44f src)
    	{ memcpy(dst, src, sizeof(M3DMatrix44f)); }
    
    inline void m3dCopyMatrix44(M3DMatrix44d dst, const M3DMatrix44d src)
    	{ memcpy(dst, src, sizeof(M3DMatrix44d)); }
    
    // LoadIdentity
    // Implemented in Math3d.cpp
    void m3dLoadIdentity33(M3DMatrix33f m);
    void m3dLoadIdentity33(M3DMatrix33d m);
    void m3dLoadIdentity44(M3DMatrix44f m);
    void m3dLoadIdentity44(M3DMatrix44d m);
    
    /////////////////////////////////////////////////////////////////////////////
    // Get/Set Column.
    inline void m3dGetMatrixColumn33(M3DVector3f dst, const M3DMatrix33f src, int column)
    	{ memcpy(dst, src + (3 * column), sizeof(float) * 3); }
    
    inline void m3dGetMatrixColumn33(M3DVector3d dst, const M3DMatrix33d src, int column)
    	{ memcpy(dst, src + (3 * column), sizeof(double) * 3); }
    
    inline void m3dSetMatrixColumn33(M3DMatrix33f dst, const M3DVector3f src, int column)
    	{ memcpy(dst + (3 * column), src, sizeof(float) * 3); }
    
    inline void m3dSetMatrixColumn33(M3DMatrix33d dst, const M3DVector3d src, int column)
    	{ memcpy(dst + (3 * column), src, sizeof(double) * 3); }
    
    inline void m3dGetMatrixColumn44(M3DVector4f dst, const M3DMatrix44f src, int column)
    	{ memcpy(dst, src + (4 * column), sizeof(float) * 4); }
    
    inline void m3dGetMatrixColumn44(M3DVector4d dst, const M3DMatrix44d src, int column)
    	{ memcpy(dst, src + (4 * column), sizeof(double) * 4); }
    
    inline void m3dSetMatrixColumn44(M3DMatrix44f dst, const M3DVector4f src, int column)
    	{ memcpy(dst + (4 * column), src, sizeof(float) * 4); }
    
    inline void m3dSetMatrixColumn44(M3DMatrix44d dst, const M3DVector4d src, int column)
    	{ memcpy(dst + (4 * column), src, sizeof(double) * 4); }
    
    
    // Get/Set row purposely omitted... use the functions below.
    // I don't think row vectors are useful for column major ordering...
    // If I'm wrong, add them later.
    
    
    //////////////////////////////////////////////////////////////////////////////
    // Get/Set RowCol - Remember column major ordering...
    // Provides for element addressing
    inline void m3dSetMatrixRowCol33(M3DMatrix33f m, int row, int col, float value)
    	{ m[(col * 3) + row] = value; }
    
    inline float m3dGetMatrixRowCol33(const M3DMatrix33f m, int row, int col)
    	{ return m[(col * 3) + row]; }
    
    inline void m3dSetMatrixRowCol33(M3DMatrix33d m, int row, int col, double value)
    	{ m[(col * 3) + row] = value; }
    
    inline double m3dGetMatrixRowCol33(const M3DMatrix33d m, int row, int col)
    	{ return m[(col * 3) + row]; }
    
    inline void m3dSetMatrixRowCol44(M3DMatrix44f m, int row, int col, float value)
    	{ m[(col * 4) + row] = value; }
    
    inline float m3dGetMatrixRowCol44(const M3DMatrix44f m, int row, int col)
    	{ return m[(col * 4) + row]; }
    
    inline void m3dSetMatrixRowCol44(M3DMatrix44d m, int row, int col, double value)
    	{ m[(col * 4) + row] = value; }
    
    inline double m3dGetMatrixRowCol44(const M3DMatrix44d m, int row, int col)
    	{ return m[(col * 4) + row]; }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Extract a rotation matrix from a 4x4 matrix
    // Extracts the rotation matrix (3x3) from a 4x4 matrix
    inline void m3dExtractRotation(M3DMatrix33f dst, const M3DMatrix44f src)
    	{	
    	memcpy(dst, src, sizeof(float) * 3); // X column
    	memcpy(dst + 3, src + 4, sizeof(float) * 3); // Y column
    	memcpy(dst + 6, src + 8, sizeof(float) * 3); // Z column
    	}
    
    // Ditto above, but for doubles
    inline void m3dExtractRotation(M3DMatrix33d dst, const M3DMatrix44d src)
    	{
    	memcpy(dst, src, sizeof(double) * 3); // X column
    	memcpy(dst + 3, src + 4, sizeof(double) * 3); // Y column
    	memcpy(dst + 6, src + 8, sizeof(double) * 3); // Z column
    	}
    
    // Inject Rotation (3x3) into a full 4x4 matrix...
    inline void m3dInjectRotation(M3DMatrix44f dst, const M3DMatrix33f src)
    	{
    	memcpy(dst, src, sizeof(float) * 4);
    	memcpy(dst + 4, src + 4, sizeof(float) * 4);
    	memcpy(dst + 8, src + 8, sizeof(float) * 4);
    	}
    
    // Ditto above for doubles
    inline void m3dInjectRotation(M3DMatrix44d dst, const M3DMatrix33d src)
    	{
    	memcpy(dst, src, sizeof(double) * 4);
    	memcpy(dst + 4, src + 4, sizeof(double) * 4);
    	memcpy(dst + 8, src + 8, sizeof(double) * 4);
    	}
    
    
    ////////////////////////////////////////////////////////////////////////////////
    // MultMatrix
    // Implemented in Math.cpp
    void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
    void m3dMatrixMultiply44(M3DMatrix44d product, const M3DMatrix44d a, const M3DMatrix44d b);
    void m3dMatrixMultiply33(M3DMatrix33f product, const M3DMatrix33f a, const M3DMatrix33f b);
    void m3dMatrixMultiply33(M3DMatrix33d product, const M3DMatrix33d a, const M3DMatrix33d b);
    
    
    // Transform - Does rotation and translation via a 4x4 matrix. Transforms
    // a point or vector.
    // By-the-way __inline means I'm asking the compiler to do a cost/benefit analysis. If 
    // these are used frequently, they may not be inlined to save memory. I'm experimenting
    // with this....
    __inline void m3dTransformVector3(M3DVector3f vOut, const M3DVector3f v, const M3DMatrix44f m)
        {
        vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] *  v[2] + m[12];// * v[3];	 
        vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] *  v[2] + m[13];// * v[3];	
        vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14];// * v[3];	
    	//vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
        }
    
    // Ditto above, but for doubles
    __inline void m3dTransformVector3(M3DVector3d vOut, const M3DVector3d v, const M3DMatrix44d m)
        {
        vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] *  v[2] + m[12];// * v[3];	 
        vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] *  v[2] + m[13];// * v[3];	
        vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14];// * v[3];	
    	//vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
        }
    
    __inline void m3dTransformVector4(M3DVector4f vOut, const M3DVector4f v, const M3DMatrix44f m)
        {
        vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] *  v[2] + m[12] * v[3];	 
        vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] *  v[2] + m[13] * v[3];	
        vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];	
    	vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
        }
    
    // Ditto above, but for doubles
    __inline void m3dTransformVector4(M3DVector4d vOut, const M3DVector4d v, const M3DMatrix44d m)
        {
        vOut[0] = m[0] * v[0] + m[4] * v[1] + m[8] *  v[2] + m[12] * v[3];	 
        vOut[1] = m[1] * v[0] + m[5] * v[1] + m[9] *  v[2] + m[13] * v[3];	
        vOut[2] = m[2] * v[0] + m[6] * v[1] + m[10] * v[2] + m[14] * v[3];	
    	vOut[3] = m[3] * v[0] + m[7] * v[1] + m[11] * v[2] + m[15] * v[3];
        }
    
    
    
    // Just do the rotation, not the translation... this is usually done with a 3x3
    // Matrix.
    __inline void m3dRotateVector(M3DVector3f vOut, const M3DVector3f p, const M3DMatrix33f m)
    	{
        vOut[0] = m[0] * p[0] + m[3] * p[1] + m[6] * p[2];	
        vOut[1] = m[1] * p[0] + m[4] * p[1] + m[7] * p[2];	
        vOut[2] = m[2] * p[0] + m[5] * p[1] + m[8] * p[2];	
    	}
    
    // Ditto above, but for doubles
    __inline void m3dRotateVector(M3DVector3d vOut, const M3DVector3d p, const M3DMatrix33d m)
    	{
        vOut[0] = m[0] * p[0] + m[3] * p[1] + m[6] * p[2];	
        vOut[1] = m[1] * p[0] + m[4] * p[1] + m[7] * p[2];	
        vOut[2] = m[2] * p[0] + m[5] * p[1] + m[8] * p[2];	
    	}
    
    
    // Scale a matrix (I don't beleive in Scaling matricies ;-)
    // Yes, it's faster to loop backwards... These could be 
    // unrolled... but eh... if you find this is a bottleneck,
    // then you should unroll it yourself
    inline void m3dScaleMatrix33(M3DMatrix33f m, float scale)
    { for(int i = 8; i >=0; i--) m[i] *= scale; }
    
    inline void m3dScaleMatrix33(M3DMatrix33d m, double scale)
    { for(int i = 8; i >=0; i--) m[i] *= scale; }
    
    inline void m3dScaleMatrix44(M3DMatrix44f m, float scale)
    { for(int i = 15; i >=0; i--) m[i] *= scale; }
    
    inline void m3dScaleMatrix44(M3DMatrix44d m, double scale)
    { for(int i = 15; i >=0; i--) m[i] *= scale; }
    
    
    // Create a Rotation matrix
    // Implemented in math.cpp
    void m3dRotationMatrix33(M3DMatrix33f m, float angle, float x, float y, float z);
    void m3dRotationMatrix33(M3DMatrix33d m, double angle, double x, double y, double z);
    void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z);
    void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z);
    
    // Create a Translation matrix. Only 4x4 matrices have translation components
    inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)
    { m3dLoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z; }
    
    inline void m3dTranslationMatrix44(M3DMatrix44d m, double x, double y, double z)
    { m3dLoadIdentity44(m); m[12] = x; m[13] = y; m[14] = z; }
    
    
    // Translate matrix. Only 4x4 matrices supported
    inline void m3dTranslateMatrix44(M3DMatrix44f m, float x, float y, float z)
    { m[12] += x; m[13] += y; m[14] += z; }
    
    inline void m3dTranslateMatrix44(M3DMatrix44d m, double x, double y, double z)
    { m[12] += x; m[13] += y; m[14] += z; }
    
    
    // Scale matrix. Only 4x4 matrices supported
    inline void m3dScaleMatrix44(M3DMatrix44f m, float x, float y, float z)
    { m[0] *= x; m[5] *= y; m[10] *= z; }
    
    inline void m3dScaleMatrix44(M3DMatrix44d m, double x, double y, double z)
    { m[0] *= x; m[5] *= y; m[10] *= z; }
    
    
    // Transpose/Invert - Only 4x4 matricies supported
    #define TRANSPOSE44(dst, src)            
    {                                        
        for (int j = 0; j < 4; j++)          
        {                                    
            for (int i = 0; i < 4; i++)      
            {                                
                dst[(j*4)+i] = src[(i*4)+j]; 
            }                                
        }                                    
    }
    inline void m3dTransposeMatrix44(M3DMatrix44f dst, const M3DMatrix44f src)
    { TRANSPOSE44(dst, src); }
    inline void m3dTransposeMatrix44(M3DMatrix44d dst, const M3DMatrix44d src)
    { TRANSPOSE44(dst, src); }
    bool m3dInvertMatrix44(M3DMatrix44f dst, const M3DMatrix44f src);
    bool m3dInvertMatrix44(M3DMatrix44d dst, const M3DMatrix44d src);
    
    ///////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////
    // Other Miscellaneous functions
    
    // Find a normal from three points
    // Implemented in math3d.cpp
    void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, 
    							const M3DVector3f point3);
    void m3dFindNormal(M3DVector3d result, const M3DVector3d point1, const M3DVector3d point2, 
    							const M3DVector3d point3);
    
    
    
    // Calculates the signed distance of a point to a plane
    inline float m3dGetDistanceToPlane(const M3DVector3f point, const M3DVector4f plane)
               { return point[0]*plane[0] + point[1]*plane[1] + point[2]*plane[2] + plane[3]; }
    
    inline double m3dGetDistanceToPlane(const M3DVector3d point, const M3DVector4d plane)
               { return point[0]*plane[0] + point[1]*plane[1] + point[2]*plane[2] + plane[3]; }
    
    
    // Get plane equation from three points and a normal
    void m3dGetPlaneEquation(M3DVector4f planeEq, const M3DVector3f p1, const M3DVector3f p2, const M3DVector3f p3);
    void m3dGetPlaneEquation(M3DVector4d planeEq, const M3DVector3d p1, const M3DVector3d p2, const M3DVector3d p3);
    
    // Determine if a ray intersects a sphere
    double m3dRaySphereTest(const M3DVector3d point, const M3DVector3d ray, const M3DVector3d sphereCenter, double sphereRadius);
    float m3dRaySphereTest(const M3DVector3f point, const M3DVector3f ray, const M3DVector3f sphereCenter, float sphereRadius);
    
    // Etc. etc.
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    // Faster (and more robust) replacements for gluProject
    void m3dProjectXY( M3DVector2f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn);    
    void m3dProjectXYZ(M3DVector3f vPointOut, const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn);
    
    
    
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // This function does a three dimensional Catmull-Rom "spline" interpolation between p1 and p2
    void m3dCatmullRom(M3DVector3f vOut, M3DVector3f vP0, M3DVector3f vP1, M3DVector3f vP2, M3DVector3f vP3, float t);
    void m3dCatmullRom(M3DVector3d vOut, M3DVector3d vP0, M3DVector3d vP1, M3DVector3d vP2, M3DVector3d vP3, double t);
    
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // Compare floats and doubles... 
    inline bool m3dCloseEnough(float fCandidate, float fCompare, float fEpsilon)
        {
        return (fabs(fCandidate - fCompare) < fEpsilon);
        }
        
    inline bool m3dCloseEnough(double dCandidate, double dCompare, double dEpsilon)
        {
        return (fabs(dCandidate - dCompare) < dEpsilon);
        }    
     
    ////////////////////////////////////////////////////////////////////////////
    // Used for normal mapping. Finds the tangent bases for a triangle...
    // Only a floating point implementation is provided.
    void m3dCalculateTangentBasis(const M3DVector3f pvTriangle[3], const M3DVector2f pvTexCoords[3], const M3DVector3f N, M3DVector3f vTangent);
    
    ////////////////////////////////////////////////////////////////////////////
    // Smoothly step between 0 and 1 between edge1 and edge 2
    double m3dSmoothStep(double edge1, double edge2, double x);
    float m3dSmoothStep(float edge1, float edge2, float x);
    
    /////////////////////////////////////////////////////////////////////////////
    // Planar shadow Matrix
    void m3dMakePlanarShadowMatrix(M3DMatrix44d proj, const M3DVector4d planeEq, const M3DVector3d vLightPos);
    void m3dMakePlanarShadowMatrix(M3DMatrix44f proj, const M3DVector4f planeEq, const M3DVector3f vLightPos);
    
    double m3dClosestPointOnRay(M3DVector3d vPointOnRay, const M3DVector3d vRayOrigin, const M3DVector3d vUnitRayDir, 
    							const M3DVector3d vPointInSpace);
    
    float m3dClosestPointOnRay(M3DVector3f vPointOnRay, const M3DVector3f vRayOrigin, const M3DVector3f vUnitRayDir, 
    							const M3DVector3f vPointInSpace);
    
    #endif


    Math3d.cpp

    // Math3d.c
    // Implementation of non-inlined functions in the Math3D Library
    // Richard S. Wright Jr.
    
    // These are pretty portable
    #include "stdafx.h"
    #include <math.h>
    #include "math3d.h"
    
    
    ////////////////////////////////////////////////////////////
    // LoadIdentity
    // For 3x3 and 4x4 float and double matricies.
    // 3x3 float
    void m3dLoadIdentity33(M3DMatrix33f m)
    	{
    	// Don't be fooled, this is still column major
    	static M3DMatrix33f	identity = { 1.0f, 0.0f, 0.0f ,
    									 0.0f, 1.0f, 0.0f,
    									 0.0f, 0.0f, 1.0f };
    
    	memcpy(m, identity, sizeof(M3DMatrix33f));
    	}
    
    // 3x3 double
    void m3dLoadIdentity33(M3DMatrix33d m)
    	{
    	// Don't be fooled, this is still column major
    	static M3DMatrix33d	identity = { 1.0, 0.0, 0.0 ,
    									 0.0, 1.0, 0.0,
    									 0.0, 0.0, 1.0 };
    
    	memcpy(m, identity, sizeof(M3DMatrix33d));
    	}
    
    // 4x4 float
    void m3dLoadIdentity44(M3DMatrix44f m)
    	{
    	// Don't be fooled, this is still column major
    	static M3DMatrix44f	identity = { 1.0f, 0.0f, 0.0f, 0.0f,
    									 0.0f, 1.0f, 0.0f, 0.0f,
    									 0.0f, 0.0f, 1.0f, 0.0f,
    									 0.0f, 0.0f, 0.0f, 1.0f };
    
    	memcpy(m, identity, sizeof(M3DMatrix44f));
    	}
    
    // 4x4 double
    void m3dLoadIdentity44(M3DMatrix44d m)
    	{
    	static M3DMatrix44d	identity = { 1.0, 0.0, 0.0, 0.0,
    									 0.0, 1.0, 0.0, 0.0,
    									 0.0, 0.0, 1.0, 0.0,
    									 0.0, 0.0, 0.0, 1.0 };
    
    	memcpy(m, identity, sizeof(M3DMatrix44d));
    	}
    
    
    ////////////////////////////////////////////////////////////////////////
    // Return the square of the distance between two points
    // Should these be inlined...?
    float m3dGetDistanceSquared(const M3DVector3f u, const M3DVector3f v)
    	{
    	float x = u[0] - v[0];
    	x = x*x;
    	
    	float y = u[1] - v[1];
    	y = y*y;
    
    	float z = u[2] - v[2];
    	z = z*z;
    
    	return (x + y + z);
        }
    
    // Ditto above, but for doubles
    double m3dGetDistanceSquared(const M3DVector3d u, const M3DVector3d v)
    	{
    	double x = u[0] - v[0];
    	x = x*x;
    	
    	double y = u[1] - v[1];
    	y = y*y;
    
    	double z = u[2] - v[2];
    	z = z*z;
    
    	return (x + y + z);
    	}
    
    #define A(row,col)  a[(col<<2)+row]
    #define B(row,col)  b[(col<<2)+row]
    #define P(row,col)  product[(col<<2)+row]
    
    ///////////////////////////////////////////////////////////////////////////////
    // Multiply two 4x4 matricies
    void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b )
    {
    	for (int i = 0; i < 4; i++) {
    		float ai0=A(i,0),  ai1=A(i,1),  ai2=A(i,2),  ai3=A(i,3);
    		P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
    		P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
    		P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
    		P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
    	}
    }
    
    // Ditto above, but for doubles
    void m3dMatrixMultiply(M3DMatrix44d product, const M3DMatrix44d a, const M3DMatrix44d b )
    {
    	for (int i = 0; i < 4; i++) {
    		double ai0=A(i,0),  ai1=A(i,1),  ai2=A(i,2),  ai3=A(i,3);
    		P(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0);
    		P(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1);
    		P(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2);
    		P(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3);
    	}
    }
    #undef A
    #undef B
    #undef P
    
    
    #define A33(row,col)  a[(col*3)+row]
    #define B33(row,col)  b[(col*3)+row]
    #define P33(row,col)  product[(col*3)+row]
    
    ///////////////////////////////////////////////////////////////////////////////
    // Multiply two 3x3 matricies
    void m3dMatrixMultiply33(M3DMatrix33f product, const M3DMatrix33f a, const M3DMatrix33f b )
    {
    	for (int i = 0; i < 3; i++) {
    		float ai0=A33(i,0), ai1=A33(i,1),  ai2=A33(i,2);
    		P33(i,0) = ai0 * B33(0,0) + ai1 * B33(1,0) + ai2 * B33(2,0);
    		P33(i,1) = ai0 * B33(0,1) + ai1 * B33(1,1) + ai2 * B33(2,1);
    		P33(i,2) = ai0 * B33(0,2) + ai1 * B33(1,2) + ai2 * B33(2,2);
    	}
    }
    
    // Ditto above, but for doubles
    void m3dMatrixMultiply44(M3DMatrix33d product, const M3DMatrix33d a, const M3DMatrix33d b )
    {
    	for (int i = 0; i < 3; i++) {
    		double ai0=A33(i,0),  ai1=A33(i,1),  ai2=A33(i,2);
    		P33(i,0) = ai0 * B33(0,0) + ai1 * B33(1,0) + ai2 * B33(2,0);
    		P33(i,1) = ai0 * B33(0,1) + ai1 * B33(1,1) + ai2 * B33(2,1);
    		P33(i,2) = ai0 * B33(0,2) + ai1 * B33(1,2) + ai2 * B33(2,2);
    	}
    }
    
    #undef A33
    #undef B33
    #undef P33
    
    #define M33(row,col)  m[col*3+row]
    
    ///////////////////////////////////////////////////////////////////////////////
    // Creates a 3x3 rotation matrix, takes radians NOT degrees
    void m3dRotationMatrix33(M3DMatrix33f m, float angle, float x, float y, float z)
    	{
    	
    	float mag, s, c;
    	float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
    
    	s = float(sin(angle));
    	c = float(cos(angle));
    
    	mag = float(sqrt( x*x + y*y + z*z ));
    
    	// Identity matrix
    	if (mag == 0.0f) {
    		m3dLoadIdentity33(m);
    		return;
    	}
    
    	// Rotation matrix is normalized
    	x /= mag;
    	y /= mag;
    	z /= mag;
    
    
    
    	xx = x * x;
    	yy = y * y;
    	zz = z * z;
    	xy = x * y;
    	yz = y * z;
    	zx = z * x;
    	xs = x * s;
    	ys = y * s;
    	zs = z * s;
    	one_c = 1.0f - c;
    
    	M33(0,0) = (one_c * xx) + c;
    	M33(0,1) = (one_c * xy) - zs;
    	M33(0,2) = (one_c * zx) + ys;
    
    	M33(1,0) = (one_c * xy) + zs;
    	M33(1,1) = (one_c * yy) + c;
    	M33(1,2) = (one_c * yz) - xs;
    
    	M33(2,0) = (one_c * zx) - ys;
    	M33(2,1) = (one_c * yz) + xs;
    	M33(2,2) = (one_c * zz) + c;
    	}
    
    #undef M33
    
    ///////////////////////////////////////////////////////////////////////////////
    // Creates a 4x4 rotation matrix, takes radians NOT degrees
    void m3dRotationMatrix44(M3DMatrix44f m, float angle, float x, float y, float z)
    	{
    	float mag, s, c;
    	float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
    
    	s = float(sin(angle));
    	c = float(cos(angle));
    
    	mag = float(sqrt( x*x + y*y + z*z ));
    
    	// Identity matrix
    	if (mag == 0.0f) {
    		m3dLoadIdentity44(m);
    		return;
    	}
    
    	// Rotation matrix is normalized
    	x /= mag;
    	y /= mag;
    	z /= mag;
    
        #define M(row,col)  m[col*4+row]
    
    	xx = x * x;
    	yy = y * y;
    	zz = z * z;
    	xy = x * y;
    	yz = y * z;
    	zx = z * x;
    	xs = x * s;
    	ys = y * s;
    	zs = z * s;
    	one_c = 1.0f - c;
    
    	M(0,0) = (one_c * xx) + c;
    	M(0,1) = (one_c * xy) - zs;
    	M(0,2) = (one_c * zx) + ys;
    	M(0,3) = 0.0f;
    
    	M(1,0) = (one_c * xy) + zs;
    	M(1,1) = (one_c * yy) + c;
    	M(1,2) = (one_c * yz) - xs;
    	M(1,3) = 0.0f;
    
    	M(2,0) = (one_c * zx) - ys;
    	M(2,1) = (one_c * yz) + xs;
    	M(2,2) = (one_c * zz) + c;
    	M(2,3) = 0.0f;
    
    	M(3,0) = 0.0f;
    	M(3,1) = 0.0f;
    	M(3,2) = 0.0f;
    	M(3,3) = 1.0f;
    
        #undef M
    	}
    
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Ditto above, but for doubles
    void m3dRotationMatrix33(M3DMatrix33d m, double angle, double x, double y, double z)
    	{
    	double mag, s, c;
    	double xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
    
    	s = sin(angle);
    	c = cos(angle);
    
    	mag = sqrt( x*x + y*y + z*z );
    
    	// Identity matrix
    	if (mag == 0.0) {
    		m3dLoadIdentity33(m);
    		return;
    	}
    
    	// Rotation matrix is normalized
    	x /= mag;
    	y /= mag;
    	z /= mag;
    
        #define M(row,col)  m[col*3+row]
    
    	xx = x * x;
    	yy = y * y;
    	zz = z * z;
    	xy = x * y;
    	yz = y * z;
    	zx = z * x;
    	xs = x * s;
    	ys = y * s;
    	zs = z * s;
    	one_c = 1.0 - c;
    
    	M(0,0) = (one_c * xx) + c;
    	M(0,1) = (one_c * xy) - zs;
    	M(0,2) = (one_c * zx) + ys;
    
    	M(1,0) = (one_c * xy) + zs;
    	M(1,1) = (one_c * yy) + c;
    	M(1,2) = (one_c * yz) - xs;
    
    	M(2,0) = (one_c * zx) - ys;
    	M(2,1) = (one_c * yz) + xs;
    	M(2,2) = (one_c * zz) + c;
    
        #undef M
    	}
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Creates a 4x4 rotation matrix, takes radians NOT degrees
    void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z)
    	{
    	double mag, s, c;
    	double xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c;
    
    	s = sin(angle);
    	c = cos(angle);
    
    	mag = sqrt( x*x + y*y + z*z );
    
    	// Identity matrix
    	if (mag == 0.0) {
    		m3dLoadIdentity44(m);
    		return;
    	}
    
    	// Rotation matrix is normalized
    	x /= mag;
    	y /= mag;
    	z /= mag;
    
        #define M(row,col)  m[col*4+row]
    
    	xx = x * x;
    	yy = y * y;
    	zz = z * z;
    	xy = x * y;
    	yz = y * z;
    	zx = z * x;
    	xs = x * s;
    	ys = y * s;
    	zs = z * s;
    	one_c = 1.0f - c;
    
    	M(0,0) = (one_c * xx) + c;
    	M(0,1) = (one_c * xy) - zs;
    	M(0,2) = (one_c * zx) + ys;
    	M(0,3) = 0.0;
    
    	M(1,0) = (one_c * xy) + zs;
    	M(1,1) = (one_c * yy) + c;
    	M(1,2) = (one_c * yz) - xs;
    	M(1,3) = 0.0;
    
    	M(2,0) = (one_c * zx) - ys;
    	M(2,1) = (one_c * yz) + xs;
    	M(2,2) = (one_c * zz) + c;
    	M(2,3) = 0.0;
    
    	M(3,0) = 0.0;
    	M(3,1) = 0.0;
    	M(3,2) = 0.0;
    	M(3,3) = 1.0;
    
        #undef M
      }
    
    // Lifted from Mesa
    /*
     * Compute inverse of 4x4 transformation matrix.
     * Code contributed by Jacques Leroy jle@star.be
     * Return GL_TRUE for success, GL_FALSE for failure (singular matrix)
     */
    bool m3dInvertMatrix44(M3DMatrix44f dst, const M3DMatrix44f src )
        {
        #define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; }
        #define MAT(m,r,c) (m)[(c)*4+(r)]
    
    	float wtmp[4][8];
    	float m0, m1, m2, m3, s;
    	float *r0, *r1, *r2, *r3;
    
    	r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];
    
    	r0[0] = MAT(src,0,0), r0[1] = MAT(src,0,1),
    	r0[2] = MAT(src,0,2), r0[3] = MAT(src,0,3),
    	r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,
    
    	r1[0] = MAT(src,1,0), r1[1] = MAT(src,1,1),
    	r1[2] = MAT(src,1,2), r1[3] = MAT(src,1,3),
    	r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,
    
    	r2[0] = MAT(src,2,0), r2[1] = MAT(src,2,1),
    	r2[2] = MAT(src,2,2), r2[3] = MAT(src,2,3),
    	r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,
    
    	r3[0] = MAT(src,3,0), r3[1] = MAT(src,3,1),
    	r3[2] = MAT(src,3,2), r3[3] = MAT(src,3,3),
    	r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;
    
    	/* choose pivot - or die */
    	if (fabs(r3[0])>fabs(r2[0])) SWAP_ROWS(r3, r2);
    	if (fabs(r2[0])>fabs(r1[0])) SWAP_ROWS(r2, r1);
    	if (fabs(r1[0])>fabs(r0[0])) SWAP_ROWS(r1, r0);
    	if (0.0 == r0[0])  return false;
    
    	/* eliminate first variable     */
    	m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0];
    	s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s;
    	s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s;
    	s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s;
    	s = r0[4];
    	if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; }
    	s = r0[5];
    	if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; }
    	s = r0[6];
    	if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; }
    	s = r0[7];
    	if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; }
    
    	/* choose pivot - or die */
    	if (fabs(r3[1])>fabs(r2[1])) SWAP_ROWS(r3, r2);
    	if (fabs(r2[1])>fabs(r1[1])) SWAP_ROWS(r2, r1);
    	if (0.0 == r1[1])  return false;
    
    	/* eliminate second variable */
    	m2 = r2[1]/r1[1]; m3 = r3[1]/r1[1];
    	r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2];
    	r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3];
    	s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; }
    	s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; }
    	s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; }
    	s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; }
    
    	/* choose pivot - or die */
    	if (fabs(r3[2])>fabs(r2[2])) SWAP_ROWS(r3, r2);
    	if (0.0 == r2[2])  return false;
    
    	/* eliminate third variable */
    	m3 = r3[2]/r2[2];
    	r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
    	r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6],
    	r3[7] -= m3 * r2[7];
    
    	/* last check */
    	if (0.0 == r3[3]) return false;
    
    	s = 1.0f/r3[3];              /* now back substitute row 3 */
    	r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s;
    
    	m2 = r2[3];                 /* now back substitute row 2 */
    	s  = 1.0f/r2[2];
    	r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
    	r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
    	m1 = r1[3];
    	r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
    	r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
    	m0 = r0[3];
    	r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
    	r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
    
    	m1 = r1[2];                 /* now back substitute row 1 */
    	s  = 1.0f/r1[1];
    	r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
    	r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
    	m0 = r0[2];
    	r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
    	r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
    
    	m0 = r0[1];                 /* now back substitute row 0 */
    	s  = 1.0f/r0[0];
    	r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
    	r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
    
    	MAT(dst,0,0) = r0[4]; MAT(dst,0,1) = r0[5],
    	MAT(dst,0,2) = r0[6]; MAT(dst,0,3) = r0[7],
    	MAT(dst,1,0) = r1[4]; MAT(dst,1,1) = r1[5],
    	MAT(dst,1,2) = r1[6]; MAT(dst,1,3) = r1[7],
    	MAT(dst,2,0) = r2[4]; MAT(dst,2,1) = r2[5],
    	MAT(dst,2,2) = r2[6]; MAT(dst,2,3) = r2[7],
    	MAT(dst,3,0) = r3[4]; MAT(dst,3,1) = r3[5],
    	MAT(dst,3,2) = r3[6]; MAT(dst,3,3) = r3[7]; 
    
    	return true;
    
    	#undef MAT
    	#undef SWAP_ROWS
    	}
    
    
    // Ditto above, but for doubles
    bool m3dInvertMatrix44(M3DMatrix44d dst, const M3DMatrix44d src)
    	{
        #define SWAP_ROWS(a, b) { double *_tmp = a; (a)=(b); (b)=_tmp; }
        #define MAT(m,r,c) (m)[(c)*4+(r)]
    
    	double wtmp[4][8];
    	double m0, m1, m2, m3, s;
    	double *r0, *r1, *r2, *r3;
    
    	r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3];
    
    	r0[0] = MAT(src,0,0), r0[1] = MAT(src,0,1),
    	r0[2] = MAT(src,0,2), r0[3] = MAT(src,0,3),
    	r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0,
    
    	r1[0] = MAT(src,1,0), r1[1] = MAT(src,1,1),
    	r1[2] = MAT(src,1,2), r1[3] = MAT(src,1,3),
    	r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0,
    
    	r2[0] = MAT(src,2,0), r2[1] = MAT(src,2,1),
    	r2[2] = MAT(src,2,2), r2[3] = MAT(src,2,3),
    	r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0,
    
    	r3[0] = MAT(src,3,0), r3[1] = MAT(src,3,1),
    	r3[2] = MAT(src,3,2), r3[3] = MAT(src,3,3),
    	r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0;
    
    	// choose pivot - or die 
    	if (fabs(r3[0])>fabs(r2[0])) SWAP_ROWS(r3, r2);
    	if (fabs(r2[0])>fabs(r1[0])) SWAP_ROWS(r2, r1);
    	if (fabs(r1[0])>fabs(r0[0])) SWAP_ROWS(r1, r0);
    	if (0.0 == r0[0])  return false;
    
    	// eliminate first variable     
    	m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0];
    	s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s;
    	s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s;
    	s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s;
    	s = r0[4];
    	if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; }
    	s = r0[5];
    	if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; }
    	s = r0[6];
    	if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; }
    	s = r0[7];
    	if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; }
    
    	// choose pivot - or die 
    	if (fabs(r3[1])>fabs(r2[1])) SWAP_ROWS(r3, r2);
    	if (fabs(r2[1])>fabs(r1[1])) SWAP_ROWS(r2, r1);
    	if (0.0 == r1[1])  return false;
    
    	// eliminate second variable 
    	m2 = r2[1]/r1[1]; m3 = r3[1]/r1[1];
    	r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2];
    	r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3];
    	s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; }
    	s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; }
    	s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; }
    	s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; }
    
    	// choose pivot - or die 
    	if (fabs(r3[2])>fabs(r2[2])) SWAP_ROWS(r3, r2);
    	if (0.0 == r2[2])  return false;
    
    	// eliminate third variable 
    	m3 = r3[2]/r2[2];
    	r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4],
    	r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6],
    	r3[7] -= m3 * r2[7];
    
    	// last check 
    	if (0.0 == r3[3]) return false;
    
    	s = 1.0f/r3[3];              // now back substitute row 3 
    	r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s;
    
    	m2 = r2[3];                 // now back substitute row 2 
    	s  = 1.0f/r2[2];
    	r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2),
    	r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2);
    	m1 = r1[3];
    	r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1,
    	r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1;
    	m0 = r0[3];
    	r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0,
    	r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0;
    
    	m1 = r1[2];                 // now back substitute row 1 
    	s  = 1.0f/r1[1];
    	r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1),
    	r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1);
    	m0 = r0[2];
    	r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0,
    	r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0;
    
    	m0 = r0[1];                 // now back substitute row 0 
    	s  = 1.0f/r0[0];
    	r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0),
    	r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0);
    
    	MAT(dst,0,0) = r0[4]; MAT(dst,0,1) = r0[5],
    	MAT(dst,0,2) = r0[6]; MAT(dst,0,3) = r0[7],
    	MAT(dst,1,0) = r1[4]; MAT(dst,1,1) = r1[5],
    	MAT(dst,1,2) = r1[6]; MAT(dst,1,3) = r1[7],
    	MAT(dst,2,0) = r2[4]; MAT(dst,2,1) = r2[5],
    	MAT(dst,2,2) = r2[6]; MAT(dst,2,3) = r2[7],
    	MAT(dst,3,0) = r3[4]; MAT(dst,3,1) = r3[5],
    	MAT(dst,3,2) = r3[6]; MAT(dst,3,3) = r3[7]; 
    
    	return true;
    
    	#undef MAT
    	#undef SWAP_ROWS
    
    	return true;
    	}
    
    
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // Get Window coordinates, discard Z...
    void m3dProjectXY(const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn, M3DVector2f vPointOut)
    	{
        M3DVector4f vBack, vForth;
    
    	memcpy(vBack, vPointIn, sizeof(float)*3);
    	vBack[3] = 1.0f;
        
        m3dTransformVector4(vForth, vBack, mModelView);
        m3dTransformVector4(vBack, vForth, mProjection);
        
        if(!m3dCloseEnough(vBack[3], 0.0f, 0.000001f)) {
            float div = 1.0f / vBack[3];
            vBack[0] *= div;
            vBack[1] *= div;
            }
    
    
        vPointOut[0] = vBack[0] * 0.5f + 0.5f;
        vPointOut[1] = vBack[1] * 0.5f + 0.5f;
    
        /* Map x,y to viewport */
        vPointOut[0] = (vPointOut[0] * iViewPort[2]) + iViewPort[0];
        vPointOut[1] = (vPointOut[1] * iViewPort[3]) + iViewPort[1];    
    	}
        
    ///////////////////////////////////////////////////////////////////////////////////////
    // Get window coordinates, we also want Z....
    void m3dProjectXYZ(const M3DMatrix44f mModelView, const M3DMatrix44f mProjection, const int iViewPort[4], const M3DVector3f vPointIn, M3DVector3f vPointOut)
    	{
        M3DVector4f vBack, vForth;
    
    	memcpy(vBack, vPointIn, sizeof(float)*3);
    	vBack[3] = 1.0f;
        
        m3dTransformVector4(vForth, vBack, mModelView);
        m3dTransformVector4(vBack, vForth, mProjection);
        
        if(!m3dCloseEnough(vBack[3], 0.0f, 0.000001f)) {
            float div = 1.0f / vBack[3];
            vBack[0] *= div;
            vBack[1] *= div;
            vBack[2] *= div; 
            }
    
        vPointOut[0] = vBack[0] * 0.5f + 0.5f;
        vPointOut[1] = vBack[1] * 0.5f + 0.5f;
        vPointOut[2] = vBack[2] * 0.5f + 0.5f;
    
        /* Map x,y to viewport */
        vPointOut[0] = (vPointOut[0] * iViewPort[2]) + iViewPort[0];
        vPointOut[1] = (vPointOut[1] * iViewPort[3]) + iViewPort[1];
    	}
    
    
    
    ///////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////
    // Misc. Utilities
    ///////////////////////////////////////////////////////////////////////////////
    // Calculates the normal of a triangle specified by the three points
    // p1, p2, and p3. Each pointer points to an array of three floats. The
    // triangle is assumed to be wound counter clockwise. 
    void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, 
    							const M3DVector3f point3)
    	{
    	M3DVector3f v1,v2;		// Temporary vectors
    
    	// Calculate two vectors from the three points. Assumes counter clockwise
    	// winding!
    	v1[0] = point1[0] - point2[0];
    	v1[1] = point1[1] - point2[1];
    	v1[2] = point1[2] - point2[2];
    
    	v2[0] = point2[0] - point3[0];
    	v2[1] = point2[1] - point3[1];
    	v2[2] = point2[2] - point3[2];
    
    	// Take the cross product of the two vectors to get
    	// the normal vector.
    	m3dCrossProduct(result, v1, v2);
    	}
    
    
    
    // Ditto above, but for doubles
    void m3dFindNormal(M3DVector3d result, const M3DVector3d point1, const M3DVector3d point2, 
    							const M3DVector3d point3)
    	{
    	M3DVector3d v1,v2;		// Temporary vectors
    
    	// Calculate two vectors from the three points. Assumes counter clockwise
    	// winding!
    	v1[0] = point1[0] - point2[0];
    	v1[1] = point1[1] - point2[1];
    	v1[2] = point1[2] - point2[2];
    
    	v2[0] = point2[0] - point3[0];
    	v2[1] = point2[1] - point3[1];
    	v2[2] = point2[2] - point3[2];
    
    	// Take the cross product of the two vectors to get
    	// the normal vector.
    	m3dCrossProduct(result, v1, v2);
    	}
    
    
    
    /////////////////////////////////////////////////////////////////////////////////////////
    // Calculate the plane equation of the plane that the three specified points lay in. The
    // points are given in clockwise winding order, with normal pointing out of clockwise face
    // planeEq contains the A,B,C, and D of the plane equation coefficients
    void m3dGetPlaneEquation(M3DVector4f planeEq, const M3DVector3f p1, const M3DVector3f p2, const M3DVector3f p3)
    {
        // Get two vectors... do the cross product
        M3DVector3f v1, v2;
    
        // V1 = p3 - p1
        v1[0] = p3[0] - p1[0];
        v1[1] = p3[1] - p1[1];
        v1[2] = p3[2] - p1[2];
    
        // V2 = P2 - p1
        v2[0] = p2[0] - p1[0];
        v2[1] = p2[1] - p1[1];
        v2[2] = p2[2] - p1[2];
    
        // Unit normal to plane - Not sure which is the best way here
        m3dCrossProduct(planeEq, v1, v2);
        m3dNormalizeVector(planeEq);
        // Back substitute to get D
        planeEq[3] = -(planeEq[0] * p3[0] + planeEq[1] * p3[1] + planeEq[2] * p3[2]);
    }
    
    
    // Ditto above, but for doubles
    void m3dGetPlaneEquation(M3DVector4d planeEq, const M3DVector3d p1, const M3DVector3d p2, const M3DVector3d p3)
    {
        // Get two vectors... do the cross product
        M3DVector3d v1, v2;
    
        // V1 = p3 - p1
        v1[0] = p3[0] - p1[0];
        v1[1] = p3[1] - p1[1];
        v1[2] = p3[2] - p1[2];
    
        // V2 = P2 - p1
        v2[0] = p2[0] - p1[0];
        v2[1] = p2[1] - p1[1];
        v2[2] = p2[2] - p1[2];
    
        // Unit normal to plane - Not sure which is the best way here
        m3dCrossProduct(planeEq, v1, v2);
        m3dNormalizeVector(planeEq);
        // Back substitute to get D
        planeEq[3] = -(planeEq[0] * p3[0] + planeEq[1] * p3[1] + planeEq[2] * p3[2]);
    }
    
    
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // This function does a three dimensional Catmull-Rom curve interpolation. Pass four points, and a
    // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points.
    // Coded by RSW
    // http://www.mvps.org/directx/articles/catmull/
    void m3dCatmullRom3(M3DVector3f vOut, M3DVector3f vP0, M3DVector3f vP1, M3DVector3f vP2, M3DVector3f vP3, float t)
        {
        // Unrolled loop to speed things up a little bit...
        float t2 = t * t;
        float t3 = t2 * t;
    
        // X    
        vOut[0] = 0.5f * ( ( 2.0f * vP1[0]) +
                           (-vP0[0] + vP2[0]) * t +
                           (2.0f * vP0[0] - 5.0f *vP1[0] + 4.0f * vP2[0] - vP3[0]) * t2 +
                           (-vP0[0] + 3.0f*vP1[0] - 3.0f *vP2[0] + vP3[0]) * t3);
        // Y
        vOut[1] = 0.5f * ( ( 2.0f * vP1[1]) +
                           (-vP0[1] + vP2[1]) * t +
                           (2.0f * vP0[1] - 5.0f *vP1[1] + 4.0f * vP2[1] - vP3[1]) * t2 +
                           (-vP0[1] + 3.0f*vP1[1] - 3.0f *vP2[1] + vP3[1]) * t3);
    
        // Z
        vOut[2] = 0.5f * ( ( 2.0f * vP1[2]) +
                           (-vP0[2] + vP2[2]) * t +
                           (2.0f * vP0[2] - 5.0f *vP1[2] + 4.0f * vP2[2] - vP3[2]) * t2 +
                           (-vP0[2] + 3.0f*vP1[2] - 3.0f *vP2[2] + vP3[2]) * t3);
        }
    
    
    //////////////////////////////////////////////////////////////////////////////////////////////////
    // This function does a three dimensional Catmull-Rom curve interpolation. Pass four points, and a
    // floating point number between 0.0 and 1.0. The curve is interpolated between the middle two points.
    // Coded by RSW
    // http://www.mvps.org/directx/articles/catmull/
    void m3dCatmullRom3(M3DVector3d vOut, M3DVector3d vP0, M3DVector3d vP1, M3DVector3d vP2, M3DVector3d vP3, double t)
        {
        // Unrolled loop to speed things up a little bit...
        double t2 = t * t;
        double t3 = t2 * t;
    
        // X    
        vOut[0] = 0.5 * ( ( 2.0 * vP1[0]) +
                           (-vP0[0] + vP2[0]) * t +
                           (2.0 * vP0[0] - 5.0 *vP1[0] + 4.0 * vP2[0] - vP3[0]) * t2 +
                           (-vP0[0] + 3.0*vP1[0] - 3.0 *vP2[0] + vP3[0]) * t3);
        // Y
        vOut[1] = 0.5 * ( ( 2.0 * vP1[1]) +
                           (-vP0[1] + vP2[1]) * t +
                           (2.0 * vP0[1] - 5.0 *vP1[1] + 4.0 * vP2[1] - vP3[1]) * t2 +
                           (-vP0[1] + 3*vP1[1] - 3.0 *vP2[1] + vP3[1]) * t3);
    
        // Z
        vOut[2] = 0.5 * ( ( 2.0 * vP1[2]) +
                           (-vP0[2] + vP2[2]) * t +
                           (2.0 * vP0[2] - 5.0 *vP1[2] + 4.0 * vP2[2] - vP3[2]) * t2 +
                           (-vP0[2] + 3.0*vP1[2] - 3.0 *vP2[2] + vP3[2]) * t3);
        }
    
    
    ///////////////////////////////////////////////////////////////////////////////
    // Determine if the ray (starting at point) intersects the sphere centered at
    // sphereCenter with radius sphereRadius
    // Return value is < 0 if the ray does not intersect
    // Return value is 0.0 if ray is tangent
    // Positive value is distance to the intersection point
    // Algorithm from "3D Math Primer for Graphics and Game Development"
    double m3dRaySphereTest(const M3DVector3d point, const M3DVector3d ray, const M3DVector3d sphereCenter, double sphereRadius)
    	{
    	//m3dNormalizeVector(ray);	// Make sure ray is unit length
    
    	M3DVector3d rayToCenter;	// Ray to center of sphere
    	rayToCenter[0] =  sphereCenter[0] - point[0];	
    	rayToCenter[1] =  sphereCenter[1] - point[1];
    	rayToCenter[2] =  sphereCenter[2] - point[2];
    	
    	// Project rayToCenter on ray to test
    	double a = m3dDotProduct(rayToCenter, ray);
    	
    	// Distance to center of sphere
    	double distance2 = m3dDotProduct(rayToCenter, rayToCenter);	// Or length
    
    	
    	double dRet = (sphereRadius * sphereRadius) - distance2 + (a*a);
    	
    	if(dRet > 0.0)			// Return distance to intersection
    		dRet = a - sqrt(dRet);
    	
    	return dRet;
    	}
    
    ///////////////////////////////////////////////////////////////////////////////
    // Determine if the ray (starting at point) intersects the sphere centered at
    // ditto above, but uses floating point math
    float m3dRaySphereTest(const M3DVector3f point, const M3DVector3f ray, const M3DVector3f sphereCenter, float sphereRadius)
    	{
    	//m3dNormalizeVectorf(ray);	// Make sure ray is unit length
    
    	M3DVector3f rayToCenter;	// Ray to center of sphere
    	rayToCenter[0] =  sphereCenter[0] - point[0];	
    	rayToCenter[1] =  sphereCenter[1] - point[1];
    	rayToCenter[2] =  sphereCenter[2] - point[2];
    	
    	// Project rayToCenter on ray to test
    	float a = m3dDotProduct(rayToCenter, ray);
    	
    	// Distance to center of sphere
    	float distance2 = m3dDotProduct(rayToCenter, rayToCenter);	// Or length
    	
    	float dRet = (sphereRadius * sphereRadius) - distance2 + (a*a);
    	
    	if(dRet > 0.0)			// Return distance to intersection
    		dRet = a - sqrtf(dRet);
    	
    	return dRet;
    	}
    
    
    ///////////////////////////////////////////////////////////////////////////////////////////////////
    // Calculate the tangent basis for a triangle on the surface of a model
    // This vector is needed for most normal mapping shaders 
    void m3dCalculateTangentBasis(const M3DVector3f vTriangle[3], const M3DVector2f vTexCoords[3], const M3DVector3f N, M3DVector3f vTangent)
    {
        M3DVector3f dv2v1, dv3v1;
        float dc2c1t, dc2c1b, dc3c1t, dc3c1b;
        float M;
        
        m3dSubtractVectors3(dv2v1, vTriangle[1], vTriangle[0]);
        m3dSubtractVectors3(dv3v1, vTriangle[2], vTriangle[0]);
        
        dc2c1t = vTexCoords[1][0] - vTexCoords[0][0];
        dc2c1b = vTexCoords[1][1] - vTexCoords[0][1];
        dc3c1t = vTexCoords[2][0] - vTexCoords[0][0];
        dc3c1b = vTexCoords[2][1] - vTexCoords[0][1];
        
        M = (dc2c1t * dc3c1b) - (dc3c1t * dc2c1b);
        M = 1.0f / M;
        
        m3dScaleVector3(dv2v1, dc3c1b);
        m3dScaleVector3(dv3v1, dc2c1b);
        
        m3dSubtractVectors3(vTangent, dv2v1, dv3v1);
        m3dScaleVector3(vTangent, M);  // This potentially changes the direction of the vector
        m3dNormalizeVector(vTangent);
    
        M3DVector3f B;
        m3dCrossProduct(B, N, vTangent);
        m3dCrossProduct(vTangent, B, N);
        m3dNormalizeVector(vTangent);
        }
    	
    	
    ////////////////////////////////////////////////////////////////////////////
    // Smoothly step between 0 and 1 between edge1 and edge 2
    double m3dSmoothStep(double edge1, double edge2, double x)
        {
        double t;
        t = (x - edge1) / (edge2 - edge1);
        if(t > 1.0)
            t = 1.0;
            
        if(t < 0.0)
            t = 0.0f;
            
        return t * t * ( 3.0 - 2.0 * t);
        }
    
    ////////////////////////////////////////////////////////////////////////////
    // Smoothly step between 0 and 1 between edge1 and edge 2
    float m3dSmoothStep(float edge1, float edge2, float x)
        {
        float t;
        t = (x - edge1) / (edge2 - edge1);
        if(t > 1.0f)
            t = 1.0f;
            
        if(t < 0.0)
            t = 0.0f;
            
        return t * t * ( 3.0f - 2.0f * t);
        }
    	
    	
    
    ///////////////////////////////////////////////////////////////////////////
    // Creae a projection to "squish" an object into the plane.
    // Use m3dGetPlaneEquationf(planeEq, point1, point2, point3);
    // to get a plane equation.
    void m3dMakePlanarShadowMatrix(M3DMatrix44f proj, const M3DVector4f planeEq, const M3DVector3f vLightPos)
    	{
    	// These just make the code below easier to read. They will be 
    	// removed by the optimizer.	
    	float a = planeEq[0];
    	float b = planeEq[1];
    	float c = planeEq[2];
    	float d = planeEq[3];
    
    	float dx = -vLightPos[0];
    	float dy = -vLightPos[1];
    	float dz = -vLightPos[2];
    
    	// Now build the projection matrix
    	proj[0] = b * dy + c * dz;
    	proj[1] = -a * dy;
    	proj[2] = -a * dz;
    	proj[3] = 0.0;
    
    	proj[4] = -b * dx;
    	proj[5] = a * dx + c * dz;
    	proj[6] = -b * dz;
    	proj[7] = 0.0;
    
    	proj[8] = -c * dx;
    	proj[9] = -c * dy;
    	proj[10] = a * dx + b * dy;
    	proj[11] = 0.0;
    
    	proj[12] = -d * dx;
    	proj[13] = -d * dy;
    	proj[14] = -d * dz;
    	proj[15] = a * dx + b * dy + c * dz;
    	// Shadow matrix ready
    	}
    	
    	
    ///////////////////////////////////////////////////////////////////////////
    // Creae a projection to "squish" an object into the plane.
    // Use m3dGetPlaneEquationd(planeEq, point1, point2, point3);
    // to get a plane equation.
    void m3dMakePlanarShadowMatrix(M3DMatrix44d proj, const M3DVector4d planeEq, const M3DVector3f vLightPos)
    	{
    	// These just make the code below easier to read. They will be 
    	// removed by the optimizer.	
    	double a = planeEq[0];
    	double b = planeEq[1];
    	double c = planeEq[2];
    	double d = planeEq[3];
    
    	double dx = -vLightPos[0];
    	double dy = -vLightPos[1];
    	double dz = -vLightPos[2];
    
    	// Now build the projection matrix
    	proj[0] = b * dy + c * dz;
    	proj[1] = -a * dy;
    	proj[2] = -a * dz;
    	proj[3] = 0.0;
    
    	proj[4] = -b * dx;
    	proj[5] = a * dx + c * dz;
    	proj[6] = -b * dz;
    	proj[7] = 0.0;
    
    	proj[8] = -c * dx;
    	proj[9] = -c * dy;
    	proj[10] = a * dx + b * dy;
    	proj[11] = 0.0;
    
    	proj[12] = -d * dx;
    	proj[13] = -d * dy;
    	proj[14] = -d * dz;
    	proj[15] = a * dx + b * dy + c * dz;
    	// Shadow matrix ready
    	}
    
    
    /////////////////////////////////////////////////////////////////////////////
    // I want to know the point on a ray, closest to another given point in space.
    // As a bonus, return the distance squared of the two points.
    // In: vRayOrigin is the origin of the ray.
    // In: vUnitRayDir is the unit vector of the ray
    // In: vPointInSpace is the point in space
    // Out: vPointOnRay is the poing on the ray closest to vPointInSpace
    // Return: The square of the distance to the ray
    double m3dClosestPointOnRay(M3DVector3d vPointOnRay, const M3DVector3d vRayOrigin, const M3DVector3d vUnitRayDir, 
    											const M3DVector3d vPointInSpace)
    	{
    	M3DVector3d v;
    	m3dSubtractVectors3(v, vPointInSpace, vRayOrigin);
    	
    	double t = m3dDotProduct(vUnitRayDir, v);
    	
    	// This is the point on the ray
    	vPointOnRay[0] = vRayOrigin[0] + (t * vUnitRayDir[0]);
    	vPointOnRay[1] = vRayOrigin[1] + (t * vUnitRayDir[1]);
    	vPointOnRay[2] = vRayOrigin[2] + (t * vUnitRayDir[2]);
    	
    	return m3dGetDistanceSquared(vPointOnRay, vPointInSpace);
    	}
    
    // ditto above... but with floats
    float m3dClosestPointOnRay(M3DVector3f vPointOnRay, const M3DVector3f vRayOrigin, const M3DVector3f vUnitRayDir, 
    							 const M3DVector3f vPointInSpace)
    	{
    	M3DVector3f v;
    	m3dSubtractVectors3(v, vPointInSpace, vRayOrigin);
    	
    	float t = m3dDotProduct(vUnitRayDir, v);
    	
    	// This is the point on the ray
    	vPointOnRay[0] = vRayOrigin[0] + (t * vUnitRayDir[0]);
    	vPointOnRay[1] = vRayOrigin[1] + (t * vUnitRayDir[1]);
    	vPointOnRay[2] = vRayOrigin[2] + (t * vUnitRayDir[2]);
    	
    	return m3dGetDistanceSquared(vPointOnRay, vPointInSpace);
    	}


  • 相关阅读:
    整数安全
    Apache HTTP Server 处理连接的方式不能扩展,无法满足互联网不断发展的需求
    UNIX password system
    ws 应用场景
    事件驱动架构在 vivo 内容平台的实践 https://mp.weixin.qq.com/s/DpaSVMXSNomXsXb0dma0CA
    dmesg
    GraphQL及元数据驱动架构在后端BFF中的实践
    指向字符串的指针
    登录加密算法破解秘籍
    内存键值对存储
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3167818.html
Copyright © 2020-2023  润新知