1 /** 2 dlsl.dual_quaternion 3 4 5 Authors: Peter Particle 6 License: MIT 7 8 Based on: Ben Kenwright: A Beginners Guide to Dual-Quaternions 9 */ 10 11 module dlsl.dual_quaternion; 12 13 public import dlsl.quaternion; 14 15 16 17 /// Pre-defined quaternion of type float. 18 alias DualQuaternion!( float ) dualQ; 19 20 21 /// If T is a quaternion, this evaluates to true, otherwise false. 22 template isDualQuaternion( T ) { enum isDualQuaternion = is( typeof( isDualQuaternionImpl( T.init ))); } 23 private void isDualQuaternionImpl( T )( Quaternion!( T ) q ) {} 24 alias isDualQ = isDualQuaternion; 25 26 27 /// Base template for all quaternion-types. 28 /// Params: 29 /// type = all values get stored as this type 30 struct DualQuaternion( type ) { 31 32 nothrow @nogc: 33 34 /// Returns the current matrix formatted as flat string. 35 char[] toString( char[] buffer ) { 36 import core.stdc.stdio : sprintf; 37 38 size_t s = 0; 39 s += sprintf( buffer.ptr + s, "[ "); 40 s += r.toString( buffer[ s .. $ ] ).length; 41 42 s += sprintf( buffer.ptr + s, ", "); 43 s += d.toString( buffer[ s .. $ ] ).length; 44 45 s += sprintf( buffer.ptr + s, " ]"); 46 47 return buffer[ 0 .. s ]; 48 } 49 50 51 pure nothrow @nogc: 52 53 /// Returns the pointer to the stored values as OpenGL requires it. 54 /// Note this will return a pointer to a $( RED column - major ) matrix, 55 /// $( RED this is the OpneGL convention and expected in programs via Uniforms or UBOs ). 56 auto ptr() { 57 return data.ptr; 58 } 59 60 61 pure nothrow @nogc @safe: 62 63 union { 64 type[8] data; 65 struct { 66 Quaternion!type r; 67 Quaternion!type d; 68 } 69 } 70 71 alias type valueType; /// Holds the internal type of the vector. 72 alias Quaternion!( type ) quaternionType; 73 74 75 /// Returns an identity vector ( x=0, y=0, z=0, w=1 ). 76 static DualQuaternion identity() { 77 return DualQuaternion( 78 cast( type )0, cast( type )0, cast( type )0, cast( type )1, 79 cast( type )0, cast( type )0, cast( type )0, cast( type )0 80 ); 81 } 82 83 84 /// Construct from eight values of type $( I valueType ) 85 /// first four the real part, rotation quaternion with last value being the w value 86 /// last four being the dual, translation part 87 this()( 88 valueType rx, valueType ry, valueType rz, valueType rw, 89 valueType dx, valueType dy, valueType dz, valueType dw 90 ) { 91 data[0] = rx; data[1] = ry; data[2] = rz; data[3] = rw; 92 data[4] = dx; data[5] = dy; data[6] = dz; data[7] = dw; 93 } 94 95 96 /// Construct from two Quaternions 97 this()( Quaternion!valueType r, Quaternion!valueType d ) { 98 this.r = r.normalize; 99 this.d = d; 100 } 101 102 this()( Quaternion!valueType r, Vector!( valueType, 3 ) v ) { 103 this.r = r.normalize; 104 this.d = Quaternion!valueType( v, 0 ) * this.r * 0.5; 105 // Todo(pp): confirm that the above is an associative operation and the same as bellow 106 // this.d = ( Quaternion!valueType( v, 0 ) * this.r ) * 0.5; 107 } 108 109 110 111 /////////////// 112 // Operators // 113 /////////////// 114 115 /// DualQuternion add or subtract DUalQuaternion 116 DualQuaternion opBinary( string op )( DualQuaternion dq ) const if(( op == "+" ) || ( op == "-" )) { 117 DualQuaternion result; 118 mixin( "result[] = data[] " ~ op ~ " dq[];" ); 119 return result; 120 } 121 122 // Multiplication order - left to right 123 //static DualQuaternion operator * ( DualQuaternion lhs, DualQuaternion rhs ) { 124 // return DualQuaternion( rhs.m_real * lhs.m_real, rhs.m_dual * lhs.m_real + rhs.m_real * lhs.m_dual ); 125 //} 126 127 DualQuaternion opBinary( string op : "*" )( DualQuaternion dq ) const { 128 return DualQuternion( dq.r * r, dq.d * r + dq.r * d ); 129 } 130 131 132 DualQuaternion opBinary( string op : "*" )( valueType s ) const { 133 auto result = this; 134 result[] *= s; 135 return result; 136 //return DualQuaternion( 137 // s * r.x, s * r.y, s * r.z, s * r.w, 138 // s * d.x, s * d.y, s * d.z, s * d.w 139 //); 140 } 141 142 143 Quaternion!valueType rotation() { 144 return r; 145 } 146 147 148 Vector!( valueType, 3 ) translation() { 149 //Quaternion t = dq.m_dual; 150 //t.scaleIt( 2.0 ); 151 //t *= dq.m_real.conjugate(); 152 //return MVector( t.x, t.y, t.z ); 153 return Vector!( valueType, 3 )( d * 2 * dlsl.quaternion.invert( r )); 154 } 155 } 156 157 158 pure nothrow @nogc @safe: 159 160 /// Convenience functions returning the vector as corresponding matrices 161 /// Params: 162 /// q = convert quternion 163 /// Returns: mat4 / mat4x4, mat4x3 164 import dlsl.matrix; 165 auto toMat4( DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4( dq ); } 166 auto toMat4x3( DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4x3( dq ); } 167 auto toMat4x4( DQ )( DQ dq ) if( isDualQuaternion!DQ && is( DQ.valueType : float )) { return mat4x4( dq ); } 168 169 170 /// Dual Quaternion dot product 171 DQ.valueType dot( DQ )( in DQ a, in DQ b ) if( isDualQuaternion!DQ ) { 172 return dot( a.r, b.r ); 173 } 174 175 176 /// Dual Quaternion normalize 177 DQ normalize( DQ )( in DQ dq ) if( isDualQuaternion!DQ ) { 178 auto l = dq.r.lengthSquared; 179 import dlsl.math : almostEqual; 180 if( l == 0 || almostEqual( 1.0f, l, 0.00000001f )) 181 return dq; 182 auto recipLength = 1.0 / l.sqrt; 183 DQ result = dq; 184 result.r *= recipLength; 185 result.d *= recipLength; 186 return result; 187 } 188 189 DQ invert( DQ )( in DQ dq ) if( isDualQuaternion!DQ ) { 190 return DQ( 191 dlsl.quaternion.invert( dq.r ), 192 dlsl.quaternion.invert( dq.d ) 193 ); 194 } alias conjugate = invert; 195