1 /**
2 dlsl.trackball
3 
4 
5 Authors: Peter Particle
6 License: MIT
7 
8 Note: All methods marked with pure are weakly pure since, they all access an instance member.
9 All static methods are strongly pure.
10 */
11 
12 module dlsl.trackball;
13 
14 import dlsl.vector;
15 import dlsl.matrix;
16 //import dlsl.quaternion;
17 
18 const float	deg2rad	= 0.0174532925199432957692369076849f;
19 const float	rad2deg	= 57.295779513082320876798154814105f;
20 
21 
22 
23 
24 struct Trackball {
25 nothrow:
26 private:
27 	float	m_phi			= 0.0f;
28 	float	m_theta			= 0.0f;
29 	float	m_dolly			= 10.0f;
30 
31 	float	m_velXform		= 1;
32 	float	m_velOrbit		= 1;
33 	float	m_velDolly		= 1;
34 
35 	float	m_absX 			= 0.0f;
36 	float	m_absY			= 0.0f;   
37 
38 	mat4	m_matrix 		= mat4.identity;
39 
40 	vec3 	m_target 		= vec3( 0 );
41 	float	m_recipFocus	= 1.0f;
42 	float	m_recipHeight	= 0.01f;
43 	bool	m_dirty			= false;	
44 
45 	void abs2rel( ref float x, ref float y ) {
46 		float ax = m_absX;
47 		float ay = m_absY;
48 		m_absX = x;
49 		m_absY = y;
50 		x = m_absX - ax;
51 		y = m_absY - ay;
52 		m_dirty = true;
53 	}
54 
55 	void update() {
56 		m_matrix
57 			= mat4.translation( 0, 0, m_dolly )
58 			* mat4.rotationX( - deg2rad * m_theta ) 
59 			* mat4.rotationY( - deg2rad * m_phi )
60 			* mat4.translation( m_target );
61 	}
62 
63 
64 public:
65 	this( vec3 eye, vec3 target = vec3( 0 ), vec3 up = vec3( 0, 1, 0 )) {
66 		lookAt( eye, target, up );
67 	}
68 
69 	void xformVelocity( float vel )		{ m_velXform  = vel; }
70 	void orbitVelocity( float vel )		{ m_velOrbit = vel; }
71 	void dollyVelocity( float vel )		{ m_velDolly = vel; }
72 	mat4 matrix() 						{ m_dirty = false; return m_matrix; }
73 	void focus(  float f )				{ m_recipFocus  = 1 / f; }
74 	void height( float h )				{ m_recipHeight = 2 / h; }
75 	bool dirty()  { return m_dirty; }
76 	//void dirty( bool b ) { m_dirty = b; }
77 
78 	void height_and_focus_from_fovy( float height, float fovy ) {
79 		import std.math : tan;
80 		m_recipHeight	= 2 / height;
81 		m_recipFocus	= - 4 * tan( 0.5f * fovy ) / height;
82 	}
83 
84 	void reference( float x, float y )	{
85 		m_absX = x;
86 		m_absY = y;
87 	}		// set 2D reference point for each navigation gesture (e.g. any click)
88 
89 	mat4 viewMatrix() {
90 		m_dirty = false; return m_matrix; 
91 	}
92 
93 
94 	void orbit( float x, float y ) {
95 		abs2rel( x, y );
96 		m_phi += m_velOrbit * x;
97 		m_theta += m_velOrbit * y;
98 		update;
99 	}
100 
101 
102 	void xform( float x, float y ) {
103 		abs2rel( x, y );
104 		//float d = abs( camDolly );	/// Tweak the distance to Rotation center, this Z Value gets rotated by Rotation Matrix
105 		/// Use X and Y Vectors from Rotation Matrix to calculate Camera Offset Parallel to Screen. Both use camRes.x, otherwise different speed in x and y !
106 		m_target += m_velXform * m_recipFocus * m_recipHeight * m_dolly * ( x * vec3( m_matrix[0].x, m_matrix[1].x, m_matrix[2].x ) - y * vec3( m_matrix[0].y, m_matrix[1].y, m_matrix[2].y )); // Both use camRes.x, otherwise different speed in x and y
107 		update;
108 	}
109 
110 
111 	void dolly( float x, float y ) {
112 		abs2rel( x, y );
113 		float d  = m_dolly;
114 		m_dolly -= m_velDolly * m_recipHeight * m_recipFocus * m_dolly * ( x + y );
115 		if ( m_dolly < 0.001f ) m_dolly = 0.001f;
116 		update;
117 		//m_matrix[ 3 ].xyz = m_matrix[ 3 ].xyz * ( m_dolly - dolly );
118 	}
119 
120 	void dolly( float d ) {
121 		m_dolly = d < 0.001f ? 0.001f : d;
122 		m_dirty = true;
123 	}
124 
125 	float  dolly() const {
126 		return m_dolly;
127 	}
128 
129 
130 	/// look at function with two points and an up vector
131 	void lookAt( vec3 eye, vec3 target = vec3( 0 ), vec3 up = vec3( 0, 1, 0 )) {
132 		vec3 vecZ	= eye - target;	// vector from target to eye equals the camera z axis as camera look direction is neagtive z
133 		m_dolly		= length( eye - target );
134 		m_target	= target;
135 		vecZ /= m_dolly;
136 		import std.math : asin, atan2;
137 		m_phi = - rad2deg * atan2( vecZ.x(), vecZ.z());
138 		m_theta = rad2deg * asin( vecZ.y());
139 
140 		// TODO(pp): compute twist from up vector
141 
142 		update;
143 		m_dirty = true; 
144 	}
145 
146 	/// look at function with nine floats representing two points and an up vector
147 	void lookAt( float ex, float ey, float ez, float tx = 0, float ty = 0, float tz = 0, float ux = 0, float uy = 1, float uz = 0 ) {
148 		this.lookAt( vec3( ex, ey, ez ), vec3( tx, ty, tz ), vec3( ux, uy, uz ));
149 	}
150 }
151 
152 
153 /// look at function with two points and an up vector
154 auto lookAt( vec3 eye, vec3 target = vec3( 0 ), vec3 up = vec3( 0, 1, 0 )) {
155 	vec3 vecZ = normalize( eye - target );	// vector from target to eye equals the camera z axis as camera look direction is negative z
156 	vec3 vecX = normalize( cross( up, vecZ ));
157 
158 	// TODO( pp ): fix bellow, matrix constructor
159 	//return mat4( vecX, 0, cross( vecZ, vecX ), 0, vecZ, 0, eye, 1 );
160 	mat4 result;
161 	result[0] = vec4( vecX, 0 );
162 	result[1] = vec4( cross( vecZ, vecX ), 0 );
163 	result[2] = vec4( vecZ, 0 );
164 	result[3] = vec4( eye , 1 );
165 
166 	return result;
167  
168 }
169 
170 /// look at function with nine floats representing two points and an up vector
171 auto lookAt( float ex, float ey, float ez, float tx = 0, float ty = 0, float tz = 0, float ux = 0, float uy = 1, float uz = 0 ) {
172 	return lookAt( vec3( ex, ey, ez ), vec3( tx, ty, tz ), vec3( ux, uy, uz ));
173 }