Code archives/3D Graphics - Maths/3D Math Functions

This code has been declared by its author to be Public Domain code.

Download source code

3D Math Functions by AndrewT2010
As of OpenGL 3.0, all matrix functions are deprecated. This means that you must handle all of your transformations by yourself. This library is designed to help with that.

Edited, 6/16/10, fixed some bugs and contained it within a GLMath UDT, because I plan on adding some vector functions soon. I also added an 'ExtractNormalMatrix()' to assist with GLSL.

Edited, 6/17/10, added PrintMatrix(), added most of the vector functions, will add the rest later, plus hopefully quaternions.
Rem
3D Math Library

By Andrew Thorwall (AndrewT)

-----------------------------------------------------------------------
INFORMATION
-----------------------------------------------------------------------

As of OpenGL 3.0, the built-in matrix functions are deprecated. This means you must handle
matrices yourself. This library is designed to help you do that.

Note that there is no Matrix UDT. Matrices must be defined as 16-element Float arrays, i.e. Local MyMatrix:Float[16].

The matrices are serialized in column-major format, i.e

   1   5   9   13

   2   6   10  14

   3   7   11  15

   4   8   12  16

-----------------------------------------------------------------------
USING IT WITH OPENGL
-----------------------------------------------------------------------

As previously mentioned, ALL of the matrix functinos are deprecated.
This includes glLoadMatrix{fd}(). In addition to this, the fixed-function
pipeline is also deprecated. This means you must use vertex shaders to transform your
vertices. In order to let your shader have access to your transformation
and projection matrices, you must declare appropriate uniforms in your shader
and set them from your OpenGL program with glUniformMatrix4fv(). The first paremeter
is the location of the uniform in your shader (get it with glGetUniformLocation), 
the second parameter is the number of matrices you're modifying (usually one,
unless you're modifying an array of matrices), the third parameter specifies whether or not
you want OpenGL to use the transpose of your matrix (only use this if you've used row-major matrices
for some reason, set it to false if you're using this library), and a pointer to the first
value of your matrix.

-----------------------------------------------------------------------
FUNCTION LIST
-----------------------------------------------------------------------

IdentityMatrix()

SetMatrix()
GetMatrix()

CopyMatrix()
AddMatrix()
SubtractMatrix()
MultiplyMatrix()

TranslationMatrix()
TranslateMatrix()

XRotationMatrix()
YRotationMatrix()
ZRotationMatrix()
XRotateMatrix()
YRotateMatrix()
ZRotateMatrix()

ScalingMatrix()
XYZScalingMatrix()
ScaleMatrix()
XYZScaleMatrix()

RHPerspectiveProjectionMatrix()

ExtractNormalMatrix()

PrintMatrix()

NewVector2()
NewVector3()
NewVector4()

AddVector()
AddVectorScalar()
SubtractVector()
SubtractVectorScalar()
MultiplyVector()
MultiplyVectorScalar()
DivideVector()
DivideVectorScalar()

VectorDotProduct()
VectorCrossProduct()

VectorMagnitude()
NormalizeVector()

ProjectVector()

MultiplyMatrixVector()


----------------------------------------------------------------------------------
Diffference between MatTranslation/Rotation/Scaling and MatTranslate/Rotate/Scale
----------------------------------------------------------------------------------

Some of the functions are similarly named and it may be difficult To know which does what.
MatTranslationMatrix(), Mat*RotationMatrix(), And MatScalingMatrix() build transformation matrices according
To the parameters you provide. However, MatTranslateMatrix(), Mat*RotateMatrix(), And MatScaleMatrix() all operate
on existing matrices--they first build the correct matrix, Then multiply the existing matrix by it. These functions are
similar To what OpenGL does--you only have transformation matrix (ModelView matrix) And you just perform different operations
on it, whereas with the former three functions you have a different matrix returned from each Function, And you have To multiply
them all together.

EndRem


Type GLMath

	'********************************************************************************************
	' MATRICES
	'********************************************************************************************
	Method IdentityMatrix:Float[]()
		Local Mat:Float[16]
		Mat[0] = 1.0
		Mat[5] = 1.0
		Mat[10] = 1.0
		Mat[15] = 1.0
		Return Mat
	EndMethod
	'Sets an element.
	Method SetMatrix(Mat:Float[], Row:Int, Column:Int, Val:Float)
		Mat[Column * 4 + Row] = Val
	EndMethod
	
	'Gets an element.
	
	Method GetMatrix:Float(Mat:Float[], Row:Int, Column:Int)
		Return Mat[Column * 4 + Row]
	EndMethod
	
	'Copies a matrix.
	
	Method CopyMatrix:Float[](Mat:Float[])
		Local Mat2:Float[16]
		MemCopy(Mat2, Mat, SizeOf(Mat))
		Return Mat2
	EndMethod
	
	'Returns Mat1 + Mat2.
	
	Method AddMatrix:Float[](Mat1:Float[], Mat2:Float[])
		Local MatResult:Float[16]
		For Local I:Int = 0 To 15
			MatResult[I] = Mat1[I] + Mat2[I]
		Next
		Return MatResult
	EndMethod
	
	'Returns Mat1 - Mat2.
	
	Method SubtractMatrix:Float[](Mat1:Float[], Mat2:Float[])
		Local MatResult:Float[16]
		For Local I:Int = 0 To 15
			MatResult[I] = Mat1[I] - Mat2[I]
		Next
		Return MatResult
	EndMethod
	
	'Returns Mat1 * Mat2.
	
	Method MultiplyMatrix:Float[](Mat1:Float[], Mat2:Float[])
		Local MatResult:Float[16]
		For Local X:Int = 0 To 3
			For Local Y:Int = 0 To 3
				For Local I:Int = 0 To 3
					MatResult[X * 4 + Y] :+ Mat1[I * 4 + Y] * Mat2[X * 4 + I]
				Next
			Next
		Next
		Return MatResult
	EndMethod
	
	'Builds a translation matrix.
	
	Method TranslationMatrix:Float[](X:Float, Y:Float, Z:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		MatResult[12] = X
		MatResult[13] = Y
		MatResult[14] = Z
		Return MatResult
	EndMethod
	
	'Translates the matrix.
	
	Method TranslateMatrix(Mat:Float[] Var, X:Float, Y:Float, Z:Float)
		Local Mat2:Float[16]
		Mat2 = TranslationMatrix(X, Y, Z)
		Mat = MultiplyMatrix(Mat, Mat2)
	EndMethod
	
	'Builds an x-axis rotation matrix.
	
	Method XRotationMatrix:Float[](Ang:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		SetMatrix(MatResult, 1, 1, Cos(Ang))
		SetMatrix(MatResult, 2, 2, Cos(Ang))
		SetMatrix(MatResult, 2, 1, -Sin(Ang))
		SetMatrix(MatResult, 1, 2, Sin(Ang))
		Return MatResult
	EndMethod
	
	'Build a y-axis rotation matrix.
	
	Method YRotationMatrix:Float[](Ang:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		SetMatrix(MatResult, 0, 0, Cos(Ang))
		SetMatrix(MatResult, 2, 2, Cos(Ang))
		SetMatrix(MatResult, 0, 2, -Sin(Ang))
		SetMatrix(MatResult, 2, 0, Sin(Ang))
		Return MatResult
	EndMethod	
	
	'Builds a z-axis rotation matrix.
	
	Method ZRotationMatrix:Float[](Ang:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		SetMatrix(MatResult, 0, 0, Cos(Ang))
		SetMatrix(MatResult, 1, 1, Cos(Ang))
		SetMatrix(MatResult, 1, 0, -Sin(Ang))
		SetMatrix(MatResult, 0, 1, Sin(Ang))
		Return MatResult
	EndMethod
	
	'Rotates the matrix on the x-axis.
	
	Method XRotateMatrix(Mat:Float[] Var, Ang:Float)
		Local Mat2:Float[16]
		Mat2 = XRotationMatrix(Ang)
		Mat = MultiplyMatrix(Mat, Mat2)
	EndMethod
	
	'Rotates the matrix on the y-axis.
	
	Method YRotateMatrix(Mat:Float[] Var, Ang:Float)
		Local Rot:Float[16]
		Rot = YRotationMatrix(Ang)
		Mat = MultiplyMatrix(Mat, Rot)
	EndMethod
	
	'Rotates the matrix on the z-axis.
	
	Method ZRotateMatrix(Mat:Float[] Var, Ang:Float)
		Local Mat2:Float[16]
		Mat2 = ZRotationMatrix(Ang)
		Mat = MultiplyMatrix(Mat, Mat2)
	EndMethod
	
	'Builds a scaling matrix with the same scale for each axis.
	
	Method ScalingMatrix:Float[](S:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		SetMatrix(MatResult, 0, 0, S)
		SetMatrix(MatResult, 1, 1, S)
		SetMatrix(MatResult, 2, 2, S)
		Return MatResult
	EndMethod
	
	'Builds a scaling matrix, with a different scale for each axis.
	
	Method XYZScalingMatrix:Float[](SX:Float, SY:Float, SZ:Float)
		Local MatResult:Float[16]
		MatResult = IdentityMatrix()
		SetMatrix(MatResult, 0, 0, SX)
		SetMatrix(MatResult, 1, 1, SY)
		SetMatrix(MatResult, 2, 2, SZ)
		Return MatResult
	EndMethod
	
	'Scales a matrix by the same amount on each axis.
	
	Method ScaleMatrix(Mat:Float[] Var, S:Float)
		Local Mat2:Float[16]
		Mat2 = ScalingMatrix(S)
		Mat = MultiplyMatrix(Mat, Mat2)
	EndMethod
	
	'Scales a matrix, with the option of choosing different scales for different axes.
	
	Method XYZScaleMatrix(Mat:Float[] Var, SX:Float, SY:Float, SZ:Float)
		Local Mat2:Float[16]
		Mat2 = XYZScalingMatrix(SX, SY, SZ)
		Mat = MultiplyMatrix(Mat, Mat2)
	EndMethod
	
	'Builds a right-handed perspective projection matrix.
	
	Method RHPerspectiveProjectionMatrix:Float[](FOV:Float, Aspect:Float, ZNear:Float, ZFar:Float)
		Local MatResult:Float[16]
		
		Local Top:Float = ZNear * Tan(FOV / 2.0)
		Local Bottom:Float = -Top
		Local _Right:Float = Top * Aspect
		Local _Left:Float = -_Right
	
		MatResult[0]  = (2 * ZNear) / (_Right - _Left)
		MatResult[1]  = 0
		MatResult[2]  = 0
		MatResult[3]  = 0
		
		MatResult[4]  = 0
		MatResult[5]  = (2 * ZNear) / (Top - Bottom)
		MatResult[6]  = 0
		MatResult[7]  = 0
		
		MatResult[8]  = (_Right + _Left) / (_Right - _Left)
		MatResult[9]  = (Top + Bottom) / (Top - Bottom)
		MatResult[10] = -(ZFar + ZNear) / (ZFar - ZNear)
		MatResult[11] = -1
		
		MatResult[12] = 0
		MatResult[13] = 0
		MatResult[14] = -(2 * ZFar * ZNear) / (ZFar - ZNear)
		MatResult[15] = 0
	
		Return MatResult
	EndMethod
	
	'Extracts the normal matrix from a modelview matrix, i.e. the transpose of the inverse of the upper-left 3x3
	'sub-matrix of our modelview matrix.
	
	Method ExtractNormalMatrix:Float[](Mat:Float[])
		'Our normal matrix. It's 3x3 instead of 4x4.
		Local NM:Float[9]
		
		'Now we set its values to the upper-left corner of our modelview matrix.
		NM[0] = Mat[0]
		NM[1] = Mat[1]
		NM[2] = Mat[2]
		
		NM[3] = Mat[4]
		NM[4] = Mat[5]
		NM[5] = Mat[6]
		
		NM[6] = Mat[8]
		NM[7] = Mat[9]
		NM[8] = Mat[10]
		
		'Now we find the determinant.
		Local Det:Float = ..
			NM[0] * (NM[4] * NM[8] - NM[5] * NM[7]) - ..
			NM[3] * (NM[1] * NM[8] - NM[2]* NM[7]) + ..
			NM[6] * (NM[1] * NM[5] - NM[2] * NM[4])
			
		'Recipricate.
		Local RecDet:Float = 1 / Det
		
		'Now we're going to do two steps in one. We are going
		'calculate the adjugate matrix and multiply it by the reciprocal
		'of the determinant in the same step.
		Local Adj:Float[9]
		Adj[0] = (NM[4] * NM[8] - NM[5] * NM[7]) * RecDet
		Adj[1] = (NM[1] * NM[8] - NM[2] * NM[7]) * RecDet
		Adj[2] = (NM[1] * NM[5] - NM[2] * NM[4]) * RecDet
		
		Adj[3] = (NM[3] * NM[8] - NM[5] * NM[6]) * RecDet
		Adj[4] = (NM[0] * NM[8] - NM[2] * NM[6]) * RecDet
		Adj[5] = (NM[0] * NM[5] - NM[2] * NM[3]) * RecDet
		
		Adj[6] = (NM[3] * NM[7] - NM[4] * NM[6]) * RecDet
		Adj[7] = (NM[0] * NM[7] - NM[1] * NM[6]) * RecDet
		Adj[8] = (NM[0] * NM[4] - NM[1] * NM[3]) * RecDet
		
		'Now we return it.
		Return Adj
	EndMethod
	
	Method PrintMatrix(Mat:Float[])
		Local MatStr:String
		For Local I:Int = 0 To 15
			If I Mod 4 = 0
				MatStr :+ Chr(10)
			EndIf
			MatStr :+ Mat[I]
			If I Mod 4 <> 3
				MatStr :+ ", "
			EndIf
		Next
		Print MatStr
	EndMethod
	'----------------------------------------------------------------------------------------------------------------------------
	'VECTORS
	'----------------------------------------------------------------------------------------------------------------------------
	
	'Creates a 2-dimensional vector.
	
	Method NewVector2:Float[](X:Float, Y:Float)
		Local Vec:Float[2]
		Vec[0] = X
		Vec[1] = Y
		Return Vec
	EndMethod
	
	'Creates a 3-dimensional vector.
	
	Method NewVector3:Float[](X:Float, Y:Float, Z:Float)
		Local Vec:Float[3]
		Vec[0] = X
		Vec[1] = Y
		Vec[2] = Z
		Return Vec
	EndMethod
	
	'Creates a 4-dimensional vector.
	
	Method NewVector4:Float[](X:Float, Y:Float, Z:Float, W:Float)
		Local Vec:Float[4]
		Vec[0] = X
		Vec[1] = Y
		Vec[2] = Z
		Vec[3] = W
		Return Vec
	EndMethod
	
	'Adds two vectors together.
	
	Method AddVector:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[Len(V1)]
		For Local I:Int = 0 To Len(V1) - 1
			VecResult[I] = V1[I] + V2[I]
		Next
		Return VecResult
	EndMethod
	
	'Adds a scalar.
	
	Method AddVectorScalar:Float[](V:Float[], S:Float)
		Local VecResult:Float[Len(V)]
		For Local I:Int = 0 To Len(V) - 1
			VecResult[I] = V[I] + S
		Next
		Return VecResult
	EndMethod
	
	'Subtracts two vectors.
	
	Method SubtractVector:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[Len(V1)]
		For Local I:Int = 0 To Len(V1) - 1
			VecResult[I] = V1[I] - V2[I]
		Next
		Return VecResult
	EndMethod
	
	'Subtracts a scalar.
	
	Method SubtractVectorScalar:Float[](V:Float[], S:Float)
		Local VecResult:Float[Len(V)]
		For Local I:Int = 0 To Len(V) - 1
			VecResult[I] = V[I] - S
		Next
		Return VecResult
	EndMethod
	
	'Multiplies by a vector. NOTE: This function just multiplies straight across,
	'if you want to do something different look at VectorDotProduct/CrossProduct.
	
	Method MultiplyVector:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[Len(V1)]
		For Local I:Int = 0 To Len(V1) - 1
			VecResult[I] = V1[I] * V2[I]
		Next
		Return VecResult
	EndMethod
	
	'Mutliplies by a scalar.
	
	Method MultiplyVectorScalar:Float[](V:Float[], S:Float)
		Local VecResult:Float[Len(V)]
		For Local I:Int = 0 To Len(V) - 1
			VecResult[I] = V[I] * S
		Next
		Return VecResult
	EndMethod
	
	'Divides by a vector. Just divides straight across.
	
	Method DivideVector:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[Len(V1)]
		For Local I:Int = 0 To Len(V1) - 1
			VecResult[I] = V1[I] / V2[I]
		Next
		Return VecResult
	EndMethod
	
	'Divides by a scalar.
	
	Method DivideVectorScalar:Float[](V:Float[], S:Float)
		Local VecResult:Float[Len(V)]
		For Local I:Int = 0 To Len(V) - 1
			VecResult[I] = V[I] / S
		Next
		Return VecResult
	EndMethod
	
	'Returns the dot product.
	
	Method VectorDotProduct:Float(V1:Float[], V2:Float[])
		Local Result:Float
		For Local I:Int = 0 To Len(V1) - 1
			Result :+ V1[I] * V2[I]
		Next
		Return Result
	EndMethod
	
	'Returns cross product. Vector must be 3D.
	
	Method VectorCrossProduct:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[3]
		VecResult[0] = V1[1] * V2[2] - V2[1] * V1[2]
		VecResult[1] = V1[2] * V2[0] - V1[0] * V2[2]
		VecResult[2] = V1[0] * V2[1] - V1[1] * V2[0]
		Return VecResult
	EndMethod
	
	'Returns the magnitude of a vector.
	
	Method VectorMagnitude:Float(V:Float[])
		Local SumSq:Float
		For Local I:Int = 0 To Len(V) - 1
			SumSq :+ V[I] * V[I]
		Next
		Return Sqr(SumSq)
	EndMethod
	
	'Returns a normalized vector.
	
	Method NormalizeVector:Float[](V:Float[])
		Local VecResult:Float[Len(V)]
		Local M:Float = VectorMagnitude(V)
		For Local I:Int = 0 To Len(V) - 1
			VecResult[I] = V[I] / M
		Next
		Return VecResult
	EndMethod
	
	'Projects vector A onto vector B.
	
	Method ProjectVector:Float[](V1:Float[], V2:Float[])
		Local VecResult:Float[Len(V1)]
		VecResult = MultiplyVectorScalar(V2, VectorDotProduct(V1, V2) / (VectorMagnitude(V2) ^ 2))
		Return VecResult
	EndMethod
	
	'Multiplies a matrix by a vector and returns the resulting vector.
	'The vector must be 4D.
	
	Method MultiplyMatrixVector:Float[](Mat:Float[], Vec:Float[])
		Local VecResult:Float[4]
		
		For Local E:Int = 0 To 3
			For Local I:Int = 0 To 3
				VecResult[E] :+ Mat[E + I * 4] * Vec[I]
			Next
		Next
		Return VecResult
	EndMethod

EndType

Comments

Kryzon2013
Wikipedia enlightens that the projection method...
Method ProjectVector:Float[](V1:Float[], V2:Float[])
	Local VecResult:Float[Len(V1)]
	VecResult = MultiplyVectorScalar(V2, VectorDotProduct(V1, V2) / (VectorMagnitude(V2) ^ 2))
	Return VecResult
EndMethod
...can be optimized with the following:
Method ProjectVector:Float[](V1:Float[], V2:Float[])
	Local VecResult:Float[Len(V1)]
	VecResult = MultiplyVectorScalar(V2, VectorDotProduct(V1, V2) / VectorDotProduct(V2, V2))
	Return VecResult
EndMethod

A vector's squared magnitude is equal to the Dot product of this vector with itself.


Code Archives Forum