Permalink
Browse files

E3D: More matrix operations and added e3d_transform.

transform tracks inverse matrices as well and can create
view matrices.

Allow all 4x4 matrices multiply points and vectors.
  • Loading branch information...
1 parent f0f6a07 commit 2665625fd67600854ba247c5e5bf4d37140a32c1 @dgud dgud committed Jun 30, 2010
Showing with 307 additions and 6 deletions.
  1. +1 −0 e3d/Makefile
  2. +8 −0 e3d/e3d.hrl
  3. +121 −6 e3d/e3d_mat.erl
  4. +177 −0 e3d/e3d_transform.erl
View
@@ -30,6 +30,7 @@ MODULES= \
e3d_file \
e3d_obj \
e3d_tds \
+ e3d_transform \
e3d_mat \
e3d_util \
e3d_vec \
View
@@ -35,6 +35,14 @@
-type e3d_bbox() :: {e3d_point(), e3d_point()}.
-type e3d_bsphere() :: {e3d_point(), number()}.
-type e3d_bv() :: e3d_bbox() | e3d_bsphere().
+
+%% Types for transform
+-record(e3d_transf,
+ {mat = e3d_mat:identity() :: e3d_matrix(),
+ inv = e3d_mat:identity() :: e3d_matrix()}).
+
+-type e3d_transform() :: #e3d_transf{}.
+
-record(e3d_face,
{vs=[], %List of vertex indices.
View
@@ -13,11 +13,13 @@
-module(e3d_mat).
--export([identity/0,is_identity/1,compress/1,expand/1,
+-export([identity/0,is_identity/1,determinant/1,print/1,
+ compress/1,expand/1,
translate/1,translate/3,scale/1,scale/3,
rotate/2,rotate_to_z/1,rotate_s_to_t/2,
project_to_plane/1,
- transpose/1,mul/2,mul_point/2,mul_vector/2,eigenv3/1]).
+ transpose/1,invert/1,
+ mul/2,mul_point/2,mul_vector/2,eigenv3/1]).
-compile(inline).
-include("e3d.hrl").
@@ -44,7 +46,10 @@ is_identity({_,_,_,_,_,_,_,_,_,_,_,_}) -> false.
compress(identity=I) -> I;
compress({A,B,C,0.0,D,E,F,0.0,G,H,I,0.0,Tx,Ty,Tz,1.0}) ->
- {A,B,C,D,E,F,G,H,I,Tx,Ty,Tz}.
+ {A,B,C,D,E,F,G,H,I,Tx,Ty,Tz};
+compress(Mat)
+ when tuple_size(Mat) =:= 12 ->
+ Mat.
-spec expand(e3d_matrix()) -> e3d_matrix().
@@ -238,7 +243,10 @@ mul({A,B,C,Q0,D,E,F,Q1,G,H,I,Q2,Tx,Ty,Tz,Q3}, {X,Y,Z,W})
{X*A + Y*D + Z*G + W*Tx,
X*B + Y*E + Z*H + W*Ty,
X*C + Y*F + Z*I + W*Tz,
- X*Q0 + Y*Q1 + Z*Q2 + W*Q3}.
+ X*Q0 + Y*Q1 + Z*Q2 + W*Q3};
+mul(M1,M2)
+ when tuple_size(M1) =:= 12; tuple_size(M2) =:= 12 ->
+ mul(expand(M1), expand(M2)).
-spec mul_point(Matrix::e3d_matrix(), Point::e3d_vector()) -> e3d_vector().
@@ -256,7 +264,24 @@ mul_point({A,B,C,0.0,D,E,F,0.0,G,H,I,0.0,Tx,Ty,Tz,1.0}, {X,Y,Z})
is_float(Tx), is_float(Ty), is_float(Tz), is_float(X), is_float(Y), is_float(Z) ->
share(X*A + Y*D + Z*G + Tx,
X*B + Y*E + Z*H + Ty,
- X*C + Y*F + Z*I + Tz).
+ X*C + Y*F + Z*I + Tz);
+mul_point({A,B,C,WX,D,E,F,WY,G,H,I,WZ,Tx,Ty,Tz,WW}, {X,Y,Z})
+ when is_float(A), is_float(B), is_float(C), is_float(D), is_float(E),
+ is_float(F), is_float(G), is_float(H), is_float(I),
+ is_float(Tx), is_float(Ty), is_float(Tz),
+ is_float(WX), is_float(WY), is_float(WZ), is_float(WW),
+ is_float(X), is_float(Y), is_float(Z) ->
+ W = WX*X + WY*Y + WZ * Z + WW,
+ case W =:= 1.0 of
+ true ->
+ share(X*A + Y*D + Z*G + Tx,
+ X*B + Y*E + Z*H + Ty,
+ X*C + Y*F + Z*I + Tz);
+ false ->
+ share((X*A + Y*D + Z*G + Tx)/W,
+ (X*B + Y*E + Z*H + Ty)/W,
+ (X*C + Y*F + Z*I + Tz)/W)
+ end.
-spec mul_vector(Matrix::e3d_matrix(), Vector::e3d_vector()) -> e3d_vector().
@@ -268,14 +293,104 @@ mul_vector({A,B,C,D,E,F,G,H,I,Tx,Ty,Tz}, {X,Y,Z})
share(X*A + Y*D + Z*G,
X*B + Y*E + Z*H,
X*C + Y*F + Z*I);
-mul_vector({A,B,C,0.0,D,E,F,0.0,G,H,I,0.0,Tx,Ty,Tz,1.0}, {X,Y,Z})
+mul_vector({A,B,C,_,D,E,F,_,G,H,I,_,Tx,Ty,Tz,_}, {X,Y,Z})
when is_float(A), is_float(B), is_float(C), is_float(D), is_float(E),
is_float(F), is_float(G), is_float(H), is_float(I),
is_float(Tx), is_float(Ty), is_float(Tz), is_float(X), is_float(Y), is_float(Z) ->
share(X*A + Y*D + Z*G,
X*B + Y*E + Z*H,
X*C + Y*F + Z*I).
+%%--------------------------------------------------------------------
+%% @doc Calculates the determinant
+%% @end
+%%--------------------------------------------------------------------
+-spec determinant(e3d_matrix()) -> float().
+determinant(identity) -> 1.0;
+determinant(Mat)
+ when tuple_size(Mat) =:= 12 ->
+ determinant(e3d_mat:expand(Mat));
+determinant({M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12,M13,M14,M15}) ->
+ A0 = M0*M5 - M1*M4, A1 = M0*M6 - M2*M4,
+ A2 = M0*M7 - M3*M4, A3 = M1*M6 - M2*M5,
+ A4 = M1*M7 - M3*M5, A5 = M2*M7 - M3*M6,
+
+ B0 = M8*M13 - M9*M12, B1 = M8*M14 - M10*M12,
+ B2 = M8*M15 - M11*M12, B3 = M9*M14 - M10*M13,
+ B4 = M9*M15 - M11*M13, B5 = M10*M15 - M11*M14,
+
+ A0*B5 - A1*B4 + A2*B3 + A3*B2 - A4*B1 + A5*B0.
+
+%%--------------------------------------------------------------------
+%% @doc Calculates the inverse matrix
+%% @end
+%%--------------------------------------------------------------------
+-spec invert(e3d_matrix()) -> e3d_matrix().
+invert(identity) -> identity;
+invert(Mat)
+ when tuple_size(Mat) =:= 12 ->
+ invert(e3d_mat:expand(Mat));
+invert({M0, M1, M2, M3,
+ M4, M5, M6, M7,
+ M8, M9, M10,M11,
+ M12,M13,M14,M15}) ->
+ A0 = M0*M5 - M1*M4, A1 = M0*M6 - M2*M4,
+ A2 = M0*M7 - M3*M4, A3 = M1*M6 - M2*M5,
+ A4 = M1*M7 - M3*M5, A5 = M2*M7 - M3*M6,
+
+ B0 = M8*M13 - M9*M12, B1 = M8*M14 - M10*M12,
+ B2 = M8*M15 - M11*M12, B3 = M9*M14 - M10*M13,
+ B4 = M9*M15 - M11*M13, B5 = M10*M15 - M11*M14,
+
+ Det = A0*B5 - A1*B4 + A2*B3 + A3*B2 - A4*B1 + A5*B0,
+ case abs(Det) > 0.00005 of
+ true ->
+ InvDet = 1.0/Det,
+ {(+ M5*B5 - M6*B4 + M7*B3) * InvDet,
+ (- M1*B5 + M2*B4 - M3*B3) * InvDet,
+ (+ M13*A5 - M14*A4 + M15*A3) * InvDet,
+ (- M9*A5 + M10*A4 - M11*A3) * InvDet,
+ (- M4*B5 + M6*B2 - M7*B1) * InvDet,
+ (+ M0*B5 - M2*B2 + M3*B1) * InvDet,
+ (- M12*A5 + M14*A2 - M15*A1) * InvDet,
+ (+ M8*A5 - M10*A2 + M11*A1) * InvDet,
+ (+ M4*B4 - M5*B2 + M7*B0) * InvDet,
+ (- M0*B4 + M1*B2 - M3*B0) * InvDet,
+ (+ M12*A4 - M13*A2 + M15*A0) * InvDet,
+ (- M8*A4 + M9*A2 - M11*A0) * InvDet,
+ (- M4*B3 + M5*B1 - M6*B0) * InvDet,
+ (+ M0*B3 - M1*B1 + M2*B0) * InvDet,
+ (- M12*A3 + M13*A1 - M14*A0) * InvDet,
+ (+ M8*A3 - M9*A1 + M10*A0) * InvDet
+ };
+ false ->
+ exit(singular_matrix)
+ end.
+
+%%--------------------------------------------------------------------
+%% @doc Prints a matrix
+%% @end
+%%--------------------------------------------------------------------
+-spec print(e3d_transform()) -> ok.
+
+print(#e3d_transf{mat=Mat}) ->
+ print_1(Mat);
+print(identity) ->
+ print_1(e3d_mat:identity());
+print(Mat) when tuple_size(Mat) =:= 12;
+ tuple_size(Mat) =:= 16 ->
+ print_1(Mat).
+
+print_1({A,B,C,D,E,F,G,H,I,Tx,Ty,Tz}) ->
+ print_1({A,B,C,0.0,D,E,F,0.0,G,H,I,0.0,Tx,Ty,Tz,1.0});
+print_1(_Mat = {A,B,C,D,E,F,G,H,I,J,K,L,TX,TY,TZ,W}) ->
+ io:format(" ~7.3f ~7.3f ~7.3f ~7.3f~n ~7.3f ~7.3f ~7.3f ~7.3f~n"
+ " ~7.3f ~7.3f ~7.3f ~7.3f~n ~7.3f ~7.3f ~7.3f ~7.3f~n~n",
+ [A,E,I,TX,
+ B,F,J,TY,
+ C,G,K,TZ,
+ D,H,L,W]).
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Calculates Eigenvalues and vectors
%% This is converted from Dave Eberly's MAGIC library
View
@@ -0,0 +1,177 @@
+%%
+%% e3d_transf.erl --
+%%
+%% More transformation matrix utilities
+%%
+%% Copyright (c) 2019 Dan Gudmundsson
+%%
+%% See the file "license.terms" for information on usage and redistribution
+%% of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+%%
+%% $Id$
+%%%
+%%% @doc More transformation matrix utilities
+%%%
+%%% All of the matrix operations operates in the following order
+%%% I = identity(),
+%%% T = translate(I, Vec),
+%%% R = rotate(T, Vec),
+%%% Matrix = Rotate(Translate())
+%%%
+%%% Also using opengl right handed coordinate system
+%%% @end
+-module(e3d_transform).
+
+-export([%% Initilizes matrices
+ identity/0, init/1,
+ lookat/3, ortho/2, perspective/3,
+ %% Get the actual matrices
+ matrix/1, inv_matrix/1,
+ %% Transform the matrices
+ inverse/1, translate/2, rotate/2, rotate/3, scale/2, mul/2
+ ]).
+
+
+-include("e3d.hrl").
+
+%%%-------------------------------------------------------------------
+%%--------------------------------------------------------------------
+%% @doc Returns the identity transform
+%% @end
+%%--------------------------------------------------------------------
+-spec identity() -> e3d_transform().
+identity() ->
+ #e3d_transf{}.
+
+%%--------------------------------------------------------------------
+%% @doc Initilizes transform from matrix mat
+%% @end
+%%--------------------------------------------------------------------
+-spec init(e3d_matrix()) -> e3d_transform().
+init(Mat) when tuple_size(Mat) =:= 12 ->
+ init(e3d_mat:expand(Mat));
+init(Mat) ->
+ #e3d_transf{mat=Mat, inv=e3d_mat:invert(Mat)}.
+
+%%--------------------------------------------------------------------
+%% @doc Returns the matrix
+%% @end
+%%--------------------------------------------------------------------
+-spec matrix(e3d_transform()) -> e3d_matrix().
+matrix(#e3d_transf{mat=M}) -> M.
+
+%%--------------------------------------------------------------------
+%% @doc Returns the inverse matrix
+%% @end
+%%--------------------------------------------------------------------
+-spec inv_matrix(e3d_transform()) -> e3d_matrix().
+inv_matrix(#e3d_transf{inv=I}) -> I.
+
+%%%-------------------------------------------------------------------
+
+
+%%--------------------------------------------------------------------
+%% @doc Inverses the transform
+%% @end
+%%--------------------------------------------------------------------
+-spec inverse(e3d_transform()) -> e3d_transform().
+inverse(#e3d_transf{mat=M, inv=I}) ->
+ #e3d_transf{mat=I, inv=M}.
+
+%%--------------------------------------------------------------------
+%% @doc Translates the matrix with vector
+%% @end
+%%--------------------------------------------------------------------
+-spec translate(e3d_transform(), e3d_vector()) -> e3d_transform().
+translate(#e3d_transf{mat=M,inv=I}, {Dx,Dy,Dz}) ->
+ #e3d_transf{mat = e3d_mat:mul(e3d_mat:translate(Dx,Dy,Dz), M),
+ inv = e3d_mat:mul(I, e3d_mat:translate(-Dx,-Dy,-Dz))}.
+
+%%--------------------------------------------------------------------
+%% @doc Rotates the matrix with rotation matrix
+%% @end
+%%--------------------------------------------------------------------
+-spec rotate(e3d_transform(), e3d_matrix()) -> e3d_transform().
+rotate(#e3d_transf{mat=M,inv=I}, Rot)
+ when tuple_size(Rot) =:= 12; tuple_size(Rot) =:= 16 ->
+ #e3d_transf{mat = e3d_mat:mul(Rot, M),
+ inv = e3d_mat:mul(I, e3d_mat:transpose(Rot))}.
+
+%%--------------------------------------------------------------------
+%% @doc Rotates the matrix with angle (in degress) and direction
+%% @end
+%%--------------------------------------------------------------------
+-spec rotate(e3d_transform(), number(), e3d_vector()) -> e3d_transform().
+rotate(Mat = #e3d_transf{}, A, Vec) ->
+ rotate(Mat, e3d_mat:rotate(A,Vec)).
+
+%%--------------------------------------------------------------------
+%% @doc Scales the matrix with {ScaleX, ScaleY, ScaleZ}
+%% @end
+%%--------------------------------------------------------------------
+-spec scale(e3d_transform(), e3d_vector()) -> e3d_transform().
+scale(#e3d_transf{mat=M,inv=I}, {X,Y,Z}) ->
+ #e3d_transf{mat = e3d_mat:mul(e3d_mat:scale(X,Y,Z), M),
+ inv = e3d_mat:mul(I, e3d_mat:scale(1/X,1/Y,1/Z))}.
+
+%%--------------------------------------------------------------------
+%% @doc Multiplies the current matrix with Mat
+%% Trans(Vec) = Mat(Current(Vec))
+%% @end
+%%--------------------------------------------------------------------
+-spec mul(e3d_transform(), e3d_transform()) -> e3d_transform().
+mul(#e3d_transf{mat=M1,inv=I1}, #e3d_transf{mat=M2,inv=I2}) ->
+ #e3d_transf{mat = e3d_mat:mul(M2, M1), inv = e3d_mat:mul(I1, I2)}.
+
+%%%-------------------------------------------------------------------
+
+%%--------------------------------------------------------------------
+%% @doc Generates a world to camera transformation
+%% @end
+%%--------------------------------------------------------------------
+-spec lookat(e3d_point(), e3d_vector(), e3d_vector()) -> e3d_transform().
+lookat(Pos, Look, Up) ->
+ Dir = e3d_vec:norm_sub(Look, Pos),
+ Right = e3d_vec:norm(e3d_vec:cross(Dir, Up)),
+ NewUp = e3d_vec:norm(e3d_vec:cross(Right, Dir)),
+ AsList = [tuple_to_list(Right), 0.0,
+ tuple_to_list(NewUp), 0.0,
+ tuple_to_list(Dir), 0.0,
+ tuple_to_list(e3d_vec:neg(Pos)), 1.0],
+ CamToWorld = list_to_tuple(lists:flatten(AsList)),
+ WorldToCam = e3d_mat:invert(CamToWorld),
+ #e3d_transf{mat=WorldToCam,inv=CamToWorld}.
+
+%%--------------------------------------------------------------------
+%% @doc Generates a ortho transformation
+%% @end
+%%--------------------------------------------------------------------
+-spec ortho(float(), float()) -> e3d_transform().
+ortho(Near, Far) ->
+ Trans = translate(identity(), {0.0, 0.0, -Near}),
+ scale(Trans, {1.0, 1.0, 1.0/ (Far-Near)}).
+
+%%--------------------------------------------------------------------
+%% @doc Generates a perspective transformation
+%% Fov = Field Of View (in degrees)
+%% Projects from camera space: Z = {-near, -far}
+%% to screen space: Z' = {0.0, 1.0}
+%% @end
+%%--------------------------------------------------------------------
+-spec perspective(Fov::float(), float(), float()) -> e3d_transform().
+perspective(Fov, Near, Far) ->
+ T = 1.0 / math:tan((Fov*math:pi()/180)/2.0),
+ %% Perform projective divide
+ D = 1.0 / (Far-Near), %% Inverted Denom
+ I = 1.0, O = 0.0,
+ Persp = {I,O,O,O,
+ O,I,O,O,
+ O,O, -Far*D,-I,
+ O,O, -Far*Near*D, O},
+ InvPersp = e3d_mat:invert(Persp),
+ e3d_transform:scale(#e3d_transf{mat=Persp, inv=InvPersp},
+ {T,T,1.0}).
+
+%%%-------------------------------------------------------------------
+
+

0 comments on commit 2665625

Please sign in to comment.