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 }